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