1 /* Memory management data structures, part 3: global data */
2 
3 /* -------------------------- Specification ---------------------------- */
4 
5 #ifdef TYPECODES
6 /* Number of possible typecodes. */
7   #define typecount  bit(oint_type_len<=8 ? oint_type_len : 8)
8 #endif
9 
10 /* Number of heaps.
11  heapcount */
12 #ifdef SPVW_MIXED
13 /* Two heaps: One for varobjects, one for two-pointer objects. */
14   #define heapcount  2
15 #endif
16 #ifdef SPVW_PURE
17 /* A heap for each possible typecode. */
18   #define heapcount  typecount
19 #endif
20 
21 /*
22   VTZ:
23   MT memory heap changes proposal
24   GC.
25   The GC will be protected with spinlock (possibly more efficient than mutex).
26   During GC all thread will be suspended at "safe" points
27   (suspension will be through mutex or spinlock - per thread).
28   The suspension and GC will be performed in the context of the thread that caused the GC.
29 
30   Allocations.
31   1. single spinlock protects the whole mem structure (all heaps).
32   2. every thread should acqiure it in order to allocate anything
33   3. the thread that causes the GC will suspend all others before invoking it.
34   4. every thread should define so called "safe" points at which
35     it can be suspended with no risk. these places will be:
36      4.1. any call to allocate_xxxxxx()
37      4.2. begin_system_call (what happens if pointers to LISP heap are passed?)
38      4.3. (*) we need some other place in order to be able to suspend
39           forms like: (loop). Probably the interrupt() macro is good candidate?
40 
41   "safe" points concept is similar to the cancellation points
42   (pthread_testcancel()) in pthreads.
43 */
44 
45 
46 /* Global memory management data structures. */
47 local struct {
48   /* Lower limit of big allocated memory block. */
49   aint MEMBOT;
50 
51   /* now comes the Lisp STACK */
52   /* now room for the heaps containing Lisp objects. */
53   Heap heaps[heapcount];
54 #if defined(MULTITHREAD)
55   /*VTZ:  we can live with just single lock for allocation and GC.
56     The alloc_lock will guard the GC as well.*/
57   spinlock_t alloc_lock;
58 #endif
59  #ifdef SPVW_PURE
60   sintB heaptype[heapcount];
61   /* for every typecode:
62      0 for conses
63      1 for varobjects containing object pointers
64      2 for varobjects containing no pointers (only immediate data)
65     -1 for SUBRs (gcinvariant)
66     -2 for unused or immediate typecodes */
67  #endif
68  #ifdef SPVW_MIXED
69   #define varobjects  heaps[0] /* objects of various lengths */
70   #define conses      heaps[1] /* conses and other two-pointer objects */
71  #endif
72  #if defined(SPVW_MIXED_BLOCKS_OPPOSITE) && !defined(TRIVIALMAP_MEMORY)
73   /* now empty, free for Lisp objects. */
74    #define MEMRES  conses.heap_end
75   /* now the emergency reserve
76      Upper limit of big allocated memory block. */
77   aint MEMTOP;
78  #endif
79   /* User provided parameters, used for deciding when to start a GC. */
80   double nextgc_trigger_factor; /* influences the amount of space
81                                    that can be allocated until the next GC */
82   /* Statistical data, used for deciding when to start a GC. */
83  #if defined(SPVW_PURE_BLOCKS) || defined(TRIVIALMAP_MEMORY) || defined(GENERATIONAL_GC)
84   uintM total_room; /* the space that may be occupied without triggering GC */
85   #ifdef GENERATIONAL_GC
86   bool last_gc_full;  /* if the last GC was a full one */
87   uintM last_gcend_space0; /* how much space was occupied after the last GC */
88   uintM last_gcend_space1; /* (from generation 0 resp. generation 1) */
89   #endif
90  #endif
91  #ifdef SPVW_PAGES
92   Pages free_pages;     /* a list of free, normal-sized pages */
93   uintM total_space; /* how much space do the occupied pages contain at all */
94   uintM used_space;  /* how much space is occupied just now */
95   uintM last_gcend_space; /* how much space was occupied after the last GC */
96   bool last_gc_compacted; /* if the last GC has already compacted */
97   uintM gctrigger_space; /* how much space may be occupied, until the next GC becomes necessary */
98  #endif
99 } mem;
100 
101 #if defined(SPVW_MIXED_BLOCKS_OPPOSITE) && !defined(TRIVIALMAP_MEMORY) && !defined(GENERATIONAL_GC)
102   #define RESERVE       0x00800L /* 2 KByte memory as reserve */
103 #else
104   #define RESERVE             0 /* need no preallocated reserve */
105 #endif
106   #define MINIMUM_SPACE 0x10000L /* 64 KByte as minimal memory for LISP-data */
107 #ifdef TRIVIALMAP_MEMORY
108 #if defined(MULTITHREAD)
109 /* in MT we malloc() the lisp stacks - let's have more memory until we find
110    a better way to allocate the stacks */
111   #define RESERVE_FOR_MALLOC 0x400000L /* leave 4 MByte address space free, for malloc */
112 #else
113   #define RESERVE_FOR_MALLOC 0x100000L /* leave 1 MByte address space free, for malloc */
114 #endif
115 #endif
116 
117 /* Iteration through all heaps.
118  for_each_heap(heapvar,statement);
119 
120  Iteration through all heaps containing varobjects.
121  for_each_varobject_heap(heapvar,statement);
122 
123  Iteration through all heaps containing conses.
124  for_each_cons_heap(heapvar,statement);
125 
126  Iteration through all pages.
127  for_each_page(page, statement using 'var Page* page');
128 
129  Iteration through all pages containing varobjects.
130  for_each_varobject_page(page, statement using 'var Page* page');
131 
132  Iteration through all pages containing conses.
133  for_each_cons_page(page, statement using 'var Page* page');
134  for_each_cons_page_reversed(page, statement using 'var Page* page');
135 
136  While iterating through all heaps (0 <= heapnr < heapcount):
137  Determine the type of a heap.
138  is_heap_containing_objects(heapnr)
139  is_varobject_heap(heapnr)
140  is_cons_heap(heapnr)
141  is_unused_heap(heapnr)
142 
143  #ifdef TYPECODES
144   Determine the heap that contains objects of the given type.
145   typecode_to_heapnr(type)
146  #endif
147 
148  Test for valid heap address, used only by consistency checks.
149  is_valid_varobject_address(address)
150  is_valid_cons_address(address)
151  is_valid_heap_object_address(address)
152  Likewise for stack-allocated objects, such as DYNAMIC_STRING.
153  is_valid_stack_address(address)
154 
155  Consistency checks.
156  CHECK_AVL_CONSISTENCY();
157  CHECK_GC_CONSISTENCY();
158  CHECK_GC_CONSISTENCY_2();
159  CHECK_PACK_CONSISTENCY();
160 
161  Initializations. */
162 #ifdef SPVW_PURE
163 local inline void init_mem_heaptypes (void);
164 #endif
165 
166 /* -------------------------- Implementation --------------------------- */
167 
168 /* partitioning of the whole memory (partly out-of-date):
169  1. C-program. Memory is allocated by the operating system.
170     un-movable after program start.
171  2. C-Stack.  Is fetched by the C-program.
172     un-movable.
173  3. C-Heap. Is unused here.
174 #ifdef SPVW_MIXED_BLOCKS
175  4. LISP-stack and LISP-data.
176     4a. LISP-stack. un-movable.
177     4b. Objects of variable length. (Un-movable).
178     4c. Conses and similar. Movable with move_conses.
179     Memory therefore is requested from the operating system (has the
180     advantage: On EXECUTE, the whole memory that LISP currently does not
181     need can be provided to the foreign program).
182     We dispense here with a partitioning into single pages.
183     || LISP-      |Objects of      |->  empty   <-|conses     | reserve |
184     || stack      |variable length !              !and similar|         |
185     |STACK_BOUND  |         objects.end     conses.start      |         |
186   MEMBOT   objects.start                                conses.end    MEMTOP
187 #endif
188 #ifdef SPVW_PURE_BLOCKS
189  4. LISP-stack. Un-movable.
190  5. LISP-data. For each type a large block of objects.
191 #endif
192 #ifdef SPVW_MIXED_PAGES
193  4. LISP-stack. Un-movable.
194  5. LISP-data.
195     subdivided into pages for objects of variable length and
196     pages for conses and similar.
197 #endif
198 #ifdef SPVW_PURE_PAGES
199  4. LISP-stack. Un-movable.
200  5. LISP-data. Subdivided into pages, that contain only objects
201     of the same type.
202 #endif
203 */
204 
205 #ifdef SPVW_MIXED
206 
207 /* Iteration through heaps. */
208 #define for_each_heap(heapvar,statement)  \
209   do {                                                   \
210     var uintL heapnr;                                    \
211     for (heapnr=0; heapnr<heapcount; heapnr++) {         \
212       var Heap* heapvar = &mem.heaps[heapnr]; statement; \
213     }                                                    \
214   } while(0)
215 #define for_each_varobject_heap(heapvar,statement)  \
216   do { var Heap* heapvar = &mem.varobjects; statement; } while(0)
217 #define for_each_cons_heap(heapvar,statement)  \
218   do { var Heap* heapvar = &mem.conses; statement; } while(0)
219 
220 /* Iteration through pages. */
221 #define for_each_page(pagevar,statement)  \
222   do {                                               \
223     var uintL heapnr;                                \
224     for (heapnr=0; heapnr<heapcount; heapnr++)       \
225       map_heap(mem.heaps[heapnr],pagevar,statement); \
226   } while(0)
227 #define for_each_varobject_page(pagevar,statement)  \
228   map_heap(mem.varobjects,pagevar,statement)
229 #define for_each_cons_page(pagevar,statement)  \
230   map_heap(mem.conses,pagevar,statement)
231 #define for_each_cons_page_reversed for_each_cons_page
232 
233 /* Heap classification. */
234   #define is_heap_containing_objects(heapnr)  (true)
235   #define is_varobject_heap(heapnr)  ((heapnr)==0)
236   #define is_cons_heap(heapnr)  ((heapnr)==1)
237   #define is_unused_heap(heapnr)  (false)
238 
239 #endif
240 
241 #ifdef SPVW_PURE
242 
243 /* During iterations, `heapnr' is the number of the heap. */
244 
245 /* Iteration through heaps. */
246 #define for_each_heap(heapvar,statement)  \
247   do {                                                     \
248     var uintL heapnr;                                      \
249     for (heapnr=0; heapnr<heapcount; heapnr++)             \
250       if (mem.heaptype[heapnr] >= 0) {                     \
251         var Heap* heapvar = &mem.heaps[heapnr]; statement; \
252       }                                                    \
253   } while(0)
254 #define for_each_varobject_heap(heapvar,statement)  \
255   do {                                                     \
256     var uintL heapnr;                                      \
257     for (heapnr=0; heapnr<heapcount; heapnr++)             \
258       if (mem.heaptype[heapnr] > 0) {                      \
259         var Heap* heapvar = &mem.heaps[heapnr]; statement; \
260       }                                                    \
261   } while(0)
262 #define for_each_cons_heap(heapvar,statement)  \
263   do {                                                     \
264     var uintL heapnr;                                      \
265     for (heapnr=0; heapnr<heapcount; heapnr++)             \
266       if (mem.heaptype[heapnr] == 0) {                     \
267         var Heap* heapvar = &mem.heaps[heapnr]; statement; \
268       }                                                    \
269   } while(0)
270 
271 /* Iteration through pages. */
272 #define for_each_page(pagevar,statement)  \
273   do {                                                     \
274     var uintL heapnr;                                  \
275     for (heapnr=0; heapnr<heapcount; heapnr++)         \
276       if (mem.heaptype[heapnr] >= 0)                   \
277         map_heap(mem.heaps[heapnr],pagevar,statement); \
278   } while(0)
279 #define for_each_varobject_page(pagevar,statement)  \
280   do {                                                     \
281     var uintL heapnr;                                  \
282     for (heapnr=0; heapnr<heapcount; heapnr++)         \
283       if (mem.heaptype[heapnr] > 0)                    \
284         map_heap(mem.heaps[heapnr],pagevar,statement); \
285   } while(0)
286 #define for_each_cons_page(pagevar,statement)  \
287   do {                                                     \
288     var uintL heapnr;                                  \
289     for (heapnr=0; heapnr<heapcount; heapnr++)         \
290       if (mem.heaptype[heapnr] == 0)                   \
291         map_heap(mem.heaps[heapnr],pagevar,statement); \
292   } while(0)
293 #define for_each_cons_page_reversed(pagevar,statement)  \
294   do {                                                 \
295     var uintL heapnr;                                  \
296     for (heapnr=heapcount; heapnr-- > 0; )             \
297       if (mem.heaptype[heapnr] == 0)                   \
298         map_heap(mem.heaps[heapnr],pagevar,statement); \
299   } while(0)
300 
301 /* Heap classification. */
302   #define is_heap_containing_objects(heapnr)  ((mem.heaptype[heapnr] >= 0) && (mem.heaptype[heapnr] < 2))
303   #define is_cons_heap(heapnr)  (mem.heaptype[heapnr] == 0)
304   #define is_varobject_heap(heapnr)  (mem.heaptype[heapnr] > 0)
305   #define is_unused_heap(heapnr)  (mem.heaptype[heapnr] < 0)
306 
307 #endif
308 
309 #ifdef TYPECODES
310   #if defined(SPVW_PURE)
311     #define typecode_to_heapnr(type) (type)
312   #else /* SPVW_MIXED */
313     /* Keep this consistent with the definition of 'case_pair'! */
314     #define typecode_to_heapnr(type) ((type)==cons_type) /* 1 or 0 */
315   #endif
316 #endif
317 
318 #if defined(SPVW_BLOCKS) && defined(DEBUG_SPVW)
319   #ifdef SPVW_PURE
320     #define is_valid_varobject_address(address)  \
321       is_varobject_heap(((aint)(address) >> oint_type_shift) & (oint_type_mask >> oint_type_shift))
322     #define is_valid_cons_address(address)  \
323       is_cons_heap(((aint)(address) >> oint_type_shift) & (oint_type_mask >> oint_type_shift))
324     #define is_valid_heap_object_address(address)  \
325       is_heap_containing_objects(((aint)(address) >> oint_type_shift) & (oint_type_mask >> oint_type_shift))
326   #else  /* SPVW_MIXED */
327     #ifdef GENERATIONAL_GC
328       #define is_valid_varobject_address(address)  \
329         ((aint)(address) >= mem.varobjects.heap_gen0_start \
330          && (aint)(address) < mem.varobjects.heap_end)
331       #ifdef SPVW_MIXED_BLOCKS_OPPOSITE
332         #define is_valid_cons_address(address)  \
333           ((aint)(address) >= mem.conses.heap_start \
334            && (aint)(address) < mem.conses.heap_gen0_end)
335       #else
336         #define is_valid_cons_address(address)  \
337           ((aint)(address) >= mem.conses.heap_gen0_start \
338            && (aint)(address) < mem.conses.heap_end)
339       #endif
340     #else
341       #define is_valid_varobject_address(address)  \
342         ((aint)(address) >= mem.varobjects.heap_start \
343          && (aint)(address) < mem.varobjects.heap_end)
344       #define is_valid_cons_address(address)  \
345         ((aint)(address) >= mem.conses.heap_start \
346          && (aint)(address) < mem.conses.heap_end)
347     #endif
348     #define is_valid_heap_object_address(address)  \
349       (is_valid_varobject_address(address) || is_valid_cons_address(address))
350   #endif
351   #if !(defined(CAN_ALLOCATE_8BIT_VECTORS_ON_C_STACK) || defined(CAN_ALLOCATE_STRINGS_ON_C_STACK))
352    /* In case we do not have C stack allocated lisp objects we do not need
353       is_valid_stack_address(). Define to false in order to GC to assert
354       in case of bad object pointer. */
355     #define is_valid_stack_address(address)  false
356   #else /* we have C stack allocated objects */
357     #ifdef SP_DOWN
358       #define is_in_stack_range(address,sp,sp_anchor)               \
359         ((aint)(address) >= (aint)sp && (aint)(address) <= (aint)sp_anchor)
360     #endif
361     #ifdef SP_UP
362       #define is_in_stack_range(addresssp,sp,sp_anchor)              \
363         ((aint)(address) <= (aint)sp && (aint)(address) >= (aint)sp_anchor)
364     #endif
365     #ifdef MULTITHREAD
366      /* In MT builds there is no fast and portable way to get the current
367         stack pointer of suspended threads - so for now it is disabled as
368         well (otherwise gc will abort in debug). This makes the assert in
369         GC useless. */
is_valid_stack_address_mt(aint address)370       static inline bool is_valid_stack_address_mt(aint address)
371       {
372         for_all_threads({
373           if (is_in_stack_range(address,thread->_SP_before_suspend,
374                                 thread->_SP_anchor))
375             return true;
376         });
377         return false;
378       }
379       #define is_valid_stack_address(address) \
380         is_valid_stack_address_mt((aint)address)
381     #else /* single thread builds */
382       #define is_valid_stack_address(address) \
383         is_in_stack_range(address,SP(),SP_anchor)
384     #endif
385   #endif
386 #else
387   #define is_valid_varobject_address(address)  true
388   #define is_valid_cons_address(address)  true
389   #define is_valid_heap_object_address(address)  true
390   #define is_valid_stack_address(address)  true
391 #endif
392 
393 /* Set during the core of GC. */
394 modexp bool inside_gc = false;
395 
396 /* check of the memory content to be GC-proof: */
397 #if defined(SPVW_PAGES) && defined(DEBUG_SPVW)
398 /* check, if the administration of the pages is okay: */
399   #define CHECK_AVL_CONSISTENCY()  check_avl_consistency()
check_avl_consistency(void)400 local void check_avl_consistency (void)
401 {
402  #ifdef DEBUG_AVL
403   var uintL heapnr;
404   for (heapnr=0; heapnr<heapcount; heapnr++) {
405     AVL(AVLID,check) (mem.heaps[heapnr].inuse);
406   }
407  #endif
408 }
409 /* check, if the boundaries of the pages are okay: */
410   #define CHECK_GC_CONSISTENCY()  check_gc_consistency()
check_gc_consistency(void)411 local void check_gc_consistency (void)
412 {
413   for_each_page(page,
414     if ((sintM)page->page_room < 0) {
415       fprintf(stderr,"\npage overrun at address 0x%lx\n",(unsigned long)page);
416       abort();
417     }
418     if (page->page_start != page_start0(page) + mem.heaps[heapnr].misaligned) {
419       fprintf(stderr,"\ninconsistent page at address 0x%lx\n",(unsigned long)page);
420       abort();
421     }
422     if (page->page_end - mem.heaps[heapnr].misaligned + page->page_room
423         != round_down(page->m_start + page->m_length,
424                       varobject_alignment)) {
425       fprintf(stderr,"\ninconsistent page at address 0x%lx\n",(unsigned long)page);
426       abort();
427     }
428     );
429 }
430 /* check, if the boundaries of the pages are okay during the compacting GC: */
431   #define CHECK_GC_CONSISTENCY_2()  check_gc_consistency_2()
check_gc_consistency_2(void)432 local void check_gc_consistency_2 (void)
433 {
434   for_each_page(page,
435     if ((sintM)page->page_room < 0) {
436       fprintf(stderr,"\npage overrun at address 0x%lx\n",(unsigned long)page);
437       abort();
438     }
439     if (page->page_end + page->page_room
440         - (page->page_start - page_start0(page))
441         != round_down(page->m_start + page->m_length,
442                       varobject_alignment)) {
443       fprintf(stderr,"\ninconsistent page at address 0x%lx\n",(unsigned long)page);
444       abort();
445     }
446     );
447 }
448 #else
449   #define CHECK_AVL_CONSISTENCY()
450   #define CHECK_GC_CONSISTENCY()
451   #define CHECK_GC_CONSISTENCY_2()
452 #endif
453 #ifdef DEBUG_SPVW
454 /* check, if the tables of the packages are to some extent okay: */
455   #define CHECK_PACK_CONSISTENCY()  check_pack_consistency()
check_pack_consistency(void)456 local void check_pack_consistency (void)
457 {
458   var object plist = O(all_packages);
459   while (consp(plist)) {
460     var object pack = Car(plist);
461     var object symtabs[2];
462     var uintC i;
463     symtabs[0] = ThePackage(pack)->pack_external_symbols;
464     symtabs[1] = ThePackage(pack)->pack_internal_symbols;
465     for (i = 0; i < 2; i++) {
466       var object symtab = symtabs[i];
467       var object table = TheSvector(symtab)->data[1];
468       var uintL index = Svector_length(table);
469       while (index!=0) {
470         var object entry = TheSvector(table)->data[--index];
471         var uintC count = 0;
472         while (consp(entry)) {
473           if (!symbolp(Car(entry)))
474             abort();
475           entry = Cdr(entry);
476           count++; if (count>=10000) abort();
477         }
478       }
479     }
480     plist = Cdr(plist);
481   }
482 }
483 #else
484   #define CHECK_PACK_CONSISTENCY()
485 #endif
486 
487 /* Initializations. */
488 #ifdef SPVW_PURE
init_mem_heaptypes(void)489 local inline void init_mem_heaptypes (void)
490 {
491   var uintL heapnr;
492   for (heapnr=0; heapnr<heapcount; heapnr++) {
493     switch (heapnr) {
494      #ifndef HAVE_SMALL_SSTRING
495       case_sstring:
496      #endif
497       case_sbvector:
498       case_sb2vector:
499       case_sb4vector:
500       case_sb8vector:
501       case_sb16vector:
502       case_sb32vector:
503       case_bignum:
504      #ifndef IMMEDIATE_FFLOAT
505       case_ffloat:
506      #endif
507       case_dfloat:
508       case_lfloat:
509       mem.heaptype[heapnr] = 2; break;
510      #ifdef HAVE_SMALL_SSTRING
511       case_sstring: /* because of the reallocated simple-strings */
512      #endif
513       case_ostring:
514       case_obvector:
515       case_ob2vector:
516       case_ob4vector:
517       case_ob8vector:
518       case_ob16vector:
519       case_ob32vector:
520       case_vector:
521       case_mdarray:
522       case_record:
523       case_symbol:
524       mem.heaptype[heapnr] = 1; break;
525       case_pair:
526       mem.heaptype[heapnr] = 0; break;
527       case_subr:
528       mem.heaptype[heapnr] = -1; break;
529       default:
530         mem.heaptype[heapnr] = -2; break;
531     }
532   }
533 }
534 #endif
535 
536 #if defined(MULTITHREAD)
537 
538 #define ACQUIRE_HEAP_LOCK() GC_SAFE_SPINLOCK_ACQUIRE(&mem.alloc_lock)
539 #define RELEASE_HEAP_LOCK() spinlock_release(&mem.alloc_lock)
540 /* helper macros for locking/unlocking global thread mutex.
541  NB: while waiting on it no interrupts are allowed (i.e. we use
542  begin_system_call() instead of begin_blocking_system_call())*/
543 #define lock_threads() do {                     \
544   begin_system_call(); /* ! blocking */         \
545   xmutex_lock(&allthreads_lock);                \
546   end_system_call();                            \
547  } while(0)
548 #define unlock_threads() do {                   \
549   begin_system_call();                          \
550   xmutex_unlock(&allthreads_lock);              \
551   end_system_call();                            \
552   } while(0)
553 
554 /* since the GC may be re-entrant we should keep track how many times
555    we have been called. Only the first time we have to really suspend
556    other threads.*/
557 local uintC gc_suspend_count=0;
558 
559 /* UP: Suspends all running threads /besides the current/ at GC safe
560    points/regions.
561  > lock_heap: if false - the caller already owns the heap lock
562  At the end the heap lock is released since the GC itself may want
563  to allocate. locks threads mutex in order to prevent race conditions
564  with threads that are exitting (in delete_thread()). the lock will be
565  released when all threads are resumed */
gc_suspend_all_threads(bool lock_heap)566 global void gc_suspend_all_threads(bool lock_heap)
567 {
568   var clisp_thread_t *me=current_thread();
569   /*fprintf(stderr,"VTZ: GC_SUSPEND(): %0x, %d\n",me,gc_suspend_count);*/
570   if (lock_heap) ACQUIRE_HEAP_LOCK();
571   /* the heap lock should be held always */
572   ASSERT(!spinlock_tryacquire(&mem.alloc_lock));
573   if (gc_suspend_count == 0) { /* first time here */
574     lock_threads();
575     var uintC suspended_threads = 0; /* count of suspended threads */
576     for_all_threads({
577       if (thread == me) { suspended_threads++; continue; } /* skip ourself */
578       if (!thread->_suspend_count) {  /* if not already suspended */
579         xmutex_raw_lock(&thread->_gc_suspend_lock); /* enable thread waiting */
580         spinlock_release(&thread->_gc_suspend_request); /* request */
581       } else {
582         suspended_threads++; /* count the thread */
583         thread->_suspend_count++; /* increase the suspend count */
584       }
585     });
586     /* TODO: this way of waiting for threads to acknowledge the suspend
587        request is ugly and cause form of starvation sometimes.
588        We need semaphore here */
589     while (suspended_threads != allthreads.count) {
590       for_all_threads({
591         /* skip ourself and all already suspended (ACK acquired) threads */
592         if (thread->_suspend_count || (thread == me)) continue;
593         if (spinlock_tryacquire(&thread->_gc_suspend_ack)) {
594           thread->_suspend_count++; /* increase the suspend count */
595           suspended_threads++; /* count the thread */
596         } else { xthread_yield(); }
597       });
598     }
599   }
600   gc_suspend_count++; /* increase the suspend count */
601   /* keep the lock on threads, but release the heap lock.
602      no other threads are running now, so no new allocations may
603      happen - only the ones from GC. Also no new thread can be created.*/
604   RELEASE_HEAP_LOCK();
605 }
606 
607 /* UP: Resumed all suspended threads after GC (or world stop)
608  > unlock_heap: if true - the heap lock will be released at the end
609  should match a call to gc_suspend_all_threads()*/
gc_resume_all_threads(bool unlock_heap)610 global void gc_resume_all_threads(bool unlock_heap)
611 {
612   /* thread lock is locked. heap lock is free. */
613   var clisp_thread_t *me=current_thread();
614   /*fprintf(stderr,"VTZ: GC_RESUME(): %0x, %d\n",me, gc_suspend_count);*/
615   /* before resuming let's report if any timeout call has failed. no need
616      to acquire any lock - since no other thread LISP is running (and the
617      signal handling thread will wait on the heap lock/gc_suspend_count
618      anyway). It's important to do this before we get the heap lock since
619      WARN may/will cause allocations. */
620   var timeout_call *tc=timeout_call_chain;
621   while (tc && tc->failed) {
622     /* not to warn twice in case of nested GC (CLSTEXT and WARN maygc) */
623     timeout_call_chain = tc->next;
624     pushSTACK(CLSTEXT("CALL-WITH-TIMEOUT has failed in thread ~S."));
625     pushSTACK(tc->thread->_lthread);
626     tc = tc->next; /* advance */
627     funcall(S(warn),2);
628   }
629   /* get the heap lock. in case we are called from allocate_xxx
630      we should not allow any other thread that will be resumed shortly
631      to acquire it. Also it guards the gc_suspend_count when accessed
632      from the signal handling thread */
633   ACQUIRE_HEAP_LOCK();
634   if (--gc_suspend_count) {
635     RELEASE_HEAP_LOCK();
636     return;
637   }
638   for_all_threads({
639     if (thread == me) continue; /* skip ourself */
640     /* currently all ACK locks belong to us as well the mutex lock */
641     if (! --thread->_suspend_count) { /* only if suspend count goes to zero */
642       spinlock_release(&thread->_gc_suspend_ack); /* release the ACK lock*/
643       xmutex_raw_unlock(&thread->_gc_suspend_lock); /* enable thread */
644     }
645   });
646   unlock_threads(); /* locked in gc_suspend_all_threads() */
647   if (unlock_heap) RELEASE_HEAP_LOCK();
648 }
649 
650 /* UP: Suspends single thread
651  > thread: the thread to be suspended
652  > have_locks: is the caller holding the heap and threads locks ?
653  < returns true of the thread has been suspended. false in case it has exited
654    meanwhile
655  Called from signal handler thread and THREAD-INTERRUPT
656  Upon exit we hold threads lock. It is released in resume_thread(). This prevents
657  race condition when several threads try to THREAD-INTERRUPT another thread. */
suspend_thread(object thread,bool have_locks)658 global maygc bool suspend_thread(object thread, bool have_locks)
659 {
660   if (!have_locks) {
661     pushSTACK(thread);
662     /* get the locks in this order - GC does the same !!! */
663     ACQUIRE_HEAP_LOCK();
664     lock_threads();
665     thread = popSTACK();
666   }
667   var clisp_thread_t *thr = TheThread(thread)->xth_globals;
668   var bool ret = false;
669   if (thr) { /* thread is still alive ?*/
670     /* should never be called on ourselves */
671     DEBUG_SPVW_ASSERT(thr != current_thread());
672     if (!thr->_suspend_count) { /* first suspend ? */
673       xmutex_raw_lock(&thr->_gc_suspend_lock); /* enable thread waiting */
674       spinlock_release(&thr->_gc_suspend_request); /* request */
675       /* wait for the thread to come to safe point. */
676       while (!spinlock_tryacquire(&thr->_gc_suspend_ack))
677         xthread_yield();
678     }
679     thr->_suspend_count++;
680     ret = true;
681   }
682   if (!have_locks) {
683     RELEASE_HEAP_LOCK(); /* allow other threads to allocate but GC is still
684                             blocked due to threads lock */
685   }
686   return ret;
687 }
688 
689 /* UP: Resumes single thread (or just decreases it's _suspend_count).
690  > thread: the thread to be suspended
691  > release_threads_lock: should we unlock threads lock
692  Called from signal handler thread and from THREAD-INTERRUPT
693  When called we should be the owner of threads lock and if specified we should
694  release it.
695  Should match a call to suspend_thread */
resume_thread(object thread,bool release_threads_lock)696 global void resume_thread(object thread, bool release_threads_lock)
697 {
698   var clisp_thread_t *thr = TheThread(thread)->xth_globals;
699   /* should never be called on ourselves */
700   ASSERT(thr != current_thread());
701   if (thr) { /* thread was alive when it was suspended ? */
702     if (! --thr->_suspend_count) { /* only if suspend count goes to zero */
703       spinlock_release(&thr->_gc_suspend_ack); /* release the ACK lock*/
704       xmutex_raw_unlock(&thr->_gc_suspend_lock); /* enable thread */
705     }
706   }
707   if (release_threads_lock) {
708     xmutex_unlock(&allthreads_lock);
709   }
710 }
711 
712 /* remove threads locking macros */
713 #undef lock_threads
714 #undef unlock_threads
715 
716 /* UP: add per thread special symbol value - initialized to SYMVALUE_EMPTY
717  > symbol: the symbol
718  < new index in the _symvalues thread array */
add_per_thread_special_var(object symbol)719 global maygc uintL add_per_thread_special_var(object symbol)
720 {
721   pushSTACK(symbol);
722   var gcv_object_t *symbol_ = &STACK_0;
723   var uintL symbol_index = SYMBOL_TLS_INDEX_NONE;
724   WITH_OS_MUTEX_LOCK(0,&thread_symvalues_lock, {
725     /* while we were waiting on the mutex, another thread may have done
726      the job*/
727     symbol_index = TheSymbol(*symbol_)->tls_index;
728     if (symbol_index == SYMBOL_TLS_INDEX_NONE) {
729       if (num_symvalues == maxnum_symvalues) {
730         /* we have to reallocate the _ptr_symvalues storage in all
731            threads in order to have enough space. stop all threads in order to
732            perform this (they may access invalid _ptr_symvalues otherwise).
733            this should happen very rarely. */
734         var uintL nsyms=num_symvalues + SYMVALUES_PER_PAGE;
735         WITH_STOPPED_WORLD(true, {
736           if (!realloc_threads_symvalues(nsyms)) {
737             fprint(stderr,"*** could not make symbol value per-thread. aborting\n");
738             abort();
739           }
740           maxnum_symvalues = nsyms;
741         });
742       }
743       /* initialize symbol's tls_index. nb: no need to initialize _ptr_symvalue
744          to SYMVALUE_EMPTY since we've already done this during allocation. */
745       TheSymbol(*symbol_)->tls_index = symbol_index = num_symvalues++;
746     }
747   });
748   if (TheSymbol(*symbol_)->tls_index == SYMBOL_TLS_INDEX_NONE)
749     error(control_error,GETTEXT("~S: could not make symbol value per-thread"));
750   skipSTACK(1); /* symbol */
751   return symbol_index;
752 }
753 
754 /* UP: Clears any per thread value for symbol. Also sets tls_index of the
755    symbol to invalid (SYMBOL_TLS_INDEX_NONE).
756  > symbol: the symbol that should not have per thread bindings anymore
757  < symbol: (modified).
758  < allthreads: all threads symvalues for this symbol set to
759  SYMVALUE_EMPTY
760  maygc because of the threads lock */
clear_per_thread_symvalues(object symbol)761 global maygc void clear_per_thread_symvalues(object symbol)
762 {
763   var uintL idx=TheSymbol(symbol)->tls_index;
764   if (idx != SYMBOL_TLS_INDEX_NONE) {
765     /* remove all per thread symbols for the index - we do not want
766        any memory leaks. threads should be locked. This gets very
767        ugly when we are gettting called for every symbol from DELETE-PACKAGE.
768        but we cannot hold the threads lock for too long - since the GC will
769        be blocked.*/
770     pushSTACK(symbol);
771     var gcv_object_t *symbol_ = &STACK_0;
772     WITH_OS_MUTEX_LOCK(0,&allthreads_lock, {
773       TheSymbol(*symbol_)->tls_index = SYMBOL_TLS_INDEX_NONE;
774       for_all_threads({ thread->_ptr_symvalues[idx] = SYMVALUE_EMPTY; });
775     });
776     skipSTACK(1);
777   }
778 }
779 
init_heaps_mt()780 local void init_heaps_mt()
781 {
782   spinlock_init(&mem.alloc_lock);
783   #ifndef SPVW_PAGES /* only in SPVW_BLOCKS we reuse the heap holes */
784   for_each_heap(heap, { heap->holes_list = 0; } );
785   #endif
786 }
787 #endif
788