~ubuntu-branches/ubuntu/trusty/ocamlnet/trusty

« back to all changes in this revision

Viewing changes to src/netmulticore/netmcore_heap.ml

  • Committer: Bazaar Package Importer
  • Author(s): Stéphane Glondu
  • Date: 2011-09-02 14:12:33 UTC
  • mfrom: (18.2.3 sid)
  • Revision ID: james.westby@ubuntu.com-20110902141233-zbj0ygxb92u6gy4z
Tags: 3.4-1
* New upstream release
  - add a new NetcgiRequire directive to ease dependency management
    (Closes: #637147)
  - remove patches that were applied upstream:
    + Added-missing-shebang-lines-in-example-shell-scripts
    + Try-also-ocamlc-for-POSIX-threads

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(* $Id: netmcore_heap.ml 1578 2011-04-12 16:20:49Z gerd $ *)
 
2
 
 
3
(* Structure of the heaps:
 
4
 
 
5
   Heaps consist of a root block and a list of extension blocks
 
6
   (which is initially empty). Extension blocks are added when no more
 
7
   memory is available in the blocks so far allocated.  Extension
 
8
   blocks can also be given back to the pool when they become empty
 
9
   again. Blocks are never changed in size. The blocks are managed
 
10
   with a doubly-linked list.
 
11
 
 
12
   Both allocated memory and free memory in the heap must have
 
13
   "Ocaml structure", i.e. there is always a header preceding the
 
14
   value. The header includes the length and the bits for gabage
 
15
   collection.
 
16
 
 
17
   Free memory is also added to free lists if the memory area
 
18
   consists of at least 2 words (1 word free memory can be first
 
19
   reclaimed by the garabage collector). There are several free lists
 
20
   for different sizes of the free areas.
 
21
 
 
22
   When there is no more available memory, a garbage collection is
 
23
   triggered (see below). If this is also not sufficient, a further
 
24
   extension block is added.
 
25
 
 
26
   Garbage collection is done by marking and sweeping, all in one
 
27
   go. We use only the colors "black" and "white". In the sweep
 
28
   phase also the free lists are completely rebuilt. This makes
 
29
   it possible to merge adjacent free memory, and to reclaim
 
30
   one word fragments.
 
31
 
 
32
   Note that custom values (incl. bigarrays, int32, int64, nativeint)
 
33
   cannot live in heaps, because the GC is not able to figure out which
 
34
   mem regions are used by the custom blocks.
 
35
 *)
 
36
 
 
37
(* FIXME: use better locking scheme (r/w locks) *)
 
38
 
 
39
(* FIXME: flag is_white: whether all values are white. Protect against
 
40
   crashes during the mark phase
 
41
 *)
 
42
 
 
43
open Printf
 
44
 
 
45
module Debug = struct
 
46
  let enable = ref false
 
47
end
 
48
 
 
49
let dlog = Netlog.Debug.mk_dlog "Netmcore_heap" Debug.enable
 
50
let dlogr = Netlog.Debug.mk_dlogr "Netmcore_heap" Debug.enable
 
51
 
 
52
let () =
 
53
  Netlog.Debug.register_module "Netmcore_heap" Debug.enable
 
54
 
 
55
 
 
56
type 'a heap =
 
57
    { heap_sem : Netsys_mem.memory;
 
58
      heap_pool : Netmcore.res_id;
 
59
      mutable heap_value : 'a;
 
60
      heap_ext : ext_block;
 
61
      heap_fl : Obj.t array;
 
62
      mutable heap_roots : Obj.t array;
 
63
    }
 
64
 
 
65
(* Repr details:
 
66
 
 
67
   heap_ext is the first extension block (root block). See below for
 
68
   repr details.
 
69
 
 
70
   heap_fl: The free lists of unused Ocaml values. This array starts
 
71
   several free lists: heap_fl.(k) is the free list for values with
 
72
   a size of k words (k > 0). heap_fl.(0) is the free list for values
 
73
   of any size.
 
74
 
 
75
   The free list pointer references the free Obj.t (which is always
 
76
   preceded by a header). The next element of the free list is in 
 
77
   field 0 of the Obj.t.
 
78
 
 
79
   The special value null_obj is used for "None".
 
80
 
 
81
   The values in [heap_roots] are considered as the roots. Values
 
82
   null_obj are ignored in this array. The [heap_roots] array resides
 
83
   in the heap. There is right now no free list providing fast access
 
84
   to unused elements of this array (TBD).
 
85
 
 
86
   Note that the heap_sem bigarray is ok because it is not stored in
 
87
   the value area (bigarrays would be incompatible with our GC).
 
88
 *)
 
89
 
 
90
 
 
91
and ext_block =
 
92
    { mutable ext_prev : int;  (* encoded start address *)
 
93
      mutable ext_next : int;  (* encoded start address *)
 
94
      ext_addr : nativeint;    (* start address of the mem block *)
 
95
      ext_size : int;          (* size of the mem block *)
 
96
      mutable ext_start : int; (* offset of value area start *)
 
97
      ext_end : int;           (* offset of value area end *)
 
98
    }
 
99
 
 
100
(* Repr details:
 
101
 
 
102
   Extension blocks: The first extension block is simply put into the
 
103
   root memory block. The other extension blocks have this format:
 
104
   At the beginning there is the [magic] string, followed by the
 
105
   Ocaml value of the [ext_block] record. The remaining part of the memory
 
106
   block can be used for storing values. This area starts at the
 
107
   offset [ext_start] (relative to the beginning of the mem block), and
 
108
   ends at [ext_end] (i.e. last byte is at [ext_end-1]).
 
109
 
 
110
   ext_prev and ext_next: contain the encoded address of the memory block
 
111
   containing the ext_block we are referring to. The encoding: Shift the
 
112
   address 1 bit to the right, so it fits into an [int] value. The
 
113
   special value [no_ext_block] is used to denote a "none". The special
 
114
   value [root_ext_block] is used to denote the first extension block
 
115
   (remember it does not have a mem block of its own, and is a special
 
116
   case).
 
117
 
 
118
   ext_addr: this is the address of the mem block containing the
 
119
   ext_block record. ext_size is the size of this mem block in bytes.
 
120
 
 
121
   Note that the ext_addr custom value is ok because it is not stored in
 
122
   the value area (custom values would be incompatible with our GC).
 
123
 *)
 
124
 
 
125
type 'a descr = int
 
126
 
 
127
type mutator =
 
128
    { heap : Obj.t heap;
 
129
      mutable alive : bool;
 
130
      mutable pinned : int list;
 
131
    }
 
132
 
 
133
let fl_size = 64
 
134
 
 
135
let null_obj = Obj.repr 0
 
136
 
 
137
let magic = "NETHEAP\n"
 
138
let magic_len = 8
 
139
 
 
140
let min_ext_size = 65536
 
141
  (* min size of an extension block *)
 
142
 
 
143
let max_ext_block = 256
 
144
  (* max space for ext_block record in bytes *)
 
145
 
 
146
let no_ext_block = 0
 
147
  (* used in ext_prev and ext_next *)
 
148
 
 
149
let root_ext_block = 1
 
150
  (* used in ext_prev and ext_next *)
 
151
 
 
152
let n_roots = 20
 
153
  (* initial number of root values *)
 
154
 
 
155
 
 
156
let bytes_per_word =
 
157
  match Sys.word_size with
 
158
    | 32 -> 4
 
159
    | 64 -> 8
 
160
    | _ -> assert false
 
161
 
 
162
let descr_of_heap heap =
 
163
  let addr = heap.heap_ext.ext_addr in
 
164
  Nativeint.to_int(Nativeint.shift_right addr 1)
 
165
 
 
166
let heap_of_descr pool ptr =
 
167
  let addr = Nativeint.shift_left (Nativeint.of_int ptr) 1 in
 
168
  try
 
169
    let size = Netmcore_mempool.size_mem_at_addr pool addr in
 
170
    let mem = Netsys_mem.grab addr size in
 
171
    let u = String.create magic_len in
 
172
    Netsys_mem.blit_memory_to_string mem 0 u 0 magic_len;
 
173
    if u <> magic then raise Not_found;
 
174
    let hoffs_s = String.create 8 in
 
175
    Netsys_mem.blit_memory_to_string mem 8 hoffs_s 0 8;
 
176
    let hoffs =
 
177
      Netnumber.int_of_int8 (Netnumber.HO.read_int8 hoffs_s 0) in
 
178
    Netsys_mem.as_value mem hoffs
 
179
  with
 
180
    | Not_found ->
 
181
        failwith "Netmcore_heap.heap_of_descr: no heap structure found \
 
182
                  at this address"
 
183
 
 
184
let create_mutator heap =       
 
185
  { heap = Obj.magic heap; alive = true; pinned = [] }
 
186
 
 
187
let ext_mem ext =
 
188
  Netsys_mem.grab ext.ext_addr ext.ext_size
 
189
 
 
190
 
 
191
let ext_block heap (ptr:int) : ext_block =
 
192
  (* for following ext_prev and ext_next *)
 
193
  if ptr = no_ext_block then
 
194
    failwith "Netmcore_heap.ext_block: null pointer";
 
195
  if ptr = root_ext_block then
 
196
    heap.heap_ext
 
197
  else (
 
198
    let nat_ptr = Nativeint.shift_left (Nativeint.of_int ptr) 1 in
 
199
    let mem = Netsys_mem.grab nat_ptr max_ext_block in
 
200
    let u = String.create magic_len in
 
201
    Netsys_mem.blit_memory_to_string mem 0 u 0 magic_len;
 
202
    if u <> magic then
 
203
      failwith "Netmcore_heap.ext_block: bad magic";
 
204
    Netsys_mem.as_value mem (magic_len + bytes_per_word)
 
205
  )
 
206
 
 
207
 
 
208
let ptr_to_ext_block heap (ext:ext_block) : int =
 
209
  if ext == heap.heap_ext then
 
210
    root_ext_block
 
211
  else
 
212
    Nativeint.to_int (Nativeint.shift_right ext.ext_addr 1)
 
213
 
 
214
 
 
215
let debug_info heap =
 
216
  let b = Buffer.create 80 in
 
217
  bprintf b "pool = %d\n"
 
218
    (match heap.heap_pool with
 
219
       | `Resource id -> id
 
220
    );
 
221
  bprintf b "value = @0x%nx\n"
 
222
    (Netsys_mem.obj_address (Obj.repr heap.heap_value));
 
223
  for k = 0 to Array.length heap.heap_roots - 1 do
 
224
    if heap.heap_roots.(k) != null_obj then
 
225
      bprintf b "root[%d] = @0x%nx\n"
 
226
        k (Netsys_mem.obj_address heap.heap_roots.(k));
 
227
  done;
 
228
  for k = 0 to Array.length heap.heap_fl - 1 do
 
229
    if heap.heap_fl.(k) != null_obj then
 
230
      bprintf b "fl[%d] = @0x%nx\n"
 
231
        k (Netsys_mem.obj_address heap.heap_fl.(k));
 
232
  done;
 
233
  let p = ref 0 in
 
234
  let ext = ref (Some heap.heap_ext) in
 
235
  while !ext <> None do
 
236
    match !ext with
 
237
      | Some x ->
 
238
          let next =
 
239
            if x.ext_next = no_ext_block then
 
240
              None
 
241
            else
 
242
              Some(ext_block heap x.ext_next) in
 
243
          bprintf b "ext[%d] = @0x%nx, size 0x%x\n"
 
244
            !p x.ext_addr x.ext_size;
 
245
          incr p;
 
246
          ext := next;
 
247
      | None -> assert false
 
248
  done;
 
249
  Buffer.contents b
 
250
 
 
251
 
 
252
let extend_heap heap size =
 
253
  (* Add another extension block to the heap so that a [size] value fits
 
254
     into it
 
255
   *)
 
256
  dlogr (fun () -> sprintf "extend_heap size=%d" size);
 
257
  let req_size = size + max_ext_block in
 
258
  (* N.B. choosing max_ext_block high enough is crucial *)
 
259
  let mem_size = max req_size min_ext_size in
 
260
  let mem = Netmcore_mempool.alloc_mem heap.heap_pool mem_size in
 
261
  try
 
262
    let mem_real_size = Netmcore_mempool.size_mem heap.heap_pool mem in
 
263
    (* mem_real_size >= mem_size! *)
 
264
    Netsys_mem.blit_string_to_memory magic 0 mem 0 magic_len;
 
265
    let old_next = heap.heap_ext.ext_next in
 
266
    let ext_orig =
 
267
      { ext_prev = ptr_to_ext_block heap heap.heap_ext;
 
268
        ext_next = old_next;
 
269
        ext_addr = Netsys_mem.memory_address mem;
 
270
        ext_size = mem_real_size;
 
271
        ext_start = 0; (* later *)
 
272
        ext_end = mem_real_size;
 
273
      } in
 
274
    let (voffs, n) = 
 
275
      Netsys_mem.init_value mem magic_len ext_orig
 
276
        [Netsys_mem.Copy_bigarray; Netsys_mem.Copy_custom_int; 
 
277
         Netsys_mem.Copy_atom] in
 
278
    let ext =
 
279
      Netsys_mem.as_value mem voffs in
 
280
    (* If the block is larger than the typical size of 64K, we initialize
 
281
       it so that only the requested value fits exactly
 
282
     *)
 
283
    ext.ext_start <- 
 
284
      if req_size = mem_size then
 
285
        mem_real_size - size
 
286
      else
 
287
        magic_len + n;
 
288
    assert(ext.ext_start >= magic_len+n);
 
289
    heap.heap_ext.ext_next <- ptr_to_ext_block heap ext;
 
290
    if old_next <> no_ext_block then
 
291
      (ext_block heap old_next).ext_prev <- ptr_to_ext_block heap ext;
 
292
    dlogr (fun () -> sprintf "extent_heap addr=%nx real_size=%d usable=%d"
 
293
             ext.ext_addr ext.ext_size (ext.ext_end - ext.ext_start));
 
294
    Some(mem, ext.ext_start, ext.ext_end - ext.ext_start)
 
295
  with
 
296
    | error ->
 
297
        Netmcore_mempool.free_mem heap.heap_pool mem;
 
298
        raise error
 
299
 
 
300
let shrink_heap heap ext =
 
301
  (* Remove ext from the chaining, and give the mem block back to the pool *)
 
302
  dlogr (fun () -> sprintf "shrink_heap addr=%nx" ext.ext_addr);
 
303
  assert (ext != heap.heap_ext);
 
304
  let mem = Netsys_mem.grab ext.ext_addr ext.ext_size in
 
305
  let u = String.create magic_len in
 
306
  Netsys_mem.blit_memory_to_string mem 0 u 0 magic_len;
 
307
  if u <> magic then
 
308
    failwith "Netmcore_heap.shrink_heap";
 
309
  let v = String.make magic_len ' ' in
 
310
  Netsys_mem.blit_string_to_memory v 0 mem 0 magic_len;
 
311
  (* ext is never the first element in the chain *)
 
312
  let prev = ext_block heap ext.ext_prev in
 
313
  prev.ext_next <- ext.ext_next;
 
314
  if ext.ext_next <> no_ext_block then (
 
315
    let next = ext_block heap ext.ext_next in
 
316
    next.ext_prev <- ext.ext_prev
 
317
  );
 
318
  Netmcore_mempool.free_mem heap.heap_pool mem;
 
319
  dlog "shrink_heap done"
 
320
 
 
321
 
 
322
let init_as_block mem offs size =
 
323
  let words = size / bytes_per_word in
 
324
  assert(words >= 2);
 
325
  Netsys_mem.init_header mem offs Obj.string_tag (*block size:*)(words-1)
 
326
 
 
327
 
 
328
let init_as_atom mem offs =
 
329
  Netsys_mem.init_header mem offs (*tag:*)0 (*block size:*)0
 
330
 
 
331
 
 
332
let del_in_fl heap (entry:Obj.t) (prev:Obj.t) =
 
333
  let next = Obj.field entry 0 in
 
334
  if prev == null_obj then (
 
335
    (* it must be one of the root pointers *)
 
336
    for k=0 to fl_size-1 do
 
337
      if heap.heap_fl.(k) == entry then
 
338
        heap.heap_fl.(k) <- next
 
339
    done
 
340
  )
 
341
  else
 
342
    Obj.set_field prev 0 next
 
343
 
 
344
 
 
345
let add_to_fl heap mem offs len =
 
346
  let words = len / bytes_per_word in
 
347
  let k = if words < fl_size then words else 0 in
 
348
  let old_head = heap.heap_fl.(k) in
 
349
  init_as_block mem offs len;
 
350
  let v = Netsys_mem.as_value mem (offs + bytes_per_word) in
 
351
  let o = Obj.repr v in
 
352
  Obj.set_field o 0 old_head;
 
353
  heap.heap_fl.(k) <- o
 
354
 
 
355
 
 
356
let do_gc heap =
 
357
  (* Our assumption is that all values have the GC color "white".
 
358
     
 
359
     Mark phase: Iter over all roots. For each root, visit the referenced
 
360
     values, and set all values to the GC color "black" if they are visited
 
361
     for the first time.
 
362
 
 
363
     Sweep phase: Iter over all extension blocks. Iter over all values
 
364
     in an extension block. "White" values are added to the freelist.
 
365
     "Black" values are changed to "white" again, but remain otherwise
 
366
     untouched. 
 
367
 
 
368
     The freelists are rebuilt during sweep. We check for the special
 
369
     case that an extension block is completely empty - in this case
 
370
     it is entirely removed.
 
371
   *)
 
372
  dlog "gc start";
 
373
 
 
374
  let res = Netmcore.get_resource heap.heap_pool in
 
375
  let (start_addr, end_addr) =
 
376
    match res#repr with
 
377
      | `Posix_shm_preallocated(_,mem) -> 
 
378
          let mem_size = Bigarray.Array1.dim mem in
 
379
          let sa = Netsys_mem.memory_address mem in
 
380
          let ea = Nativeint.add sa (Nativeint.of_int mem_size) in
 
381
          (sa, ea)
 
382
      | _ -> 
 
383
          assert false in
 
384
  
 
385
  dlogr (fun () ->
 
386
           sprintf "range: 0x%nx - 0x%nx" start_addr end_addr);
 
387
 
 
388
  let debug_addr = Hashtbl.create 20 in
 
389
  (* For debugging [mark] *)
 
390
 
 
391
  let rec mark (v:Obj.t) =
 
392
    (* FIXME: this recursion can cause stack overflows *)
 
393
    if Obj.is_block v then (
 
394
      let a = Netsys_mem.hdr_address v in
 
395
      (* We do not follow blocks that are outside the shm area. In general,
 
396
         such out-of-shm blocks are likely to be erroneous, though
 
397
       *)
 
398
      if a >= start_addr && a < end_addr then (
 
399
        if Netsys_mem.color v = Netsys_mem.White then (
 
400
          dlogr (fun () -> sprintf "marking 0x%nx" 
 
401
                   (Netsys_mem.obj_address v));
 
402
          Netsys_mem.set_color v Netsys_mem.Black;
 
403
          if !Debug.enable then
 
404
            Hashtbl.replace debug_addr (Netsys_mem.obj_address v) ();
 
405
          if Obj.tag v < Obj.no_scan_tag then (
 
406
            let sz = Obj.size v in
 
407
            for k = 0 to sz - 2 do
 
408
              mark (Obj.field v k)
 
409
            done;
 
410
            if sz >= 1 then
 
411
              mark (Obj.field v (sz-1))  (* tail-rec *)
 
412
          )
 
413
        )
 
414
        else (
 
415
          if !Debug.enable then (
 
416
            if not (Hashtbl.mem debug_addr (Netsys_mem.obj_address v)) then (
 
417
              dlog (sprintf "wrong color at 0x%nx"
 
418
                      (Netsys_mem.obj_address v))
 
419
            )
 
420
          )
 
421
        )
 
422
      )
 
423
      else dlog "addr out of range"
 
424
    ) in
 
425
  
 
426
  let sweep_ext ext =
 
427
    dlogr (fun () -> sprintf "sweep_ext addr=%nx" ext.ext_addr);
 
428
    let mem = Netsys_mem.grab ext.ext_addr ext.ext_size in
 
429
    let offs = ref ext.ext_start in
 
430
    let cur_fl_entry = ref None in
 
431
    let all_free = ref true in
 
432
    let free_size = ref 0 in
 
433
    let push() =
 
434
      match !cur_fl_entry with
 
435
        | Some(fl_offs,fl_len) ->
 
436
            free_size := !free_size + fl_len;
 
437
            if fl_len > bytes_per_word then
 
438
              add_to_fl heap mem fl_offs fl_len
 
439
            else
 
440
              init_as_atom mem fl_offs;
 
441
            cur_fl_entry := None
 
442
        | None -> ()
 
443
    in
 
444
    while !offs < ext.ext_end do
 
445
      let v = Netsys_mem.as_value mem (!offs + bytes_per_word) in
 
446
      let sz = Obj.size v in
 
447
      ( match Netsys_mem.color v with
 
448
          | Netsys_mem.White ->
 
449
              dlogr (fun () -> sprintf "freeing 0x%nx"
 
450
                       (Nativeint.add ext.ext_addr (Nativeint.of_int !offs)));
 
451
              ( match !cur_fl_entry with
 
452
                  | None ->
 
453
                      cur_fl_entry := Some(!offs, (sz+1)*bytes_per_word)
 
454
                  | Some(fl_offs, fl_len) ->
 
455
                      cur_fl_entry := Some(fl_offs,
 
456
                                           fl_len +
 
457
                                             (sz+1)*bytes_per_word)
 
458
              )
 
459
          | _ ->
 
460
              dlogr (fun () -> sprintf "keeping 0x%nx"
 
461
                       (Nativeint.add ext.ext_addr (Nativeint.of_int !offs)));
 
462
              all_free := false;
 
463
              Netsys_mem.set_color v Netsys_mem.White;
 
464
              push()
 
465
      );
 
466
      offs := !offs + (sz+1)*bytes_per_word
 
467
    done;
 
468
    if !all_free && ext != heap.heap_ext then
 
469
      shrink_heap heap ext
 
470
    else
 
471
      push();
 
472
 
 
473
    dlogr (fun () -> sprintf "sweep_ext free_size=%d" !free_size);
 
474
 
 
475
    (!free_size, ext.ext_end - ext.ext_start)
 
476
  in
 
477
 
 
478
  let sweep () =
 
479
    (* Reset the free lists: *)
 
480
    for k = 0 to fl_size - 1 do
 
481
      heap.heap_fl.(k) <- null_obj
 
482
    done;
 
483
    (* Iterate over the extension blocks: *)
 
484
    let ext = ref (Some heap.heap_ext) in
 
485
    let free_total = ref 0 in
 
486
    let size_total = ref 0 in
 
487
    while !ext <> None do
 
488
      match !ext with
 
489
        | Some x ->
 
490
            (* Get the [next] block now, because [x] may be deleted *)
 
491
            let next =
 
492
              if x.ext_next = no_ext_block then
 
493
                None
 
494
              else
 
495
                Some(ext_block heap x.ext_next) in
 
496
            let (f,s) = sweep_ext x in
 
497
            free_total := !free_total + f;
 
498
            size_total := !size_total + s;
 
499
            ext := next
 
500
        | None -> assert false
 
501
    done;
 
502
    (!free_total, !size_total) in
 
503
 
 
504
  dlog "mark";
 
505
  let root = Obj.repr heap.heap_roots in
 
506
  (* root is the only value that is not reset to white color! *)
 
507
  Netsys_mem.set_color root Netsys_mem.White;
 
508
  mark root;
 
509
 
 
510
  dlog "sweep";
 
511
  let (f,s) = sweep() in
 
512
  dlog "gc done";
 
513
  (f, s)
 
514
 
 
515
 
 
516
let do_gc_adjust heap size =
 
517
  (* Do a GC pass and adjust the amount of free mem. If new mem is allocated
 
518
     it should be at least [size]
 
519
   *)
 
520
  let (free, total) = do_gc heap in
 
521
  if free < total/2 then (
 
522
    let alloc_size0 = max size (total/2 - free) in
 
523
    let alloc_size = ((alloc_size0 - 1) / 8 + 1) * 8 in
 
524
    dlogr (fun () -> sprintf "do_gc_adjust: alloc_size=%d" alloc_size);
 
525
    ( match extend_heap heap alloc_size with
 
526
        | Some(mem, offs, len) ->
 
527
            add_to_fl heap mem offs len
 
528
        | None ->
 
529
            ()
 
530
    )
 
531
  )
 
532
  
 
533
 
 
534
let find_free_block heap size =
 
535
  (* Find a free block >= [size] in the freelists *)
 
536
  dlogr (fun () -> sprintf "find_free_block size=%d" size);
 
537
  let words = size / bytes_per_word in
 
538
  let k = ref(if words < fl_size then words else 0) in
 
539
  let prev = ref null_obj in
 
540
  let cur = ref heap.heap_fl.( !k ) in
 
541
  let found = ref false in
 
542
  let best = ref null_obj in
 
543
  let best_prev = ref null_obj in
 
544
  let best_size = ref max_int in
 
545
  while not !found && (!cur != null_obj || !k > 0) do
 
546
    if !cur == null_obj then (
 
547
      incr k;
 
548
      if !k = fl_size then k := 0;
 
549
      prev := null_obj;
 
550
      cur := heap.heap_fl.( !k )
 
551
    ) else (
 
552
      let n = Obj.size !cur in
 
553
      (* Actually, we have one more word than n because of the value header *)
 
554
      if n+1 >= words && n+1 < !best_size then (
 
555
        best := !cur;
 
556
        best_prev := !prev;
 
557
        best_size := n+1;
 
558
      );
 
559
      if n+1=words then found := true;
 
560
      prev := !cur;
 
561
      cur := Obj.field !cur 0
 
562
    )
 
563
  done;
 
564
  if !best != null_obj then (
 
565
    dlog "found free block";
 
566
    let addr = Netsys_mem.hdr_address !best in
 
567
    let byte_size = (Obj.size !best + 1) * bytes_per_word in
 
568
    let mem = Netsys_mem.grab addr byte_size in
 
569
    Some(mem, 0, byte_size, !best, !best_prev)
 
570
  )
 
571
  else
 
572
    None
 
573
 
 
574
 
 
575
let alloc_in_free_block heap size mem offs len entry prev =
 
576
  (* Take a part of the free block at [mem+offs..mem+offs+len-1] to
 
577
     satisfy the allocation of a block of [size]. [entry] is the
 
578
     entry in the freelist. [prev] is the  predecessor in the freelist
 
579
     or [null].
 
580
   *)
 
581
  dlogr (fun () -> 
 
582
           sprintf "alloc_in_free_block size=%d len=%d" size len);
 
583
  del_in_fl heap entry prev;
 
584
  init_as_block mem offs size;
 
585
  if len = size then
 
586
    (* The whole block can be used *)
 
587
    (mem, offs)
 
588
  else (
 
589
    (* The block needs to be split *)
 
590
    if len = size + bytes_per_word then (
 
591
      (* The remaining part would only have 1 word. We initialize this word
 
592
         as zero-length block, but it is not entered into a freelist
 
593
       *)
 
594
      init_as_atom mem (offs+size);
 
595
      (mem, offs)
 
596
    )
 
597
    else (
 
598
      (* the remaining part is added to a freelist *)
 
599
      add_to_fl heap mem (offs+size) (len - size);
 
600
      (mem, offs)
 
601
    )
 
602
  )
 
603
 
 
604
 
 
605
let alloc heap size =
 
606
  (* First search in the freelists *)
 
607
  (* assert: size divisible by word size *)
 
608
  dlogr (fun () -> sprintf "alloc size=%d" size);
 
609
  match find_free_block heap size with
 
610
    | Some(mem, offs, len, obj, prev) ->
 
611
        dlog "alloc: got block from free list";
 
612
        alloc_in_free_block heap size mem offs len obj prev
 
613
    | None ->
 
614
        (* Nothing found in the freelists: Do now a GC pass, and try again.
 
615
         *)
 
616
        ( do_gc_adjust heap size;
 
617
          match find_free_block heap size with
 
618
            | Some(mem, offs, len, obj, prev) ->
 
619
                dlog "alloc: got block from free list";
 
620
                alloc_in_free_block heap size mem offs len obj prev
 
621
            | None ->
 
622
                (* Still unsuccessful. Add another block and try again *)
 
623
                dlog "alloc: extending heap";
 
624
                ( match extend_heap heap size with
 
625
                    | Some(mem, offs, len) ->
 
626
(*
 
627
eprintf "mem=%nx offs=%x len=%d\n%!"
 
628
  (Netsys_mem.memory_address mem)
 
629
  offs
 
630
  len;
 
631
 *)
 
632
                        if len = size then (
 
633
                          init_as_block mem offs size;
 
634
                          (mem,offs)
 
635
                        )
 
636
                        else (
 
637
                          assert(len <> size + bytes_per_word);
 
638
                          init_as_block mem offs size;
 
639
                          add_to_fl heap mem (offs+size) (len - size);
 
640
 
 
641
                          (mem,offs)
 
642
                        )
 
643
                    | None ->
 
644
                        raise Netmcore_mempool.Out_of_pool_memory
 
645
                )
 
646
        )
 
647
 
 
648
 
 
649
let add mut newval =
 
650
  (* It is assumed that we already got the lock for the heap *)
 
651
  dlog "add";
 
652
  if not mut.alive then
 
653
    failwith "Netmcore_heap.add: invalid mutator";
 
654
  if Obj.is_int (Obj.repr newval) then
 
655
    newval
 
656
  else (
 
657
    let heap = mut.heap in
 
658
    let heap_mem = ext_mem heap.heap_ext in
 
659
    let _, size =
 
660
      Netsys_mem.init_value
 
661
        heap_mem 0 newval 
 
662
        [Netsys_mem.Copy_simulate; Netsys_mem.Copy_atom] in
 
663
    assert(size mod bytes_per_word = 0);
 
664
    (* We need [size] bytes to store [newval] *)
 
665
    let (mem, offs) = alloc heap size in
 
666
    (* Do the copy: Note that we need the same flags here as above,
 
667
       except Copy_simulate which is omitted
 
668
     *)
 
669
    let voffs, size' =
 
670
      Netsys_mem.init_value
 
671
        mem offs newval 
 
672
        [Netsys_mem.Copy_atom] in
 
673
    assert(size = size');
 
674
    (* Return the new value: *)
 
675
    dlog "add done";
 
676
    Netsys_mem.as_value mem voffs
 
677
  )
 
678
 
 
679
 
 
680
let add_some mut (x:'a) =
 
681
  (* Very low-level! *)
 
682
  let y_orig = (Some (Obj.magic 0) : 'a option) in
 
683
  let y = add mut y_orig in
 
684
  Obj.set_field (Obj.repr y) 0 (Obj.repr x);
 
685
  y
 
686
 
 
687
let set_tmp_root heap x =
 
688
  if Obj.is_block (Obj.repr x) then (
 
689
    dlog "set_tmp_root: searching for free root element";
 
690
    (* Look for a free entry in the list of roots. There is always a
 
691
       free entry
 
692
     *)
 
693
    let n = Array.length heap.heap_roots in
 
694
    let found = ref false in
 
695
    let k = ref (-1) in
 
696
    while not !found && !k < n-1 do
 
697
      incr k;
 
698
      found := heap.heap_roots.( !k ) == null_obj
 
699
    done;
 
700
    assert(!found);
 
701
    dlogr
 
702
      (fun () -> sprintf "set_tmp_root: root element %d" !k);
 
703
    heap.heap_roots.( !k ) <- Obj.repr x;
 
704
    (* If the array of roots is full, reallocate it.
 
705
       At this point we can reallocate, because x is already member of
 
706
       the roots array. (Realloction can trigger the GC!)
 
707
     *)
 
708
    let j = ref !k in
 
709
    found := false;
 
710
    while not !found && !j < n-1 do
 
711
      incr j;
 
712
      found := heap.heap_roots.( !j ) == null_obj
 
713
    done;
 
714
    if not !found then (
 
715
      dlog "set_tmp_root: reallocation";
 
716
      let r_orig = Array.make (2*n) null_obj in
 
717
      Array.blit heap.heap_roots 0 r_orig 0 n;
 
718
      let mut = create_mutator heap in
 
719
      let r = add mut r_orig in
 
720
      heap.heap_roots <- r
 
721
    );
 
722
    !k
 
723
  )
 
724
  else (-1)
 
725
 
 
726
 
 
727
let release_tmp_root heap k =
 
728
  dlog "release_tmp_root: freeing root";
 
729
  if k >= 0 then
 
730
    heap.heap_roots.(k) <- null_obj
 
731
 
 
732
 
 
733
let add_uniform_array mut n x_orig =
 
734
  if not mut.alive then
 
735
    failwith "Netmcore_heap.add_uniform_array: invalid mutator";
 
736
  let heap = mut.heap in
 
737
  let heap_mem = ext_mem heap.heap_ext in
 
738
  let x_orig_obj = Obj.repr x_orig in
 
739
  let x_is_float =
 
740
    Obj.is_block x_orig_obj && Obj.tag x_orig_obj = Obj.double_tag in
 
741
  let x_is_block =
 
742
    Obj.is_block x_orig_obj && Obj.tag x_orig_obj <> Obj.double_tag in
 
743
  let x_size =
 
744
    if x_is_block then
 
745
      snd (
 
746
        Netsys_mem.init_value
 
747
          heap_mem 0 x_orig
 
748
          [Netsys_mem.Copy_simulate; Netsys_mem.Copy_atom])
 
749
    else
 
750
      0 in
 
751
  let a_size = 
 
752
    if x_is_float then
 
753
      Netsys_mem.init_float_array_bytelen n
 
754
    else
 
755
      Netsys_mem.init_array_bytelen n in
 
756
  let t_size = x_size + a_size in
 
757
  (* allocate in one go, so the new value cannot be garbage collected *)
 
758
  let (mem,offs) = alloc heap t_size in
 
759
  let x =
 
760
    if x_is_block then (
 
761
      let x_voffs, _ =
 
762
        Netsys_mem.init_value
 
763
          mem offs x_orig
 
764
          [Netsys_mem.Copy_atom] in
 
765
      Netsys_mem.as_value mem x_voffs
 
766
    )
 
767
    else x_orig in
 
768
  let a_offs = offs + x_size in
 
769
  let a =
 
770
    if x_is_float then (
 
771
      let (a_voffs, _) = Netsys_mem.init_float_array mem a_offs n in
 
772
      let a = (Netsys_mem.as_value mem a_voffs : _ array) in
 
773
      let a_obj = Obj.repr a in
 
774
      let x_float = (Obj.obj x_orig_obj : float) in
 
775
      for k = 0 to n-1 do
 
776
        Obj.set_double_field a_obj k x_float
 
777
      done;
 
778
      a
 
779
    )
 
780
    else (
 
781
      let (a_voffs, _) = Netsys_mem.init_array mem a_offs n in
 
782
      let a = (Netsys_mem.as_value mem a_voffs : _ array) in
 
783
      let a_obj = Obj.repr a in
 
784
      let x_obj = Obj.repr x in
 
785
      for k = 0 to n-1 do
 
786
        Obj.set_field a_obj k x_obj
 
787
      done;
 
788
      a
 
789
    ) in
 
790
  a
 
791
 
 
792
 
 
793
let add_init_array mut n f =
 
794
  if not mut.alive then
 
795
    failwith "Netmcore_heap.add_init_array: invalid mutator";
 
796
  if n=0 then
 
797
    Obj.magic(add_uniform_array mut 0 0)
 
798
  else (
 
799
    let x0 = f 0 in
 
800
    let a = add_uniform_array mut n x0 in
 
801
    let r = set_tmp_root mut.heap a in
 
802
    for k = 1 to n-1 do
 
803
      Array.unsafe_set a k (add mut (f k))
 
804
    done;
 
805
    release_tmp_root mut.heap r;
 
806
    a
 
807
  )
 
808
 
 
809
 
 
810
let with_lock heap f =
 
811
  dlog "with_lock waiting";
 
812
  let sem = Netsys_posix.as_sem heap.heap_sem 0 in
 
813
  Netsys_posix.sem_wait sem Netsys_posix.SEM_WAIT_BLOCK;
 
814
  dlog "with_lock cont";
 
815
  try
 
816
    let r = f() in
 
817
    Netsys_posix.sem_post sem;
 
818
    dlog "with_lock returning";
 
819
    r
 
820
  with
 
821
    | error ->
 
822
        Netsys_posix.sem_post sem;
 
823
        dlog "with_lock exception";
 
824
        raise error
 
825
 
 
826
 
 
827
let gc heap =
 
828
  with_lock heap
 
829
    (fun () ->
 
830
       ignore(do_gc heap)
 
831
    )
 
832
 
 
833
 
 
834
let pin mut x =
 
835
  (* FIXME: there is a cheaper way of pinning, because we have the
 
836
     heap lock. We could also just gather the roots in a list, and
 
837
     consider this list during GC
 
838
   *)
 
839
  let k = set_tmp_root mut.heap x in
 
840
  mut.pinned <- k :: mut.pinned
 
841
 
 
842
 
 
843
let modify heap mutate =
 
844
  with_lock heap
 
845
    (fun () ->
 
846
       let mut = create_mutator (Obj.magic heap) in
 
847
       let finish() =
 
848
         mut.alive <- false;
 
849
         List.iter (fun k -> release_tmp_root heap k) mut.pinned in
 
850
       try
 
851
         let r = mutate mut in
 
852
         finish();
 
853
         r
 
854
       with
 
855
         | error ->
 
856
             finish();
 
857
             raise error
 
858
    )
 
859
 
 
860
 
 
861
let copy x =
 
862
  if Obj.is_block (Obj.repr x) then
 
863
    Netsys_mem.copy_value [Netsys_mem.Copy_atom] x
 
864
  else
 
865
    x
 
866
 
 
867
 
 
868
let with_value_n heap find process =
 
869
  dlog "with_value";
 
870
  let l, k_list =
 
871
    with_lock heap
 
872
      (fun () ->
 
873
         let l = find() in
 
874
         let k_list = List.map (fun x -> set_tmp_root heap x) l in
 
875
         l, k_list
 
876
      ) in
 
877
  dlog "with_value: process";
 
878
  let y = process l in
 
879
  (* We need the lock again *)
 
880
  with_lock heap
 
881
    (fun () ->
 
882
       List.iter (release_tmp_root heap) k_list
 
883
    );
 
884
  dlog "with_value: returning";
 
885
  y
 
886
 
 
887
let with_value heap find process =
 
888
  with_value_n
 
889
    heap
 
890
    (fun () -> [find()])
 
891
    (function [x] -> process x | _ -> assert false)
 
892
 
 
893
let with_value_2 heap (find : unit -> ('t1 * 't2)) process =
 
894
  with_value_n
 
895
    heap
 
896
    (fun () -> 
 
897
       let (x1,x2) = find() in
 
898
       [ Obj.repr x1; Obj.repr x2 ]
 
899
    )
 
900
    (function
 
901
       | [x1; x2] -> process ((Obj.obj x1 : 't1), (Obj.obj x2 : 't2))
 
902
       | _ -> assert false
 
903
    )
 
904
 
 
905
let with_value_3 heap find process =
 
906
  with_value_n
 
907
    heap
 
908
    (fun () -> 
 
909
       let (x1,x2,x3) = find() in
 
910
       [ Obj.repr x1; Obj.repr x2; Obj.repr x3 ]
 
911
    )
 
912
    (function
 
913
       | [x1; x2; x3] -> process ((Obj.obj x1), (Obj.obj x2), (Obj.obj x3))
 
914
       | _ -> assert false
 
915
    )
 
916
 
 
917
let with_value_4 heap find process =
 
918
  with_value_n
 
919
    heap
 
920
    (fun () -> 
 
921
       let (x1,x2,x3,x4) = find() in
 
922
       [ Obj.repr x1; Obj.repr x2; Obj.repr x3; Obj.repr x4 ]
 
923
    )
 
924
    (function
 
925
       | [x1; x2; x3; x4] -> 
 
926
           process ((Obj.obj x1), (Obj.obj x2), (Obj.obj x3), (Obj.obj x4))
 
927
       | _ -> assert false
 
928
    )
 
929
 
 
930
let with_value_5 heap find process =
 
931
  with_value_n
 
932
    heap
 
933
    (fun () -> 
 
934
       let (x1,x2,x3,x4,x5) = find() in
 
935
       [ Obj.repr x1; Obj.repr x2; Obj.repr x3; Obj.repr x4; Obj.repr x5 ]
 
936
    )
 
937
    (function
 
938
       | [x1; x2; x3; x4; x5] -> 
 
939
           process
 
940
             ((Obj.obj x1), (Obj.obj x2), (Obj.obj x3), (Obj.obj x4),
 
941
              (Obj.obj x5))
 
942
       | _ -> assert false
 
943
    )
 
944
 
 
945
 
 
946
let root heap =
 
947
  heap.heap_value
 
948
 
 
949
 
 
950
let dummy_mem =
 
951
  Bigarray.Array1.create Bigarray.char Bigarray.c_layout bytes_per_word
 
952
 
 
953
 
 
954
let minimum_size x =
 
955
  if Obj.is_block (Obj.repr x) then
 
956
    let (_, n) =
 
957
      Netsys_mem.init_value
 
958
        dummy_mem 0 x
 
959
        [Netsys_mem.Copy_simulate; Netsys_mem.Copy_atom] in
 
960
    n + ((40 + fl_size + n_roots) * bytes_per_word)
 
961
      (* this is just an estimate *)
 
962
  else
 
963
    ((40 + fl_size + n_roots) * bytes_per_word)
 
964
 
 
965
 
 
966
let destroy heap =
 
967
  let ext = ref (Some heap.heap_ext) in
 
968
  let first = ref true in
 
969
  while !ext <> None do
 
970
    match !ext with
 
971
      | Some x ->
 
972
          (* Get the [next] block now, because [x] is deleted *)
 
973
          let next =
 
974
            if x.ext_next = no_ext_block then
 
975
              None
 
976
            else
 
977
              Some(ext_block heap x.ext_next) in
 
978
          if not !first then
 
979
            shrink_heap heap x;
 
980
          ext := next;
 
981
          first := false
 
982
      | None -> assert false
 
983
  done;
 
984
  let heap_mem =
 
985
    Netsys_mem.grab heap.heap_ext.ext_addr heap.heap_ext.ext_size in
 
986
  Netmcore_mempool.free_mem heap.heap_pool heap_mem
 
987
 
 
988
 
 
989
let create_sem_mem() =
 
990
  let m =
 
991
    Bigarray.Array1.create
 
992
      Bigarray.char Bigarray.c_layout Netsys_posix.sem_size in
 
993
  ignore(Netsys_posix.sem_init m 0 true 1);
 
994
  m
 
995
 
 
996
let create_heap pool size rootval_orig =
 
997
  if not (Obj.is_block (Obj.repr rootval_orig)) then
 
998
    failwith "Netmcore_heap.create_heap: the root element is not a block";
 
999
  let heap_mem = Netmcore_mempool.alloc_mem pool size in
 
1000
  try
 
1001
    let heap_ext_orig =
 
1002
      { ext_prev = no_ext_block;
 
1003
        ext_next = no_ext_block;
 
1004
        ext_addr = Netsys_mem.memory_address heap_mem;
 
1005
        ext_size = Bigarray.Array1.dim heap_mem;
 
1006
        ext_start = 0;   (* fixed later *)
 
1007
        ext_end = Bigarray.Array1.dim heap_mem
 
1008
      } in
 
1009
    let heap_orig =
 
1010
      { heap_sem = create_sem_mem();
 
1011
        heap_pool = pool;
 
1012
        heap_value = Obj.obj null_obj;
 
1013
        heap_ext = heap_ext_orig;
 
1014
        heap_fl = Array.make fl_size null_obj;
 
1015
        heap_roots = Array.make n_roots null_obj;
 
1016
        (* FIXME: the initial roots array should better be allocated in the
 
1017
           value area 
 
1018
         *)
 
1019
      } in
 
1020
    let p = ref 0 in
 
1021
    Netsys_mem.blit_string_to_memory magic 0 heap_mem !p magic_len;
 
1022
    p := !p + magic_len;
 
1023
    let p_hoffs = !p in
 
1024
    p := !p + 8;
 
1025
    let (voffs, n) = 
 
1026
      Netsys_mem.init_value heap_mem !p heap_orig
 
1027
        [Netsys_mem.Copy_bigarray; Netsys_mem.Copy_custom_int; 
 
1028
         Netsys_mem.Copy_atom] in
 
1029
    let hoffs_s =
 
1030
      Netnumber.HO.int8_as_string (Netnumber.int8_of_int voffs) in
 
1031
    Netsys_mem.blit_string_to_memory hoffs_s 0 heap_mem p_hoffs 8;
 
1032
    p := !p + n;
 
1033
    let heap = (Netsys_mem.as_value heap_mem voffs : _ heap) in
 
1034
    heap.heap_ext.ext_start <- !p;
 
1035
    add_to_fl heap heap_mem !p (heap.heap_ext.ext_end - !p);
 
1036
    let mut = create_mutator heap in
 
1037
    let rootval = add mut rootval_orig in
 
1038
    heap.heap_value <- rootval;
 
1039
    heap.heap_roots.(0) <- Obj.repr rootval;
 
1040
    heap
 
1041
  with
 
1042
    | error ->
 
1043
        Netmcore_mempool.free_mem pool heap_mem;
 
1044
        raise error
 
1045
 
 
1046