diff options
author | Johannes Totz <jtotz@ic.ac.uk> | 2012-03-18 22:33:08 +0000 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2012-11-14 14:17:32 +0400 |
commit | 31876d79fb6237e6d71932e48770e4ec1c4df064 (patch) | |
tree | 2d93659520a88a332892637d76830008876fa630 | |
parent | a8367cbf87badcc04d303dd801568c3e8d95cb3d (diff) | |
parent | 6859feb53580bb4ae6f558767a0e7572cbf72a69 (diff) | |
download | bdwgc-jtotz_bdwgc.tar.gz |
update to new version. not entirely sure if it works correctly, only did a quick test.jtotz_bdwgc
Conflicts:
.hgtags
include/gc.h
include/gc_cpp.h
include/private/gcconfig.h
82 files changed, 5389 insertions, 3618 deletions
@@ -1,17 +1,50 @@ +*.bsc +*.csm +*.dll +*.err +*.exe +*.exp +*.la +*.lb1 +*.lib +*.lnk *.lo +*.map +*.o +*.obj +*.out +*.pdb +*.rbj +*.res +*.sbr +*.stackdump +*.sym +*.tmp +.deps .libs Makefile +add_gc_prefix bdw-gc.pc config.log config.status +cordtest +core +de +gc-* +gcname gctest hugetest +if_mach +if_not_there +initsecondarythread leaktest -libcord.la -libgc.la -libstaticrootslib.la libtool middletest +realloc_test +setjmp_test smashtest staticrootstest +test_cpp +threadkey_test threadleaktest +threadlibs @@ -2,6 +2,7 @@ 24a2e5b43a289dc9ba38fbde2b602c79c632ce03 gc7_2alpha6 3c239e80bee0341c7834b5c5f85738c26da09f87 gc7_0 43c85b2e652869da9de1058eb3e94b79c755941a gc7_0alpha7 +24a2e5b43a289dc9ba38fbde2b602c79c632ce03 gc7_2alpha6 664dc1f702d2a4665b13964252fb63df0c58c93e start 813e2af7cb1f3bffc43c54350847fa634b9a7e6a gc7_1alpha2 de9672597e74978f9220b7b54c618b33a6d5c507 gc7_0alpha9 @@ -1,3 +1,1140 @@ +2011-07-26 Ivan Maidanski <ivmai@mail.ru> + + * tests/realloc_test.c: New file. + * tests/tests.am (TESTS, check_PROGRAMS): Add realloc_test. + * .cvsignore: Add realloc_test. + * configure: Regenerate. + * Makefile.in: Ditto. + +2011-07-14 Ivan Maidanski <ivmai@mail.ru> + + * .cvsignore: Add more auto-generated files. + +2011-07-14 Ivan Maidanski <ivmai@mail.ru> + + * new_hblk.c (GC_build_fl): Adjust "h" local variable cast type + when setting obj_link (to prevent compiler warning); reformat the + comment. + * tests/test.c (reverse_test_inner): Use proper type when touching + "b" and "c" local variables (to prevent compiler warning). + +2011-07-05 Ivan Maidanski <ivmai@mail.ru> + + * misc.c (GC_init): Use HOTTER_THAN (instead of STACK_GROWS_DOWN) + for GC_stackbottom assertion. + * os_dep.c (GC_enclosing_mapping): Define only if IA64 or + INCLUDE_LINUX_THREAD_DESCR; make GC_INNER. + * pthread_support.c (GC_enclosing_mapping): Declare (only if + INCLUDE_LINUX_THREAD_DESCR). + * os_dep.c (GC_get_main_stack_base): Don't call pthread_getattr_np + if REDIRECT_MALLOC as the former is observed to call redirected + malloc (while GC is not initialized yet) on some Linux platforms. + * include/private/gc_priv.h (MAX_HEAP_SECTS): Don't use a smaller + value for SMALL_CONFIG if USE_PROC_FOR_LIBRARIES defined. + +2011-07-05 Ivan Maidanski <ivmai@mail.ru> + + * malloc.c (GC_init_lib_bounds): Call GC_init (to ensure GC is + initialized before doing GC_text_mapping). + * misc.c (GC_init): Add a check for GC_init recursion in case of + malloc is redirected (abort with the corresponding message). + * pthread.c (GC_thr_init): Place GC_add_roots_inner call into + "else" branch to prevent "local variable might be uninitialized" + compiler warning; add comment. + +2011-07-05 Ivan Maidanski <ivmai@mail.ru> + + * dyn_load.c (GC_register_dynamic_libraries): Remove duplicate + call of GC_FirstDLOpenedLinkMap (twice). + * dyn_load.c (GC_register_main_static_data): Add comment. + * cord/cordbscs.c (CORD_riter): Check for empty string passed (do + not call CORD_riter4 if CORD_len() returned zero). + * cord/cordbscs.c (CORD_init_min_len): Replace the K&R-style + function definition with the ANSI C one. + * cord/cordbscs.c: Expand all tabs to spaces; remove + trailing spaces at EOLn. + * tests/threadkey_test.c (on_thread_exit_inner): Check + GC_pthread_create() result. + +2011-07-04 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gcconfig.h (etext): Don't define if unused + (NetBSD). + * include/private/gcconfig.h (GC_data_start, DATASTART): Define + for NetBSD/amd64 if ELF. + * include/private/gcconfig.h (SEARCH_FOR_DATA_START): Don't define + for NetBSD/amd64 if ELF. + +2011-07-04 Ivan Maidanski <ivmai@mail.ru> (mostly really Andy Wingo) + + * configure.ac (GC_THREADS): Refine the comment. + * configure.ac (GC_WIN32_PTHREADS): Add template. + * configure.ac (cygwin, win32): Define GC_WIN32_THREADS instead of + GC_THREADS. + * configure.ac (pthreads): Add mingw target (GC_WIN32_PTHREADS). + * configure: Regenerate. + * include/private/config.h.in: Ditto. + * include/private/gc_locks.h (GC_WIN32_PTHREADS): Remove nested + test for the macro; add comment. + +2011-07-01 Ivan Maidanski <ivmai@mail.ru> + + * win32_threads.c (CHECK_LOOKUP_MY_THREAD): New macro definition. + * win32_threads.c (GC_reset_finalizer_nested, + GC_check_finalizer_nested, GC_unregister_my_thread, + GC_do_blocking_inner, GC_call_with_gc_active, GC_init_parallel): + Insert CHECK_LOOKUP_MY_THREAD before dereferencing thread + descriptor pointer (to instruct a LINT-like tool that it is ok to + dereference the pointer). + * win32_threads.c (GC_get_next_stack): Assert plast_stack_min is + non-NULL if current_min is not ADDR_LIMIT (for a LINT-like tool). + * win32_threads.c (GC_init_parallel): Define and use "me" local + variable. + * cord/cordtest.c (test_basics): Test CORD_substr() result is + non-NULL. + * cord/cordtest.c (test_extras): Test fopen() result is non-NULL. + * cord/cordtest.c (test_basics, test_extras, test_printf, main): + Replace the K&R-style function definition with the ANSI C one. + * cord/cordtest.c: Expand all tabs to spaces; remove + trailing spaces at EOLn. + * include/private/gc_priv.h (ABORT): Define as abort() when + checking the code with a LINT-like tool (Win32 only). + * tests/test.c (FAIL): Ditto. + * tests/test.c (CHECH_GCLIB_VERSION): New macro (to check that the + version of libgc.so used at runtime matches that at compile time). + * tests/test.c (GC_COND_INIT): Use CHECH_GCLIB_VERSION. + * tests/test.c (CHECK_OUT_OF_MEMORY): New macro (to test malloc + result for out of memory). + * tests/test.c (cons, small_cons, small_cons_uncollectable, + gcj_cons, reverse_test_inner, mktree, alloc8bytes, typed_test, + run_one_test): Use CHECK_OUT_OF_MEMORY. + +2011-06-30 Ivan Maidanski <ivmai@mail.ru> + + * dyn_load.c (GC_register_map_entries): Remove "count" local + variable as unused. + * gc_dlopen.c (disable_gc_for_dlopen): Define only if not + USE_PROC_FOR_LIBRARIES. + * malloc.c (calloc): Add parentheses around '&&' operator. + * mark.c (GC_noop_sink): New global variable (instead of a static + local variable inside GC_noop1). + * mark.c (GC_noop1): Use GC_noop_sink variable (to prevent + "variable set but not used" compiler warning). + * include/private/gcconfig.h (USE_PROC_FOR_LIBRARIES): Define only + if undefined yet. + * tests/smash_test.c (main): Don't dereference "p" local variable + if it is NULL. + * tests/staticrootslib.c (main): Ditto. + +2011-06-30 Ivan Maidanski <ivmai@mail.ru> + + * pthread_support.c (GC_segment_is_thread_stack): Replace '&' + operator with '&&' one in conditional expressions. + * specific.c (remove_specific): Dereference "entry" local variable + only if it is non-NULL. + * include/gc.h (GC_NEW): Refine the comment (about the returned + value). + +2011-06-15 Ivan Maidanski <ivmai@mail.ru> + + * include/gc_version.h, configure.ac, doc/README: Change to + version 7.2alpha7. + * configure: Regenerate. + +[7.2alpha6] + +2011-06-14 Ivan Maidanski <ivmai@mail.ru> + + * configure_atomic_ops.sh: Remove. + * Makefile.direct (dist gc.tar): Remove configure_atomic_ops.sh. + * Makefile.am (EXTRA_DIST): Add autogen.sh. + * Makefile.in: Regenerate. + * configure: Ditto. + +2011-06-14 Ivan Maidanski <ivmai@mail.ru> + + * include/gc_version.h, configure.ac, doc/README: Change to + version 7.2alpha6. + * configure: Regenerate. + +2011-05-31 Ivan Maidanski <ivmai@mail.ru> + + * NT_STATIC_THREADS_MAKEFILE (.cpp.obj): Remove duplicate .cpp + filename passed. + * NT_X64_THREADS_MAKEFILE (.cpp.obj): Use lowercase file + extension. + * NT_X64_STATIC_THREADS_MAKEFILE (.cpp.obj): Ditto. + * NT_MAKEFILE (.cpp.obj): Ditto. + +2011-05-31 Ivan Maidanski <ivmai@mail.ru> + + * alloc.c (GC_add_current_malloc_heap, GC_build_back_graph, + GC_traverse_back_graph): Move prototype to gc_priv.h. + * checksums.c (GC_page_was_ever_dirty): Ditto. + * dbg_mlc.c (GC_default_print_heap_obj_proc): Ditto. + * dyn_load.c (GC_parse_map_entry, GC_get_maps, + GC_segment_is_thread_stack, GC_roots_present, GC_is_heap_base, + GC_get_next_stack): Ditto. + * finalize.c (GC_reset_finalizer_nested, + GC_check_finalizer_nested): Ditto. + * gcj_mlc.c (GC_start_debugging, GC_store_debug_info): Ditto. + * malloc.c (GC_extend_size_map, GC_text_mapping): Ditto. + * mark_rts.c (GC_mark_thread_local_free_lists): Ditto. + * misc.c (GC_register_main_static_data, GC_init_win32, + GC_setpagesize, GC_init_linux_data_start, + GC_set_and_save_fault_handler, GC_init_dyld, GC_init_netbsd_elf, + GC_initialize_offsets, GC_bl_init, GC_do_blocking_inner, + GC_bl_init_no_interiors): Ditto. + * os_dep.c (GC_greatest_stack_base_below, GC_push_all_stacks): + Ditto. + * reclaim.c (GC_check_leaked): Ditto. + * win32_threads.c (GC_gww_dirty_init): Ditto. + * darwin_stop_world.c (GC_is_mach_marker, GC_mprotect_stop, + GC_mprotect_resume): Move prototype to darwin_stop_world.h. + * pthread_support.c (GC_FindTopOfStack): Ditto. + * dyn_load.c (GC_cond_add_roots): Merge adjacent definitions. + * mark.c (GC_page_was_ever_dirty): Remove (as already declared). + * mark_rts.c (GC_roots_present): Change return type to void + pointer (to match the prototype); return NULL instead of FALSE. + * mark_rts.c (GC_add_roots_inner): Cast GC_roots_present() result. + * os_dep.c (NEED_PROC_MAPS): Move definition to gcconfig.h. + * os_dep.c (GC_write_fault_handler): Make STATIC. + * os_dep.c (GC_set_write_fault_handler): New function (only if + GC_WIN32_THREADS). + * pthread_start.c (GC_start_rtn_prepare_thread, + GC_thread_exit_proc): Move prototype to pthread_support.h. + * pthread_support.c (GC_nacl_initialize_gc_thread, + GC_nacl_shutdown_gc_thread, GC_unblock_gc_signals): + Ditto. + * pthread_support.c (GC_stop_init): Move prototype to + pthread_stop_world.h. + * thread_local_alloc.c (GC_check_tls_for): Reformat comment. + * win32_threads.c (GC_write_fault_handler): Remove prototype. + * win32_threads.c (GC_register_my_thread_inner): Call + GC_set_write_fault_handler instead of SetUnhandledExceptionFilter + (only if MPROTECT_VDB). + * doc/README.win32: Add information about DMC. + * include/private/gc_priv.h (GC_set_write_fault_handler): New + prototype (only if GC_WIN32_THREADS and MPROTECT_VDB). + +2011-05-31 Ivan Maidanski <ivmai@mail.ru> + + * misc.c (vsnprintf): Redirect to vsprintf() if NO_VSNPRINTF. + +2011-05-31 Ivan Maidanski <ivmai@mail.ru> + + * win32_threads.c (GC_unregister_my_thread): Use KNOWN_FINISHED() + instead of FINISHED macro. + * tests/test.c (check_heap_stats): Round up max_heap_sz value for + Win32 (same as for USE_MMAP). + +2011-05-31 Ivan Maidanski <ivmai@mail.ru> + + * tests/test.c (check_heap_stats): Adjust printf format specifier + for max_heap_sz; cast max_heap_sz accordingly. + +2011-05-30 Ivan Maidanski <ivmai@mail.ru> + + * doc/README.solaris2: Add note. + +2011-05-30 Ivan Maidanski <ivmai@mail.ru> + + * configure.ac (SOLARIS25_PROC_VDB_BUG_FIXED): Don't define for + Solaris/x86 2.10+. + * configure: Regenerate. + +2011-05-23 Ivan Maidanski <ivmai@mail.ru> + + * tests/threadkey_test.c (SKIP_THREADKEY_TEST): Skip the test if + defined; explicitly define for some targets. + +2011-05-23 Ivan Maidanski <ivmai@mail.ru> + + * mark.c (GC_dirty): Add prototype (only if MANUAL_VDB). + * stubborn.c (GC_dirty): Ditto. + * include/private/gcconfig.h (GWW_VDB, MPROTECT_VDB, PCR_VDB, + PROC_VDB): Undefine if MANUAL_VDB. + * include/private/gcconfig.h (DEFAULT_VDB): Don't define if + MANUAL_VDB. + * os_dep.c (async_set_pht_entry_from_index): Define for + MANUAL_VDB. + * os_dep.c (GC_read_dirty): Set GC_dirty_maintained only if + success; if ioctl() failed then just print warning instead of + aborting. + +2011-05-23 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gc_priv.h (GC_ASSERT): Use "%d" (instead of %ld) + for line number printing. + +2011-05-23 Ivan Maidanski <ivmai@mail.ru> + + * os_dep.c (GC_read_dirty): Add debug logging if DEBUG_DIRTY_BITS + (for PROC_VDB only); print errors via GC_err_printf; rename "ps" + and "np" local variables to npages and pagesize, respectively; + remove "current_addr" local variable. + * os_dep.c: Reformat comments. + +2011-05-22 Ivan Maidanski <ivmai@mail.ru> + + * os_dep.c (GC_get_main_stack_base): Convert to GC_get_stack_base + for BeOS and OS/2; define HAVE_GET_STACK_BASE. + * os_dep.c (GET_MAIN_STACKBASE_SPECIAL): Define when a specific + GC_get_main_stack_base implementation is defined. + * os_dep.c (GC_get_main_stack_base): Define that based on + GC_get_stack_base() in a single place (only if + GET_MAIN_STACKBASE_SPECIAL is unset); check GC_get_stack_base() + result. + +2011-05-20 Ivan Maidanski <ivmai@mail.ru> + + * mark.c (GC_push_selected): Remove "push_fn" argument (use + GC_push_all directly); update the documentation; reformat the + comment. + * mark.c (GC_push_conditional): Simplify the code (for better + readability). + +2011-05-20 Ivan Maidanski <ivmai@mail.ru> + + * mark.c (alloc_mark_stack): Use FALSE/TRUE (instead of 0/1) for + boolean local variables. + * doc/README.macros (GC_PREFER_MPROTECT_VDB): Update. + * os_dep.c (GC_page_was_dirty, GC_page_was_ever_dirty, + GC_remove_protection): Define for GWW_VDB and PROC_VDB in a single + place. + * os_dep.c (GC_page_was_dirty, GC_page_was_ever_dirty): Compute + PHT_HASH(h) only once (store result to a local variable). + +2011-05-20 Ivan Maidanski <ivmai@mail.ru> + + * doc/README.solaris2: Update. + +2011-05-19 Ivan Maidanski <ivmai@mail.ru> (really Jie Liu) + + * include/private/gcconfig.h (end, InitStackBottom): Declare + extern variable for RTEMS. + * include/private/gcconfig.h (DATASTART, DATAEND, STACKBOTTOM): + Update (for RTEMS). + * include/private/gcconfig.h (DATAEND): Fix a typo in the macro + name (for RTEMS). + * tests/test.c (CONFIGURE_APPLICATION_DOES_NOT_NEED_CLOCK_DRIVER): + Replace with CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER (for RTEMS). + +2011-05-18 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gcconfig.h (MPROTECT_VDB): Enable for Solaris in + single-threaded environment. + +2011-05-18 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gcconfig.h (MPROTECT_VDB): Undefine if PROC_VDB. + * tests/test.c (NUMBER_ROUND_UP): New macro. + * tests/test.c (check_heap_stats): Round up total expected heap + size to the nearest 4 MiB bound. + * tests/test.c (check_heap_stats): Print the current and expected + heap sizes in case of failure. + +2011-05-18 Ivan Maidanski <ivmai@mail.ru> + + * checksums.c (GC_check_blocks, GC_check_dirty): Do log printing + only if GC_print_stats; print errors using GC_err_printf. + * checksums.c (GC_check_blocks): Join adjacent printf() calls into + a single one. + +2011-05-17 Ivan Maidanski <ivmai@mail.ru> + + * pthread_support.c (pthread_join): Add assertion (check thread is + finished). + * pthread_support.c (GC_register_my_thread): Don't detach the + thread if invoked from the thread destructor. + * win32_threads.c (GC_register_my_thread): Ditto. + * win32_threads.c (GC_unregister_my_thread): Don't delete the + thread (just set FINISHED) if the thread is not detached (only if + GC_PTHREADS); add assertion (check the thread is not finished). + * tests/threadkey_test.c (main): Join some created threads. + +2011-05-17 Ivan Maidanski <ivmai@mail.ru> + + * pthread_support.c (GC_delete_gc_thread): Rename "gc_id" local + variable to "t". + * win32_threads.c (GC_delete_gc_thread): Ditto. + * pthread_support.c (pthread_join, pthread_detach, + pthread_cancel): Rename "thread_gc_id" local variable to "t". + * win32_threads.c (GC_pthread_detach): Ditto. + * win32_threads.c (GC_delete_gc_thread): Remove "gc_nvid" local + variable. + * win32_threads.c (GC_pthread_join): Rename "joinee" local + variable to "t". + +2011-05-16 Ivan Maidanski <ivmai@mail.ru> + + * pthread_stop_world.c (pthread_sigmask): Undefine even if not + DEBUG_THREADS. + * pthread_stop_world.c (GC_unblock_gc_signals): New function (only + if GC_EXPLICIT_SIGNALS_UNBLOCK). + * pthread_support.c (GC_unblock_gc_signals): New prototype. + * pthread_support.c (GC_register_my_thread_inner, + GC_register_my_thread): Call GC_unblock_gc_signals (only if + GC_EXPLICIT_SIGNALS_UNBLOCK); add comment. + * include/private/gcconfig.h (GC_EXPLICIT_SIGNALS_UNBLOCK): New + macro. + +2011-05-16 Ivan Maidanski <ivmai@mail.ru> + + * pthread_stop_world.c (GC_suspend_handler_inner): Remove "dummy", + "sig" local variables; rename my_thread local variable to "self". + +2011-05-13 Ivan Maidanski <ivmai@mail.ru> + + * tests/threadkey_test.c (LIMIT): Use smaller value (don't create + more than 30 in parallel by default). + +2011-05-13 Ivan Maidanski <ivmai@mail.ru> + + * tests/threadkey_test.c (key_once, main): Work around for Solaris + PTHREAD_ONCE_INIT. + * tests/threadkey_test.c (LIMIT): Use smaller value for Solaris. + +2011-05-13 Ivan Maidanski <ivmai@mail.ru> + + * dyn_load.c (GC_FirstDLOpenedLinkMap): Remove unused "r" local + variable. + * pthread_support.c (GC_unregister_my_thread_inner): Revert back + GC_remove_specific invocation; add a comment. + * include/private/thread_local_alloc.h (GC_remove_specific): + Revert back. + * specific.c: Expand all tabs to spaces. + * specific.c (slow_getspecific): Cast qtid to AO_t. + * include/private/specific.h (quick_thread_id): Reformat comment. + * include/private/specific.h (key_create, setspecific, + remove_specific): Remove "extern" keyword. + * include/private/specific.h (getspecific): Change type of "qtid" + local variable to unsigned long. + +2011-05-11 Ivan Maidanski <ivmai@mail.ru> + + * pthread_support.c (GC_check_tls): Fix "#endif" comment. + * include/gc.h (GC_REDIRECT_TO_LOCAL): Remove deprecated comment. + * include/private/thread_local_alloc.h (THREAD_LOCAL_ALLOC): + Remove redundant test of the macro. + * include/private/thread_local_alloc.h: Reformat the code. + +2011-05-11 Ivan Maidanski <ivmai@mail.ru> + + * backgraph.c (add_edge): Recognize DEBUG_PRINT_BIG_N_EDGES macro. + * os_dep.c (GC_set_and_save_fault_handler): Recognize + SIGACTION_FLAGS_NODEFER_HACK macro. + * pthread_support.c (mark_mutex): Recognize GLIBC_2_1_MUTEX_HACK + macro. + * pthread_support.c (GC_acquire_mark_lock): Remove commented out + code. + * include/gc_inline.h (GC_MALLOC_WORDS, GC_MALLOC_ATOMIC_WORDS, + GC_CONS): Remove trailing space (before back-slash). + * include/private/gc_hdrs.h (GET_BI, GET_HDR_ADDR): Ditto. + * include/private/gc_pmark.h (PUSH_OBJ, PUSH_CONTENTS, + SET_MARK_BIT_EXIT_IF_SET, LONG_MULT, PUSH_CONTENTS_HDR, + GC_PUSH_ONE_STACK, GC_PUSH_ONE_HEAP): Ditto. + * include/private/thread_local_alloc.h (GC_key_create): Ditto. + * include/private/gc_priv.h (SUNOS5SIGS): Don't include + sys/siginfo.h on Linux. + * include/private/gcconfig.h: Reformat comments (and some code). + * include/private/gcconfig.h (FORCE_WRITE_PREFETCH): New macro + recognized, force PREFETCH_FOR_WRITE to be defined on x86. + * include/private/gcconfig.h (USE_HPUX_FIXED_STACKBOTTOM): New + macro recognized (for HP/UX). + +2011-05-11 Ivan Maidanski <ivmai@mail.ru> + + * os_dep.c (GC_gww_page_was_ever_dirty): Fix comment (for + GWW_VDB). + * os_dep.c (GC_dirty_init): Use memset() for GC_written_pages + resetting (for PROC_VDB). + +2011-05-11 Ivan Maidanski <ivmai@mail.ru> (mostly really Ludovic Courtes) + + * tests/threadkey_test.c: New file. + * .cvsignore (threadkey_test): Add. + * tests/tests.am (TESTS, check_PROGRAMS): Add 'threadkey_test'. + * tests/tests.am (threadkey_test_SOURCES, threadkey_test_LDADD): + New variable. + * Makefile.in: Regenerate. + +2011-05-11 Ivan Maidanski <ivmai@mail.ru> + + * pthread_support.c (GC_unregister_my_thread_inner): Don't call + GC_remove_specific. + * include/private/thread_local_alloc.h (GC_remove_specific): + Remove (since it is empty for all targets). + * pthread_support.c (GC_record_stack_base): New inline function. + * win32_threads.c (GC_record_stack_base): Ditto. + * pthread_support.c (GC_register_my_thread_inner): Invoke + GC_record_stack_base. + * win32_threads.c (GC_register_my_thread_inner): Ditto. + * pthread_support.c (GC_register_my_thread): If thread is FINISHED + then call GC_record_stack_base, clear FINISHED, initialize + thread-local list and return success. + * win32_threads.c (GC_register_my_thread): Ditto. + * include/gc.h (GC_register_my_thread): Update documentation. + * include/private/thread_local_alloc.h (GC_thread_key): Ditto. + +2011-05-10 Ivan Maidanski <ivmai@mail.ru> + + * thread_local_alloc.c (GC_malloc, GC_malloc_atomic): Join + adjacent "#ifdef". + * thread_local_alloc.c (GC_malloc_atomic): Call + GC_core_malloc_atomic (instead of GC_core_malloc). + +2011-05-10 Ivan Maidanski <ivmai@mail.ru> + + * pthread_start.c (GC_start_rtn_prepare_thread): Change return + type to GC_thread. + * pthread_start.c (GC_inner_start_routine): Pass the current + thread descriptor to pthread_cleanup_push (same as in + win32_threads.c). + * pthread_stop_world.c (GC_push_all_stacks): Rename "me" local + variable to "self". + * win32_threads.c (GC_push_all_stacks): Ditto. + * pthread_stop_world.c (GC_suspend_all, GC_start_world): Rename + "my_thread" local variable to "self". + * pthread_support.c (GC_unregister_my_thread_inner): New static + function. + * pthread_support.c (GC_unregister_my_thread, + GC_thread_exit_proc): Use GC_unregister_my_thread_inner. + * win32_threads.c (GC_register_my_thread, GC_unregister_my_thread, + GC_do_blocking_inner): Rename "t" local variable to "thread_id". + * win32_threads.c (GC_wait_marker, GC_notify_all_marker): Rename + "id" local variable to "thread_id". + +2011-05-10 Ivan Maidanski <ivmai@mail.ru> + + * pthread_support.c (GC_unregister_my_thread): Call pthread_self + only once. + * win32_threads.c (GC_pthread_start_inner): Ditto. + * pthread_support.c (GC_unregister_my_thread): Add debug output. + * win32_threads.c (GC_unregister_my_thread): Ditto. + * pthread_support.c (GC_register_my_thread, + GC_start_rtn_prepare_thread): Rename "my_pthread" local variable + to "self". + +2011-05-10 Ivan Maidanski <ivmai@mail.ru> + + * include/gc.h (GC_HIDE_POINTER, GC_REVEAL_POINTER): Define + unconditionally (do not test GC_I_HIDE_POINTERS); update the + comment. + * include/gc.h (HIDE_POINTER, REVEAL_POINTER): Define as alias to + GC_HIDE/REVEAL_POINTER, respectively. + * include/private/gc_pmark.h (GC_I_HIDE_POINTERS): Do not define. + * include/private/gc_priv.h (GC_I_HIDE_POINTERS): Ditto. + +2011-05-10 Ivan Maidanski <ivmai@mail.ru> + + * include/gc.h (GC_register_my_thread): Refine the comment. + +2011-05-08 Ivan Maidanski <ivmai@mail.ru> + + * include/gc_inline.h (GC_MALLOC_WORDS, GC_CONS): Add missing + parentheses. + * include/gc_typed.h (GC_get_bit, GC_set_bit, + GC_CALLOC_EXPLICITLY_TYPED): Ditto. + +2011-05-07 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gcconfig.h (NO_GETCONTEXT): Add missing ')'. + +2011-05-07 Ivan Maidanski <ivmai@mail.ru> (really Thorsten Glaser) + + * include/private/gcconfig.h (NO_GETCONTEXT): Do not use + getcontext(2) on m68k because it is not implemented there. + +2011-05-07 Ivan Maidanski <ivmai@mail.ru> + + * alloc.c (GC_clear_a_few_frames): Use BZERO(). + * mark_rts.c (GC_clear_roots, GC_rebuild_root_index): Ditto. + * reclaim.c (GC_start_reclaim): Ditto. + * blacklst.c (total_stack_black_listed): Remove "len" local + variable. + * dbg_mlc.c (GC_generate_random_valid_address): Replace "for" + statement with "do-while" one. + * dyn_load.c (GC_register_dynamic_libraries, + GC_register_dynlib_callback): Remove redundant parentheses. + +2011-05-06 Ivan Maidanski <ivmai@mail.ru> + + * cord/cordxtra.c (CORD_from_file_lazy_inner): Suppress + "unused result" compiler warning for fread(). + * cord/cordxtra.c: Expand all tabs to spaces. + +2011-05-06 Ivan Maidanski <ivmai@mail.ru> + + * os_dep.c (GC_write_fault_handler): Break when in_allocd_block + is set to true. + +2011-05-06 Ivan Maidanski <ivmai@mail.ru> + + * dbg_mlc.c (GC_has_other_debug_info): Change return type to int; + return -1 if the object has (or had) debugging info but was + marked deallocated. + * include/private/dbg_mlc.h (GC_has_other_debug_info): Ditto. + * dbg_mlc.c (GC_has_other_debug_info): Update documentation; + remove "ohdr" local variable. + * dbg_mlc.c (GC_debug_free): Don't call GC_free if the object has + probably been deallocated. + * dbg_mlc.c (GC_debug_free): Don't actually free the object even + in the leak-finding mode if GC_findleak_delay_free. + * dbg_mlc.c (GC_print_all_smashed_proc): Print a trailing blank + line. + * dbg_mlc.c (GC_check_leaked): New function (only unless + SHORT_DBG_HDRS). + * doc/README.environment (GC_FINDLEAK_DELAY_FREE): Document. + * doc/README.macros (GC_FINDLEAK_DELAY_FREE): Ditto. + * include/private/dbg_mlc.h (START_FLAG, END_FLAG): Use GC_WORD_C + on 64-bit architectures. + * include/private/dbg_mlc.h (NOT_MARKED): Remove redundant + parentheses. + * include/private/dbg_mlc.h (GC_HAS_DEBUG_INFO): Update (due to + GC_has_other_debug_info change). + * include/private/gc_priv.h (GC_findleak_delay_free): New global + variable declaration (unless SHORT_DBG_HDRS). + * misc.c (GC_findleak_delay_free): New global variable; recognize + GC_FINDLEAK_DELAY_FREE. + * misc.c (GC_init): Recognize GC_FINDLEAK_DELAY_FREE environment + variable (unless SHORT_DBG_HDRS). + * reclaim.c (GC_check_leaked): Declare (unless SHORT_DBG_HDRS). + * reclaim.c (GC_add_leaked): Don't add the object to leaked list + if marked as deallocated. + +2011-05-05 Ivan Maidanski <ivmai@mail.ru> + + * dbg_mlc.c (GC_has_other_debug_info): Fix punctuation in the + comment. + * dbg_mlc.c (GC_FREED_MEM_MARKER): New macro. + * dbg_mlc.c (GC_debug_free): Use GC_FREED_MEM_MARKER. + * dbg_mlc.c (GC_smashed): Refine documentation. + * mark.c (GC_push_selected): Change dirty_fn return type to + GC_bool. + * os_dep.c (GC_page_was_ever_dirty): Make GC_INNER. + * reclaim.c (GC_reclaim_small_nonempty_block): Remove "kind" + local variable. + * reclaim.c (GC_reclaim_block): Pass true constant to + GC_reclaim_small_nonempty_block (instead of report_if_found). + * doc/README.autoconf: Update; fix a typo. + * include/private/gcconfig.h (GC_WORD_C): New macro. + +2011-05-03 Ivan Maidanski <ivmai@mail.ru> + + * dbg_mlc.c (GC_store_debug_info_inner): Cast "linenum". + * dbg_mlc.c (GC_check_annotated_obj): Fix punctuation in the + comment. + * dbg_mlc.c (GC_print_smashed_obj): Add (and print) "msg" + argument. + * dbg_mlc.c (GC_debug_free, GC_print_all_smashed_proc): Pass + message to GC_print_smashed_obj. + * dbg_mlc.c (GC_debug_free): Call GC_size once. + * dbg_mlc.c (GC_debug_realloc): Calculate old_sz only if + allocation succeeded; remove unnecessary check for object is + smashed (since this is done in GC_debug_free); remove "clobbered" + local variable. + +2011-05-03 Ivan Maidanski <ivmai@mail.ru> + + * dbg_mlc.c (GC_store_debug_info_inner, GC_store_debug_info): + Rename "integer" argument to "linenum"; change the type of the + argument to int. + * gcj_mlc.c (GC_store_debug_info): Ditto. + * dbg_mlc.c (GET_OH_LINENUM): New macro. + * dbg_mlc.c (GC_print_obj, GC_print_smashed_obj): Use + GET_OH_LINENUM; adjust print format specifier. + * dbg_mlc.c (GC_debug_malloc, GC_debug_malloc_ignore_off_page, + GC_debug_malloc_atomic_ignore_off_page, + GC_debug_generic_malloc_inner, + GC_debug_generic_malloc_inner_ignore_off_page, + GC_debug_malloc_stubborn, GC_debug_malloc_atomic, + GC_debug_malloc_uncollectable, + GC_debug_malloc_atomic_uncollectable): Remove unnecessary cast of + "i". + * gcj_mlc.c (GC_debug_gcj_malloc): Ditto. + +2011-04-26 Ivan Maidanski <ivmai@mail.ru> + + * .cvsignore (initsecondarythread, test_cpp): Add. + * os_dep.c (GC_linux_stack_base): Rename to + GC_linux_main_stack_base. + * os_dep.c (GC_freebsd_stack_base): Rename to + GC_freebsd_main_stack_base; adjust error message. + * pthread_stop_world.c (GC_stop_init): Use GC_SEM_INIT_PSHARED + as an argument for sem_init(). + * pthread_support.c (pthread_create): Ditto. + * pthread_support.c (pthread_create): Abort in case sem_init() + fails. + * include/private/gc_priv.h (GC_SEM_INIT_PSHARED): Define. + * tests/initsecondarythread.c: Include gcconfig.h; call GC_INIT + from main() if it should be done from the primordial thread only. + +2011-04-26 Ivan Maidanski <ivmai@mail.ru> + + * alloc.c: Don't include sys/types.h for ArmCC. + * dyn_load.c: Ditto. + * os_dep.c: Ditto. + * mach_dep.c (_setjmp, _longjmp): Redirect to setjmp/longjmp for + ArmCC. + * mark.c (GC_noop): Define specially for ArmCC. + * include/private/gc_priv.h (GC_noop): Ditto. + * misc.c (GC_init): Don't test pointers comparison for ArmCC. + * misc.c: Don't include unistd.h for ArmCC. + * os_dep.c (pages_executable): Rename to GC_pages_executable; + make STATIC. + * os_dep.c (GC_unix_mmap_get_mem): Don't define for ArmCC. + * ptr_chck.c (GC_is_visible): Explicitly cast + (GC_DS_PER_OBJECT-GC_INDIR_PER_OBJ_BIAS) to word (to suppress + a compiler warning). + * include/private/gcconfig.h: Recognize __arm. + * include/private/gcconfig.h (HBLKPTR): Define for ArmCC. + * include/private/gcconfig.h (HBLKPTR): Add parentheses for + "bytes" argument. + +2011-04-24 Ivan Maidanski <ivmai@mail.ru> + + * pthread_support.c (GC_get_nprocs): Don't define for Android. + * pthread_support.c (GC_dummy_thread_local): Don't test + GC_LINUX_THREADS. + * include/gc_config_macros.h (GC_ADD_CALLER, GC_RETURN_ADDR): + Define for Android. + +2011-04-24 Ivan Maidanski <ivmai@mail.ru> + + * mach_dep.c (NO_GETCONTEXT): Move to gcconfig.h. + * os_dep.c (GC_write_fault_handler): Don't include ucontext.h if + NO_GETCONTEXT. + * include/private/gcconfig.h (GETPAGESIZE): Define as a sysconf + call for Android. + +2011-04-23 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gc_locks.h (WIN32_LEAN_AND_MEAN, NOSERVICE): + Define before including windows.h. + * include/private/gc_priv.h (WIN32_LEAN_AND_MEAN, NOSERVICE): + Ditto. + * include/private/thread_local_alloc.h (WIN32_LEAN_AND_MEAN, + NOSERVICE): Ditto. + * include/private/gc_priv.h (CLOCKS_PER_SEC): Reformat the + comment. + * include/private/gc_priv.h (MS_TIME_DIFF): Avoid floating-point + arithmetics; add a comment. + +2011-04-23 Ivan Maidanski <ivmai@mail.ru> + + * mark.c (GC_clear_hdr_marks): Don't test USE_MARK_BYTES. + * extra/setjmp_t.c (main): Don't test USE_MARK_BITS. + * include/private/gc_pmark.h (SET_MARK_BIT_EXIT_IF_SET): Ditto. + * include/private/gc_pmark.h (SET_MARK_BIT_EXIT_IF_SET): Remove + "mark_byte" local variable. + * include/private/gc_pmark.h (OR_WORD_EXIT_IF_SET): Add a comment + about that AO_or() is not used by GC unless USE_MARK_BITS + explicitly set. + * include/private/gc_priv.h (OR_WORD): Ditto. + * include/private/gc_pmark.h (INCR_MARKS): Remove trailing ';', + add parentheses. + * include/private/gc_priv.h (ONES): Define before use by + MAKE_COOLER. + * include/private/gc_priv.h (MARK_BITS_SZ): Define where used. + * include/private/gc_priv.h (OR_WORD): Don't define if + USE_MARK_BYTES. + * include/private/gcconfig.h (USE_MARK_BYTES); Remove duplicate + definition; simplify expression. + +2011-04-22 Ivan Maidanski <ivmai@mail.ru> + + * os_dep.c (GC_get_maps): Always close the file. + * pthread_support.c (GC_get_nprocs): Ditto. + * os_dep.c (READ): Define similarly across the file (without + parameters). + * pthread_support.c (GC_get_nprocs): Use signed int type for "i" + and "len" local variables (since read() may return -1). + * include/private/gc_pmark.h (LONG_MULT): Add prefix/suffix + double underscore; add "volatile" for asm. + * include/private/gc_pmark.h (LONG_MULT): Add missing + parentheses. + * include/private/gc_priv.h (OR_WORD): Ditto. + * include/private/gc_priv.h (OR_WORD): Remove unnecessary brackets + and ';' symbol. + +2011-04-22 Ivan Maidanski <ivmai@mail.ru> + + * os_dep.c (GC_get_stack_base): Implement for Android (same as + for Linux). + * pthread_support.c (GC_get_nprocs): Return 1 (instead of -1) if + failed to open "stat" file (not to issue a warning twice); update + the comment. + * pthread_support.c (GC_thr_init): Call sysconf() on Android to + get the number of CPUs. + +2011-04-21 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gc_priv.h (_GNU_SOURCE): Revert one of the + recent patches regarding this macro as the macro should be set + (to 1) before including any other system header. + +2011-04-21 Ivan Maidanski <ivmai@mail.ru> + + * doc/README.environment (GC_INITIAL_HEAP_SIZE, + GC_MAXIMUM_HEAP_SIZE): Update. + +2011-04-20 Ivan Maidanski <ivmai@mail.ru> + + * misc.c (GC_parse_mem_size_arg): Allow 'k', 'M', 'G' suffixes in + heap size specifier; return 0 if not a valid one. + * include/gc_cpp.h: Explicitly define inline one-argument delete + operator for Cygwin (as a workaround). + * include/gc_cpp.h: Reformat the code. + * tests/test_cpp.cc: Ditto. + * tests/test_cpp.cc (main): Suppress compiler warnings about + "assigned value is unused". + +2011-04-19 Ivan Maidanski <ivmai@mail.ru> + + * misc.c (GC_parse_mem_size_arg): New function. + * misc.c (GC_init): Use GC_parse_mem_size_arg(). + * pthread_stop_world.c (tkill): Declare for Android. + +2011-04-19 Ivan Maidanski <ivmai@mail.ru> + + * include/private/gc_priv.h (_GNU_SOURCE): Include features.h + first (except for NaCl) and then define the macro to 1 if not yet. + +2011-04-18 Ivan Maidanski <ivmai@mail.ru> (really Ludovic Courtes) + + * tests/tests.am (TESTS, check_PROGRAMS): Add + 'initsecondarythread'. + * tests/tests.am (initsecondarythread_SOURCES, + initsecondarythread_LDADD): New variable. + * Makefile.in: Regenerate. + * configure: Ditto. + +2011-04-18 Ivan Maidanski <ivmai@mail.ru> + + * dbg_mlc.c (GC_store_debug_info_inner): Always define; add + "const" to its string argument. + * dbg_mlc.c (GC_store_debug_info): Call GC_store_debug_info_inner. + * dbg_mlc.c (GC_debug_free): Set GC_have_errors in case of + smashed or previously deallocated found. + * dbg_mlc.c (GC_check_heap_block): Replace while loop with a for + one. + * reclaim.c (GC_reclaim_check): Ditto. + * dbg_mlc.c (GC_check_heap_proc): Remove redundant cast to word. + * os_dep.c (GC_get_stack_base): Don't initialize + stackbase_main_self/ss_sp on Solaris if thr_main() is zero (thus + calling GC_INIT() from a non-primordial thread is possible now). + * reclaim.c (GC_add_leaked): Turn into an inline one. + * reclaim.c (GC_reclaim_small_nonempty_block): + Change report_if_found type from int/word to boolean. + * include/private/gc_priv.h (GC_start_reclaim): Ditto. + * include/private/gc_priv.h (set_mark_bit_from_hdr, + clear_mark_bit_from_hdr): Place closing parenthesis properly; + reformat the code. + +2011-04-16 Ivan Maidanski <ivmai@mail.ru> + + * os_dep.c (GC_get_main_stack_base): Try to use + pthread_attr_getstack first for Linux if THREADS. + * doc/README.macros (USE_GET_STACKBASE_FOR_MAIN): Adjust text + alignment. + +2011-04-13 Ivan Maidanski <ivmai@mail.ru> + + * dbg_mlc.c (GC_generate_random_backtrace_no_gc): Fix a message + typo. + * dbg_mlc.c (GC_debug_malloc): Add a comment (about zero size). + * dbg_mlc.c (GC_debug_generic_malloc_inner): Reformat the comment. + * dbg_mlc.c (GC_strdup): Call GC_err_printf instead of WARN (in + case of NULL argument). + * dbg_mlc.c (GC_free): In case of NULL argument, just return + (without any warning printed); eliminate "uncollectable" local + variable. + +2011-04-13 Ivan Maidanski <ivmai@mail.ru> (mostly really Rainer Orth) + + * configure.ac (THREADDLLIBS): Use alternate thread library on + Solaris 8. + * configure.ac (need_atomic_ops_asm): Set to true only for Sparc + Solaris. + * configure.ac: Don't use libdl on mips-sgi-irix6. + * configure: Regenerate. + +2011-04-11 Ivan Maidanski <ivmai@mail.ru> (really Jie Liu) + + * mach_dep.c (NO_GETCONTEXT); Define for RTEMS. + * mach_dep.c (GC_with_callee_saves_pushed): Don't call + __builtin_unwind_init() for RTEMS; use setjmp() without the + leading underscore (for RTEMS). + * tests/test.c (BIG): Use smaller value for RTEMS. + * tests/test.c (main): Customize for RTEMS. + +2011-04-11 Ivan Maidanski <ivmai@mail.ru> (mostly really Jim Meyering) + + * configure.host: Remove doubled words in comments. + * os_dep.c: Ditto. + * doc/README: Ditto. + * extra/setjmp_t.c: Ditto. + * tests/huge_test.c: Ditto. + * extra/setjmp_t.c (getpagesize, nested_sp, main, g): Replace the + K&R-style function definition with the ANSI C one. + * extra/setjmp_t.c: Expand all tabs to spaces. + * extra/setjmp_t.c (nested_sp): Implement in the same way as + GC_approx_sp. + +2011-04-10 Ivan Maidanski <ivmai@mail.ru> (really Iain Sandoe, Mike Stump) + + * dyn_load.c (GC_dyld_sections): Add more sctions. + * dyn_load.c (GC_dyld_add_sect_fmts): New static varaible. + * dyn_load.c (L2_MAX_OFILE_ALIGNMENT): New macro. + * dyn_load.c (GC_dyld_image_add, GC_dyld_image_remove): Improve + logging; add support for on-demand sections. + * dyn_load.c (GC_dyld_image_add, GC_dyld_image_remove): Reformat + the code. + +2011-04-10 Ivan Maidanski <ivmai@mail.ru> + + * gcj_mlc.c (GC_gcj_malloc_initialized): Use STATIC unless + GC_ASSERTIONS. + * include/private/gc_priv.h (GC_gcj_malloc_initialized): Don't + declare (as external) unless GC_ASSERTIONS. + * os_dep.c (GC_win32_free_heap): Clear GC_heap_bases[] also for + Cygwin; add FIXME. + * include/private/gcconfig.h: Include <sys/unistd.h> for RTEMS. + * include/private/gcconfig.h: Add "#error" for every "-->" mark. + * include/private/gcconfig.h (CLEAR_DOUBLE): Turn the code into + an expression. + * include/private/pthread_support.h (SUSPENDED_EXT): Add new flag + (which existed previously as SUSPENDED and still exists in GCJ). + * include/private/pthread_support.h (DISABLED_GC): Change the + value (as it is already used by SUSPENDED_EXT). + +2011-04-10 Ivan Maidanski <ivmai@mail.ru> (mostly really Iain Sandoe) + + * tests/test.c (reverse_test): Modify count (BIG) for + ppc64-darwin. + +2011-04-09 Ivan Maidanski <ivmai@mail.ru> + + * reclaim.c (GC_print_all_errors): Recognize new GC_ABORT_ON_LEAK + macro and environment variable; abort if any error has been + printed provided the environment variable (or macro) is set. + * doc/README.environment (GC_ABORT_ON_LEAK): Document. + * doc/README.macros (GC_ABORT_ON_LEAK): Ditto. + * doc/README.macros (FIND_LEAK, SUNOS5SIGS, PCR, + USE_COMPILER_TLS): Reformat the text. + +2011-04-09 Ivan Maidanski <ivmai@mail.ru> (really Jie Liu) + + * os_dep.c (GC_unix_sbrk_get_mem, GC_unix_get_mem): Don't define + for RTEMS. + * include/private/gcconfig.h (RTEMS): Add support for. + * include/private/gcconfig.h (GET_MEM): Use calloc() for RTEMS. + +2011-04-09 Ivan Maidanski <ivmai@mail.ru> + + * mallocx.c (GC_malloc_uncollectable): Move to malloc.c (since + it is used internally in some places). + +2011-04-09 Ivan Maidanski <ivmai@mail.ru> + + * dbg_mlc.c (GC_register_finalizer_no_order): Remove redundant + declaration. + * dbg_mlc.c (GC_debug_malloc_replacement, + GC_debug_realloc_replacement): Rename RA to GC_DBG_RA. + * malloc.c (GC_debug_malloc_replacement): Ditto. + * mallocx.c (GC_debug_realloc_replacement): Ditto. + * dbg_mlc.c (GC_store_debug_info): Move proto from dbg_mlc.h. + * malloc.c (GC_strdup, GC_strndup, GC_wcsdup): Move to mallocx.c. + * malloc.c: Include errno.h only REDIRECT_MALLOC; remove redundant + includes of string.h. + * mallocx.c: Include string.h (for GC_strdup). + * include/private/dbg_mlc.h (GC_store_debug_info): Move declaration + to dbg_mlc.c. + * include/private/gc_locks.h (UNCOND_LOCK, UNCOND_UNLOCK): Remove + redundant trailing ';'. + * include/private/gc_priv.h (START_WORLD, COND_DUMP): Ditto. + * include/private/gc_locks.h (LOCK, UNLOCK): Place opening '{' + properly. + * include/private/gc_priv.h (GC_DBG_RA): Move from dbg_mlc.c, + malloc.c, mallocx.c. + +2011-04-07 Ivan Maidanski <ivmai@mail.ru> + + * alloc.c (GC_check_heap, GC_print_all_smashed): Move the + definition from misc.c. + * dbg_mlc.c (GC_debug_malloc_atomic_uncollectable): Define as + public. + * include/gc.h (GC_debug_malloc_atomic_uncollectable): Declare. + * include/gc.h (GC_MALLOC_ATOMIC_UNCOLLECTABLE): Define new public + macro. + * dbg_mlc.c (MAX_SMASHED): Don't define if already set. + * reclaim.c (MAX_LEAKED): Ditto. + * dbg_mlc.c (GC_add_smashed): Add FIXME about the concurrent + access to the global array. + * reclaim.c (GC_add_leaked): Ditto. + * misc.c (GC_print_back_height): Set on if GC_PRINT_BACK_HEIGHT + (new macro) is defined. + * doc/README.macros (GC_PRINT_BACK_HEIGHT): Document. + * misc.c (GC_dump_regularly, GC_init): Replace 0/1 for + GC_dump_regularly and GC_print_back_height variables with + FALSE/TRUE. + * reclaim.c (GC_print_all_errors): Refine the comment. + +2011-04-04 Ivan Maidanski <ivmai@mail.ru> + + * tests/test.c (reverse_test_inner): Undo one of the previous + patches which shifts "c" and "d" pointers only if + ALL_INTERIOR_POINTERS (since interior pointers are always + recognized in stacks). + +2011-04-03 Ivan Maidanski <ivmai@mail.ru> + + * misc.c (GC_stdout, GC_stderr): Move the definition to the place + where GC_log is defined (Unix only). + * misc.c (GC_init): Recognize "GC_ONLY_LOG_TO_FILE" environment + variable and the similar macro; redirect GC_stdout and GC_stderr + to GC_log if "GC_LOG_FILE" environment variable is set unless + prohibited by GC_ONLY_LOG_TO_FILE (Unix only). + * doc/README.environment (GC_ONLY_LOG_TO_FILE): Document. + * doc/README.macros (GC_ONLY_LOG_TO_FILE): Ditto. + +2011-04-03 Ivan Maidanski <ivmai@mail.ru> + + * misc.c (GC_stdout, GC_write): Rename GC_stdout to GC_log (Win32 + only). + * misc.c (GC_write): Add for MacOS (and OS/2); change WRITE() + accordingly. + * misc.c (GC_write): Reformat code (Unix only). + * misc.c (GC_printf): Check GC_quiet before va_start(). + +2011-04-03 Ivan Maidanski <ivmai@mail.ru> + + * allchblk.c (GC_freehblk): Use GC_log_printf instead of GC_printf + inside "if (GC_print_stats)" branch. + * alloc.c (GC_collect_or_expand): Ditto. + * dyn_load.c (GC_register_dynamic_libraries): Ditto. + * headers.c (GC_scratch_alloc): Ditto. + * os_dep.c (GC_get_maps, GC_remap, PROTECT, + GC_write_fault_handler, GC_dirty_init, GC_mprotect_thread): Ditto. + * alloc.c (min_bytes_allocd): Use GC_log_printf instead of + GC_printf for DEBUG_THREADS output. + * darwin_stop_world.c (GC_stack_range_for, GC_suspend_thread_list, + GC_stop_world, GC_thread_resume, GC_start_world): Ditto. + * pthread_start.c (GC_inner_start_routine): Ditto. + * pthread_stop_world.c (GC_suspend_handler, GC_restart_handler, + GC_push_all_stacks, GC_suspend_all, GC_stop_world, + GC_start_world): Ditto. + * pthread_support.c (GC_mark_thread, GC_get_nprocs, + GC_start_rtn_prepare_thread, pthread_create): Ditto. + * alloc.c (GC_adj_bytes_allocd, GC_maybe_gc, GC_stopped_mark, + GC_finish_collection): Reformat code. + * pthread_stop_world.c (GC_print_sig_mask): Ditto. + * pthread_support.c (GC_thr_init): Ditto. + * checksums.c (GC_update_check_page): Use GC_printf() instead of + GC_err_printf() for error printing. + * checksums.c (GC_check_blocks, GC_check_dirty): Use GC_log_printf + instead of GC_printf for logging purposes. + * dyn_load.c (sys_errlist, sys_nerr, errno): Move declaration of + external variable outside from GC_register_dynamic_libraries. + * dyn_load.c (GC_register_dynamic_libraries): Don't use + sys_errlist value if errno equals to sys_nerr. + * dyn_load.c (GC_register_dynamic_libraries): Use GC_log_printf + instead of GC_printf for DL_VERBOSE output. + * dyn_load.c (GC_dyld_image_add, GC_dyld_image_remove, + GC_init_dyld): Use GC_log_printf instead of GC_printf for + DARWIN_DEBUG output. + * os_dep.c (catch_exception_raise): Use GC_log_printf + instead of GC_printf for DEBUG_EXCEPTION_HANDLING output. + * reclaim.c (GC_print_free_list): Move "n" increment out of + GC_printf() call. + +2011-04-03 Ivan Maidanski <ivmai@mail.ru> + + * win32_threads.c (DEBUG_CYGWIN_THREADS, DEBUG_WIN32_PTHREADS, + DEBUG_WIN32_THREADS): Remove. + * win32_threads.c (GC_register_my_thread_inner, + GC_win32_start_inner): Use GC_log_printf instead of GC_printf + inside "if (GC_print_stats)" branch. + * win32_threads.c (GC_PTHREAD_PTRVAL): New macro (defined only if + GC_PTHREADS). + * win32_threads.c (GC_delete_gc_thread, NUMERIC_THREAD_ID, + GC_pthread_join, GC_pthread_create): Use GC_PTHREAD_PTRVAL + macro. + * win32_threads.c (GC_push_stack_for, GC_mark_thread, + GC_CreateThread, GC_beginthreadex, GC_pthread_join, + GC_pthread_create, GC_pthread_start_inner, GC_thread_exit_proc, + GC_mark_thread_local_free_lists): Use GC_log_printf instead of + GC_printf for DEBUG_THREADS output. + * win32_threads.c (GC_win32_start_inner, GC_CreateThread, + GC_beginthreadex, GC_pthread_join, GC_pthread_create, + GC_pthread_start_inner, GC_thread_exit_proc): Cast + GetCurrentThreadId result to long; don't cast value of pthread_t + type to int; adjust printf format specifiers. + * doc/README.win32 (DEBUG_WIN32_PTHREADS): Remove obsolete + information. + +2011-04-03 Ivan Maidanski <ivmai@mail.ru> + + * tests/test.c (cons, small_cons, gcj_cons, check_ints, + check_uncollectable_ints, print_int_list, check_marks_int_list, + fork_a_thread, finalizer, mktree, chktree, alloc8bytes, + alloc_small, tree_test, typed_test, check_heap_stats, WinMain, + test, main): Remove unnecessary casts of GC_printf calls to void. + +2011-04-02 Ivan Maidanski <ivmai@mail.ru> + + * allchblk.c (GC_print_hblkfreelist): Adjust (make uniform across + BDWGC) printed message (adjust letters case, terminating dot and + new line symbols). + * alloc.c (GC_check_fl_marks): Ditto. + * backgraph.c (new_back_edges): Ditto. + * checksums.c (GC_check_dirty): Ditto. + * darwin_stop_world.c (GC_push_all_stacks, + GC_suspend_thread_list): Ditto. + * dbg_mlc.c (GC_print_type, GC_debug_free, GC_debug_realloc, + store_old): Ditto. + * dyn_load.c (GC_register_dynamic_libraries): Ditto. + * mark.c (GC_initiate_gc, GC_mark_some, GC_mark_from, GC_push_all, + GC_push_selected, GC_push_next_marked_dirty): Ditto. + * mark_rts.c (GC_exclude_static_roots_inner): Ditto. + * os_dep.c (GC_remap, GC_default_push_other_roots, + GC_push_thread_structures, GC_dirty_init, GC_read_dirty, + catch_exception_raise_state, catch_exception_raise_state_identity, + GC_mprotect_thread_notify, GC_mprotect_thread, + catch_exception_raise): Ditto. + * pthread_stop_world.c (GC_print_sig_mask, GC_push_all_stacks, + GC_stop_world, GC_stop_init): Ditto. + * pthread_support.c (GC_thr_init, GC_register_my_thread_inner, + GC_start_routine): Ditto. + * win32_threads.c (GC_register_my_thread_inner, + GC_push_all_stacks, GC_win32_start_inner, GC_pthread_join, + GC_pthread_start_inner): Ditto. + * alloc.c (GC_expand_hp_inner): Realign the code. + * mark.c (GC_mark_from, GC_mark_local, GC_do_parallel_mark): + Ditto. + * misc.c (GC_init): Ditto. + * os_dep.c (GC_dirty_init, GC_read_dirty): Ditto. + * include/private/gc_pmark.h (PUSH_CONTENTS_HDR): Ditto. + * tests/test.c (run_one_test): Ditto. + * misc.c (GC_err_puts): Document. + * misc.c (GC_err_write): Remove. + * os_dep.c (dump_maps): Ditto. + * include/private/gc_priv.h (GC_err_write): Ditto. + * os_dep.c (GC_print_address_map): Call GC_err_puts() instead of + dump_maps() and GC_err_write(). + * os_dep.c (GC_read_dirty): Remove redundant brackets. + +2011-04-02 Ivan Maidanski <ivmai@mail.ru> + + * tests/test.c (reverse_test_inner): Test interior pointer + recognition only if ALL_INTERIOR_POINTERS. + * tests/test.c (run_one_test): Replace GC_all_interior_pointers + with GC_get_all_interior_pointers(); simplify the expression. + * tests/test.c (check_heap_stats): Replace GC_bytes_allocd and + GC_bytes_allocd_before_gc with GC_get_total_bytes(). + * tests/test.c (main): Replace GC_gc_no with GC_get_gc_no(). + 2011-03-27 Ivan Maidanski <ivmai@mail.ru> * dbg_mlc.c (GC_debug_strdup, GC_debug_free): Output a portability @@ -49,12 +1186,13 @@ 2011-03-22 Ivan Maidanski <ivmai@mail.ru> - * misc.c (GC_abort): Use _exit() (instead of DebugBreak) on Win32 when - doing code static analysis (to inform the tool that the function is - a no-return one). - * os_dep.c (GC_linux_stack_base): Remove a duplicate validation of the - length of "stat" file; use signed int type for "i", "buf_offset" and - "len" local variables (since read() may return -1). + * misc.c (GC_abort): Use _exit() (instead of DebugBreak) on Win32 + when doing code static analysis (to inform the tool that the + function is a no-return one). + * os_dep.c (GC_linux_stack_base): Remove a duplicate validation + of the length of "stat" file; use signed int type for "i", + "buf_offset" and "len" local variables (since read() may + return -1). 2011-03-20 Ivan Maidanski <ivmai@mail.ru> @@ -584,7 +1722,7 @@ type). * os_dep.c (GC_dirty_init): Print a message about SIG_IGN detected (for SIGSEGV/BUS) only if GC_print_stats. - * os_dep.c (catch_exception_raise): Join 2 adjucent GC_err_printf + * os_dep.c (catch_exception_raise): Join 2 adjacent GC_err_printf calls. 2010-11-25 Ivan Maidanski <ivmai@mail.ru> diff --git a/Makefile.am b/Makefile.am index 319e116d..4de6e46f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -172,7 +172,7 @@ EXTRA_DIST += README.QUICK EXTRA_DIST += BCC_MAKEFILE NT_MAKEFILE \ OS2_MAKEFILE PCR-Makefile digimars.mak EMX_MAKEFILE \ Makefile.direct Makefile.dj Makefile.DLLs SMakefile.amiga \ - WCC_MAKEFILE build_atomic_ops.sh build_atomic_ops.sh.cygwin \ + WCC_MAKEFILE autogen.sh build_atomic_ops.sh build_atomic_ops.sh.cygwin \ NT_STATIC_THREADS_MAKEFILE NT_X64_STATIC_THREADS_MAKEFILE \ NT_X64_THREADS_MAKEFILE CMakeLists.txt tests/CMakeLists.txt diff --git a/Makefile.direct b/Makefile.direct index 99075fcd..707a94e2 100644 --- a/Makefile.direct +++ b/Makefile.direct @@ -418,7 +418,6 @@ gcname: $(srcdir)/extra/gcname.c $(srcdir)/include/gc_version.h dist gc.tar: $(SRCS) $(DOC_FILES) $(OTHER_FILES) add_gc_prefix gcname cp Makefile Makefile.old cp Makefile.direct Makefile - CC=$(CC) ./configure_atomic_ops.sh cd $(AO_SRC_DIR); $(MAKE) dist if test $(srcdir)/libatomic_ops = $(AO_SRC_DIR); \ then \ diff --git a/Makefile.in b/Makefile.in index 27163860..29d41ea0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -96,8 +96,9 @@ target_triplet = @target@ @USE_INTERNAL_LIBATOMIC_OPS_TRUE@ -I$(top_srcdir)/libatomic_ops/src check_PROGRAMS = gctest$(EXEEXT) leaktest$(EXEEXT) middletest$(EXEEXT) \ - smashtest$(EXEEXT) hugetest$(EXEEXT) staticrootstest$(EXEEXT) \ - $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) + smashtest$(EXEEXT) hugetest$(EXEEXT) realloc_test$(EXEEXT) \ + staticrootstest$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) \ + $(am__EXEEXT_3) # C Library: Architecture Dependent # --------------------------------- @@ -121,8 +122,11 @@ DIST_COMMON = $(am__configure_deps) $(am__pkginclude_HEADERS_DIST) \ mkinstalldirs @KEEP_BACK_PTRS_TRUE@am__append_7 = tracetest$(EXEEXT) @KEEP_BACK_PTRS_TRUE@am__append_8 = tracetest -@THREADS_TRUE@am__append_9 = threadleaktest$(EXEEXT) -@THREADS_TRUE@am__append_10 = threadleaktest +@THREADS_TRUE@am__append_9 = threadleaktest$(EXEEXT) \ +@THREADS_TRUE@ threadkey_test$(EXEEXT) \ +@THREADS_TRUE@ initsecondarythread$(EXEEXT) +@THREADS_TRUE@am__append_10 = threadleaktest threadkey_test \ +@THREADS_TRUE@ initsecondarythread @CPLUSPLUS_TRUE@am__append_11 = test_cpp$(EXEEXT) @CPLUSPLUS_TRUE@am__append_12 = test_cpp subdir = . @@ -213,19 +217,30 @@ libstaticrootslib_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libstaticrootslib_la_LDFLAGS) $(LDFLAGS) -o $@ @KEEP_BACK_PTRS_TRUE@am__EXEEXT_1 = tracetest$(EXEEXT) -@THREADS_TRUE@am__EXEEXT_2 = threadleaktest$(EXEEXT) +@THREADS_TRUE@am__EXEEXT_2 = threadleaktest$(EXEEXT) \ +@THREADS_TRUE@ threadkey_test$(EXEEXT) \ +@THREADS_TRUE@ initsecondarythread$(EXEEXT) @CPLUSPLUS_TRUE@am__EXEEXT_3 = test_cpp$(EXEEXT) am_gctest_OBJECTS = test.$(OBJEXT) gctest_OBJECTS = $(am_gctest_OBJECTS) am_hugetest_OBJECTS = huge_test.$(OBJEXT) hugetest_OBJECTS = $(am_hugetest_OBJECTS) hugetest_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__initsecondarythread_SOURCES_DIST = tests/initsecondarythread.c +@THREADS_TRUE@am_initsecondarythread_OBJECTS = \ +@THREADS_TRUE@ initsecondarythread.$(OBJEXT) +initsecondarythread_OBJECTS = $(am_initsecondarythread_OBJECTS) +@THREADS_TRUE@initsecondarythread_DEPENDENCIES = \ +@THREADS_TRUE@ $(am__DEPENDENCIES_2) am_leaktest_OBJECTS = leak_test.$(OBJEXT) leaktest_OBJECTS = $(am_leaktest_OBJECTS) leaktest_DEPENDENCIES = $(am__DEPENDENCIES_2) am_middletest_OBJECTS = middle.$(OBJEXT) middletest_OBJECTS = $(am_middletest_OBJECTS) middletest_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_realloc_test_OBJECTS = realloc_test.$(OBJEXT) +realloc_test_OBJECTS = $(am_realloc_test_OBJECTS) +realloc_test_DEPENDENCIES = $(am__DEPENDENCIES_2) am_smashtest_OBJECTS = smash_test.$(OBJEXT) smashtest_OBJECTS = $(am_smashtest_OBJECTS) smashtest_DEPENDENCIES = $(am__DEPENDENCIES_2) @@ -241,6 +256,10 @@ test_cpp_OBJECTS = $(am_test_cpp_OBJECTS) @AVOID_CPP_LIB_FALSE@@CPLUSPLUS_TRUE@ $(am__DEPENDENCIES_2) @AVOID_CPP_LIB_TRUE@@CPLUSPLUS_TRUE@test_cpp_DEPENDENCIES = gc_cpp.o \ @AVOID_CPP_LIB_TRUE@@CPLUSPLUS_TRUE@ $(am__DEPENDENCIES_2) +am__threadkey_test_SOURCES_DIST = tests/threadkey_test.c +@THREADS_TRUE@am_threadkey_test_OBJECTS = threadkey_test.$(OBJEXT) +threadkey_test_OBJECTS = $(am_threadkey_test_OBJECTS) +@THREADS_TRUE@threadkey_test_DEPENDENCIES = $(am__DEPENDENCIES_2) am__threadleaktest_SOURCES_DIST = tests/thread_leak_test.c @THREADS_TRUE@am_threadleaktest_OBJECTS = thread_leak_test.$(OBJEXT) threadleaktest_OBJECTS = $(am_threadleaktest_OBJECTS) @@ -283,16 +302,20 @@ LTCCASCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ SOURCES = $(libcord_la_SOURCES) $(libgc_la_SOURCES) \ $(EXTRA_libgc_la_SOURCES) $(nodist_libgc_la_SOURCES) \ $(libgccpp_la_SOURCES) $(libstaticrootslib_la_SOURCES) \ - $(gctest_SOURCES) $(hugetest_SOURCES) $(leaktest_SOURCES) \ - $(middletest_SOURCES) $(smashtest_SOURCES) \ - $(staticrootstest_SOURCES) $(test_cpp_SOURCES) \ + $(gctest_SOURCES) $(hugetest_SOURCES) \ + $(initsecondarythread_SOURCES) $(leaktest_SOURCES) \ + $(middletest_SOURCES) $(realloc_test_SOURCES) \ + $(smashtest_SOURCES) $(staticrootstest_SOURCES) \ + $(test_cpp_SOURCES) $(threadkey_test_SOURCES) \ $(threadleaktest_SOURCES) $(tracetest_SOURCES) DIST_SOURCES = $(libcord_la_SOURCES) $(am__libgc_la_SOURCES_DIST) \ $(EXTRA_libgc_la_SOURCES) $(am__libgccpp_la_SOURCES_DIST) \ $(libstaticrootslib_la_SOURCES) $(gctest_SOURCES) \ - $(hugetest_SOURCES) $(leaktest_SOURCES) $(middletest_SOURCES) \ - $(smashtest_SOURCES) $(staticrootstest_SOURCES) \ - $(am__test_cpp_SOURCES_DIST) \ + $(hugetest_SOURCES) $(am__initsecondarythread_SOURCES_DIST) \ + $(leaktest_SOURCES) $(middletest_SOURCES) \ + $(realloc_test_SOURCES) $(smashtest_SOURCES) \ + $(staticrootstest_SOURCES) $(am__test_cpp_SOURCES_DIST) \ + $(am__threadkey_test_SOURCES_DIST) \ $(am__threadleaktest_SOURCES_DIST) \ $(am__tracetest_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ @@ -538,13 +561,13 @@ AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include \ EXTRA_DIST = gc_cpp.cpp README.QUICK BCC_MAKEFILE NT_MAKEFILE \ OS2_MAKEFILE PCR-Makefile digimars.mak EMX_MAKEFILE \ Makefile.direct Makefile.dj Makefile.DLLs SMakefile.amiga \ - WCC_MAKEFILE build_atomic_ops.sh build_atomic_ops.sh.cygwin \ - NT_STATIC_THREADS_MAKEFILE NT_X64_STATIC_THREADS_MAKEFILE \ - NT_X64_THREADS_MAKEFILE CMakeLists.txt tests/CMakeLists.txt \ - extra/add_gc_prefix.c extra/gc.c extra/gcname.c \ - extra/if_mach.c extra/if_not_there.c hpux_test_and_clear.s \ - gc.mak extra/MacOS.c MacProjects.sit.hqx mach_dep.c \ - extra/setjmp_t.c extra/threadlibs.c extra/AmigaOS.c \ + WCC_MAKEFILE autogen.sh build_atomic_ops.sh \ + build_atomic_ops.sh.cygwin NT_STATIC_THREADS_MAKEFILE \ + NT_X64_STATIC_THREADS_MAKEFILE NT_X64_THREADS_MAKEFILE \ + CMakeLists.txt tests/CMakeLists.txt extra/add_gc_prefix.c \ + extra/gc.c extra/gcname.c extra/if_mach.c extra/if_not_there.c \ + hpux_test_and_clear.s gc.mak extra/MacOS.c MacProjects.sit.hqx \ + mach_dep.c extra/setjmp_t.c extra/threadlibs.c extra/AmigaOS.c \ Mac_files/datastart.c Mac_files/dataend.c \ Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \ include/private/msvc_dbg.h extra/msvc_dbg.c libatomic_ops \ @@ -583,8 +606,9 @@ dist_noinst_HEADERS = include/private/gc_hdrs.h \ include/ec.h include/javaxfc.h check_LTLIBRARIES = libstaticrootslib.la TESTS = gctest$(EXEEXT) leaktest$(EXEEXT) middletest$(EXEEXT) \ - smashtest$(EXEEXT) hugetest$(EXEEXT) staticrootstest$(EXEEXT) \ - $(am__append_7) $(am__append_9) $(am__append_11) + smashtest$(EXEEXT) hugetest$(EXEEXT) realloc_test$(EXEEXT) \ + staticrootstest$(EXEEXT) $(am__append_7) $(am__append_9) \ + $(am__append_11) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = bdw-gc.pc libgc_la_SOURCES = allchblk.c alloc.c blacklst.c checksums.c dbg_mlc.c \ @@ -652,6 +676,8 @@ smashtest_SOURCES = tests/smash_test.c smashtest_LDADD = $(test_ldadd) hugetest_SOURCES = tests/huge_test.c hugetest_LDADD = $(test_ldadd) +realloc_test_SOURCES = tests/realloc_test.c +realloc_test_LDADD = $(test_ldadd) staticrootstest_SOURCES = tests/staticrootstest.c staticrootstest_LDADD = $(test_ldadd) libstaticrootslib.la libstaticrootslib_la_SOURCES = tests/staticrootslib.c @@ -662,6 +688,10 @@ libstaticrootslib_la_DEPENDENCIES = $(top_builddir)/libgc.la @KEEP_BACK_PTRS_TRUE@tracetest_LDADD = $(test_ldadd) @THREADS_TRUE@threadleaktest_SOURCES = tests/thread_leak_test.c @THREADS_TRUE@threadleaktest_LDADD = $(test_ldadd) +@THREADS_TRUE@threadkey_test_SOURCES = tests/threadkey_test.c +@THREADS_TRUE@threadkey_test_LDADD = $(test_ldadd) +@THREADS_TRUE@initsecondarythread_SOURCES = tests/initsecondarythread.c +@THREADS_TRUE@initsecondarythread_LDADD = $(test_ldadd) @CPLUSPLUS_TRUE@test_cpp_SOURCES = tests/test_cpp.cc @AVOID_CPP_LIB_FALSE@@CPLUSPLUS_TRUE@test_cpp_LDADD = libgccpp.la $(test_ldadd) @AVOID_CPP_LIB_TRUE@@CPLUSPLUS_TRUE@test_cpp_LDADD = gc_cpp.o $(test_ldadd) @@ -826,12 +856,18 @@ gctest$(EXEEXT): $(gctest_OBJECTS) $(gctest_DEPENDENCIES) hugetest$(EXEEXT): $(hugetest_OBJECTS) $(hugetest_DEPENDENCIES) @rm -f hugetest$(EXEEXT) $(LINK) $(hugetest_OBJECTS) $(hugetest_LDADD) $(LIBS) +initsecondarythread$(EXEEXT): $(initsecondarythread_OBJECTS) $(initsecondarythread_DEPENDENCIES) + @rm -f initsecondarythread$(EXEEXT) + $(LINK) $(initsecondarythread_OBJECTS) $(initsecondarythread_LDADD) $(LIBS) leaktest$(EXEEXT): $(leaktest_OBJECTS) $(leaktest_DEPENDENCIES) @rm -f leaktest$(EXEEXT) $(LINK) $(leaktest_OBJECTS) $(leaktest_LDADD) $(LIBS) middletest$(EXEEXT): $(middletest_OBJECTS) $(middletest_DEPENDENCIES) @rm -f middletest$(EXEEXT) $(LINK) $(middletest_OBJECTS) $(middletest_LDADD) $(LIBS) +realloc_test$(EXEEXT): $(realloc_test_OBJECTS) $(realloc_test_DEPENDENCIES) + @rm -f realloc_test$(EXEEXT) + $(LINK) $(realloc_test_OBJECTS) $(realloc_test_LDADD) $(LIBS) smashtest$(EXEEXT): $(smashtest_OBJECTS) $(smashtest_DEPENDENCIES) @rm -f smashtest$(EXEEXT) $(LINK) $(smashtest_OBJECTS) $(smashtest_LDADD) $(LIBS) @@ -841,6 +877,9 @@ staticrootstest$(EXEEXT): $(staticrootstest_OBJECTS) $(staticrootstest_DEPENDENC test_cpp$(EXEEXT): $(test_cpp_OBJECTS) $(test_cpp_DEPENDENCIES) @rm -f test_cpp$(EXEEXT) $(CXXLINK) $(test_cpp_OBJECTS) $(test_cpp_LDADD) $(LIBS) +threadkey_test$(EXEEXT): $(threadkey_test_OBJECTS) $(threadkey_test_DEPENDENCIES) + @rm -f threadkey_test$(EXEEXT) + $(LINK) $(threadkey_test_OBJECTS) $(threadkey_test_LDADD) $(LIBS) threadleaktest$(EXEEXT): $(threadleaktest_OBJECTS) $(threadleaktest_DEPENDENCIES) @rm -f threadleaktest$(EXEEXT) $(LINK) $(threadleaktest_OBJECTS) $(threadleaktest_LDADD) $(LIBS) @@ -875,6 +914,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gcj_mlc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/headers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/huge_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initsecondarythread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/leak_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mallocx.Plo@am__quote@ @@ -891,6 +931,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pthread_support.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptr_chck.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/real_malloc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/realloc_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reclaim.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smash_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sparc_mach_dep.Plo@am__quote@ @@ -902,6 +943,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cpp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread_leak_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread_local_alloc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/threadkey_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trace_test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/typd_mlc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win32_threads.Plo@am__quote@ @@ -1018,6 +1060,20 @@ huge_test.obj: tests/huge_test.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o huge_test.obj `if test -f 'tests/huge_test.c'; then $(CYGPATH_W) 'tests/huge_test.c'; else $(CYGPATH_W) '$(srcdir)/tests/huge_test.c'; fi` +initsecondarythread.o: tests/initsecondarythread.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initsecondarythread.o -MD -MP -MF $(DEPDIR)/initsecondarythread.Tpo -c -o initsecondarythread.o `test -f 'tests/initsecondarythread.c' || echo '$(srcdir)/'`tests/initsecondarythread.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/initsecondarythread.Tpo $(DEPDIR)/initsecondarythread.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/initsecondarythread.c' object='initsecondarythread.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o initsecondarythread.o `test -f 'tests/initsecondarythread.c' || echo '$(srcdir)/'`tests/initsecondarythread.c + +initsecondarythread.obj: tests/initsecondarythread.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initsecondarythread.obj -MD -MP -MF $(DEPDIR)/initsecondarythread.Tpo -c -o initsecondarythread.obj `if test -f 'tests/initsecondarythread.c'; then $(CYGPATH_W) 'tests/initsecondarythread.c'; else $(CYGPATH_W) '$(srcdir)/tests/initsecondarythread.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/initsecondarythread.Tpo $(DEPDIR)/initsecondarythread.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/initsecondarythread.c' object='initsecondarythread.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o initsecondarythread.obj `if test -f 'tests/initsecondarythread.c'; then $(CYGPATH_W) 'tests/initsecondarythread.c'; else $(CYGPATH_W) '$(srcdir)/tests/initsecondarythread.c'; fi` + leak_test.o: tests/leak_test.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT leak_test.o -MD -MP -MF $(DEPDIR)/leak_test.Tpo -c -o leak_test.o `test -f 'tests/leak_test.c' || echo '$(srcdir)/'`tests/leak_test.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/leak_test.Tpo $(DEPDIR)/leak_test.Po @@ -1046,6 +1102,20 @@ middle.obj: tests/middle.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o middle.obj `if test -f 'tests/middle.c'; then $(CYGPATH_W) 'tests/middle.c'; else $(CYGPATH_W) '$(srcdir)/tests/middle.c'; fi` +realloc_test.o: tests/realloc_test.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT realloc_test.o -MD -MP -MF $(DEPDIR)/realloc_test.Tpo -c -o realloc_test.o `test -f 'tests/realloc_test.c' || echo '$(srcdir)/'`tests/realloc_test.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/realloc_test.Tpo $(DEPDIR)/realloc_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/realloc_test.c' object='realloc_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o realloc_test.o `test -f 'tests/realloc_test.c' || echo '$(srcdir)/'`tests/realloc_test.c + +realloc_test.obj: tests/realloc_test.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT realloc_test.obj -MD -MP -MF $(DEPDIR)/realloc_test.Tpo -c -o realloc_test.obj `if test -f 'tests/realloc_test.c'; then $(CYGPATH_W) 'tests/realloc_test.c'; else $(CYGPATH_W) '$(srcdir)/tests/realloc_test.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/realloc_test.Tpo $(DEPDIR)/realloc_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/realloc_test.c' object='realloc_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o realloc_test.obj `if test -f 'tests/realloc_test.c'; then $(CYGPATH_W) 'tests/realloc_test.c'; else $(CYGPATH_W) '$(srcdir)/tests/realloc_test.c'; fi` + smash_test.o: tests/smash_test.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT smash_test.o -MD -MP -MF $(DEPDIR)/smash_test.Tpo -c -o smash_test.o `test -f 'tests/smash_test.c' || echo '$(srcdir)/'`tests/smash_test.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/smash_test.Tpo $(DEPDIR)/smash_test.Po @@ -1074,6 +1144,20 @@ staticrootstest.obj: tests/staticrootstest.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o staticrootstest.obj `if test -f 'tests/staticrootstest.c'; then $(CYGPATH_W) 'tests/staticrootstest.c'; else $(CYGPATH_W) '$(srcdir)/tests/staticrootstest.c'; fi` +threadkey_test.o: tests/threadkey_test.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT threadkey_test.o -MD -MP -MF $(DEPDIR)/threadkey_test.Tpo -c -o threadkey_test.o `test -f 'tests/threadkey_test.c' || echo '$(srcdir)/'`tests/threadkey_test.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/threadkey_test.Tpo $(DEPDIR)/threadkey_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/threadkey_test.c' object='threadkey_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o threadkey_test.o `test -f 'tests/threadkey_test.c' || echo '$(srcdir)/'`tests/threadkey_test.c + +threadkey_test.obj: tests/threadkey_test.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT threadkey_test.obj -MD -MP -MF $(DEPDIR)/threadkey_test.Tpo -c -o threadkey_test.obj `if test -f 'tests/threadkey_test.c'; then $(CYGPATH_W) 'tests/threadkey_test.c'; else $(CYGPATH_W) '$(srcdir)/tests/threadkey_test.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/threadkey_test.Tpo $(DEPDIR)/threadkey_test.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tests/threadkey_test.c' object='threadkey_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o threadkey_test.obj `if test -f 'tests/threadkey_test.c'; then $(CYGPATH_W) 'tests/threadkey_test.c'; else $(CYGPATH_W) '$(srcdir)/tests/threadkey_test.c'; fi` + thread_leak_test.o: tests/thread_leak_test.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT thread_leak_test.o -MD -MP -MF $(DEPDIR)/thread_leak_test.Tpo -c -o thread_leak_test.o `test -f 'tests/thread_leak_test.c' || echo '$(srcdir)/'`tests/thread_leak_test.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/thread_leak_test.Tpo $(DEPDIR)/thread_leak_test.Po diff --git a/NT_MAKEFILE b/NT_MAKEFILE index f9077a3d..809d9f77 100644 --- a/NT_MAKEFILE +++ b/NT_MAKEFILE @@ -18,7 +18,7 @@ all: gctest.exe cord\de.exe test_cpp.exe $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -D_CRT_SECURE_NO_DEPRECATE $*.c /Fo$*.obj .cpp.obj: - $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -D_CRT_SECURE_NO_DEPRECATE $*.CPP /Fo$*.obj + $(cc) $(cdebug) $(cflags) $(cvars) -Iinclude -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj $(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h include\private\msvc_dbg.h diff --git a/NT_STATIC_THREADS_MAKEFILE b/NT_STATIC_THREADS_MAKEFILE index 6892d291..9a003478 100644 --- a/NT_STATIC_THREADS_MAKEFILE +++ b/NT_STATIC_THREADS_MAKEFILE @@ -25,7 +25,7 @@ all: gctest.exe cord\de.exe test_cpp.exe $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -DGC_THREADS -DTHREAD_LOCAL_ALLOC -DPARALLEL_MARK -D_CRT_SECURE_NO_DEPRECATE $*.c /Fo$*.obj .cpp.obj: - $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DGC_NOT_DLL $*.CPP -DGC_THREADS -DTHREAD_LOCAL_ALLOC -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj + $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -DGC_THREADS -DTHREAD_LOCAL_ALLOC -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj $(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h include\private\msvc_dbg.h diff --git a/NT_X64_STATIC_THREADS_MAKEFILE b/NT_X64_STATIC_THREADS_MAKEFILE index 7b31bfd5..93de9727 100644 --- a/NT_X64_STATIC_THREADS_MAKEFILE +++ b/NT_X64_STATIC_THREADS_MAKEFILE @@ -28,7 +28,7 @@ all: gctest.exe cord\de.exe test_cpp.exe # of safe uses of strncpy. It would be nice to leave the rest enabled. .cpp.obj: - $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -DGC_THREADS -DTHREAD_LOCAL_ALLOC -D_CRT_SECURE_NO_DEPRECATE $*.CPP /Fo$*.obj + $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DGC_NOT_DLL -DGC_THREADS -DTHREAD_LOCAL_ALLOC -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj $(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h include\private\msvc_dbg.h diff --git a/NT_X64_THREADS_MAKEFILE b/NT_X64_THREADS_MAKEFILE index eadd6163..e0a252b7 100644 --- a/NT_X64_THREADS_MAKEFILE +++ b/NT_X64_THREADS_MAKEFILE @@ -35,7 +35,7 @@ all: gc64.dll gctest.exe cord\de.exe test_cpp.exe # Disable crt security warnings, since unfortunately they warn about all sorts # of safe uses of strncpy. It would be nice to leave the rest enabled. .cpp.obj: - $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DALL_INTERIOR_POINTERS -DGC_DLL -DGC_THREADS -D_CRT_SECURE_NO_DEPRECATE $*.CPP /Fo$*.obj + $(cc) $(cdebug) $(cflags) $(cvarsmt) -Iinclude -I$(AO_INCLUDE_DIR) -DALL_INTERIOR_POINTERS -DALL_INTERIOR_POINTERS -DGC_DLL -DGC_THREADS -D_CRT_SECURE_NO_DEPRECATE $*.cpp /Fo$*.obj $(OBJS) tests\test.obj: include\private\gc_priv.h include\private\gc_hdrs.h include\gc.h include\private\gcconfig.h include\private\gc_locks.h include\private\gc_pmark.h include\gc_mark.h include\private\msvc_dbg.h @@ -120,7 +120,7 @@ void GC_print_hblkfreelist(void) # ifdef USE_MUNMAP if (0 != h) GC_printf("Free list %u:\n", i); # else - if (0 != h) GC_printf("Free list %u (Total size %lu):\n", + if (0 != h) GC_printf("Free list %u (total size %lu):\n", i, (unsigned long)GC_free_bytes[i]); # endif while (h != 0) { @@ -848,7 +848,7 @@ GC_INNER void GC_freehblk(struct hblk *hbp) /* Check for duplicate deallocation in the easy case */ if (HBLK_IS_FREE(hhdr)) { if (GC_print_stats) - GC_printf("Duplicate large block deallocation of %p\n", hbp); + GC_log_printf("Duplicate large block deallocation of %p\n", hbp); ABORT("Duplicate large block deallocation"); } @@ -20,7 +20,9 @@ #include <stdio.h> #if !defined(MACOS) && !defined(MSWINCE) # include <signal.h> -# include <sys/types.h> +# if !defined(__CC_ARM) +# include <sys/types.h> +# endif #endif /* @@ -180,7 +182,7 @@ GC_API GC_stop_func GC_CALL GC_get_stop_func(void) time_diff = MS_TIME_DIFF(current_time,GC_start_time); if (time_diff >= GC_time_limit) { if (GC_print_stats) { - GC_log_printf( + GC_log_printf( "Abandoning stopped marking after %lu msecs (attempt %d)\n", time_diff, GC_n_attempts); } @@ -217,7 +219,8 @@ static word min_bytes_allocd(void) stack_size = GC_total_stacksize; /* For now, we just use the value computed during the latest GC. */ # ifdef DEBUG_THREADS - GC_printf("Total stacks size: %lu\n", (unsigned long)stack_size); + GC_log_printf("Total stacks size: %lu\n", + (unsigned long)stack_size); # endif } # endif @@ -238,9 +241,8 @@ static word min_bytes_allocd(void) STATIC word GC_adj_bytes_allocd(void) { signed_word result; - signed_word expl_managed = - (signed_word)GC_non_gc_bytes - - (signed_word)GC_non_gc_bytes_at_gc; + signed_word expl_managed = (signed_word)GC_non_gc_bytes + - (signed_word)GC_non_gc_bytes_at_gc; /* Don't count what was explicitly freed, or newly allocated for */ /* explicit management. Note that deallocating an explicitly */ @@ -283,9 +285,7 @@ STATIC void GC_clear_a_few_frames(void) # define CLEAR_NWORDS 64 # endif volatile word frames[CLEAR_NWORDS]; - int i; - - for (i = 0; i < CLEAR_NWORDS; i++) frames[i] = 0; + BZERO((word *)frames, CLEAR_NWORDS * sizeof(word)); } /* Heap size at which we need a collection to avoid expanding past */ @@ -364,10 +364,9 @@ STATIC void GC_maybe_gc(void) # endif if (GC_need_full_gc || n_partial_gcs >= GC_full_freq) { if (GC_print_stats) { - GC_log_printf( + GC_log_printf( "***>Full mark for collection %lu after %ld allocd bytes\n", - (unsigned long)GC_gc_no+1, - (long)GC_bytes_allocd); + (unsigned long)GC_gc_no + 1, (long)GC_bytes_allocd); } GC_promote_black_lists(); (void)GC_reclaim_all((GC_stop_func)0, TRUE); @@ -471,7 +470,7 @@ GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) if (GC_print_stats) { GET_TIME(current_time); GC_log_printf("Complete collection took %lu msecs\n", - MS_TIME_DIFF(current_time,start_time)); + MS_TIME_DIFF(current_time,start_time)); } # endif return(TRUE); @@ -542,6 +541,9 @@ GC_INNER void GC_collect_a_little_inner(int n) RESTORE_CANCEL(cancel_state); } +GC_INNER void (*GC_check_heap)(void) = 0; +GC_INNER void (*GC_print_all_smashed)(void) = 0; + GC_API int GC_CALL GC_collect_a_little(void) { int result; @@ -555,14 +557,6 @@ GC_API int GC_CALL GC_collect_a_little(void) return(result); } -#if !defined(REDIRECT_MALLOC) && (defined(MSWIN32) || defined(MSWINCE)) - GC_INNER void GC_add_current_malloc_heap(void); -#endif - -#ifdef MAKE_BACK_GRAPH - GC_INNER void GC_build_back_graph(void); -#endif - #ifndef SMALL_CONFIG /* Variables for world-stop average delay time statistic computation. */ /* "divisor" is incremented every world-stop and halved when reached */ @@ -624,20 +618,20 @@ STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func) GC_clear_a_few_frames(); GC_noop(0,0,0,0,0,0); GC_initiate_gc(); - for(i = 0;;i++) { - if ((*stop_func)()) { - if (GC_print_stats) { - GC_log_printf("Abandoned stopped marking after " - "%u iterations\n", i); - } - GC_deficit = i; /* Give the mutator a chance. */ -# ifdef THREAD_LOCAL_ALLOC - GC_world_stopped = FALSE; -# endif - START_WORLD(); - return(FALSE); + for (i = 0;;i++) { + if ((*stop_func)()) { + if (GC_print_stats) { + GC_log_printf("Abandoned stopped marking after %u iterations\n", + i); } - if (GC_mark_some((ptr_t)(&dummy))) break; + GC_deficit = i; /* Give the mutator a chance. */ +# ifdef THREAD_LOCAL_ALLOC + GC_world_stopped = FALSE; +# endif + START_WORLD(); + return(FALSE); + } + if (GC_mark_some((ptr_t)(&dummy))) break; } GC_gc_no++; @@ -731,7 +725,7 @@ GC_INNER void GC_set_fl_marks(ptr_t q) for (p = q; p != 0; p = obj_link(p)) { if (!GC_is_marked(p)) { GC_err_printf("Unmarked object %p on list %p\n", p, q); - ABORT("Unmarked local free list entry."); + ABORT("Unmarked local free list entry"); } } } @@ -786,10 +780,6 @@ STATIC void GC_clear_fl_marks(ptr_t q) void GC_check_tls(void); #endif -#ifdef MAKE_BACK_GRAPH - GC_INNER void GC_traverse_back_graph(void); -#endif - /* Finish up a collection. Assumes mark bits are consistent, lock is */ /* held, but the world is otherwise running. */ STATIC void GC_finish_collection(void) @@ -876,7 +866,7 @@ STATIC void GC_finish_collection(void) if (GC_print_stats == VERBOSE) GC_log_printf("Bytes recovered before sweep - f.l. count = %ld\n", - (long)GC_bytes_found); + (long)GC_bytes_found); /* Reconstruct free lists to contain everything not marked */ GC_start_reclaim(FALSE); @@ -895,16 +885,16 @@ STATIC void GC_finish_collection(void) } if (GC_print_stats == VERBOSE) { -# ifdef USE_MUNMAP - GC_log_printf("Immediately reclaimed %ld bytes in heap" - " of size %lu bytes (%lu unmapped)\n", - (long)GC_bytes_found, (unsigned long)GC_heapsize, - (unsigned long)GC_unmapped_bytes); -# else - GC_log_printf("Immediately reclaimed %ld bytes in heap" - " of size %lu bytes\n", - (long)GC_bytes_found, (unsigned long)GC_heapsize); -# endif +# ifdef USE_MUNMAP + GC_log_printf("Immediately reclaimed %ld bytes in heap" + " of size %lu bytes (%lu unmapped)\n", + (long)GC_bytes_found, (unsigned long)GC_heapsize, + (unsigned long)GC_unmapped_bytes); +# else + GC_log_printf( + "Immediately reclaimed %ld bytes in heap of size %lu bytes\n", + (long)GC_bytes_found, (unsigned long)GC_heapsize); +# endif } /* Reset or increment counters for next cycle */ @@ -928,7 +918,7 @@ STATIC void GC_finish_collection(void) /* A convenient place to output finalization statistics. */ GC_print_finalization_stats(); - GC_log_printf("Finalize + initiate sweep took %lu + %lu msecs\n", + GC_log_printf("Finalize plus initiate sweep took %lu + %lu msecs\n", MS_TIME_DIFF(finalize_time,start_time), MS_TIME_DIFF(done_time,finalize_time)); } @@ -1075,19 +1065,19 @@ GC_INNER void GC_add_to_heap(struct hblk *p, size_t bytes) { unsigned i; - GC_printf("Total heap size: %lu\n", (unsigned long) GC_heapsize); + GC_printf("Total heap size: %lu\n", (unsigned long)GC_heapsize); for (i = 0; i < GC_n_heap_sects; i++) { - ptr_t start = GC_heap_sects[i].hs_start; - size_t len = GC_heap_sects[i].hs_bytes; - struct hblk *h; - unsigned nbl = 0; + ptr_t start = GC_heap_sects[i].hs_start; + size_t len = GC_heap_sects[i].hs_bytes; + struct hblk *h; + unsigned nbl = 0; - for (h = (struct hblk *)start; h < (struct hblk *)(start + len); h++) { - if (GC_is_black_listed(h, HBLKSIZE)) nbl++; - } - GC_printf("Section %d from %p to %p %lu/%lu blacklisted\n", - i, start, start + len, - (unsigned long)nbl, (unsigned long)(len/HBLKSIZE)); + for (h = (struct hblk *)start; h < (struct hblk *)(start + len); h++) { + if (GC_is_black_listed(h, HBLKSIZE)) nbl++; + } + GC_printf("Section %d from %p to %p %lu/%lu blacklisted\n", + i, start, start + len, + (unsigned long)nbl, (unsigned long)(len/HBLKSIZE)); } } #endif @@ -1141,7 +1131,7 @@ GC_INNER GC_bool GC_expand_hp_inner(word n) } space = GET_MEM(bytes); GC_add_to_our_memory((ptr_t)space, bytes); - if( space == 0 ) { + if (space == 0) { if (GC_print_stats) { GC_log_printf("Failed to expand heap by %ld bytes\n", (unsigned long)bytes); @@ -1149,9 +1139,8 @@ GC_INNER GC_bool GC_expand_hp_inner(word n) return(FALSE); } if (GC_print_stats) { - GC_log_printf("Increasing heap size by %lu after %lu allocated bytes\n", - (unsigned long)bytes, - (unsigned long)GC_bytes_allocd); + GC_log_printf("Increasing heap size by %lu after %lu allocated bytes\n", + (unsigned long)bytes, (unsigned long)GC_bytes_allocd); } /* Adjust heap limits generously for blacklisting to work better. */ /* GC_add_to_heap performs minimal adjustment needed for */ @@ -1273,7 +1262,7 @@ GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, return(FALSE); } } else if (GC_fail_count && GC_print_stats) { - GC_printf("Memory available again ...\n"); + GC_log_printf("Memory available again...\n"); } RESTORE_CANCEL(cancel_state); return(TRUE); diff --git a/backgraph.c b/backgraph.c index c496e003..60a9e0f9 100644 --- a/backgraph.c +++ b/backgraph.c @@ -98,7 +98,7 @@ static back_edges * new_back_edges(void) return result; } if (GC_n_back_edge_structs >= MAX_BACK_EDGE_STRUCTS - 1) { - ABORT("needed too much space for back edges: adjust " + ABORT("Needed too much space for back edges: adjust " "MAX_BACK_EDGE_STRUCTS"); } return back_edge_space + (GC_n_back_edge_structs++); @@ -254,14 +254,12 @@ static void add_edge(ptr_t p, ptr_t q) } be_cont -> edges[i] = p; be -> n_edges++; - if (be -> n_edges == 100) { -# if 0 - if (GC_print_stats) { - GC_err_printf("The following object has in-degree >= 100:\n"); - GC_print_heap_obj(q); - } -# endif - } +# ifdef DEBUG_PRINT_BIG_N_EDGES + if (GC_print_stats == VERBOSE && be -> n_edges == 100) { + GC_err_printf("The following object has big in-degree:\n"); + GC_print_heap_obj(q); + } +# endif } typedef void (*per_object_func)(ptr_t p, size_t n_bytes, word gc_descr); @@ -281,8 +281,7 @@ static word total_stack_black_listed(void) for (i = 0; i < GC_n_heap_sects; i++) { struct hblk * start = (struct hblk *) GC_heap_sects[i].hs_start; - size_t len = (word) GC_heap_sects[i].hs_bytes; - struct hblk * endp1 = start + len/HBLKSIZE; + struct hblk * endp1 = start + GC_heap_sects[i].hs_bytes/HBLKSIZE; total += GC_number_stack_black_listed(start, endp1); } diff --git a/checksums.c b/checksums.c index e2e5a69f..3f2af938 100644 --- a/checksums.c +++ b/checksums.c @@ -97,8 +97,6 @@ int GC_n_changed_errors = 0; int GC_n_clean = 0; int GC_n_dirty = 0; -GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h); - STATIC void GC_update_check_page(struct hblk *h, int index) { page_entry *pe = GC_sums + index; @@ -110,7 +108,7 @@ STATIC void GC_update_check_page(struct hblk *h, int index) pe -> new_sum = GC_checksum(h); # if !defined(MSWIN32) && !defined(MSWINCE) if (pe -> new_sum != 0x80000000 && !GC_page_was_ever_dirty(h)) { - GC_printf("GC_page_was_ever_dirty(%p) is wrong\n", h); + GC_err_printf("GC_page_was_ever_dirty(%p) is wrong\n", h); } # endif if (GC_page_was_dirty(h)) { @@ -165,12 +163,14 @@ STATIC void GC_check_blocks(void) GC_bytes_in_used_blocks = 0; GC_apply_to_all_blocks(GC_add_block, (word)0); - GC_printf("GC_bytes_in_used_blocks = %lu, bytes_in_free_blocks = %lu ", - (unsigned long)GC_bytes_in_used_blocks, - (unsigned long)bytes_in_free_blocks); - GC_printf("GC_heapsize = %lu\n", (unsigned long)GC_heapsize); + if (GC_print_stats) + GC_log_printf("GC_bytes_in_used_blocks = %lu," + " bytes_in_free_blocks = %lu, heapsize = %lu\n", + (unsigned long)GC_bytes_in_used_blocks, + (unsigned long)bytes_in_free_blocks, + (unsigned long)GC_heapsize); if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) { - GC_printf("LOST SOME BLOCKS!!\n"); + GC_err_printf("LOST SOME BLOCKS!!\n"); } } @@ -202,19 +202,21 @@ void GC_check_dirty(void) } } out: - GC_printf("Checked %lu clean and %lu dirty pages\n", - (unsigned long) GC_n_clean, (unsigned long) GC_n_dirty); + if (GC_print_stats) + GC_log_printf("Checked %lu clean and %lu dirty pages\n", + (unsigned long)GC_n_clean, (unsigned long)GC_n_dirty); if (GC_n_dirty_errors > 0) { - GC_printf("Found %d dirty bit errors (%d were faulted)\n", - GC_n_dirty_errors, GC_n_faulted_dirty_errors); + GC_err_printf("Found %d dirty bit errors (%d were faulted)\n", + GC_n_dirty_errors, GC_n_faulted_dirty_errors); } if (GC_n_changed_errors > 0) { - GC_printf("Found %lu changed bit errors\n", - (unsigned long)GC_n_changed_errors); - GC_printf("These may be benign (provoked by nonpointer changes)\n"); + GC_err_printf("Found %lu changed bit errors\n", + (unsigned long)GC_n_changed_errors); + GC_err_printf( + "These may be benign (provoked by nonpointer changes)\n"); # ifdef THREADS - GC_printf( - "Also expect 1 per thread currently allocating a stubborn obj.\n"); + GC_err_printf( + "Also expect 1 per thread currently allocating a stubborn obj\n"); # endif } for (i = 0; i < GC_n_faulted; ++i) { @@ -1,7 +1,7 @@ #! /bin/sh -# From configure.ac Revision: 1.64 . +# From configure.ac Revision: 1.69 . # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for gc 7.2alpha5. +# Generated by GNU Autoconf 2.68 for gc 7.2alpha7. # # Report bugs to <Hans.Boehm@hp.com>. # @@ -571,8 +571,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='gc' PACKAGE_TARNAME='gc' -PACKAGE_VERSION='7.2alpha5' -PACKAGE_STRING='gc 7.2alpha5' +PACKAGE_VERSION='7.2alpha7' +PACKAGE_STRING='gc 7.2alpha7' PACKAGE_BUGREPORT='Hans.Boehm@hp.com' PACKAGE_URL='' @@ -1368,7 +1368,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures gc 7.2alpha5 to adapt to many kinds of systems. +\`configure' configures gc 7.2alpha7 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1439,7 +1439,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of gc 7.2alpha5:";; + short | recursive ) echo "Configuration of gc 7.2alpha7:";; esac cat <<\_ACEOF @@ -1567,7 +1567,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -gc configure 7.2alpha5 +gc configure 7.2alpha7 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. @@ -2057,7 +2057,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by gc $as_me 7.2alpha5, which was +It was created by gc $as_me 7.2alpha7, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -3031,7 +3031,7 @@ fi # Define the identity of the package. PACKAGE='gc' - VERSION='7.2alpha5' + VERSION='7.2alpha7' cat >>confdefs.h <<_ACEOF @@ -4969,6 +4969,7 @@ fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } if ${ac_cv_c_inline+:} false; then : @@ -5062,7 +5063,8 @@ if test "x$ac_cv_lib_pthread_pthread_self" = xyes; then : fi case "$host" in - x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* | alpha-*-linux* | sparc*-*-linux*) + x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* \ + | x86_64-*-linux* | alpha-*-linux* | sparc*-*-linux*) $as_echo "#define GC_LINUX_THREADS 1" >>confdefs.h $as_echo "#define _REENTRANT 1" >>confdefs.h @@ -5167,18 +5169,18 @@ $as_echo "$as_me: WARNING: \"Only on NetBSD 2.0 or later.\"" >&2;} $as_echo "#define THREAD_LOCAL_ALLOC 1" >>confdefs.h - THREADDLLIBS="-lpthread -lrt" - if test "$GCC" != yes; then - CFLAGS="$CFLAGS -O" - need_atomic_ops_asm=true - fi + # Need to use alternate thread library, otherwise gctest hangs + # on Solaris 8. + multi_os_directory=`$CC -print-multi-os-directory` + THREADDLLIBS="-L/usr/lib/lwp/$multi_os_directory \ + -R/usr/lib/lwp/$multi_os_directory -lpthread -lrt" ;; *-*-irix*) $as_echo "#define GC_IRIX_THREADS 1" >>confdefs.h ;; *-*-cygwin*) - $as_echo "#define GC_THREADS 1" >>confdefs.h + $as_echo "#define GC_WIN32_THREADS 1" >>confdefs.h if test "${enable_parallel_mark}" = yes; then $as_echo "#define PARALLEL_MARK 1" >>confdefs.h @@ -5191,6 +5193,18 @@ $as_echo "$as_me: WARNING: \"Only on NetBSD 2.0 or later.\"" >&2;} THREADDLLIBS="" win32_threads=true ;; + *-*-mingw*) + $as_echo "#define GC_WIN32_PTHREADS 1" >>confdefs.h + + # Using win32-pthreads + if test "${enable_parallel_mark}" = yes; then + $as_echo "#define PARALLEL_MARK 1" >>confdefs.h + + fi + $as_echo "#define THREAD_LOCAL_ALLOC 1" >>confdefs.h + + THREADDLLIBS="-lpthread" + ;; *-*-darwin*) $as_echo "#define GC_DARWIN_THREADS 1" >>confdefs.h @@ -5225,9 +5239,17 @@ $as_echo "$as_me: WARNING: \"Explicit GC_INIT() calls may be required.\"" >&2;}; as_fn_error $? "\"Pthreads not supported by the GC on this platform.\"" "$LINENO" 5 ;; esac + case "$host" in + sparc*-*-solaris*) + if test "$GCC" != yes; then + CFLAGS="$CFLAGS -O" + need_atomic_ops_asm=true + fi + ;; + esac ;; win32) - $as_echo "#define GC_THREADS 1" >>confdefs.h + $as_echo "#define GC_WIN32_THREADS 1" >>confdefs.h if test "${enable_parallel_mark}" = yes; then $as_echo "#define PARALLEL_MARK 1" >>confdefs.h @@ -5244,7 +5266,7 @@ $as_echo "#define EMPTY_GETENV_RESULTS 1" >>confdefs.h THREADS=dgux386 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $THREADDLLIBS" >&5 $as_echo "$THREADDLLIBS" >&6; } - # Use pthread GCC switch + # Use pthread GCC switch THREADDLLIBS=-pthread if test "${enable_parallel_mark}" = yes; then $as_echo "#define PARALLEL_MARK 1" >>confdefs.h @@ -5366,9 +5388,12 @@ $as_echo "#define DARWIN_DONT_PARSE_STACK 1" >>confdefs.h fi +case "$host" in +# While IRIX 6 has libdl for the O32 and N32 ABIs, it's missing for N64 +# and unnecessary everywhere. + mips-sgi-irix6*) ;; # We never want libdl on darwin. It is a fake libdl that just ends up making # dyld calls anyway. The same applies to Cygwin. -case "$host" in *-*-darwin*) ;; *-*-cygwin*) ;; *) @@ -5545,7 +5570,8 @@ $as_echo "$as_me: WARNING: OpenBSD/Alpha without dlopen(). Shared library suppor alpha*-*-linux*) machdep="mach_dep.lo" ;; - i?86-*-solaris2.[89] | i?86-*-solaris2.1?) + i?86-*-solaris2.[89]) + # PROC_VDB appears to work in 2.8 and 2.9 but not in 2.10+ (for now). $as_echo "#define SOLARIS25_PROC_VDB_BUG_FIXED 1" >>confdefs.h @@ -17216,7 +17242,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by gc $as_me 7.2alpha5, which was +This file was extended by gc $as_me 7.2alpha7, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -17282,7 +17308,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -gc config.status 7.2alpha5 +gc config.status 7.2alpha7 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 6df2ea87..f66ce6bd 100644 --- a/configure.ac +++ b/configure.ac @@ -17,12 +17,12 @@ dnl Process this file with autoconf to produce configure. # Initialization # ============== -AC_INIT(gc,7.2alpha5,Hans.Boehm@hp.com) +AC_INIT(gc,7.2alpha7,Hans.Boehm@hp.com) ## version must conform to [0-9]+[.][0-9]+(alpha[0-9]+)? AC_CONFIG_SRCDIR(gcj_mlc.c) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_TARGET -AC_PREREQ(2.53) +AC_PREREQ(2.64) AC_REVISION($Revision$) GC_SET_VERSION AM_INIT_AUTOMAKE([foreign dist-bzip2 nostdinc]) @@ -78,7 +78,8 @@ AH_TEMPLATE([USE_COMPILER_TLS], [Define to use of compiler-support for thread-local variables.]) dnl Thread selection macros. -AH_TEMPLATE([GC_THREADS], [Define to support threads.]) +AH_TEMPLATE([GC_THREADS], [Define to support platform-specific \ + threads.]) AH_TEMPLATE([GC_AIX_THREADS], [Define to support IBM AIX threads.]) AH_TEMPLATE([GC_DARWIN_THREADS], [Define to support Darwin pthreads.]) AH_TEMPLATE([GC_FREEBSD_THREADS], [Define to support FreeBSD pthreads.]) @@ -90,7 +91,8 @@ AH_TEMPLATE([GC_NETBSD_THREADS], [Define to support NetBSD pthreads.]) AH_TEMPLATE([GC_OPENBSD_THREADS], [Define to support OpenBSD pthreads.]) AH_TEMPLATE([GC_OSF1_THREADS], [Define to support Tru64 pthreads.]) AH_TEMPLATE([GC_SOLARIS_THREADS], [Define to support Solaris pthreads.]) -AH_TEMPLATE([GC_WIN32_THREADS], [Define to support win32 threads.]) +AH_TEMPLATE([GC_WIN32_THREADS], [Define to support Win32 threads.]) +AH_TEMPLATE([GC_WIN32_PTHREADS], [Define to support win32-pthreads.]) dnl System header feature requests. AH_TEMPLATE([_POSIX_C_SOURCE], [The POSIX feature macro.]) @@ -110,7 +112,8 @@ case "$THREADS" in THREADS=posix AC_CHECK_LIB(pthread, pthread_self, THREADDLLIBS="-lpthread",,) case "$host" in - x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* | x86_64-*-linux* | alpha-*-linux* | sparc*-*-linux*) + x86-*-linux* | ia64-*-linux* | i586-*-linux* | i686-*-linux* \ + | x86_64-*-linux* | alpha-*-linux* | sparc*-*-linux*) AC_DEFINE(GC_LINUX_THREADS) AC_DEFINE(_REENTRANT) if test "${enable_parallel_mark}" = yes; then @@ -180,17 +183,17 @@ case "$THREADS" in *-*-solaris*) AC_DEFINE(GC_SOLARIS_THREADS) AC_DEFINE(THREAD_LOCAL_ALLOC) - THREADDLLIBS="-lpthread -lrt" - if test "$GCC" != yes; then - CFLAGS="$CFLAGS -O" - need_atomic_ops_asm=true - fi + # Need to use alternate thread library, otherwise gctest hangs + # on Solaris 8. + multi_os_directory=`$CC -print-multi-os-directory` + THREADDLLIBS="-L/usr/lib/lwp/$multi_os_directory \ + -R/usr/lib/lwp/$multi_os_directory -lpthread -lrt" ;; *-*-irix*) AC_DEFINE(GC_IRIX_THREADS) ;; *-*-cygwin*) - AC_DEFINE(GC_THREADS) + AC_DEFINE(GC_WIN32_THREADS) if test "${enable_parallel_mark}" = yes; then AC_DEFINE(PARALLEL_MARK) fi @@ -200,6 +203,15 @@ case "$THREADS" in THREADDLLIBS="" win32_threads=true ;; + *-*-mingw*) + AC_DEFINE(GC_WIN32_PTHREADS) + # Using win32-pthreads + if test "${enable_parallel_mark}" = yes; then + AC_DEFINE(PARALLEL_MARK) + fi + AC_DEFINE(THREAD_LOCAL_ALLOC) + THREADDLLIBS="-lpthread" + ;; *-*-darwin*) AC_DEFINE(GC_DARWIN_THREADS) AC_DEFINE(THREAD_LOCAL_ALLOC) @@ -226,9 +238,17 @@ case "$THREADS" in AC_MSG_ERROR("Pthreads not supported by the GC on this platform.") ;; esac + case "$host" in + sparc*-*-solaris*) + if test "$GCC" != yes; then + CFLAGS="$CFLAGS -O" + need_atomic_ops_asm=true + fi + ;; + esac ;; win32) - AC_DEFINE(GC_THREADS) + AC_DEFINE(GC_WIN32_THREADS) if test "${enable_parallel_mark}" = yes; then AC_DEFINE(PARALLEL_MARK) AC_DEFINE(THREAD_LOCAL_ALLOC) @@ -240,7 +260,7 @@ case "$THREADS" in dgux386) THREADS=dgux386 AC_MSG_RESULT($THREADDLLIBS) - # Use pthread GCC switch + # Use pthread GCC switch THREADDLLIBS=-pthread if test "${enable_parallel_mark}" = yes; then AC_DEFINE(PARALLEL_MARK) @@ -293,9 +313,12 @@ if test $compiler_xlc = yes -a "$powerpc_darwin" = true; then AC_DEFINE([DARWIN_DONT_PARSE_STACK], 1, [See doc/README.macros.]) fi +case "$host" in +# While IRIX 6 has libdl for the O32 and N32 ABIs, it's missing for N64 +# and unnecessary everywhere. + mips-sgi-irix6*) ;; # We never want libdl on darwin. It is a fake libdl that just ends up making # dyld calls anyway. The same applies to Cygwin. -case "$host" in *-*-darwin*) ;; *-*-cygwin*) ;; *) @@ -384,7 +407,8 @@ case "$host" in alpha*-*-linux*) machdep="mach_dep.lo" ;; - i?86-*-solaris2.[[89]] | i?86-*-solaris2.1?) + i?86-*-solaris2.[[89]]) + # PROC_VDB appears to work in 2.8 and 2.9 but not in 2.10+ (for now). AC_DEFINE([SOLARIS25_PROC_VDB_BUG_FIXED], 1, [See the comment in gcconfig.h.]) ;; diff --git a/configure.host b/configure.host index a98a0a7c..898c9236 100644 --- a/configure.host +++ b/configure.host @@ -2,7 +2,7 @@ # This shell script handles all host based configuration for the garbage # collector. -# It sets various shell variables based on the the host and the +# It sets various shell variables based on the host and the # configuration options. You can modify this shell script without # needing to rerun autoconf. @@ -26,7 +26,7 @@ gc_cflags="" if test :"$GCC": = :yes: ; then gc_cflags="${gc_cflags} -fexceptions" else - case "$host" in + case "$host" in hppa*-*-hpux* ) if test :$GCC: != :"yes": ; then gc_cflags="${gc_flags} +ESdbgasm" diff --git a/configure_atomic_ops.sh b/configure_atomic_ops.sh deleted file mode 100755 index 932579f1..00000000 --- a/configure_atomic_ops.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -P=`pwd`/libatomic_ops-install -cd libatomic_ops -./configure --prefix=$P diff --git a/cord/cordbscs.c b/cord/cordbscs.c index d83f4067..924bf445 100644 --- a/cord/cordbscs.c +++ b/cord/cordbscs.c @@ -19,64 +19,64 @@ # include <stdio.h> # include <string.h> -/* An implementation of the cord primitives. These are the only */ -/* Functions that understand the representation. We perform only */ -/* minimal checks on arguments to these functions. Out of bounds */ -/* arguments to the iteration functions may result in client functions */ -/* invoked on garbage data. In most cases, client functions should be */ -/* programmed defensively enough that this does not result in memory */ -/* smashes. */ +/* An implementation of the cord primitives. These are the only */ +/* Functions that understand the representation. We perform only */ +/* minimal checks on arguments to these functions. Out of bounds */ +/* arguments to the iteration functions may result in client functions */ +/* invoked on garbage data. In most cases, client functions should be */ +/* programmed defensively enough that this does not result in memory */ +/* smashes. */ typedef void (* oom_fn)(void); oom_fn CORD_oom_fn = (oom_fn) 0; # define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ - ABORT("Out of memory\n"); } + ABORT("Out of memory\n"); } # define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } typedef unsigned long word; typedef union { struct Concatenation { - char null; - char header; - char depth; /* concatenation nesting depth. */ - unsigned char left_len; - /* Length of left child if it is sufficiently */ - /* short; 0 otherwise. */ -# define MAX_LEFT_LEN 255 - word len; - CORD left; /* length(left) > 0 */ - CORD right; /* length(right) > 0 */ + char null; + char header; + char depth; /* concatenation nesting depth. */ + unsigned char left_len; + /* Length of left child if it is sufficiently */ + /* short; 0 otherwise. */ +# define MAX_LEFT_LEN 255 + word len; + CORD left; /* length(left) > 0 */ + CORD right; /* length(right) > 0 */ } concatenation; struct Function { - char null; - char header; - char depth; /* always 0 */ - char left_len; /* always 0 */ - word len; - CORD_fn fn; - void * client_data; + char null; + char header; + char depth; /* always 0 */ + char left_len; /* always 0 */ + word len; + CORD_fn fn; + void * client_data; } function; struct Generic { - char null; - char header; - char depth; - char left_len; - word len; + char null; + char header; + char depth; + char left_len; + word len; } generic; char string[1]; } CordRep; # define CONCAT_HDR 1 - + # define FN_HDR 4 # define SUBSTR_HDR 6 - /* Substring nodes are a special case of function nodes. */ - /* The client_data field is known to point to a substr_args */ - /* structure, and the function is either CORD_apply_access_fn */ - /* or CORD_index_access_fn. */ + /* Substring nodes are a special case of function nodes. */ + /* The client_data field is known to point to a substr_args */ + /* structure, and the function is either CORD_apply_access_fn */ + /* or CORD_index_access_fn. */ /* The following may be applied only to function and concatenation nodes: */ #define IS_CONCATENATION(s) (((CordRep *)s)->generic.header == CONCAT_HDR) @@ -90,26 +90,26 @@ typedef union { #define GEN_LEN(s) (CORD_IS_STRING(s) ? strlen(s) : LEN(s)) #define LEFT_LEN(c) ((c) -> left_len != 0? \ - (c) -> left_len \ - : (CORD_IS_STRING((c) -> left) ? \ - (c) -> len - GEN_LEN((c) -> right) \ - : LEN((c) -> left))) + (c) -> left_len \ + : (CORD_IS_STRING((c) -> left) ? \ + (c) -> len - GEN_LEN((c) -> right) \ + : LEN((c) -> left))) #define SHORT_LIMIT (sizeof(CordRep) - 1) - /* Cords shorter than this are C strings */ + /* Cords shorter than this are C strings */ -/* Dump the internal representation of x to stdout, with initial */ -/* indentation level n. */ +/* Dump the internal representation of x to stdout, with initial */ +/* indentation level n. */ void CORD_dump_inner(CORD x, unsigned n) { register size_t i; - + for (i = 0; i < (size_t)n; i++) { fputs(" ", stdout); } if (x == 0) { - fputs("NIL\n", stdout); + fputs("NIL\n", stdout); } else if (CORD_IS_STRING(x)) { for (i = 0; i <= SHORT_LIMIT; i++) { if (x[i] == '\0') break; @@ -119,14 +119,14 @@ void CORD_dump_inner(CORD x, unsigned n) putchar('\n'); } else if (IS_CONCATENATION(x)) { register struct Concatenation * conc = - &(((CordRep *)x) -> concatenation); + &(((CordRep *)x) -> concatenation); printf("Concatenation: %p (len: %d, depth: %d)\n", x, (int)(conc -> len), (int)(conc -> depth)); CORD_dump_inner(conc -> left, n+1); CORD_dump_inner(conc -> right, n+1); } else /* function */{ register struct Function * func = - &(((CordRep *)x) -> function); + &(((CordRep *)x) -> function); if (IS_SUBSTR(x)) printf("(Substring) "); printf("Function: %p (len: %d): ", x, (int)(func -> len)); for (i = 0; i < 20 && i < func -> len; i++) { @@ -137,7 +137,7 @@ void CORD_dump_inner(CORD x, unsigned n) } } -/* Dump the internal representation of x to stdout */ +/* Dump the internal representation of x to stdout */ void CORD_dump(CORD x) { CORD_dump_inner(x, 0); @@ -149,7 +149,7 @@ CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) register size_t result_len; register size_t lenx; register int depth; - + if (x == CORD_EMPTY) return(y); if (leny == 0) return(x); if (CORD_IS_STRING(x)) { @@ -157,7 +157,7 @@ CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) result_len = lenx + leny; if (result_len <= SHORT_LIMIT) { register char * result = GC_MALLOC_ATOMIC(result_len+1); - + if (result == 0) OUT_OF_MEMORY; memcpy(result, x, lenx); memcpy(result + lenx, y, leny); @@ -167,35 +167,35 @@ CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) depth = 1; } } else { - register CORD right; - register CORD left; - register char * new_right; - register size_t right_len; - - lenx = LEN(x); - + register CORD right; + register CORD left; + register char * new_right; + register size_t right_len; + + lenx = LEN(x); + if (leny <= SHORT_LIMIT/2 - && IS_CONCATENATION(x) + && IS_CONCATENATION(x) && CORD_IS_STRING(right = ((CordRep *)x) -> concatenation.right)) { /* Merge y into right part of x. */ if (!CORD_IS_STRING(left = ((CordRep *)x) -> concatenation.left)) { - right_len = lenx - LEN(left); + right_len = lenx - LEN(left); } else if (((CordRep *)x) -> concatenation.left_len != 0) { right_len = lenx - ((CordRep *)x) -> concatenation.left_len; } else { - right_len = strlen(right); + right_len = strlen(right); } result_len = right_len + leny; /* length of new_right */ if (result_len <= SHORT_LIMIT) { - new_right = GC_MALLOC_ATOMIC(result_len + 1); - memcpy(new_right, right, right_len); - memcpy(new_right + right_len, y, leny); - new_right[result_len] = '\0'; - y = new_right; - leny = result_len; - x = left; - lenx -= right_len; - /* Now fall through to concatenate the two pieces: */ + new_right = GC_MALLOC_ATOMIC(result_len + 1); + memcpy(new_right, right, right_len); + memcpy(new_right + right_len, y, leny); + new_right[result_len] = '\0'; + y = new_right; + leny = result_len; + x = left; + lenx -= right_len; + /* Now fall through to concatenate the two pieces: */ } if (CORD_IS_STRING(x)) { depth = 1; @@ -209,21 +209,21 @@ CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) } { /* The general case; lenx, result_len is known: */ - register struct Concatenation * result; - - result = GC_NEW(struct Concatenation); - if (result == 0) OUT_OF_MEMORY; - result->header = CONCAT_HDR; - result->depth = depth; - if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; - result->len = result_len; - result->left = x; - result->right = y; - if (depth >= MAX_DEPTH) { - return(CORD_balance((CORD)result)); - } else { - return((CORD) result); - } + register struct Concatenation * result; + + result = GC_NEW(struct Concatenation); + if (result == 0) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = depth; + if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; + result->len = result_len; + result->left = x; + result->right = y; + if (depth >= MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } } } @@ -233,7 +233,7 @@ CORD CORD_cat(CORD x, CORD y) register size_t result_len; register int depth; register size_t lenx; - + if (x == CORD_EMPTY) return(y); if (y == CORD_EMPTY) return(x); if (CORD_IS_STRING(y)) { @@ -243,28 +243,28 @@ CORD CORD_cat(CORD x, CORD y) depth = DEPTH(y) + 1; } else { register int depthy = DEPTH(y); - + lenx = LEN(x); depth = DEPTH(x) + 1; if (depthy >= depth) depth = depthy + 1; } result_len = lenx + LEN(y); { - register struct Concatenation * result; - - result = GC_NEW(struct Concatenation); - if (result == 0) OUT_OF_MEMORY; - result->header = CONCAT_HDR; - result->depth = depth; - if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; - result->len = result_len; - result->left = x; - result->right = y; - if (depth >= MAX_DEPTH) { - return(CORD_balance((CORD)result)); - } else { - return((CORD) result); - } + register struct Concatenation * result; + + result = GC_NEW(struct Concatenation); + if (result == 0) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = depth; + if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; + result->len = result_len; + result->left = x; + result->right = y; + if (depth >= MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } } } @@ -278,7 +278,7 @@ CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len) register size_t i; char buf[SHORT_LIMIT+1]; register char c; - + for (i = 0; i < len; i++) { c = (*fn)(i, client_data); if (c == '\0') goto gen_case; @@ -293,25 +293,25 @@ CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len) } gen_case: { - register struct Function * result; - - result = GC_NEW(struct Function); - if (result == 0) OUT_OF_MEMORY; - result->header = FN_HDR; - /* depth is already 0 */ - result->len = len; - result->fn = fn; - result->client_data = client_data; - return((CORD) result); + register struct Function * result; + + result = GC_NEW(struct Function); + if (result == 0) OUT_OF_MEMORY; + result->header = FN_HDR; + /* depth is already 0 */ + result->len = len; + result->fn = fn; + result->client_data = client_data; + return((CORD) result); } } size_t CORD_len(CORD x) { if (x == 0) { - return(0); + return(0); } else { - return(GEN_LEN(x)); + return(GEN_LEN(x)); } } @@ -323,7 +323,7 @@ struct substr_args { char CORD_index_access_fn(size_t i, void * client_data) { register struct substr_args *descr = (struct substr_args *)client_data; - + return(((char *)(descr->sa_cord))[i + descr->sa_index]); } @@ -331,19 +331,19 @@ char CORD_apply_access_fn(size_t i, void * client_data) { register struct substr_args *descr = (struct substr_args *)client_data; register struct Function * fn_cord = &(descr->sa_cord->function); - + return((*(fn_cord->fn))(i + descr->sa_index, fn_cord->client_data)); } -/* A version of CORD_substr that simply returns a function node, thus */ -/* postponing its work. The fourth argument is a function that may */ -/* be used for efficient access to the ith character. */ -/* Assumes i >= 0 and i + n < length(x). */ +/* A version of CORD_substr that simply returns a function node, thus */ +/* postponing its work. The fourth argument is a function that may */ +/* be used for efficient access to the ith character. */ +/* Assumes i >= 0 and i + n < length(x). */ CORD CORD_substr_closure(CORD x, size_t i, size_t n, CORD_fn f) { register struct substr_args * sa = GC_NEW(struct substr_args); CORD result; - + if (sa == 0) OUT_OF_MEMORY; sa->sa_cord = (CordRep *)x; sa->sa_index = i; @@ -353,9 +353,9 @@ CORD CORD_substr_closure(CORD x, size_t i, size_t n, CORD_fn f) } # define SUBSTR_LIMIT (10 * SHORT_LIMIT) - /* Substrings of function nodes and flat strings shorter than */ - /* this are flat strings. Othewise we use a functional */ - /* representation, which is significantly slower to access. */ + /* Substrings of function nodes and flat strings shorter than */ + /* this are flat strings. Othewise we use a functional */ + /* representation, which is significantly slower to access. */ /* A version of CORD_substr that assumes i >= 0, n > 0, and i + n < length(x).*/ CORD CORD_substr_checked(CORD x, size_t i, size_t n) @@ -365,56 +365,56 @@ CORD CORD_substr_checked(CORD x, size_t i, size_t n) return(CORD_substr_closure(x, i, n, CORD_index_access_fn)); } else { register char * result = GC_MALLOC_ATOMIC(n+1); - + if (result == 0) OUT_OF_MEMORY; strncpy(result, x+i, n); result[n] = '\0'; return(result); } } else if (IS_CONCATENATION(x)) { - register struct Concatenation * conc - = &(((CordRep *)x) -> concatenation); - register size_t left_len; - register size_t right_len; - - left_len = LEFT_LEN(conc); - right_len = conc -> len - left_len; - if (i >= left_len) { - if (n == right_len) return(conc -> right); - return(CORD_substr_checked(conc -> right, i - left_len, n)); - } else if (i+n <= left_len) { - if (n == left_len) return(conc -> left); - return(CORD_substr_checked(conc -> left, i, n)); - } else { - /* Need at least one character from each side. */ - register CORD left_part; - register CORD right_part; - register size_t left_part_len = left_len - i; - - if (i == 0) { - left_part = conc -> left; - } else { - left_part = CORD_substr_checked(conc -> left, i, left_part_len); - } - if (i + n == right_len + left_len) { - right_part = conc -> right; - } else { - right_part = CORD_substr_checked(conc -> right, 0, - n - left_part_len); - } - return(CORD_cat(left_part, right_part)); - } + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + register size_t left_len; + register size_t right_len; + + left_len = LEFT_LEN(conc); + right_len = conc -> len - left_len; + if (i >= left_len) { + if (n == right_len) return(conc -> right); + return(CORD_substr_checked(conc -> right, i - left_len, n)); + } else if (i+n <= left_len) { + if (n == left_len) return(conc -> left); + return(CORD_substr_checked(conc -> left, i, n)); + } else { + /* Need at least one character from each side. */ + register CORD left_part; + register CORD right_part; + register size_t left_part_len = left_len - i; + + if (i == 0) { + left_part = conc -> left; + } else { + left_part = CORD_substr_checked(conc -> left, i, left_part_len); + } + if (i + n == right_len + left_len) { + right_part = conc -> right; + } else { + right_part = CORD_substr_checked(conc -> right, 0, + n - left_part_len); + } + return(CORD_cat(left_part, right_part)); + } } else /* function */ { if (n > SUBSTR_LIMIT) { if (IS_SUBSTR(x)) { - /* Avoid nesting substring nodes. */ - register struct Function * f = &(((CordRep *)x) -> function); - register struct substr_args *descr = - (struct substr_args *)(f -> client_data); - - return(CORD_substr_closure((CORD)descr->sa_cord, - i + descr->sa_index, - n, f -> fn)); + /* Avoid nesting substring nodes. */ + register struct Function * f = &(((CordRep *)x) -> function); + register struct substr_args *descr = + (struct substr_args *)(f -> client_data); + + return(CORD_substr_closure((CORD)descr->sa_cord, + i + descr->sa_index, + n, f -> fn)); } else { return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); } @@ -426,13 +426,13 @@ CORD CORD_substr_checked(CORD x, size_t i, size_t n) register char c; register int j; register int lim = i + n; - + for (j = i; j < lim; j++) { - c = (*(f -> fn))(j, f -> client_data); - if (c == '\0') { - return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); - } - *p++ = c; + c = (*(f -> fn))(j, f -> client_data); + if (c == '\0') { + return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); + } + *p++ = c; } *p = '\0'; result = GC_MALLOC_ATOMIC(n+1); @@ -446,59 +446,59 @@ CORD CORD_substr_checked(CORD x, size_t i, size_t n) CORD CORD_substr(CORD x, size_t i, size_t n) { register size_t len = CORD_len(x); - + if (i >= len || n <= 0) return(0); - /* n < 0 is impossible in a correct C implementation, but */ - /* quite possible under SunOS 4.X. */ + /* n < 0 is impossible in a correct C implementation, but */ + /* quite possible under SunOS 4.X. */ if (i + n > len) n = len - i; # ifndef __STDC__ if (i < 0) ABORT("CORD_substr: second arg. negative"); - /* Possible only if both client and C implementation are buggy. */ - /* But empirically this happens frequently. */ + /* Possible only if both client and C implementation are buggy. */ + /* But empirically this happens frequently. */ # endif return(CORD_substr_checked(x, i, n)); } -/* See cord.h for definition. We assume i is in range. */ +/* See cord.h for definition. We assume i is in range. */ int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, - CORD_batched_iter_fn f2, void * client_data) + CORD_batched_iter_fn f2, void * client_data) { if (x == 0) return(0); if (CORD_IS_STRING(x)) { - register const char *p = x+i; - - if (*p == '\0') ABORT("2nd arg to CORD_iter5 too big"); + register const char *p = x+i; + + if (*p == '\0') ABORT("2nd arg to CORD_iter5 too big"); if (f2 != CORD_NO_FN) { return((*f2)(p, client_data)); } else { - while (*p) { + while (*p) { if ((*f1)(*p, client_data)) return(1); p++; - } - return(0); + } + return(0); } } else if (IS_CONCATENATION(x)) { - register struct Concatenation * conc - = &(((CordRep *)x) -> concatenation); - - - if (i > 0) { - register size_t left_len = LEFT_LEN(conc); - - if (i >= left_len) { - return(CORD_iter5(conc -> right, i - left_len, f1, f2, - client_data)); - } - } - if (CORD_iter5(conc -> left, i, f1, f2, client_data)) { - return(1); - } - return(CORD_iter5(conc -> right, 0, f1, f2, client_data)); + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + + + if (i > 0) { + register size_t left_len = LEFT_LEN(conc); + + if (i >= left_len) { + return(CORD_iter5(conc -> right, i - left_len, f1, f2, + client_data)); + } + } + if (CORD_iter5(conc -> left, i, f1, f2, client_data)) { + return(1); + } + return(CORD_iter5(conc -> right, 0, f1, f2, client_data)); } else /* function */ { register struct Function * f = &(((CordRep *)x) -> function); register size_t j; register size_t lim = f -> len; - + for (j = i; j < lim; j++) { if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { return(1); @@ -507,7 +507,7 @@ int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, return(0); } } - + #undef CORD_iter int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data) { @@ -518,36 +518,36 @@ int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data) { if (x == 0) return(0); if (CORD_IS_STRING(x)) { - register const char *p = x + i; - register char c; - - for(;;) { - c = *p; - if (c == '\0') ABORT("2nd arg to CORD_riter4 too big"); + register const char *p = x + i; + register char c; + + for(;;) { + c = *p; + if (c == '\0') ABORT("2nd arg to CORD_riter4 too big"); if ((*f1)(c, client_data)) return(1); - if (p == x) break; + if (p == x) break; p--; - } - return(0); + } + return(0); } else if (IS_CONCATENATION(x)) { - register struct Concatenation * conc - = &(((CordRep *)x) -> concatenation); - register CORD left_part = conc -> left; - register size_t left_len; - - left_len = LEFT_LEN(conc); - if (i >= left_len) { - if (CORD_riter4(conc -> right, i - left_len, f1, client_data)) { - return(1); - } - return(CORD_riter4(left_part, left_len - 1, f1, client_data)); - } else { - return(CORD_riter4(left_part, i, f1, client_data)); - } + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + register CORD left_part = conc -> left; + register size_t left_len; + + left_len = LEFT_LEN(conc); + if (i >= left_len) { + if (CORD_riter4(conc -> right, i - left_len, f1, client_data)) { + return(1); + } + return(CORD_riter4(left_part, left_len - 1, f1, client_data)); + } else { + return(CORD_riter4(left_part, i, f1, client_data)); + } } else /* function */ { register struct Function * f = &(((CordRep *)x) -> function); register size_t j; - + for (j = i; ; j--) { if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { return(1); @@ -559,7 +559,9 @@ int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data) int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data) { - return(CORD_riter4(x, CORD_len(x) - 1, f1, client_data)); + size_t len = CORD_len(x); + if (len == 0) return(0); + return(CORD_riter4(x, len - 1, f1, client_data)); } /* @@ -579,7 +581,7 @@ int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data) typedef struct { CORD c; - size_t len; /* Actual length of c */ + size_t len; /* Actual length of c */ } ForestElement; static size_t min_len [ MAX_DEPTH ]; @@ -589,24 +591,24 @@ static int min_len_init = 0; int CORD_max_len; typedef ForestElement Forest [ MAX_DEPTH ]; - /* forest[i].len >= fib(i+1) */ - /* The string is the concatenation */ - /* of the forest in order of DECREASING */ - /* indices. */ + /* forest[i].len >= fib(i+1) */ + /* The string is the concatenation */ + /* of the forest in order of DECREASING */ + /* indices. */ void CORD_init_min_len() { register int i; register size_t last, previous, current; - + min_len[0] = previous = 1; min_len[1] = last = 2; for (i = 2; i < MAX_DEPTH; i++) { - current = last + previous; - if (current < last) /* overflow */ current = last; - min_len[i] = current; - previous = last; - last = current; + current = last + previous; + if (current < last) /* overflow */ current = last; + min_len[i] = current; + previous = last; + last = current; } CORD_max_len = last - 1; min_len_init = 1; @@ -616,48 +618,48 @@ void CORD_init_min_len() void CORD_init_forest(ForestElement * forest, size_t max_len) { register int i; - + for (i = 0; i < MAX_DEPTH; i++) { - forest[i].c = 0; - if (min_len[i] > max_len) return; + forest[i].c = 0; + if (min_len[i] > max_len) return; } ABORT("Cord too long"); } -/* Add a leaf to the appropriate level in the forest, cleaning */ -/* out lower levels as necessary. */ -/* Also works if x is a balanced tree of concatenations; however */ -/* in this case an extra concatenation node may be inserted above x; */ -/* This node should not be counted in the statement of the invariants. */ +/* Add a leaf to the appropriate level in the forest, cleaning */ +/* out lower levels as necessary. */ +/* Also works if x is a balanced tree of concatenations; however */ +/* in this case an extra concatenation node may be inserted above x; */ +/* This node should not be counted in the statement of the invariants. */ void CORD_add_forest(ForestElement * forest, CORD x, size_t len) { register int i = 0; register CORD sum = CORD_EMPTY; register size_t sum_len = 0; - + while (len > min_len[i + 1]) { - if (forest[i].c != 0) { - sum = CORD_cat(forest[i].c, sum); - sum_len += forest[i].len; - forest[i].c = 0; - } + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + forest[i].c = 0; + } i++; } - /* Sum has depth at most 1 greter than what would be required */ - /* for balance. */ + /* Sum has depth at most 1 greter than what would be required */ + /* for balance. */ sum = CORD_cat(sum, x); sum_len += len; - /* If x was a leaf, then sum is now balanced. To see this */ - /* consider the two cases in which forest[i-1] either is or is */ - /* not empty. */ + /* If x was a leaf, then sum is now balanced. To see this */ + /* consider the two cases in which forest[i-1] either is or is */ + /* not empty. */ while (sum_len >= min_len[i]) { - if (forest[i].c != 0) { - sum = CORD_cat(forest[i].c, sum); - sum_len += forest[i].len; - /* This is again balanced, since sum was balanced, and has */ - /* allowable depth that differs from i by at most 1. */ - forest[i].c = 0; - } + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + /* This is again balanced, since sum was balanced, and has */ + /* allowable depth that differs from i by at most 1. */ + forest[i].c = 0; + } i++; } i--; @@ -670,37 +672,37 @@ CORD CORD_concat_forest(ForestElement * forest, size_t expected_len) register int i = 0; CORD sum = 0; size_t sum_len = 0; - + while (sum_len != expected_len) { - if (forest[i].c != 0) { - sum = CORD_cat(forest[i].c, sum); - sum_len += forest[i].len; - } + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + } i++; } return(sum); } -/* Insert the frontier of x into forest. Balanced subtrees are */ -/* treated as leaves. This potentially adds one to the depth */ -/* of the final tree. */ +/* Insert the frontier of x into forest. Balanced subtrees are */ +/* treated as leaves. This potentially adds one to the depth */ +/* of the final tree. */ void CORD_balance_insert(CORD x, size_t len, ForestElement * forest) { register int depth; - + if (CORD_IS_STRING(x)) { CORD_add_forest(forest, x, len); } else if (IS_CONCATENATION(x) && ((depth = DEPTH(x)) >= MAX_DEPTH || len < min_len[depth])) { - register struct Concatenation * conc - = &(((CordRep *)x) -> concatenation); - size_t left_len = LEFT_LEN(conc); - - CORD_balance_insert(conc -> left, left_len, forest); - CORD_balance_insert(conc -> right, len - left_len, forest); + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + size_t left_len = LEFT_LEN(conc); + + CORD_balance_insert(conc -> left, left_len, forest); + CORD_balance_insert(conc -> right, len - left_len, forest); } else /* function or balanced */ { - CORD_add_forest(forest, x, len); + CORD_add_forest(forest, x, len); } } @@ -709,7 +711,7 @@ CORD CORD_balance(CORD x) { Forest forest; register size_t len; - + if (x == 0) return(0); if (CORD_IS_STRING(x)) return(x); if (!min_len_init) CORD_init_min_len(); @@ -720,13 +722,13 @@ CORD CORD_balance(CORD x) } -/* Position primitives */ +/* Position primitives */ /* Private routines to deal with the hard cases only: */ -/* P contains a prefix of the path to cur_pos. Extend it to a full */ -/* path and set up leaf info. */ -/* Return 0 if past the end of cord, 1 o.w. */ +/* P contains a prefix of the path to cur_pos. Extend it to a full */ +/* path and set up leaf info. */ +/* Return 0 if past the end of cord, 1 o.w. */ void CORD__extend_path(register CORD_pos p) { register struct CORD_pe * current_pe = &(p[0].path[p[0].path_len]); @@ -734,25 +736,25 @@ void CORD__extend_path(register CORD_pos p) register size_t pos = p[0].cur_pos; register size_t top_pos = current_pe -> pe_start_pos; register size_t top_len = GEN_LEN(top); - + /* Fill in the rest of the path. */ while(!CORD_IS_STRING(top) && IS_CONCATENATION(top)) { - register struct Concatenation * conc = - &(((CordRep *)top) -> concatenation); - register size_t left_len; - - left_len = LEFT_LEN(conc); - current_pe++; - if (pos >= top_pos + left_len) { - current_pe -> pe_cord = top = conc -> right; - current_pe -> pe_start_pos = top_pos = top_pos + left_len; - top_len -= left_len; - } else { - current_pe -> pe_cord = top = conc -> left; - current_pe -> pe_start_pos = top_pos; - top_len = left_len; - } - p[0].path_len++; + register struct Concatenation * conc = + &(((CordRep *)top) -> concatenation); + register size_t left_len; + + left_len = LEFT_LEN(conc); + current_pe++; + if (pos >= top_pos + left_len) { + current_pe -> pe_cord = top = conc -> right; + current_pe -> pe_start_pos = top_pos = top_pos + left_len; + top_len -= left_len; + } else { + current_pe -> pe_cord = top = conc -> left; + current_pe -> pe_start_pos = top_pos; + top_len = left_len; + } + p[0].path_len++; } /* Fill in leaf description for fast access. */ if (CORD_IS_STRING(top)) { @@ -771,7 +773,7 @@ char CORD__pos_fetch(register CORD_pos p) struct CORD_pe * pe = &((p)[0].path[(p)[0].path_len]); CORD leaf = pe -> pe_cord; register struct Function * f = &(((CordRep *)leaf) -> function); - + if (!IS_FUNCTION(leaf)) ABORT("CORD_pos_fetch: bad leaf"); return ((*(f -> fn))(p[0].cur_pos - pe -> pe_start_pos, f -> client_data)); } @@ -781,48 +783,48 @@ void CORD__next(register CORD_pos p) register size_t cur_pos = p[0].cur_pos + 1; register struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); register CORD leaf = current_pe -> pe_cord; - + /* Leaf is not a string or we're at end of leaf */ p[0].cur_pos = cur_pos; if (!CORD_IS_STRING(leaf)) { - /* Function leaf */ - register struct Function * f = &(((CordRep *)leaf) -> function); - register size_t start_pos = current_pe -> pe_start_pos; - register size_t end_pos = start_pos + f -> len; - - if (cur_pos < end_pos) { - /* Fill cache and return. */ - register size_t i; - register size_t limit = cur_pos + FUNCTION_BUF_SZ; - register CORD_fn fn = f -> fn; - register void * client_data = f -> client_data; - - if (limit > end_pos) { - limit = end_pos; - } - for (i = cur_pos; i < limit; i++) { - p[0].function_buf[i - cur_pos] = - (*fn)(i - start_pos, client_data); - } - p[0].cur_start = cur_pos; - p[0].cur_leaf = p[0].function_buf; - p[0].cur_end = limit; - return; - } + /* Function leaf */ + register struct Function * f = &(((CordRep *)leaf) -> function); + register size_t start_pos = current_pe -> pe_start_pos; + register size_t end_pos = start_pos + f -> len; + + if (cur_pos < end_pos) { + /* Fill cache and return. */ + register size_t i; + register size_t limit = cur_pos + FUNCTION_BUF_SZ; + register CORD_fn fn = f -> fn; + register void * client_data = f -> client_data; + + if (limit > end_pos) { + limit = end_pos; + } + for (i = cur_pos; i < limit; i++) { + p[0].function_buf[i - cur_pos] = + (*fn)(i - start_pos, client_data); + } + p[0].cur_start = cur_pos; + p[0].cur_leaf = p[0].function_buf; + p[0].cur_end = limit; + return; + } } - /* End of leaf */ - /* Pop the stack until we find two concatenation nodes with the */ - /* same start position: this implies we were in left part. */ + /* End of leaf */ + /* Pop the stack until we find two concatenation nodes with the */ + /* same start position: this implies we were in left part. */ { - while (p[0].path_len > 0 - && current_pe[0].pe_start_pos != current_pe[-1].pe_start_pos) { - p[0].path_len--; - current_pe--; - } - if (p[0].path_len == 0) { - p[0].path_len = CORD_POS_INVALID; + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos != current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } + if (p[0].path_len == 0) { + p[0].path_len = CORD_POS_INVALID; return; - } + } } p[0].path_len--; CORD__extend_path(p); @@ -831,26 +833,26 @@ void CORD__next(register CORD_pos p) void CORD__prev(register CORD_pos p) { register struct CORD_pe * pe = &(p[0].path[p[0].path_len]); - + if (p[0].cur_pos == 0) { p[0].path_len = CORD_POS_INVALID; return; } p[0].cur_pos--; if (p[0].cur_pos >= pe -> pe_start_pos) return; - - /* Beginning of leaf */ - - /* Pop the stack until we find two concatenation nodes with the */ - /* different start position: this implies we were in right part. */ + + /* Beginning of leaf */ + + /* Pop the stack until we find two concatenation nodes with the */ + /* different start position: this implies we were in right part. */ { - register struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); - - while (p[0].path_len > 0 - && current_pe[0].pe_start_pos == current_pe[-1].pe_start_pos) { - p[0].path_len--; - current_pe--; - } + register struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); + + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos == current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } } p[0].path_len--; CORD__extend_path(p); @@ -866,7 +868,7 @@ void CORD__prev(register CORD_pos p) char CORD_pos_fetch(register CORD_pos p) { if (p[0].cur_start <= p[0].cur_pos && p[0].cur_pos < p[0].cur_end) { - return(p[0].cur_leaf[p[0].cur_pos - p[0].cur_start]); + return(p[0].cur_leaf[p[0].cur_pos - p[0].cur_start]); } else { return(CORD__pos_fetch(p)); } @@ -875,18 +877,18 @@ char CORD_pos_fetch(register CORD_pos p) void CORD_next(CORD_pos p) { if (p[0].cur_pos < p[0].cur_end - 1) { - p[0].cur_pos++; + p[0].cur_pos++; } else { - CORD__next(p); + CORD__next(p); } } void CORD_prev(CORD_pos p) { if (p[0].cur_end != 0 && p[0].cur_pos > p[0].cur_start) { - p[0].cur_pos--; + p[0].cur_pos--; } else { - CORD__prev(p); + CORD__prev(p); } } @@ -908,8 +910,8 @@ int CORD_pos_valid(CORD_pos p) void CORD_set_pos(CORD_pos p, CORD x, size_t i) { if (x == CORD_EMPTY) { - p[0].path_len = CORD_POS_INVALID; - return; + p[0].path_len = CORD_POS_INVALID; + return; } p[0].path[0].pe_cord = x; p[0].path[0].pe_start_pos = 0; diff --git a/cord/cordtest.c b/cord/cordtest.c index 08333ca0..42c1fe61 100644 --- a/cord/cordtest.c +++ b/cord/cordtest.c @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED @@ -10,18 +10,18 @@ * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ -/* Boehm, August 24, 1994 11:58 am PDT */ -# include "gc.h" /* For GC_INIT() only */ + +# include "gc.h" /* For GC_INIT() only */ # include "cord.h" # include <string.h> # include <stdio.h> # include <stdlib.h> -/* This is a very incomplete test of the cord package. It knows about */ -/* a few internals of the package (e.g. when C strings are returned) */ -/* that real clients shouldn't rely on. */ +/* This is a very incomplete test of the cord package. It knows about */ +/* a few internals of the package (e.g. when C strings are returned) */ +/* that real clients shouldn't rely on. */ # define ABORT(string) \ -{ int x = 0; fprintf(stderr, "FAILED: %s\n", string); x = 1 / x; abort(); } + { int x = 0; fprintf(stderr, "FAILED: %s\n", string); x = 1 / x; abort(); } int count; @@ -48,60 +48,64 @@ char id_cord_fn(size_t i, void * client_data) return((char)i); } -void test_basics() +void test_basics(void) { CORD x = CORD_from_char_star("ab"); register int i; char c; CORD y; CORD_pos p; - + x = CORD_cat(x,x); if (!CORD_IS_STRING(x)) ABORT("short cord should usually be a string"); if (strcmp(x, "abab") != 0) ABORT("bad CORD_cat result"); - + for (i = 1; i < 16; i++) { x = CORD_cat(x,x); } x = CORD_cat(x,"c"); if (CORD_len(x) != 128*1024+1) ABORT("bad length"); - + count = 0; if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { ABORT("CORD_iter5 failed"); } if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); - + count = 0; CORD_set_pos(p, x, 64*1024-1); while(CORD_pos_valid(p)) { - (void) test_fn(CORD_pos_fetch(p), (void *)13); - CORD_next(p); + (void) test_fn(CORD_pos_fetch(p), (void *)13); + CORD_next(p); } if (count != 64*1024 + 2) ABORT("Position based iteration failed"); - + y = CORD_substr(x, 1023, 5); + if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); - + y = CORD_substr(x, 1024, 8); + if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "abababab") != 0) ABORT("bad CORD_substr result"); - + y = CORD_substr(x, 128*1024-1, 8); + if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "bc") != 0) ABORT("bad CORD_substr result"); - + x = CORD_balance(x); if (CORD_len(x) != 128*1024+1) ABORT("bad length"); - + count = 0; if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { ABORT("CORD_iter5 failed"); } if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); - + y = CORD_substr(x, 1023, 5); + if (!y) ABORT("CORD_substr returned NULL"); if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); y = CORD_from_fn(id_cord_fn, 0, 13); @@ -109,23 +113,23 @@ void test_basics() CORD_set_pos(p, y, i); while(CORD_pos_valid(p)) { c = CORD_pos_fetch(p); - if(c != i) ABORT("Traversal of function node failed"); - CORD_next(p); i++; + if(c != i) ABORT("Traversal of function node failed"); + CORD_next(p); i++; } if (i != 13) ABORT("Bad apparent length for function node"); } -void test_extras() +void test_extras(void) { # if defined(__OS2__) || defined(__DJGPP__) -# define FNAME1 "tmp1" -# define FNAME2 "tmp2" +# define FNAME1 "tmp1" +# define FNAME2 "tmp2" # elif defined(AMIGA) -# define FNAME1 "T:tmp1" -# define FNAME2 "T:tmp2" +# define FNAME1 "T:tmp1" +# define FNAME2 "T:tmp2" # else -# define FNAME1 "/tmp/cord_test" -# define FNAME2 "/tmp/cord_test2" +# define FNAME1 "/tmp/cord_test" +# define FNAME2 "/tmp/cord_test2" # endif register int i; CORD y = "abcdefghijklmnopqrstuvwxyz0123456789"; @@ -133,7 +137,7 @@ void test_extras() CORD w, z; FILE *f; FILE *f1a, *f1b, *f2; - + w = CORD_cat(CORD_cat(y,y),y); z = CORD_catn(3,y,y,y); if (CORD_cmp(w,z) != 0) ABORT("CORD_catn comparison wrong"); @@ -148,12 +152,16 @@ void test_extras() if ((f = fopen(FNAME1, "w")) == 0) ABORT("open failed"); if (CORD_put(z,f) == EOF) ABORT("CORD_put failed"); if (fclose(f) == EOF) ABORT("fclose failed"); - w = CORD_from_file(f1a = fopen(FNAME1, "rb")); + f1a = fopen(FNAME1, "rb"); + if (!f1a) ABORT("Unable to open " FNAME1); + w = CORD_from_file(f1a); if (CORD_len(w) != CORD_len(z)) ABORT("file length wrong"); if (CORD_cmp(w,z) != 0) ABORT("file comparison wrong"); if (CORD_cmp(CORD_substr(w, 50*36+2, 36), y) != 0) - ABORT("file substr wrong"); - z = CORD_from_file_lazy(f1b = fopen(FNAME1, "rb")); + ABORT("file substr wrong"); + f1b = fopen(FNAME1, "rb"); + if (!f1b) ABORT("2nd open failed: " FNAME1); + z = CORD_from_file_lazy(f1b); if (CORD_cmp(w,z) != 0) ABORT("File conversions differ"); if (CORD_chr(w, 0, '9') != 37) ABORT("CORD_chr failed 1"); if (CORD_chr(w, 3, 'a') != 38) ABORT("CORD_chr failed 2"); @@ -169,46 +177,48 @@ void test_extras() # endif if (CORD_put(x,f) == EOF) ABORT("CORD_put failed"); if (fclose(f) == EOF) ABORT("fclose failed"); - w = CORD_from_file(f2 = fopen(FNAME2, "rb")); + f2 = fopen(FNAME2, "rb"); + if (!f2) ABORT("Unable to open " FNAME2); + w = CORD_from_file(f2); if (CORD_len(w) != CORD_len(x)) ABORT("file length wrong"); if (CORD_cmp(w,x) != 0) ABORT("file comparison wrong"); if (CORD_cmp(CORD_substr(w, 1000*36, 36), y) != 0) - ABORT("file substr wrong"); + ABORT("file substr wrong"); if (strcmp(CORD_to_char_star(CORD_substr(w, 1000*36, 36)), y) != 0) - ABORT("char * file substr wrong"); + ABORT("char * file substr wrong"); if (strcmp(CORD_substr(w, 1000*36, 2), "ab") != 0) - ABORT("short file substr wrong"); + ABORT("short file substr wrong"); if (CORD_str(x,1,"9a") != 35) ABORT("CORD_str failed 1"); if (CORD_str(x,0,"9abcdefghijk") != 35) ABORT("CORD_str failed 2"); if (CORD_str(x,0,"9abcdefghijx") != CORD_NOT_FOUND) - ABORT("CORD_str failed 3"); + ABORT("CORD_str failed 3"); if (CORD_str(x,0,"9>") != CORD_NOT_FOUND) ABORT("CORD_str failed 4"); if (remove(FNAME1) != 0) { - /* On some systems, e.g. OS2, this may fail if f1 is still open. */ - if ((fclose(f1a) == EOF) & (fclose(f1b) == EOF)) - ABORT("fclose(f1) failed"); - if (remove(FNAME1) != 0) ABORT("remove 1 failed"); + /* On some systems, e.g. OS2, this may fail if f1 is still open. */ + if ((fclose(f1a) == EOF) & (fclose(f1b) == EOF)) + ABORT("fclose(f1) failed"); + if (remove(FNAME1) != 0) ABORT("remove 1 failed"); } if (remove(FNAME2) != 0) { - if (fclose(f2) == EOF) ABORT("fclose(f2) failed"); - if (remove(FNAME2) != 0) ABORT("remove 2 failed"); + if (fclose(f2) == EOF) ABORT("fclose(f2) failed"); + if (remove(FNAME2) != 0) ABORT("remove 2 failed"); } } -void test_printf() +void test_printf(void) { CORD result; char result2[200]; long l; short s; CORD x; - + if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7) - ABORT("CORD_sprintf failed 1"); + ABORT("CORD_sprintf failed 1"); if (CORD_cmp(result, " 3.14") != 0)ABORT("CORD_sprintf goofed 1"); if (l != 7) ABORT("CORD_sprintf goofed 2"); if (CORD_sprintf(&result, "%-7.2s%hn%c%s", "abcd", &s, 'x', "yz") != 10) - ABORT("CORD_sprintf failed 2"); + ABORT("CORD_sprintf failed 2"); if (CORD_cmp(result, "ab xyz") != 0)ABORT("CORD_sprintf goofed 3"); if (s != 7) ABORT("CORD_sprintf goofed 4"); x = "abcdefghij"; @@ -216,12 +226,12 @@ void test_printf() x = CORD_cat(x,x); x = CORD_cat(x,x); if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124) - ABORT("CORD_sprintf failed 3"); + ABORT("CORD_sprintf failed 3"); (void) sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x)); if (CORD_cmp(result, result2) != 0)ABORT("CORD_sprintf goofed 5"); } -int main() +int main(void) { # ifdef THINK_C printf("cordtest:\n"); diff --git a/cord/cordxtra.c b/cord/cordxtra.c index b0a74622..d5394dba 100644 --- a/cord/cordxtra.c +++ b/cord/cordxtra.c @@ -24,17 +24,17 @@ # include <stdarg.h> # include "cord.h" # include "ec.h" -# define I_HIDE_POINTERS /* So we get access to allocation lock. */ - /* We use this for lazy file reading, */ - /* so that we remain independent */ - /* of the threads primitives. */ +# define I_HIDE_POINTERS /* So we get access to allocation lock. */ + /* We use this for lazy file reading, */ + /* so that we remain independent */ + /* of the threads primitives. */ # include "gc.h" -/* For now we assume that pointer reads and writes are atomic, */ -/* i.e. another thread always sees the state before or after */ -/* a write. This might be false on a Motorola M68K with */ -/* pointers that are not 32-bit aligned. But there probably */ -/* aren't too many threads packages running on those. */ +/* For now we assume that pointer reads and writes are atomic, */ +/* i.e. another thread always sees the state before or after */ +/* a write. This might be false on a Motorola M68K with */ +/* pointers that are not 32-bit aligned. But there probably */ +/* aren't too many threads packages running on those. */ # define ATOMIC_WRITE(x,y) (x) = (y) # define ATOMIC_READ(x) (*(x)) @@ -46,19 +46,19 @@ # define SEEK_END 2 # endif -# define BUFSZ 2048 /* Size of stack allocated buffers when */ - /* we want large buffers. */ +# define BUFSZ 2048 /* Size of stack allocated buffers when */ + /* we want large buffers. */ typedef void (* oom_fn)(void); # define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ - ABORT("Out of memory\n"); } + ABORT("Out of memory\n"); } # define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } CORD CORD_cat_char(CORD x, char c) { register char * string; - + if (c == '\0') return(CORD_cat(x, CORD_nul(1))); string = GC_MALLOC_ATOMIC(2); if (string == 0) OUT_OF_MEMORY; @@ -83,22 +83,22 @@ CORD CORD_catn(int nargs, ...) } typedef struct { - size_t len; - size_t count; - char * buf; + size_t len; + size_t count; + char * buf; } CORD_fill_data; int CORD_fill_proc(char c, void * client_data) { register CORD_fill_data * d = (CORD_fill_data *)client_data; register size_t count = d -> count; - + (d -> buf)[count] = c; d -> count = ++count; if (count >= d -> len) { - return(1); + return(1); } else { - return(0); + return(0); } } @@ -109,7 +109,7 @@ int CORD_batched_fill_proc(const char * s, void * client_data) register size_t max = d -> len; register char * buf = d -> buf; register const char * t = s; - + while((buf[count] = *t++) != '\0') { count++; if (count >= max) { @@ -121,12 +121,12 @@ int CORD_batched_fill_proc(const char * s, void * client_data) return(0); } -/* Fill buf with len characters starting at i. */ -/* Assumes len characters are available. */ +/* Fill buf with len characters starting at i. */ +/* Assumes len characters are available. */ void CORD_fill_buf(CORD x, size_t i, size_t len, char * buf) { CORD_fill_data fd; - + fd.len = len; fd.buf = buf; fd.count = 0; @@ -138,7 +138,7 @@ int CORD_cmp(CORD x, CORD y) CORD_pos xpos; CORD_pos ypos; register size_t avail, yavail; - + if (y == CORD_EMPTY) return(x != CORD_EMPTY); if (x == CORD_EMPTY) return(-1); if (CORD_IS_STRING(y) && CORD_IS_STRING(x)) return(strcmp(x,y)); @@ -147,7 +147,7 @@ int CORD_cmp(CORD x, CORD y) for(;;) { if (!CORD_pos_valid(xpos)) { if (CORD_pos_valid(ypos)) { - return(-1); + return(-1); } else { return(0); } @@ -163,12 +163,12 @@ int CORD_cmp(CORD x, CORD y) CORD_next(xpos); CORD_next(ypos); } else { - /* process as many characters as we can */ + /* process as many characters as we can */ register int result; - + if (avail > yavail) avail = yavail; result = strncmp(CORD_pos_cur_char_addr(xpos), - CORD_pos_cur_char_addr(ypos), avail); + CORD_pos_cur_char_addr(ypos), avail); if (result != 0) return(result); CORD_pos_advance(xpos, avail); CORD_pos_advance(ypos, avail); @@ -182,13 +182,13 @@ int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len) CORD_pos ypos; register size_t count; register long avail, yavail; - + CORD_set_pos(xpos, x, x_start); CORD_set_pos(ypos, y, y_start); for(count = 0; count < len;) { if (!CORD_pos_valid(xpos)) { if (CORD_pos_valid(ypos)) { - return(-1); + return(-1); } else { return(0); } @@ -205,14 +205,14 @@ int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len) CORD_next(ypos); count++; } else { - /* process as many characters as we can */ + /* process as many characters as we can */ register int result; - + if (avail > yavail) avail = yavail; count += avail; if (count > len) avail -= (count - len); result = strncmp(CORD_pos_cur_char_addr(xpos), - CORD_pos_cur_char_addr(ypos), (size_t)avail); + CORD_pos_cur_char_addr(ypos), (size_t)avail); if (result != 0) return(result); CORD_pos_advance(xpos, (size_t)avail); CORD_pos_advance(ypos, (size_t)avail); @@ -225,7 +225,7 @@ char * CORD_to_char_star(CORD x) { register size_t len = CORD_len(x); char * result = GC_MALLOC_ATOMIC(len + 1); - + if (result == 0) OUT_OF_MEMORY; CORD_fill_buf(x, 0, len, result); result[len] = '\0'; @@ -254,7 +254,7 @@ const char * CORD_to_const_char_star(CORD x) char CORD_fetch(CORD x, size_t i) { CORD_pos xpos; - + CORD_set_pos(xpos, x, i); if (!CORD_pos_valid(xpos)) ABORT("bad index?"); return(CORD_pos_fetch(xpos)); @@ -264,36 +264,36 @@ char CORD_fetch(CORD x, size_t i) int CORD_put_proc(char c, void * client_data) { register FILE * f = (FILE *)client_data; - + return(putc(c, f) == EOF); } int CORD_batched_put_proc(const char * s, void * client_data) { register FILE * f = (FILE *)client_data; - + return(fputs(s, f) == EOF); } - + int CORD_put(CORD x, FILE * f) { if (CORD_iter5(x, 0, CORD_put_proc, CORD_batched_put_proc, f)) { return(EOF); } else { - return(1); + return(1); } } typedef struct { - size_t pos; /* Current position in the cord */ - char target; /* Character we're looking for */ + size_t pos; /* Current position in the cord */ + char target; /* Character we're looking for */ } chr_data; int CORD_chr_proc(char c, void * client_data) { register chr_data * d = (chr_data *)client_data; - + if (c == d -> target) return(1); (d -> pos) ++; return(0); @@ -302,7 +302,7 @@ int CORD_chr_proc(char c, void * client_data) int CORD_rchr_proc(char c, void * client_data) { register chr_data * d = (chr_data *)client_data; - + if (c == d -> target) return(1); (d -> pos) --; return(0); @@ -312,48 +312,48 @@ int CORD_batched_chr_proc(const char *s, void * client_data) { register chr_data * d = (chr_data *)client_data; register char * occ = strchr(s, d -> target); - + if (occ == 0) { - d -> pos += strlen(s); - return(0); + d -> pos += strlen(s); + return(0); } else { - d -> pos += occ - s; - return(1); + d -> pos += occ - s; + return(1); } } size_t CORD_chr(CORD x, size_t i, int c) { chr_data d; - + d.pos = i; d.target = c; if (CORD_iter5(x, i, CORD_chr_proc, CORD_batched_chr_proc, &d)) { return(d.pos); } else { - return(CORD_NOT_FOUND); + return(CORD_NOT_FOUND); } } size_t CORD_rchr(CORD x, size_t i, int c) { chr_data d; - + d.pos = i; d.target = c; if (CORD_riter4(x, i, CORD_rchr_proc, &d)) { return(d.pos); } else { - return(CORD_NOT_FOUND); + return(CORD_NOT_FOUND); } } -/* Find the first occurrence of s in x at position start or later. */ -/* This uses an asymptotically poor algorithm, which should typically */ -/* perform acceptably. We compare the first few characters directly, */ -/* and call CORD_ncmp whenever there is a partial match. */ -/* This has the advantage that we allocate very little, or not at all. */ -/* It's very fast if there are few close misses. */ +/* Find the first occurrence of s in x at position start or later. */ +/* This uses an asymptotically poor algorithm, which should typically */ +/* perform acceptably. We compare the first few characters directly, */ +/* and call CORD_ncmp whenever there is a partial match. */ +/* This has the advantage that we allocate very little, or not at all. */ +/* It's very fast if there are few close misses. */ size_t CORD_str(CORD x, size_t start, CORD s) { CORD_pos xpos; @@ -361,14 +361,14 @@ size_t CORD_str(CORD x, size_t start, CORD s) size_t slen; register size_t start_len; const char * s_start; - unsigned long s_buf = 0; /* The first few characters of s */ - unsigned long x_buf = 0; /* Start of candidate substring. */ - /* Initialized only to make compilers */ - /* happy. */ + unsigned long s_buf = 0; /* The first few characters of s */ + unsigned long x_buf = 0; /* Start of candidate substring. */ + /* Initialized only to make compilers */ + /* happy. */ unsigned long mask = 0; register size_t i; register size_t match_pos; - + if (s == CORD_EMPTY) return(start); if (CORD_IS_STRING(s)) { s_start = s; @@ -391,17 +391,17 @@ size_t CORD_str(CORD x, size_t start, CORD s) CORD_next(xpos); } for (match_pos = start; ; match_pos++) { - if ((x_buf & mask) == s_buf) { - if (slen == start_len || - CORD_ncmp(x, match_pos + start_len, - s, start_len, slen - start_len) == 0) { - return(match_pos); - } - } - if ( match_pos == xlen - slen ) { - return(CORD_NOT_FOUND); - } - x_buf <<= 8; + if ((x_buf & mask) == s_buf) { + if (slen == start_len || + CORD_ncmp(x, match_pos + start_len, + s, start_len, slen - start_len) == 0) { + return(match_pos); + } + } + if ( match_pos == xlen - slen ) { + return(CORD_NOT_FOUND); + } + x_buf <<= 8; x_buf |= (unsigned char)CORD_pos_fetch(xpos); CORD_next(xpos); } @@ -442,16 +442,16 @@ CORD CORD_from_file_eager(FILE * f) { register int c; CORD_ec ecord; - + CORD_ec_init(ecord); for(;;) { c = getc(f); if (c == 0) { - /* Append the right number of NULs */ - /* Note that any string of NULs is rpresented in 4 words, */ - /* independent of its length. */ + /* Append the right number of NULs */ + /* Note that any string of NULs is rpresented in 4 words, */ + /* independent of its length. */ register size_t count = 1; - + CORD_ec_flush_buf(ecord); while ((c = getc(f)) == 0) count++; ecord[0].ec_cord = CORD_cat(ecord[0].ec_cord, CORD_nul(count)); @@ -463,18 +463,18 @@ CORD CORD_from_file_eager(FILE * f) return(CORD_balance(CORD_ec_to_cord(ecord))); } -/* The state maintained for a lazily read file consists primarily */ -/* of a large direct-mapped cache of previously read values. */ -/* We could rely more on stdio buffering. That would have 2 */ -/* disadvantages: */ -/* 1) Empirically, not all fseek implementations preserve the */ -/* buffer whenever they could. */ -/* 2) It would fail if 2 different sections of a long cord */ -/* were being read alternately. */ -/* We do use the stdio buffer for read ahead. */ -/* To guarantee thread safety in the presence of atomic pointer */ -/* writes, cache lines are always replaced, and never modified in */ -/* place. */ +/* The state maintained for a lazily read file consists primarily */ +/* of a large direct-mapped cache of previously read values. */ +/* We could rely more on stdio buffering. That would have 2 */ +/* disadvantages: */ +/* 1) Empirically, not all fseek implementations preserve the */ +/* buffer whenever they could. */ +/* 2) It would fail if 2 different sections of a long cord */ +/* were being read alternately. */ +/* We do use the stdio buffer for read ahead. */ +/* To guarantee thread safety in the presence of atomic pointer */ +/* writes, cache lines are always replaced, and never modified in */ +/* place. */ # define LOG_CACHE_SZ 14 # define CACHE_SZ (1 << LOG_CACHE_SZ) @@ -484,12 +484,12 @@ CORD CORD_from_file_eager(FILE * f) typedef struct { size_t tag; char data[LINE_SZ]; - /* data[i%LINE_SZ] = ith char in file if tag = i/LINE_SZ */ + /* data[i%LINE_SZ] = ith char in file if tag = i/LINE_SZ */ } cache_line; typedef struct { FILE * lf_file; - size_t lf_current; /* Current file pointer value */ + size_t lf_current; /* Current file pointer value */ cache_line * volatile lf_cache[CACHE_SZ/LINE_SZ]; } lf_state; @@ -501,7 +501,7 @@ typedef struct { typedef struct { lf_state * state; - size_t file_pos; /* Position of needed character. */ + size_t file_pos; /* Position of needed character. */ cache_line * new_cache; } refill_data; @@ -515,14 +515,14 @@ refill_data * client_data; size_t line_start = LINE_START(file_pos); size_t line_no = DIV_LINE_SZ(MOD_CACHE_SZ(file_pos)); cache_line * new_cache = client_data -> new_cache; - + if (line_start != state -> lf_current && fseek(f, line_start, SEEK_SET) != 0) { - ABORT("fseek failed"); + ABORT("fseek failed"); } if (fread(new_cache -> data, sizeof(char), LINE_SZ, f) - <= file_pos - line_start) { - ABORT("fread failed"); + <= file_pos - line_start) { + ABORT("fread failed"); } new_cache -> tag = DIV_LINE_SZ(file_pos); /* Store barrier goes here. */ @@ -535,47 +535,50 @@ char CORD_lf_func(size_t i, void * client_data) { register lf_state * state = (lf_state *)client_data; register cache_line * volatile * cl_addr = - &(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]); + &(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]); register cache_line * cl = (cache_line *)ATOMIC_READ(cl_addr); - + if (cl == 0 || cl -> tag != DIV_LINE_SZ(i)) { - /* Cache miss */ - refill_data rd; - + /* Cache miss */ + refill_data rd; + rd.state = state; rd.file_pos = i; rd.new_cache = GC_NEW_ATOMIC(cache_line); if (rd.new_cache == 0) OUT_OF_MEMORY; return((char)(GC_word) - GC_call_with_alloc_lock((GC_fn_type) refill_cache, &rd)); + GC_call_with_alloc_lock((GC_fn_type) refill_cache, &rd)); } return(cl -> data[MOD_LINE_SZ(i)]); -} +} /*ARGSUSED*/ -void CORD_lf_close_proc(void * obj, void * client_data) +void CORD_lf_close_proc(void * obj, void * client_data) { if (fclose(((lf_state *)obj) -> lf_file) != 0) { - ABORT("CORD_lf_close_proc: fclose failed"); + ABORT("CORD_lf_close_proc: fclose failed"); } -} +} CORD CORD_from_file_lazy_inner(FILE * f, size_t len) { register lf_state * state = GC_NEW(lf_state); register int i; - + if (state == 0) OUT_OF_MEMORY; if (len != 0) { - /* Dummy read to force buffer allocation. */ - /* This greatly increases the probability */ - /* of avoiding deadlock if buffer allocation */ - /* is redirected to GC_malloc and the */ - /* world is multithreaded. */ - char buf[1]; - - (void) fread(buf, 1, 1, f); - rewind(f); + /* Dummy read to force buffer allocation. */ + /* This greatly increases the probability */ + /* of avoiding deadlock if buffer allocation */ + /* is redirected to GC_malloc and the */ + /* world is multithreaded. */ + char buf[1]; + + if (fread(buf, 1, 1, f) > 1) { + /* Just to suppress "unused result" compiler warning. */ + ABORT("fread unexpected result"); + } + rewind(f); } state -> lf_file = f; for (i = 0; i < CACHE_SZ/LINE_SZ; i++) { @@ -589,7 +592,7 @@ CORD CORD_from_file_lazy_inner(FILE * f, size_t len) CORD CORD_from_file_lazy(FILE * f) { register long len; - + if (fseek(f, 0l, SEEK_END) != 0) { ABORT("Bad fd argument - fseek failed"); } @@ -605,7 +608,7 @@ CORD CORD_from_file_lazy(FILE * f) CORD CORD_from_file(FILE * f) { register long len; - + if (fseek(f, 0l, SEEK_END) != 0) { ABORT("Bad fd argument - fseek failed"); } diff --git a/darwin_stop_world.c b/darwin_stop_world.c index db462295..f05dc685 100644 --- a/darwin_stop_world.c +++ b/darwin_stop_world.c @@ -145,7 +145,7 @@ STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p, (natural_t *)&state, &thread_state_count); # ifdef DEBUG_THREADS - GC_printf("thread_get_state return value = %d\n", kern_result); + GC_log_printf("thread_get_state returns value = %d\n", kern_result); # endif if (kern_result != KERN_SUCCESS) ABORT("thread_get_state failed"); @@ -255,8 +255,8 @@ STATIC ptr_t GC_stack_range_for(ptr_t *phi, thread_act_t thread, GC_thread p, *phi = (p->flags & MAIN_THREAD) != 0 ? GC_stackbottom : p->stack_end; # endif # ifdef DEBUG_THREADS - GC_printf("Darwin: Stack for thread 0x%lx = [%p,%p)\n", - (unsigned long) thread, lo, *phi); + GC_log_printf("Darwin: Stack for thread 0x%lx = [%p,%p)\n", + (unsigned long)thread, lo, *phi); # endif return lo; } @@ -322,7 +322,7 @@ GC_INNER void GC_push_all_stacks(void) if (GC_print_stats == VERBOSE) GC_log_printf("Pushed %d thread stacks\n", nthreads); if (!found_me && !GC_in_thread_creation) - ABORT("Collecting from unknown thread."); + ABORT("Collecting from unknown thread"); GC_total_stacksize = total_size; } @@ -352,10 +352,6 @@ GC_INNER void GC_push_all_stacks(void) STATIC int GC_mach_threads_count = 0; /* FIXME: it is better to implement GC_mach_threads as a hash set. */ -# ifdef PARALLEL_MARK - GC_INNER GC_bool GC_is_mach_marker(thread_act_t thread); -# endif - /* returns true if there's a thread in act_list that wasn't in old_list */ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, thread_act_array_t old_list, @@ -386,8 +382,8 @@ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, # endif # ifdef DEBUG_THREADS - GC_printf("Attempting to suspend thread 0x%lx\n", - (unsigned long)thread); + GC_log_printf("Attempting to suspend thread 0x%lx\n", + (unsigned long)thread); # endif /* find the current thread in the old list */ found = FALSE; @@ -411,7 +407,7 @@ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, if (!found) { /* add it to the GC_mach_threads list */ if (GC_mach_threads_count == GC_MAX_MACH_THREADS) - ABORT("too many threads"); + ABORT("Too many threads"); GC_mach_threads[GC_mach_threads_count].thread = thread; /* default is not suspended */ GC_mach_threads[GC_mach_threads_count].already_suspended = FALSE; @@ -431,8 +427,8 @@ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, continue; } # ifdef DEBUG_THREADS - GC_printf("Thread state for 0x%lx = %d\n", (unsigned long)thread, - info.run_state); + GC_log_printf("Thread state for 0x%lx = %d\n", (unsigned long)thread, + info.run_state); # endif if (info.suspend_count != 0) { /* thread is already suspended. */ @@ -442,7 +438,7 @@ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, } # ifdef DEBUG_THREADS - GC_printf("Suspending 0x%lx\n", (unsigned long)thread); + GC_log_printf("Suspending 0x%lx\n", (unsigned long)thread); # endif kern_result = thread_suspend(thread); if (kern_result != KERN_SUCCESS) { @@ -460,11 +456,6 @@ STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list, int count, #endif /* !GC_NO_THREADS_DISCOVERY */ -#ifdef MPROTECT_VDB - GC_INNER void GC_mprotect_stop(void); - GC_INNER void GC_mprotect_resume(void); -#endif - /* Caller holds allocation lock. */ GC_INNER void GC_stop_world(void) { @@ -474,8 +465,8 @@ GC_INNER void GC_stop_world(void) kern_return_t kern_result; # ifdef DEBUG_THREADS - GC_printf("Stopping the world from thread 0x%lx\n", - (unsigned long)my_thread); + GC_log_printf("Stopping the world from thread 0x%lx\n", + (unsigned long)my_thread); # endif # ifdef PARALLEL_MARK if (GC_parallel) { @@ -564,7 +555,7 @@ GC_INNER void GC_stop_world(void) # endif # ifdef DEBUG_THREADS - GC_printf("World stopped from 0x%lx\n", (unsigned long)my_thread); + GC_log_printf("World stopped from 0x%lx\n", (unsigned long)my_thread); # endif mach_port_deallocate(my_task, my_thread); } @@ -581,8 +572,8 @@ GC_INLINE void GC_thread_resume(thread_act_t thread) ABORT("thread_info failed"); # endif # ifdef DEBUG_THREADS - GC_printf("Resuming thread 0x%lx with state %d\n", - (unsigned long)thread, info.run_state); + GC_log_printf("Resuming thread 0x%lx with state %d\n", + (unsigned long)thread, info.run_state); # endif /* Resume the thread */ kern_result = thread_resume(thread); @@ -597,7 +588,7 @@ GC_INNER void GC_start_world(void) task_t my_task = current_task(); int i; # ifdef DEBUG_THREADS - GC_printf("World starting\n"); + GC_log_printf("World starting\n"); # endif # ifdef MPROTECT_VDB if(GC_incremental) { @@ -639,8 +630,8 @@ GC_INNER void GC_start_world(void) /* The thread is found in GC_mach_threads. */ if (GC_mach_threads[j].already_suspended) { # ifdef DEBUG_THREADS - GC_printf("Not resuming already suspended thread 0x%lx\n", - (unsigned long)thread); + GC_log_printf("Not resuming already suspended thread 0x%lx\n", + (unsigned long)thread); # endif } else { GC_thread_resume(thread); @@ -669,7 +660,7 @@ GC_INNER void GC_start_world(void) } # ifdef DEBUG_THREADS - GC_printf("World started\n"); + GC_log_printf("World started\n"); # endif } @@ -22,43 +22,37 @@ #endif #include <string.h> -GC_INNER void GC_default_print_heap_obj_proc(ptr_t p); - -GC_API void GC_CALL GC_register_finalizer_no_order(void * obj, - GC_finalization_proc fn, void * cd, - GC_finalization_proc *ofn, void * *ocd); - #ifndef SHORT_DBG_HDRS - /* Check whether object with base pointer p has debugging info */ + /* Check whether object with base pointer p has debugging info. */ /* p is assumed to point to a legitimate object in our part */ /* of the heap. */ /* This excludes the check as to whether the back pointer is */ /* odd, which is added by the GC_HAS_DEBUG_INFO macro. */ /* Note that if DBG_HDRS_ALL is set, uncollectable objects */ /* on free lists may not have debug information set. Thus it's */ - /* not always safe to return TRUE, even if the client does */ - /* its part. */ - GC_INNER GC_bool GC_has_other_debug_info(ptr_t p) + /* not always safe to return TRUE (1), even if the client does */ + /* its part. Return -1 if the object with debug info has been */ + /* marked as deallocated. */ + GC_INNER int GC_has_other_debug_info(ptr_t p) { - oh * ohdr = (oh *)p; - ptr_t body = (ptr_t)(ohdr + 1); - word sz = GC_size((ptr_t) ohdr); + ptr_t body = (ptr_t)((oh *)p + 1); + word sz = GC_size(p); - if (HBLKPTR((ptr_t)ohdr) != HBLKPTR((ptr_t)body) + if (HBLKPTR(p) != HBLKPTR((ptr_t)body) || sz < DEBUG_BYTES + EXTRA_BYTES) { - return(FALSE); + return 0; } - if (ohdr -> oh_sz == sz) { - /* Object may have had debug info, but has been deallocated */ - return(FALSE); + if (((oh *)p) -> oh_sf != (START_FLAG ^ (word)body) + && ((word *)p)[BYTES_TO_WORDS(sz)-1] != (END_FLAG ^ (word)body)) { + return 0; } - if (ohdr -> oh_sf == (START_FLAG ^ (word)body)) return(TRUE); - if (((word *)ohdr)[BYTES_TO_WORDS(sz)-1] == (END_FLAG ^ (word)body)) { - return(TRUE); + if (((oh *)p)->oh_sz == sz) { + /* Object may have had debug info, but has been deallocated */ + return -1; } - return(FALSE); + return 1; } -#endif +#endif /* !SHORT_DBG_HDRS */ #ifdef KEEP_BACK_PTRS @@ -175,13 +169,11 @@ GC_API void GC_CALL GC_register_finalizer_no_order(void * obj, { ptr_t result; ptr_t base; - for (;;) { - result = GC_generate_random_heap_address(); - base = GC_base(result); - if (0 == base) continue; - if (!GC_is_marked(base)) continue; - return result; - } + do { + result = GC_generate_random_heap_address(); + base = GC_base(result); + } while (base == 0 || !GC_is_marked(base)); + return result; } /* Print back trace for p */ @@ -237,7 +229,7 @@ GC_API void GC_CALL GC_register_finalizer_no_order(void * obj, { void * current; current = GC_generate_random_valid_address(); - GC_printf("\n****Chose address %p in object\n", current); + GC_printf("\n****Chosen address %p in object\n", current); GC_print_backtrace(current); } @@ -255,15 +247,14 @@ GC_API void GC_CALL GC_register_finalizer_no_order(void * obj, # define CROSSES_HBLK(p, sz) \ (((word)(p + sizeof(oh) + sz - 1) ^ (word)p) >= HBLKSIZE) -/* Store debugging info into p. Return displaced pointer. */ -/* Assumes we don't hold allocation lock. */ -GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string, - word integer) + +/* Store debugging info into p. Return displaced pointer. */ +/* This version assumes we do hold the allocation lock. */ +STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz, const char *string, + int linenum) { word * result = (word *)((oh *)p + 1); - DCL_LOCK_STATE; - LOCK(); GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz))); # ifdef KEEP_BACK_PTRS @@ -273,48 +264,31 @@ GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string, ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0); # endif ((oh *)p) -> oh_string = string; - ((oh *)p) -> oh_int = integer; + ((oh *)p) -> oh_int = (word)linenum; # ifndef SHORT_DBG_HDRS ((oh *)p) -> oh_sz = sz; ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; # endif - UNLOCK(); return((ptr_t)result); } -#ifdef DBG_HDRS_ALL -/* Store debugging info into p. Return displaced pointer. */ -/* This version assumes we do hold the allocation lock. */ -STATIC ptr_t GC_store_debug_info_inner(ptr_t p, word sz, char *string, - word integer) +GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *string, + int linenum) { - word * result = (word *)((oh *)p + 1); + ptr_t result; + DCL_LOCK_STATE; - GC_ASSERT(GC_size(p) >= sizeof(oh) + sz); - GC_ASSERT(!(SMALL_OBJ(sz) && CROSSES_HBLK(p, sz))); -# ifdef KEEP_BACK_PTRS - ((oh *)p) -> oh_back_ptr = HIDE_BACK_PTR(NOT_MARKED); -# endif -# ifdef MAKE_BACK_GRAPH - ((oh *)p) -> oh_bg_ptr = HIDE_BACK_PTR((ptr_t)0); -# endif - ((oh *)p) -> oh_string = string; - ((oh *)p) -> oh_int = integer; -# ifndef SHORT_DBG_HDRS - ((oh *)p) -> oh_sz = sz; - ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; - ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = - result[SIMPLE_ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; -# endif - return((ptr_t)result); + LOCK(); + result = GC_store_debug_info_inner(p, sz, string, linenum); + UNLOCK(); + return result; } -#endif #ifndef SHORT_DBG_HDRS - /* Check the object with debugging info at ohdr */ - /* return NULL if it's OK. Else return clobbered */ + /* Check the object with debugging info at ohdr. */ + /* Return NULL if it's OK. Else return clobbered */ /* address. */ STATIC ptr_t GC_check_annotated_obj(oh *ohdr) { @@ -380,12 +354,14 @@ STATIC void GC_print_type(ptr_t p) GC_err_puts("STUBBORN"); break; default: - GC_err_printf("kind %d, descr 0x%lx", kind, + GC_err_printf("kind=%d descr=0x%lx", kind, (unsigned long)(hhdr -> hb_descr)); } } } +#define GET_OH_LINENUM(ohdr) ((int)(ohdr)->oh_int) + /* Print a human-readable description of the object to stderr. p points */ /* to somewhere inside an object with the debugging info. */ STATIC void GC_print_obj(ptr_t p) @@ -399,10 +375,10 @@ STATIC void GC_print_obj(ptr_t p) GC_err_printf("%p (", ((ptr_t)ohdr + sizeof(oh))); GC_err_puts(ohdr -> oh_string); # ifdef SHORT_DBG_HDRS - GC_err_printf(":%ld, ", (unsigned long)(ohdr -> oh_int)); + GC_err_printf(":%d, ", GET_OH_LINENUM(ohdr)); # else - GC_err_printf(":%ld, sz=%ld, ", (unsigned long)(ohdr -> oh_int), - (unsigned long)(ohdr -> oh_sz)); + GC_err_printf(":%d, sz=%lu, ", + GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz)); # endif GC_print_type((ptr_t)(ohdr + 1)); GC_err_puts(")\n"); @@ -423,7 +399,8 @@ STATIC void GC_debug_print_heap_obj_proc(ptr_t p) /* Use GC_err_printf and friends to print a description of the object */ /* whose client-visible address is p, and which was smashed at */ /* clobbered_addr. */ - STATIC void GC_print_smashed_obj(ptr_t p, ptr_t clobbered_addr) + STATIC void GC_print_smashed_obj(const char *msg, ptr_t p, + ptr_t clobbered_addr) { oh * ohdr = (oh *)GC_base(p); @@ -434,17 +411,16 @@ STATIC void GC_debug_print_heap_obj_proc(ptr_t p) if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz)) || ohdr -> oh_string == 0) { GC_err_printf( - "%p in or near object at %p(<smashed>, appr. sz = %lu)\n", - clobbered_addr, p, + "%s %p in or near object at %p(<smashed>, appr. sz = %lu)\n", + msg, clobbered_addr, p, (unsigned long)(GC_size((ptr_t)ohdr) - DEBUG_BYTES)); } else { - GC_err_printf("%p in or near object at %p(%s:%lu, sz=%lu)\n", - clobbered_addr, p, + GC_err_printf("%s %p in or near object at %p (%s:%d, sz=%lu)\n", + msg, clobbered_addr, p, (word)(ohdr -> oh_string) < HBLKSIZE ? "(smashed string)" : ohdr -> oh_string[0] == '\0' ? "EMPTY(smashed?)" : ohdr -> oh_string, - (unsigned long)(ohdr -> oh_int), - (unsigned long)(ohdr -> oh_sz)); + GET_OH_LINENUM(ohdr), (unsigned long)(ohdr -> oh_sz)); PRINT_CALL_CHAIN(ohdr); } } @@ -459,16 +435,16 @@ STATIC void GC_debug_print_heap_obj_proc(ptr_t p) GC_INNER void GC_start_debugging(void) { -# ifndef SHORT_DBG_HDRS - GC_check_heap = GC_check_heap_proc; - GC_print_all_smashed = GC_print_all_smashed_proc; -# else - GC_check_heap = GC_do_nothing; - GC_print_all_smashed = GC_do_nothing; -# endif - GC_print_heap_obj = GC_debug_print_heap_obj_proc; - GC_debugging_started = TRUE; - GC_register_displacement((word)sizeof(oh)); +# ifndef SHORT_DBG_HDRS + GC_check_heap = GC_check_heap_proc; + GC_print_all_smashed = GC_print_all_smashed_proc; +# else + GC_check_heap = GC_do_nothing; + GC_print_all_smashed = GC_do_nothing; +# endif + GC_print_heap_obj = GC_debug_print_heap_obj_proc; + GC_debugging_started = TRUE; + GC_register_displacement((word)sizeof(oh)); } size_t GC_debug_header_size = sizeof(oh); @@ -481,7 +457,11 @@ GC_API void GC_CALL GC_debug_register_displacement(size_t offset) GC_API void * GC_CALL GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS) { - void * result = GC_malloc(lb + DEBUG_BYTES); + void * result; + /* Note that according to malloc() specification, if size is 0 then */ + /* malloc() returns either NULL, or a unique pointer value that can */ + /* later be successfully passed to free(). We always do the latter. */ + result = GC_malloc(lb + DEBUG_BYTES); if (result == 0) { GC_err_printf("GC_debug_malloc(%lu) returning NULL (", @@ -494,7 +474,7 @@ GC_API void * GC_CALL GC_debug_malloc(size_t lb, GC_EXTRA_PARAMS) GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); + return (GC_store_debug_info(result, (word)lb, s, i)); } GC_API void * GC_CALL GC_debug_malloc_ignore_off_page(size_t lb, @@ -513,7 +493,7 @@ GC_API void * GC_CALL GC_debug_malloc_ignore_off_page(size_t lb, GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); + return (GC_store_debug_info(result, (word)lb, s, i)); } GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb, @@ -523,7 +503,7 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb, if (result == 0) { GC_err_printf("GC_debug_malloc_atomic_ignore_off_page(%lu)" - " returning NULL (", (unsigned long) lb); + " returning NULL (", (unsigned long)lb); GC_err_puts(s); GC_err_printf(":%lu)\n", (unsigned long)i); return(0); @@ -532,18 +512,15 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb, GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); + return (GC_store_debug_info(result, (word)lb, s, i)); } #ifdef DBG_HDRS_ALL - /* - * An allocation function for internal use. - * Normally internally allocated objects do not have debug information. - * But in this case, we need to make sure that all objects have debug - * headers. - * We assume debugging was started in collector initialization, - * and we already hold the GC lock. - */ + /* An allocation function for internal use. Normally internally */ + /* allocated objects do not have debug information. But in this */ + /* case, we need to make sure that all objects have debug headers. */ + /* We assume debugging was started in collector initialization, and */ + /* we already hold the GC lock. */ GC_INNER void * GC_debug_generic_malloc_inner(size_t lb, int k) { void * result = GC_generic_malloc_inner(lb + DEBUG_BYTES, k); @@ -554,7 +531,7 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb, return(0); } ADD_CALL_CHAIN(result, GC_RETURN_ADDR); - return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", (word)0)); + return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0)); } GC_INNER void * GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, @@ -569,13 +546,13 @@ GC_API void * GC_CALL GC_debug_malloc_atomic_ignore_off_page(size_t lb, return(0); } ADD_CALL_CHAIN(result, GC_RETURN_ADDR); - return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", (word)0)); + return (GC_store_debug_info_inner(result, (word)lb, "INTERNAL", 0)); } -#endif +#endif /* DBG_HDRS_ALL */ #ifdef STUBBORN_ALLOC -GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) -{ + GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) + { void * result = GC_malloc_stubborn(lb + DEBUG_BYTES); if (result == 0) { @@ -589,11 +566,11 @@ GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); -} + return (GC_store_debug_info(result, (word)lb, s, i)); + } -GC_API void GC_CALL GC_debug_change_stubborn(void *p) -{ + GC_API void GC_CALL GC_debug_change_stubborn(void *p) + { void * q = GC_base(p); hdr * hhdr; @@ -607,10 +584,10 @@ GC_API void GC_CALL GC_debug_change_stubborn(void *p) ABORT("GC_debug_change_stubborn: arg not stubborn"); } GC_change_stubborn(q); -} + } -GC_API void GC_CALL GC_debug_end_stubborn_change(void *p) -{ + GC_API void GC_CALL GC_debug_end_stubborn_change(void *p) + { void * q = GC_base(p); hdr * hhdr; @@ -624,21 +601,20 @@ GC_API void GC_CALL GC_debug_end_stubborn_change(void *p) ABORT("GC_debug_end_stubborn_change: arg not stubborn"); } GC_end_stubborn_change(q); -} + } #else /* !STUBBORN_ALLOC */ -GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) -{ + GC_API void * GC_CALL GC_debug_malloc_stubborn(size_t lb, GC_EXTRA_PARAMS) + { return GC_debug_malloc(lb, OPT_RA s, i); -} - -/*ARGSUSED*/ -GC_API void GC_CALL GC_debug_change_stubborn(void *p) {} + } -/*ARGSUSED*/ -GC_API void GC_CALL GC_debug_end_stubborn_change(void *p) {} + /*ARGSUSED*/ + GC_API void GC_CALL GC_debug_change_stubborn(void *p) {} + /*ARGSUSED*/ + GC_API void GC_CALL GC_debug_end_stubborn_change(void *p) {} #endif /* !STUBBORN_ALLOC */ GC_API void * GC_CALL GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS) @@ -656,7 +632,7 @@ GC_API void * GC_CALL GC_debug_malloc_atomic(size_t lb, GC_EXTRA_PARAMS) GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); + return (GC_store_debug_info(result, (word)lb, s, i)); } GC_API char * GC_CALL GC_debug_strdup(const char *str, GC_EXTRA_PARAMS) @@ -665,9 +641,10 @@ GC_API char * GC_CALL GC_debug_strdup(const char *str, GC_EXTRA_PARAMS) size_t lb; if (str == NULL) { if (GC_find_leak) - WARN("strdup(NULL) behavior is undefined\n", 0); + GC_err_printf("strdup(NULL) behavior is undefined\n"); return NULL; } + lb = strlen(str) + 1; copy = GC_debug_malloc_atomic(lb, OPT_RA s, i); if (copy == NULL) { @@ -738,11 +715,12 @@ GC_API void * GC_CALL GC_debug_malloc_uncollectable(size_t lb, GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); + return (GC_store_debug_info(result, (word)lb, s, i)); } #ifdef ATOMIC_UNCOLLECTABLE - void * GC_debug_malloc_atomic_uncollectable(size_t lb, GC_EXTRA_PARAMS) + GC_API void * GC_CALL GC_debug_malloc_atomic_uncollectable(size_t lb, + GC_EXTRA_PARAMS) { void * result = GC_malloc_atomic_uncollectable(lb + UNCOLLECTABLE_DEBUG_BYTES); @@ -759,69 +737,73 @@ GC_API void * GC_CALL GC_debug_malloc_uncollectable(size_t lb, GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); + return (GC_store_debug_info(result, (word)lb, s, i)); } #endif /* ATOMIC_UNCOLLECTABLE */ +#ifndef GC_FREED_MEM_MARKER +# if CPP_WORDSZ == 32 +# define GC_FREED_MEM_MARKER 0xdeadbeef +# else +# define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef) +# endif +#endif + GC_API void GC_CALL GC_debug_free(void * p) { ptr_t base; -# ifndef SHORT_DBG_HDRS - ptr_t clobbered; -# endif + if (0 == p) return; - if (0 == p) { - if (GC_find_leak) - WARN("free(NULL) is non-portable\n", 0); - return; - } base = GC_base(p); if (base == 0) { - GC_err_printf("Attempt to free invalid pointer %p\n", p); - ABORT("free(invalid pointer)"); + GC_err_printf("Attempt to free invalid pointer %p\n", p); + ABORT("Invalid pointer passed to free()"); } if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { - GC_err_printf( - "GC_debug_free called on pointer %p w/o debugging info\n", p); + GC_err_printf( + "GC_debug_free called on pointer %p w/o debugging info\n", p); } else { # ifndef SHORT_DBG_HDRS - clobbered = GC_check_annotated_obj((oh *)base); + ptr_t clobbered = GC_check_annotated_obj((oh *)base); + word sz = GC_size(base); if (clobbered != 0) { - if (((oh *)base) -> oh_sz == GC_size(base)) { - GC_err_printf( - "GC_debug_free: found previously deallocated (?) object at "); + GC_have_errors = TRUE; + if (((oh *)base) -> oh_sz == sz) { + GC_print_smashed_obj( + "GC_debug_free: found previously deallocated (?) object at", + p, clobbered); + return; /* ignore double free */ } else { - GC_err_printf("GC_debug_free: found smashed location at "); + GC_print_smashed_obj("GC_debug_free: found smashed location at", + p, clobbered); } - GC_print_smashed_obj(p, clobbered); } - /* Invalidate size */ - ((oh *)base) -> oh_sz = GC_size(base); + /* Invalidate size (mark the object as deallocated) */ + ((oh *)base) -> oh_sz = sz; # endif /* SHORT_DBG_HDRS */ } - if (GC_find_leak) { - GC_free(base); - } else { - hdr * hhdr = HDR(p); - GC_bool uncollectable = FALSE; - - if (hhdr -> hb_obj_kind == UNCOLLECTABLE) { - uncollectable = TRUE; - } -# ifdef ATOMIC_UNCOLLECTABLE - if (hhdr -> hb_obj_kind == AUNCOLLECTABLE) { - uncollectable = TRUE; - } + if (GC_find_leak +# ifndef SHORT_DBG_HDRS + && ((ptr_t)p - (ptr_t)base != sizeof(oh) || !GC_findleak_delay_free) # endif - if (uncollectable) { - GC_free(base); - } else { - size_t i; - size_t obj_sz = BYTES_TO_WORDS(hhdr -> hb_sz - sizeof(oh)); + ) { + GC_free(base); + } else { + hdr * hhdr = HDR(p); + if (hhdr -> hb_obj_kind == UNCOLLECTABLE +# ifdef ATOMIC_UNCOLLECTABLE + || hhdr -> hb_obj_kind == AUNCOLLECTABLE +# endif + ) { + GC_free(base); + } else { + size_t i; + size_t obj_sz = BYTES_TO_WORDS(hhdr -> hb_sz - sizeof(oh)); - for (i = 0; i < obj_sz; ++i) ((word *)p)[i] = 0xdeadbeef; - GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz)); - } + for (i = 0; i < obj_sz; ++i) + ((word *)p)[i] = GC_FREED_MEM_MARKER; + GC_ASSERT((word *)p + i == (word *)(base + hhdr -> hb_sz)); + } } /* !GC_find_leak */ } @@ -845,19 +827,15 @@ GC_API void GC_CALL GC_debug_free(void * p) GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS) { void * base; -# ifndef SHORT_DBG_HDRS - ptr_t clobbered; -# endif void * result; - size_t copy_sz = lb; - size_t old_sz; hdr * hhdr; + if (p == 0) + return(GC_debug_malloc(lb, OPT_RA s, i)); - if (p == 0) return(GC_debug_malloc(lb, OPT_RA s, i)); base = GC_base(p); if (base == 0) { GC_err_printf("Attempt to reallocate invalid pointer %p\n", p); - ABORT("realloc(invalid pointer)"); + ABORT("Invalid pointer passed to realloc()"); } if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { GC_err_printf( @@ -888,38 +866,38 @@ GC_API void * GC_CALL GC_debug_realloc(void * p, size_t lb, GC_EXTRA_PARAMS) default: result = NULL; /* initialized to prevent warning. */ GC_err_printf("GC_debug_realloc: encountered bad kind\n"); - ABORT("bad kind"); + ABORT("Bad kind"); + } + + if (result != NULL) { + size_t old_sz; +# ifdef SHORT_DBG_HDRS + old_sz = GC_size(base) - sizeof(oh); +# else + old_sz = ((oh *)base) -> oh_sz; +# endif + BCOPY(p, result, old_sz < lb ? old_sz : lb); + GC_debug_free(p); } -# ifdef SHORT_DBG_HDRS - old_sz = GC_size(base) - sizeof(oh); -# else - clobbered = GC_check_annotated_obj((oh *)base); - if (clobbered != 0) { - GC_err_printf("GC_debug_realloc: found smashed location at "); - GC_print_smashed_obj(p, clobbered); - } - old_sz = ((oh *)base) -> oh_sz; -# endif - if (old_sz < copy_sz) copy_sz = old_sz; - if (result == 0) return(0); - BCOPY(p, result, copy_sz); - GC_debug_free(p); return(result); } #ifndef SHORT_DBG_HDRS -/* List of smashed objects. We defer printing these, since we can't */ -/* always print them nicely with the allocation lock held. */ -/* We put them here instead of in GC_arrays, since it may be useful to */ -/* be able to look at them with the debugger. */ -#define MAX_SMASHED 20 +/* List of smashed (clobbered) locations. We defer printing these, */ +/* since we can't always print them nicely with the allocation lock */ +/* held. We put them here instead of in GC_arrays, since it may be */ +/* useful to be able to look at them with the debugger. */ +#ifndef MAX_SMASHED +# define MAX_SMASHED 20 +#endif STATIC ptr_t GC_smashed[MAX_SMASHED] = {0}; STATIC unsigned GC_n_smashed = 0; STATIC void GC_add_smashed(ptr_t smashed) { GC_ASSERT(GC_is_marked(GC_base(smashed))); + /* FIXME: Prevent adding an object while printing smashed list. */ GC_smashed[GC_n_smashed] = smashed; if (GC_n_smashed < MAX_SMASHED - 1) ++GC_n_smashed; /* In case of overflow, we keep the first MAX_SMASHED-1 */ @@ -936,11 +914,12 @@ STATIC void GC_print_all_smashed_proc(void) if (GC_n_smashed == 0) return; GC_err_printf("GC_check_heap_block: found smashed heap objects:\n"); for (i = 0; i < GC_n_smashed; ++i) { - GC_print_smashed_obj((ptr_t)GC_base(GC_smashed[i]) + sizeof(oh), + GC_print_smashed_obj("", (ptr_t)GC_base(GC_smashed[i]) + sizeof(oh), GC_smashed[i]); GC_smashed[i] = 0; } GC_n_smashed = 0; + GC_err_printf("\n"); } /* Check all marked objects in the given block for validity */ @@ -954,23 +933,19 @@ STATIC void GC_check_heap_block(struct hblk *hbp, word dummy) char *p, *plim; p = hbp->hb_body; - bit_no = 0; if (sz > MAXOBJBYTES) { - plim = p; + plim = p; } else { - plim = hbp->hb_body + HBLKSIZE - sz; + plim = hbp->hb_body + HBLKSIZE - sz; } /* go through all words in block */ - while( p <= plim ) { - if( mark_bit_from_hdr(hhdr, bit_no) - && GC_HAS_DEBUG_INFO((ptr_t)p)) { - ptr_t clobbered = GC_check_annotated_obj((oh *)p); - - if (clobbered != 0) GC_add_smashed(clobbered); - } - bit_no += MARK_BIT_OFFSET(sz); - p += sz; - } + for (bit_no = 0; p <= plim; bit_no += MARK_BIT_OFFSET(sz), p += sz) { + if (mark_bit_from_hdr(hhdr, bit_no) && GC_HAS_DEBUG_INFO((ptr_t)p)) { + ptr_t clobbered = GC_check_annotated_obj((oh *)p); + if (clobbered != 0) + GC_add_smashed(clobbered); + } + } } /* This assumes that all accessible objects are marked, and that */ @@ -979,7 +954,33 @@ STATIC void GC_check_heap_proc(void) { GC_STATIC_ASSERT((sizeof(oh) & (GRANULE_BYTES - 1)) == 0); /* FIXME: Should we check for twice that alignment? */ - GC_apply_to_all_blocks(GC_check_heap_block, (word)0); + GC_apply_to_all_blocks(GC_check_heap_block, 0); +} + +GC_INNER GC_bool GC_check_leaked(ptr_t base) +{ + size_t i; + size_t obj_sz; + word *p; + + if ( +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + (*(word *)base & 1) != 0 && +# endif + GC_has_other_debug_info(base) >= 0) + return TRUE; /* object has leaked */ + + /* Validate freed object's content. */ + p = (word *)(base + sizeof(oh)); + obj_sz = BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh)); + for (i = 0; i < obj_sz; ++i) + if (p[i] != GC_FREED_MEM_MARKER) { + GC_set_mark_bit(base); /* do not reclaim it in this cycle */ + GC_add_smashed((ptr_t)(&p[i])); /* alter-after-free detected */ + break; /* don't report any other smashed locations in the object */ + } + + return FALSE; /* GC_debug_free() has been called */ } #endif /* !SHORT_DBG_HDRS */ @@ -1027,7 +1028,7 @@ static void store_old(void *obj, GC_finalization_proc my_old_fn, return; } if (my_old_fn != GC_debug_invoke_finalizer) { - GC_err_printf("Debuggable object at %p had non-debug finalizer.\n", + GC_err_printf("Debuggable object at %p had a non-debug finalizer\n", obj); /* This should probably be fatal. */ } else { @@ -1162,18 +1163,12 @@ GC_API void GC_CALL GC_debug_register_finalizer_ignore_self store_old(obj, my_old_fn, (struct closure *)my_old_cd, ofn, ocd); } -#ifdef GC_ADD_CALLER -# define RA GC_RETURN_ADDR, -#else -# define RA -#endif - GC_API void * GC_CALL GC_debug_malloc_replacement(size_t lb) { - return GC_debug_malloc(lb, RA "unknown", 0); + return GC_debug_malloc(lb, GC_DBG_RA "unknown", 0); } GC_API void * GC_CALL GC_debug_realloc_replacement(void *p, size_t lb) { - return GC_debug_realloc(p, lb, RA "unknown", 0); + return GC_debug_realloc(p, lb, GC_DBG_RA "unknown", 0); } @@ -36,7 +36,7 @@ Public License, but is not needed by, nor linked into the collector library. It is included here only becuase the atomic_ops distribution is, for simplicity, included in its entirety. -This is version 7.2alpha5 of a conservative garbage collector for C and C++. +This is version 7.2alpha7 of a conservative garbage collector for C and C++. You might find a more recent version of this at @@ -129,7 +129,7 @@ ALL_INTERIOR_POINTERS defined, or GC_all_interior_pointers is otherwise set, as is now the default. Compiling without ALL_INTERIOR_POINTERS may reduce accidental retention -of garbage objects, by requiring pointers from the heap to to the beginning +of garbage objects, by requiring pointers from the heap to the beginning of an object. But this no longer appears to be a significant issue for most programs occupying a small fraction of the possible address space. diff --git a/doc/README.autoconf b/doc/README.autoconf index f8640bec..94833f65 100644 --- a/doc/README.autoconf +++ b/doc/README.autoconf @@ -1,4 +1,4 @@ -As of GC6.0alpha8, we attempt to support GNU-style builds based on automake, +Starting from GC v6.0, we support GNU-style builds based on automake, autoconf and libtool. This is based almost entirely on Tom Tromey's work with gcj. @@ -36,7 +36,7 @@ as well as the sources needed to regenerate the derived files. (If I missed some, please let me know.) Note that the distribution comes with a "Makefile" which will be overwritten -by "configure" with one that is not at all equiavelent to the original. The +by "configure" with one that is not at all equivalent to the original. The distribution contains a copy of the original "Makefile" in "Makefile.direct". Important options to configure: @@ -48,7 +48,7 @@ Important options to configure: --enable-threads=TYPE choose threading package --enable-parallel-mark parallelize marking and free list construction --enable-gc-debug (--enable-full-debug before about 7.0) - include full support for pointer backtracing etc. + include full support for pointer back-tracing etc. Unless --prefix is set (or --exec-prefix or one of the more obscure options), diff --git a/doc/README.environment b/doc/README.environment index 07a4ebf3..866e9055 100644 --- a/doc/README.environment +++ b/doc/README.environment @@ -1,15 +1,17 @@ -The garbage collector looks at a number of environment variables which are -then used to affect its operation. These are examined only on Un*x-like -platforms and win32. +The garbage collector looks at a number of environment variables which are, +then, used to affect its operation. GC_INITIAL_HEAP_SIZE=<bytes> - Initial heap size in bytes. May speed up - process start-up. + process start-up. Optionally, may be + specified with a multiplier ('k', 'M' or 'G') + suffix. -GC_MAXIMUM_HEAP_SIZE=<bytes> - Maximum collected heap size. +GC_MAXIMUM_HEAP_SIZE=<bytes> - Maximum collected heap size. Allows + a multiplier suffix. GC_LOOP_ON_ABORT - Causes the collector abort routine to enter a tight loop. This may make it easier to debug, such a process, especially - for multithreaded platforms that don't produce usable core + for multi-threaded platforms that don't produce usable core files, or if a core file would be too large. On some platforms, this also causes SIGSEGV to be caught and result in an infinite loop in a handler, allowing @@ -20,6 +22,10 @@ GC_PRINT_STATS - Turn on GC logging. Not functional with SMALL_CONFIG. GC_LOG_FILE - The name of the log file. Stderr by default. Not functional with SMALL_CONFIG. +GC_ONLY_LOG_TO_FILE - Turns off redirection of GC stdout and stderr to the log + file specified by GC_LOG_FILE. Has no effect unless + GC_LOG_FILE is set. Not functional with SMALL_CONFIG. + GC_PRINT_VERBOSE_STATS - Turn on even more logging. Not functional with SMALL_CONFIG. @@ -28,7 +34,7 @@ GC_DUMP_REGULARLY - Generate a GC debugging dump GC_dump() on startup if you have a bug to report, but please include only the last complete dump. -GC_BACKTRACES=<n> - Generate n random backtraces (for heap profiling) after +GC_BACKTRACES=<n> - Generate n random back-traces (for heap profiling) after each GC. Collector must have been built with KEEP_BACK_PTRS. This won't generate useful output unless most objects in the heap were allocated through debug @@ -70,7 +76,7 @@ GC_IGNORE_GCJ_INFO - Ignore the type descriptors implicitly supplied by descriptor generation problems, and possibly for temporarily working around such problems. It forces a fully conservative scan of all heap objects except - those known to be pointerfree, and may thus have other + those known to be pointer-free, and may thus have other adverse effects. GC_PRINT_BACK_HEIGHT - Print max length of chain through unreachable objects @@ -114,10 +120,10 @@ other means, but this may help with debugging and testing: GC_ENABLE_INCREMENTAL - Turn on incremental collection at startup. Note that, depending on platform and collector configuration, this may involve write protecting pieces of the heap to - track modifications. These pieces may include pointerfree - objects or not. Although this is intended to be - transparent, it may cause unintended system call failures. - Use with caution. + track modifications. These pieces may include + pointer-free objects or not. Although this is intended + to be transparent, it may cause unintended system call + failures. Use with caution. GC_PAUSE_TIME_TARGET - Set the desired garbage collector pause time in msecs. This only has an effect if incremental collection is @@ -154,6 +160,13 @@ GC_FIND_LEAK - Turns on GC_find_leak and thus leak detection. Forces a collection at program termination to detect leaks that would otherwise occur after the last GC. +GC_FINDLEAK_DELAY_FREE - Turns on deferred freeing of objects in the + leak-finding mode (see the corresponding macro + description for more information). + +GC_ABORT_ON_LEAK - Causes the application to be terminated once leaked or + smashed objects are found. + GC_ALL_INTERIOR_POINTERS - Turns on GC_all_interior_pointers and thus interior pointer recognition. diff --git a/doc/README.macros b/doc/README.macros index ac788971..a6291baf 100644 --- a/doc/README.macros +++ b/doc/README.macros @@ -81,23 +81,32 @@ GC_REQUIRE_WCSDUP Force GC to export GC_wcsdup() (the Unicode version These define arguments influence the collector configuration: -FIND_LEAK causes GC_find_leak to be initially set. - This causes the collector to assume that all inaccessible - objects should have been explicitly deallocated, and reports exceptions. - Finalization and the test program are not usable in this mode. + +FIND_LEAK Causes GC_find_leak to be initially set. This causes the + collector to assume that all inaccessible objects should have been + explicitly deallocated, and reports exceptions. Finalization and the test + program are not usable in this mode. + +GC_FINDLEAK_DELAY_FREE Turns on deferred freeing of objects in the + leak-finding mode letting the collector to detect alter-object-after-free + errors as well as detect leaked objects sooner (instead of only when program + terminates). Has no effect if SHORT_DBG_HDRS. + +GC_ABORT_ON_LEAK Causes the application to be terminated once leaked or + smashed (corrupted on use-after-free) objects are found (after printing the + information about that objects). SUNOS5SIGS Solaris-like signal handling. This is probably misnamed, - since it really doesn't guarantee much more than Posix. - Currently set only for Solaris2.X, HPUX, and DRSNX. Should - probably be set for some other platforms. + since it really doesn't guarantee much more than POSIX. Currently set only + for Solaris2.X, HPUX, and DRSNX. Should probably be set for some other + platforms. -PCR Set if the collector is being built as part of the Xerox - Portable Common Runtime. +PCR Set if the collector is being built as part of the Xerox Portable + Common Runtime. -USE_COMPILER_TLS Assume the existence of __thread-style thread-local - storage. Set automatically for thread-local allocation with - the HP/UX vendor compiler. Usable with gcc on sufficiently - up-to-date ELF platforms. +USE_COMPILER_TLS Assume the existence of __thread-style thread-local storage. + Set automatically for thread-local allocation with the HP/UX vendor + compiler. Usable with gcc on sufficiently up-to-date ELF platforms. IMPORTANT: Any of the _THREADS options must normally also be defined in the client before including gc.h. This redefines thread primitives to @@ -416,6 +425,10 @@ MAKE_BACK_GRAPH Enable GC_PRINT_BACK_HEIGHT environment variable. support. Implies DBG_HDRS_ALL. All allocation should be done using the debug interface. +GC_PRINT_BACK_HEIGHT Permanently turn on back-height printing mode + (useful when NO_GETENV). See the similar environment variable description + in README.environment. Requires MAKE_BACK_GRAPH defined. + STUBBORN_ALLOC Allows allocation of "hard to change" objects, and thus makes incremental collection easier. Was enabled by default until 6.0. Rarely used, to my knowledge. @@ -493,8 +506,9 @@ DONT_USE_USER32_DLL (Win32 only) Don't use "user32" DLL import library (containing MessageBox() entry); useful for a static GC library. GC_PREFER_MPROTECT_VDB Choose MPROTECT_VDB manually in case of multiple - virtual dirty bit strategies are implemented (at present useful on Win32 - to force MPROTECT_VDB strategy instead of the default GWW_VDB one). + virtual dirty bit strategies are implemented (at present useful on Win32 and + Solaris to force MPROTECT_VDB strategy instead of the default GWW_VDB or + PROC_VDB ones). GC_IGNORE_GCJ_INFO Disable GCJ-style type information (useful for debugging on WinCE). @@ -502,6 +516,10 @@ GC_IGNORE_GCJ_INFO Disable GCJ-style type information (useful for GC_PRINT_VERBOSE_STATS Permanently turn on verbose logging (useful for debugging and profiling on WinCE). +GC_ONLY_LOG_TO_FILE Don't redirect GC stdout and stderr to the log file + specified by GC_LOG_FILE environment variable. Has effect only when the + variable is set (to anything other than "0"). + GC_DONT_EXPAND Don't expand the heap unless explicitly requested or forced to. GC_USE_ENTIRE_HEAP Causes the non-incremental collector to use the @@ -539,6 +557,6 @@ PLATFORM_ANDROID Compile for Android NDK platform. SN_TARGET_PS3 Compile for Sony PS/3. USE_GET_STACKBASE_FOR_MAIN (Linux only) Use pthread_attr_getstack() instead -of __libc_stack_end (or instead of any hard-coded value) for getting the -primordial thread stack base (useful if the client modifies the program's -address space). + of __libc_stack_end (or instead of any hard-coded value) for getting the + primordial thread stack base (useful if the client modifies the program's + address space). diff --git a/doc/README.solaris2 b/doc/README.solaris2 index f9141063..d46fe262 100644 --- a/doc/README.solaris2 +++ b/doc/README.solaris2 @@ -13,10 +13,14 @@ not safe: "Many library routines use malloc() internally, so use brk() and sbrk() only when you know that malloc() definitely will not be used by any library routine." This doesn't make a lot of sense to me, since there seems to be no documentation as to which routines can transitively call malloc. -Nonetheless, under Solaris2, the collector now (since 4.12) allocates +Nonetheless, under Solaris2, the collector now allocates memory using mmap by default. (It defines USE_MMAP in gcconfig.h.) You may want to reverse this decisions if you use -DREDIRECT_MALLOC=... +Note: +Before you run "make check", you need to set your LD_LIBRARY_PATH correctly +(eg., to "/usr/local/lib") so that tests can find the shared library +libgcc_s.so.1. Alternatively, you can configure with --disable-shared. SOLARIS THREADS: @@ -24,21 +28,19 @@ The collector must be compiled with -DGC_THREADS to be thread safe. This assumes use of the pthread_ interface. Old style Solaris threads are no longer supported. -It is also essential that gc.h be included in files that call thr_create, -thr_join, thr_suspend, thr_continue, or dlopen. Gc.h macro defines -these to also do GC bookkeeping, etc. Gc.h must be included with -one or both of these macros defined, otherwise -these replacements are not visible. -A collector built in this way way only be used by programs that are -linked with the threads library. +It is also essential that gc.h be included in files that call pthread_create, +pthread_join, pthread_detach, or dlopen. gc.h macro defines these to also do +GC bookkeeping, etc. gc.h must be included with one or both of these macros +defined, otherwise these replacements are not visible. A collector built in +this way way only be used by programs that are linked with the threads library. Since 5.0 alpha5, dlopen disables collection temporarily, unless USE_PROC_FOR_LIBRARIES is defined. In some unlikely cases, this can result in unpleasant heap growth. But it seems better than the race/deadlock issues we had before. -If solaris_threads are used on an X86 processor with malloc redirected to -GC_malloc, it is necessary to call GC_thr_init explicitly before forking the +If threads are used on an X86 processor with malloc redirected to +GC_malloc, it is necessary to call GC_INIT explicitly before forking the first thread. (This avoids a deadlock arising from calling GC_thr_init with the allocation lock held.) @@ -46,7 +48,7 @@ It appears that there is a problem in using gc_cpp.h in conjunction with Solaris threads and Sun's C++ runtime. Apparently the overloaded new operator is invoked by some iostream initialization code before threads are correctly initialized. As a result, call to thr_self() in garbage collector -initialization segfaults. Currently the only known workaround is to not +initialization SEGV faults. Currently the only known workaround is to not invoke the garbage collector from a user defined global operator new, or to have it invoke the garbage-collector's allocators only after main has started. (Note that the latter requires a moderately expensive test in operator diff --git a/doc/README.win32 b/doc/README.win32 index 2e43b63e..4a030cf3 100644 --- a/doc/README.win32 +++ b/doc/README.win32 @@ -107,6 +107,12 @@ version, change the line near the top. By default, it does not require the assembler. If you do have the assembler, I recommend removing the -DUSE_GENERIC. +Digital Mars compiler +--------------------- + +Same as MS Visual C++ but might require +-DAO_OLD_STYLE_INTERLOCKED_COMPARE_EXCHANGE option to compile with the +parallel marker enabled. Watcom compiler --------------- @@ -225,6 +231,4 @@ correctly track threads. To build the collector for Mingw32 Pthreads, use Makefile.direct and explicitly set GC_WIN32_PTHREADS. Use -DPTW32_STATIC_LIB for the static -threads library. Note that the DEBUG_WIN32_PTHREADS support in -win32_threads.c is currently broken and looking for someone to debug it. -(This information and the port came from Romano Paolo Tenca). +threads library. @@ -29,7 +29,7 @@ * But then not much of anything is safe in the presence of dlclose. */ -#if !defined(MACOS) && !defined(_WIN32_WCE) +#if !defined(MACOS) && !defined(_WIN32_WCE) && !defined(__CC_ARM) # include <sys/types.h> #endif @@ -135,7 +135,6 @@ GC_FirstDLOpenedLinkMap(void) { extern ElfW(Dyn) _DYNAMIC; ElfW(Dyn) *dp; - struct r_debug *r; static struct link_map * cachedResult = 0; static ElfW(Dyn) *dynStructureAddr = 0; /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */ @@ -186,12 +185,9 @@ GC_FirstDLOpenedLinkMap(void) # ifndef USE_PROC_FOR_LIBRARIES GC_INNER void GC_register_dynamic_libraries(void) { - struct link_map *lm = GC_FirstDLOpenedLinkMap(); - + struct link_map *lm; - for (lm = GC_FirstDLOpenedLinkMap(); - lm != (struct link_map *) 0; lm = lm->l_next) - { + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) { ElfW(Ehdr) * e; ElfW(Phdr) * p; unsigned long offset; @@ -205,7 +201,7 @@ GC_INNER void GC_register_dynamic_libraries(void) # endif p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); offset = ((unsigned long)(lm->l_addr)); - for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) { + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { switch( p->p_type ) { case PT_LOAD: { @@ -242,11 +238,6 @@ GC_INNER void GC_register_dynamic_libraries(void) #define MAPS_BUF_SIZE (32*1024) -GC_INNER char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, - char **prot, unsigned int *maj_dev, - char **mapping_name); -GC_INNER char *GC_get_maps(void); /* from os_dep.c */ - /* Sort an array of HeapSects by start address. */ /* Unfortunately at least some versions of */ /* Linux qsort end up calling malloc by way of sysconf, and hence can't */ @@ -277,15 +268,10 @@ static void sort_heap_sects(struct HeapSect *base, size_t number_of_elements) } } -#ifdef THREADS - GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi); -#endif - STATIC word GC_register_map_entries(char *maps) { char *prot; char *buf_ptr = maps; - int count; ptr_t start, end; unsigned int maj_dev; ptr_t least_ha, greatest_ha; @@ -383,7 +369,7 @@ STATIC word GC_register_map_entries(char *maps) GC_INNER void GC_register_dynamic_libraries(void) { if (!GC_register_map_entries(GC_get_maps())) - ABORT("Failed to read /proc for library registration."); + ABORT("Failed to read /proc for library registration"); } /* We now take care of the main data segment ourselves: */ @@ -454,7 +440,7 @@ STATIC int GC_register_dynlib_callback(struct dl_phdr_info * info, return -1; p = info->dlpi_phdr; - for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) { + for( i = 0; i < (int)info->dlpi_phnum; i++, p++ ) { switch( p->p_type ) { # ifdef PT_GNU_RELRO case PT_GNU_RELRO: @@ -534,7 +520,7 @@ GC_INNER GC_bool GC_register_main_static_data(void) /* zero (otherwise a compiler might issue a warning). */ return FALSE; # else - return (dl_iterate_phdr == 0); + return (dl_iterate_phdr == 0); /* implicit conversion to function ptr */ # endif } @@ -680,9 +666,7 @@ GC_INNER void GC_register_dynamic_libraries(void) return; } # endif - lm = GC_FirstDLOpenedLinkMap(); - for (lm = GC_FirstDLOpenedLinkMap(); - lm != (struct link_map *) 0; lm = lm->l_next) + for (lm = GC_FirstDLOpenedLinkMap(); lm != 0; lm = lm->l_next) { ElfW(Ehdr) * e; ElfW(Phdr) * p; @@ -697,7 +681,7 @@ GC_INNER void GC_register_dynamic_libraries(void) # endif p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); offset = ((unsigned long)(lm->l_addr)); - for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) { + for( i = 0; i < (int)e->e_phnum; i++, p++ ) { switch( p->p_type ) { case PT_LOAD: { @@ -729,10 +713,6 @@ GC_INNER void GC_register_dynamic_libraries(void) # define IRIX6 #endif -GC_INNER void * GC_roots_present(ptr_t); - /* The type is a lie, since the real type doesn't make sense here, */ - /* and we only test for NULL. */ - /* We use /proc to track down all parts of the address space that are */ /* mapped by the process, and throw out regions we know we shouldn't */ /* worry about. This may also work under other SVR4 variants. */ @@ -858,15 +838,9 @@ GC_INNER void GC_register_dynamic_libraries(void) /* We traverse the entire address space and register all segments */ /* that could possibly have been written to. */ - - GC_INNER GC_bool GC_is_heap_base(ptr_t p); - -# ifdef GC_WIN32_THREADS - GC_INNER void GC_get_next_stack(char *start, char * limit, char **lo, - char **hi); - - STATIC void GC_cond_add_roots(char *base, char * limit) - { + STATIC void GC_cond_add_roots(char *base, char * limit) + { +# ifdef GC_WIN32_THREADS char * curr_base = base; char * next_stack_lo; char * next_stack_hi; @@ -880,10 +854,7 @@ GC_INNER void GC_register_dynamic_libraries(void) curr_base = next_stack_hi; } if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE); - } -# else - STATIC void GC_cond_add_roots(char *base, char * limit) - { +# else char dummy; char * stack_top = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1)); @@ -893,8 +864,8 @@ GC_INNER void GC_register_dynamic_libraries(void) return; } GC_add_roots_inner(base, limit, TRUE); - } -# endif +# endif + } #ifdef DYNAMIC_LOADING /* GC_register_main_static_data is not needed unless DYNAMIC_LOADING. */ @@ -998,6 +969,10 @@ GC_INNER void GC_register_dynamic_libraries(void) #include <loader.h> +extern char *sys_errlist[]; +extern int sys_nerr; +extern int errno; + GC_INNER void GC_register_dynamic_libraries(void) { int status; @@ -1031,18 +1006,15 @@ GC_INNER void GC_register_dynamic_libraries(void) /* Check status AFTER checking moduleid because */ /* of a bug in the non-shared ldr_next_module stub */ if (status != 0) { - if (GC_print_stats) { - extern char *sys_errlist[]; - extern int sys_nerr; - extern int errno; - GC_printf("dynamic_load: status = %d\n", status); - if (errno <= sys_nerr) { - GC_printf("dynamic_load: %s\n", sys_errlist[errno]); - } else { - GC_printf("dynamic_load: err_code = %d\n", errno); - } + if (GC_print_stats) { + GC_log_printf("dynamic_load: status = %d\n", status); + if (errno < sys_nerr) { + GC_log_printf("dynamic_load: %s\n", sys_errlist[errno]); + } else { + GC_log_printf("dynamic_load: err_code = %d\n", errno); } - ABORT("ldr_next_module failed"); + } + ABORT("ldr_next_module failed"); } /* Get the module information */ @@ -1056,16 +1028,15 @@ GC_INNER void GC_register_dynamic_libraries(void) continue; /* skip the main module */ # ifdef DL_VERBOSE - GC_printf("---Module---\n"); - GC_printf("Module ID = %16ld\n", moduleinfo.lmi_modid); - GC_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion); - GC_printf("flags for module = %16lx\n", moduleinfo.lmi_flags); - GC_printf("pathname of module = \"%s\"\n", moduleinfo.lmi_name); + GC_log_printf("---Module---\n"); + GC_log_printf("Module ID\t = %16ld\n", moduleinfo.lmi_modid); + GC_log_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion); + GC_log_printf("flags for module = %16lx\n", moduleinfo.lmi_flags); + GC_log_printf("module pathname\t = \"%s\"\n", moduleinfo.lmi_name); # endif /* For each region in this module */ for (region = 0; region < moduleinfo.lmi_nregion; region++) { - /* Get the region information */ status = ldr_inq_region(mypid, moduleid, region, ®ioninfo, regioninfosize, ®ionreturnsize); @@ -1077,21 +1048,21 @@ GC_INNER void GC_register_dynamic_libraries(void) continue; # ifdef DL_VERBOSE - GC_printf("--- Region ---\n"); - GC_printf("Region number = %16ld\n", - regioninfo.lri_region_no); - GC_printf("Protection flags = %016x\n", regioninfo.lri_prot); - GC_printf("Virtual address = %16p\n", regioninfo.lri_vaddr); - GC_printf("Mapped address = %16p\n", regioninfo.lri_mapaddr); - GC_printf("Region size = %16ld\n", regioninfo.lri_size); - GC_printf("Region name = \"%s\"\n", regioninfo.lri_name); + GC_log_printf("--- Region ---\n"); + GC_log_printf("Region number\t = %16ld\n", + regioninfo.lri_region_no); + GC_log_printf("Protection flags = %016x\n", regioninfo.lri_prot); + GC_log_printf("Virtual address\t = %16p\n", regioninfo.lri_vaddr); + GC_log_printf("Mapped address\t = %16p\n", + regioninfo.lri_mapaddr); + GC_log_printf("Region size\t = %16ld\n", regioninfo.lri_size); + GC_log_printf("Region name\t = \"%s\"\n", regioninfo.lri_name); # endif /* register region as a garbage collection root */ - GC_add_roots_inner ( - (char *)regioninfo.lri_mapaddr, - (char *)regioninfo.lri_mapaddr + regioninfo.lri_size, - TRUE); + GC_add_roots_inner((char *)regioninfo.lri_mapaddr, + (char *)regioninfo.lri_mapaddr + regioninfo.lri_size, + TRUE); } } @@ -1129,10 +1100,10 @@ GC_INNER void GC_register_dynamic_libraries(void) break; /* Moved past end of shared library list --> finished */ } else { if (GC_print_stats) { - if (errno <= sys_nerr) { - GC_printf("dynamic_load: %s\n", sys_errlist[errno]); + if (errno < sys_nerr) { + GC_log_printf("dynamic_load: %s\n", sys_errlist[errno]); } else { - GC_printf("dynamic_load: err_code = %d\n", errno); + GC_log_printf("dynamic_load: err_code = %d\n", errno); } } ABORT("shl_get failed"); @@ -1141,16 +1112,16 @@ GC_INNER void GC_register_dynamic_libraries(void) } # ifdef DL_VERBOSE - GC_printf("---Shared library---\n"); - GC_printf("\tfilename = \"%s\"\n", shl_desc->filename); - GC_printf("\tindex = %d\n", index); - GC_printf("\thandle = %08x\n", - (unsigned long) shl_desc->handle); - GC_printf("\ttext seg. start = %08x\n", shl_desc->tstart); - GC_printf("\ttext seg. end = %08x\n", shl_desc->tend); - GC_printf("\tdata seg. start = %08x\n", shl_desc->dstart); - GC_printf("\tdata seg. end = %08x\n", shl_desc->dend); - GC_printf("\tref. count = %lu\n", shl_desc->ref_count); + GC_log_printf("---Shared library---\n"); + GC_log_printf("\tfilename\t= \"%s\"\n", shl_desc->filename); + GC_log_printf("\tindex\t\t= %d\n", index); + GC_log_printf("\thandle\t\t= %08x\n", + (unsigned long) shl_desc->handle); + GC_log_printf("\ttext seg.start\t= %08x\n", shl_desc->tstart); + GC_log_printf("\ttext seg.end\t= %08x\n", shl_desc->tend); + GC_log_printf("\tdata seg.start\t= %08x\n", shl_desc->dstart); + GC_log_printf("\tdata seg.end\t= %08x\n", shl_desc->dend); + GC_log_printf("\tref.count\t= %lu\n", shl_desc->ref_count); # endif /* register shared library's data segment as a garbage collection root */ @@ -1209,15 +1180,44 @@ GC_INNER void GC_register_dynamic_libraries(void) /*#define DARWIN_DEBUG*/ +/* Writable sections generally available on Darwin. */ STATIC const struct { - const char *seg; - const char *sect; + const char *seg; + const char *sect; } GC_dyld_sections[] = { - { SEG_DATA, SECT_DATA }, - { SEG_DATA, SECT_BSS }, - { SEG_DATA, SECT_COMMON } + { SEG_DATA, SECT_DATA }, + /* Used by FSF GCC, but not by OS X system tools, so far. */ + { SEG_DATA, "__static_data" }, + { SEG_DATA, SECT_BSS }, + { SEG_DATA, SECT_COMMON }, + /* FSF GCC - zero-sized object sections for targets */ + /*supporting section anchors. */ + { SEG_DATA, "__zobj_data" }, + { SEG_DATA, "__zobj_bss" } +}; + +/* Additional writable sections: */ +/* GCC on Darwin constructs aligned sections "on demand", where */ +/* the alignment size is embedded in the section name. */ +/* Furthermore, there are distinctions between sections */ +/* containing private vs. public symbols. It also constructs */ +/* sections specifically for zero-sized objects, when the */ +/* target supports section anchors. */ +STATIC const char * GC_dyld_add_sect_fmts[] = +{ + "__bss%u", + "__pu_bss%u", + "__zo_bss%u", + "__zo_pu_bss%u", + NULL }; +/* Currently, mach-o will allow up to the max of 2^15 alignment */ +/* in an object file. */ +#ifndef L2_MAX_OFILE_ALIGNMENT +# define L2_MAX_OFILE_ALIGNMENT 15 +#endif + STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr) { unsigned long i, c; @@ -1228,63 +1228,119 @@ STATIC const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr) return NULL; } -/* This should never be called by a thread holding the lock */ -STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, intptr_t slide) +/* This should never be called by a thread holding the lock. */ +STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, + intptr_t slide) { - unsigned long start,end,i; - const struct GC_MACH_SECTION *sec; - const char *name; - GC_has_static_roots_func callback = GC_has_static_roots; - DCL_LOCK_STATE; - if (GC_no_dls) return; -# ifdef DARWIN_DEBUG - name = GC_dyld_name_for_hdr(hdr); -# else - name = callback != 0 ? GC_dyld_name_for_hdr(hdr) : NULL; -# endif - for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) { - sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, - GC_dyld_sections[i].sect); - if(sec == NULL || sec->size < sizeof(word)) continue; + unsigned long start, end; + int i, j; + const struct GC_MACH_SECTION *sec; + const char *name; + GC_has_static_roots_func callback = GC_has_static_roots; + char secnam[16]; + const char *fmt; + DCL_LOCK_STATE; + + if (GC_no_dls) return; +# ifdef DARWIN_DEBUG + name = GC_dyld_name_for_hdr(hdr); +# else + name = callback != 0 ? GC_dyld_name_for_hdr(hdr) : NULL; +# endif + for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) { + sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, + GC_dyld_sections[i].sect); + if (sec == NULL || sec->size < sizeof(word)) + continue; + start = slide + sec->addr; + end = start + sec->size; + LOCK(); + /* The user callback is called holding the lock. */ + if (callback == 0 || callback(name, (void*)start, (size_t)sec->size)) { +# ifdef DARWIN_DEBUG + GC_log_printf( + "Adding section __DATA,%s at %p-%p (%lu bytes) from image %s\n", + GC_dyld_sections[i].sect, (void*)start, (void*)end, + (unsigned long)sec->size, name); +# endif + GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE); + } + UNLOCK(); + } + + /* Sections constructed on demand. */ + for (j = 0; (fmt = GC_dyld_add_sect_fmts[j]) != NULL; j++) { + /* Add our manufactured aligned BSS sections. */ + for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) { + snprintf(secnam, sizeof(secnam), fmt, (unsigned)i); + sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam); + if (sec == NULL || sec->size == 0) + continue; start = slide + sec->addr; end = start + sec->size; - LOCK(); - /* The user callback is called holding the lock */ - if (callback == 0 || callback(name, (void*)start, (size_t)sec->size)) { -# ifdef DARWIN_DEBUG - GC_printf("Adding section at %p-%p (%lu bytes) from image %s\n", - start,end,sec->size,name); -# endif - GC_add_roots_inner((ptr_t)start, (ptr_t)end, FALSE); - } - UNLOCK(); +# ifdef DARWIN_DEBUG + GC_log_printf("Adding on-demand section __DATA,%s at" + " %p-%p (%lu bytes) from image %s\n", + secnam, (void*)start, (void*)end, + (unsigned long)sec->size, name); +# endif + GC_add_roots((char*)start, (char*)end); } -# ifdef DARWIN_DEBUG - GC_print_static_roots(); -# endif + } + +# ifdef DARWIN_DEBUG + GC_print_static_roots(); +# endif } -/* This should never be called by a thread holding the lock */ +/* This should never be called by a thread holding the lock. */ STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr, intptr_t slide) { - unsigned long start,end,i; - const struct GC_MACH_SECTION *sec; - for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) { - sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, - GC_dyld_sections[i].sect); - if(sec == NULL || sec->size == 0) continue; - start = slide + sec->addr; - end = start + sec->size; + unsigned long start, end; + int i, j; + const struct GC_MACH_SECTION *sec; + char secnam[16]; + const char *fmt; + + for (i = 0; i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]); i++) { + sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg, + GC_dyld_sections[i].sect); + if (sec == NULL || sec->size == 0) + continue; + start = slide + sec->addr; + end = start + sec->size; # ifdef DARWIN_DEBUG - GC_printf("Removing section at %p-%p (%lu bytes) from image %s\n", - start,end,sec->size,GC_dyld_name_for_hdr(hdr)); + GC_log_printf( + "Removing section __DATA,%s at %p-%p (%lu bytes) from image %s\n", + GC_dyld_sections[i].sect, (void*)start, (void*)end, + (unsigned long)sec->size, GC_dyld_name_for_hdr(hdr)); # endif - GC_remove_roots((char*)start,(char*)end); + GC_remove_roots((char*)start, (char*)end); + } + + /* Remove our on-demand sections. */ + for (j = 0; (fmt = GC_dyld_add_sect_fmts[j]) != NULL; j++) { + for (i = 0; i <= L2_MAX_OFILE_ALIGNMENT; i++) { + snprintf(secnam, sizeof(secnam), fmt, (unsigned)i); + sec = GC_GETSECTBYNAME(hdr, SEG_DATA, secnam); + if (sec == NULL || sec->size == 0) + continue; + start = slide + sec->addr; + end = start + sec->size; +# ifdef DARWIN_DEBUG + GC_log_printf("Removing on-demand section __DATA,%s at" + " %p-%p (%lu bytes) from image %s\n", secnam, + (void*)start, (void*)end, (unsigned long)sec->size, + GC_dyld_name_for_hdr(hdr)); +# endif + GC_remove_roots((char*)start, (char*)end); } -# ifdef DARWIN_DEBUG - GC_print_static_roots(); -# endif + } + +# ifdef DARWIN_DEBUG + GC_print_static_roots(); +# endif } GC_INNER void GC_register_dynamic_libraries(void) @@ -1306,7 +1362,7 @@ GC_INNER void GC_init_dyld(void) if (initialized) return; # ifdef DARWIN_DEBUG - GC_printf("Registering dyld callbacks...\n"); + GC_log_printf("Registering dyld callbacks...\n"); # endif /* Apple's Documentation: @@ -1341,7 +1397,7 @@ GC_INNER void GC_init_dyld(void) if (GETENV("DYLD_BIND_AT_LAUNCH") == 0) { /* The environment variable is unset, so we should bind manually. */ # ifdef DARWIN_DEBUG - GC_printf("Forcing full bind of GC code...\n"); + GC_log_printf("Forcing full bind of GC code...\n"); # endif /* FIXME: '_dyld_bind_fully_image_containing_address' is deprecated. */ if (!_dyld_bind_fully_image_containing_address( @@ -1385,10 +1441,8 @@ GC_INNER GC_bool GC_register_main_static_data(void) for (q = p -> lf_ls; q != NIL; q = q -> ls_next) { if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK) == PCR_IL_SegFlags_Traced_on) { - GC_add_roots_inner - ((char *)(q -> ls_addr), - (char *)(q -> ls_addr) + q -> ls_bytes, - TRUE); + GC_add_roots_inner((char *)(q -> ls_addr), + (char *)(q -> ls_addr) + q -> ls_bytes, TRUE); } } } diff --git a/extra/setjmp_t.c b/extra/setjmp_t.c index 5a171df0..0952fb03 100644 --- a/extra/setjmp_t.c +++ b/extra/setjmp_t.c @@ -10,126 +10,127 @@ * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ - + /* Check whether setjmp actually saves registers in jmp_buf. */ /* If it doesn't, the generic mark_regs code won't work. */ -/* Compilers vary as to whether they will put x in a */ -/* (callee-save) register without -O. The code is */ +/* Compilers vary as to whether they will put x in a */ +/* (callee-save) register without -O. The code is */ /* contrived such that any decent compiler should put x in */ -/* a callee-save register with -O. Thus it is is */ +/* a callee-save register with -O. Thus it is */ /* recommended that this be run optimized. (If the machine */ /* has no callee-save registers, then the generic code is */ /* safe, but this will not be noticed by this piece of */ -/* code.) This test appears to be far from perfect. */ +/* code.) This test appears to be far from perfect. */ #include <stdio.h> #include <setjmp.h> #include <string.h> #include "private/gc_priv.h" #ifdef OS2 -/* GETPAGESIZE() is set to getpagesize() by default, but that */ -/* doesn't really exist, and the collector doesn't need it. */ +/* GETPAGESIZE() is set to getpagesize() by default, but that */ +/* doesn't really exist, and the collector doesn't need it. */ #define INCL_DOSFILEMGR #define INCL_DOSMISC #define INCL_DOSERRORS #include <os2.h> -int -getpagesize() +int getpagesize(void) { ULONG result[1]; - + if (DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, - (void *)result, sizeof(ULONG)) != NO_ERROR) { - fprintf(stderr, "DosQuerySysInfo failed\n"); - result[0] = 4096; + (void *)result, sizeof(ULONG)) != NO_ERROR) { + fprintf(stderr, "DosQuerySysInfo failed\n"); + result[0] = 4096; } return((int)(result[0])); } #endif -struct {char a_a; char * a_b;} a; +struct { + char a_a; + char * a_b; +} a; -int * nested_sp() +int * nested_sp(void) { - int dummy; - - return(&dummy); + volatile int sp; + sp = (int)&sp; + return (int *)sp; } -int main() +int main(void) { - int dummy; - long ps = GETPAGESIZE(); - jmp_buf b; - register int x = (int)strlen("a"); /* 1, slightly disguised */ - static int y = 0; + int dummy; + long ps = GETPAGESIZE(); + jmp_buf b; + register int x = (int)strlen("a"); /* 1, slightly disguised */ + static int y = 0; - printf("This appears to be a %s running %s\n", MACH_TYPE, OS_TYPE); - if (nested_sp() < &dummy) { - printf("Stack appears to grow down, which is the default.\n"); - printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", - ((unsigned long)(&dummy) + ps) & ~(ps-1)); - } else { - printf("Stack appears to grow up.\n"); - printf("Define STACK_GROWS_UP in gc_private.h\n"); - printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", - ((unsigned long)(&dummy) + ps) & ~(ps-1)); - } - printf("Note that this may vary between machines of ostensibly\n"); - printf("the same architecture (e.g. Sun 3/50s and 3/80s).\n"); - printf("On many machines the value is not fixed.\n"); - printf("A good guess for ALIGNMENT on this machine is %ld.\n", - (unsigned long)(&(a.a_b))-(unsigned long)(&a)); - - printf("The following is a very dubious test of one root marking" - " strategy.\n"); - printf("Results may not be accurate/useful:\n"); - /* Encourage the compiler to keep x in a callee-save register */ - x = 2*x-1; - printf(""); - x = 2*x-1; - setjmp(b); - if (y == 1) { - if (x == 2) { - printf("Setjmp-based generic mark_regs code probably wont work.\n"); - printf("But we rarely try that anymore. If you have getcontect()\n"); - printf("this probably doesn't matter.\n"); - } else if (x == 1) { - printf("Setjmp-based register marking code may work.\n"); - } else { - printf("Very strange setjmp implementation.\n"); - } - } - y++; - x = 2; - if (y == 1) longjmp(b,1); - printf("Some GC internal configuration stuff: \n"); - printf("\tWORDSZ = %d, ALIGNMENT = %d, GC_GRANULE_BYTES = %d\n", - WORDSZ, ALIGNMENT, GC_GRANULE_BYTES); - printf("\tUsing one mark "); -# if defined(USE_MARK_BYTES) - printf("byte"); -# elif defined(USE_MARK_BITS) - printf("bit"); -# endif - printf(" per "); -# if defined(MARK_BIT_PER_OBJ) - printf("object.\n"); -# elif defined(MARK_BIT_PER_GRANULE) - printf("granule.\n"); -# endif -# ifdef THREAD_LOCAL_ALLOC - printf("Thread local allocation enabled.\n"); -# endif -# ifdef PARALLEL_MARK - printf("Parallel marking enabled.\n"); -# endif - return(0); + printf("This appears to be a %s running %s\n", MACH_TYPE, OS_TYPE); + if (nested_sp() < &dummy) { + printf("Stack appears to grow down, which is the default.\n"); + printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", + ((unsigned long)(&dummy) + ps) & ~(ps-1)); + } else { + printf("Stack appears to grow up.\n"); + printf("Define STACK_GROWS_UP in gc_private.h\n"); + printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", + ((unsigned long)(&dummy) + ps) & ~(ps-1)); + } + printf("Note that this may vary between machines of ostensibly\n"); + printf("the same architecture (e.g. Sun 3/50s and 3/80s).\n"); + printf("On many machines the value is not fixed.\n"); + printf("A good guess for ALIGNMENT on this machine is %ld.\n", + (unsigned long)(&(a.a_b))-(unsigned long)(&a)); + + printf("The following is a very dubious test of one root marking" + " strategy.\n"); + printf("Results may not be accurate/useful:\n"); + /* Encourage the compiler to keep x in a callee-save register */ + x = 2*x-1; + printf(""); + x = 2*x-1; + setjmp(b); + if (y == 1) { + if (x == 2) { + printf("Setjmp-based generic mark_regs code probably wont work.\n"); + printf("But we rarely try that anymore. If you have getcontect()\n"); + printf("this probably doesn't matter.\n"); + } else if (x == 1) { + printf("Setjmp-based register marking code may work.\n"); + } else { + printf("Very strange setjmp implementation.\n"); + } + } + y++; + x = 2; + if (y == 1) longjmp(b,1); + printf("Some GC internal configuration stuff: \n"); + printf("\tWORDSZ = %d, ALIGNMENT = %d, GC_GRANULE_BYTES = %d\n", + WORDSZ, ALIGNMENT, GC_GRANULE_BYTES); + printf("\tUsing one mark "); +# if defined(USE_MARK_BYTES) + printf("byte"); +# else + printf("bit"); +# endif + printf(" per "); +# if defined(MARK_BIT_PER_OBJ) + printf("object.\n"); +# elif defined(MARK_BIT_PER_GRANULE) + printf("granule.\n"); +# endif +# ifdef THREAD_LOCAL_ALLOC + printf("Thread local allocation enabled.\n"); +# endif +# ifdef PARALLEL_MARK + printf("Parallel marking enabled.\n"); +# endif + return(0); } -int g(x) -int x; +int g(int x) { - return(x); + return(x); } @@ -485,18 +485,13 @@ GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj, } } } -#endif +#endif /* !NO_DEBUGGING */ #ifndef SMALL_CONFIG STATIC word GC_old_dl_entries = 0; /* for stats printing */ #endif -#ifdef THREADS - /* Defined in pthread_support.c or win32_threads.c. Called with the */ - /* allocation lock held. */ - GC_INNER void GC_reset_finalizer_nested(void); - GC_INNER unsigned char *GC_check_finalizer_nested(void); -#else +#ifndef THREADS /* Global variables to minimize the level of recursion when a client */ /* finalizer allocates memory. */ STATIC unsigned char GC_finalizer_nested = 0; diff --git a/gc_dlopen.c b/gc_dlopen.c index e466d65c..f76230ec 100644 --- a/gc_dlopen.c +++ b/gc_dlopen.c @@ -43,16 +43,18 @@ /* dlopen calls in either a multi-threaded environment, or if the */ /* library initialization code allocates substantial amounts of GC'ed */ /* memory. */ -static void disable_gc_for_dlopen(void) -{ - DCL_LOCK_STATE; - LOCK(); - while (GC_incremental && GC_collection_in_progress()) { - GC_collect_a_little_inner(1000); +#ifndef USE_PROC_FOR_LIBRARIES + static void disable_gc_for_dlopen(void) + { + DCL_LOCK_STATE; + LOCK(); + while (GC_incremental && GC_collection_in_progress()) { + GC_collect_a_little_inner(1000); + } + ++GC_dont_gc; + UNLOCK(); } - ++GC_dont_gc; - UNLOCK(); -} +#endif /* Redefine dlopen to guarantee mutual exclusion with */ /* GC_register_dynamic_libraries. Should probably happen for */ @@ -39,7 +39,12 @@ #include "gc_gcj.h" #include "private/dbg_mlc.h" -GC_INNER GC_bool GC_gcj_malloc_initialized = FALSE; +#ifdef GC_ASSERTIONS + GC_INNER /* variable is also used in thread_local_alloc.c */ +#else + STATIC +#endif +GC_bool GC_gcj_malloc_initialized = FALSE; int GC_gcj_kind = 0; /* Object kind for objects with descriptors */ /* in "vtable". */ @@ -199,8 +204,6 @@ static void maybe_finalize(void) return((void *) op); } -GC_INNER void GC_start_debugging(void); - /* Similar to GC_gcj_malloc, but add debug info. This is allocated */ /* with GC_gcj_debug_kind. */ GC_API void * GC_CALL GC_debug_gcj_malloc(size_t lb, @@ -209,7 +212,7 @@ GC_API void * GC_CALL GC_debug_gcj_malloc(size_t lb, void * result; DCL_LOCK_STATE; - /* We're careful to avoid extra calls, which could */ + /* We're careful to avoid extra calls, which could */ /* confuse the backtrace. */ LOCK(); maybe_finalize(); @@ -229,7 +232,7 @@ GC_API void * GC_CALL GC_debug_gcj_malloc(size_t lb, GC_start_debugging(); } ADD_CALL_CHAIN(result, ra); - return (GC_store_debug_info(result, (word)lb, s, (word)i)); + return (GC_store_debug_info(result, (word)lb, s, i)); } /* There is no THREAD_LOCAL_ALLOC for GC_gcj_malloc_ignore_off_page(). */ @@ -145,7 +145,7 @@ GC_INNER ptr_t GC_scratch_alloc(size_t bytes) GC_add_to_our_memory(result, bytes_to_get); if (result == 0) { if (GC_print_stats) - GC_printf("Out of memory - trying to allocate less\n"); + GC_log_printf("Out of memory - trying to allocate less\n"); scratch_free_ptr -= bytes; bytes_to_get = bytes; # ifdef USE_MMAP diff --git a/include/gc.h b/include/gc.h index a00112eb..28cf86f7 100644 --- a/include/gc.h +++ b/include/gc.h @@ -382,12 +382,6 @@ GC_API void * GC_CALL GC_memalign(size_t /* align */, size_t /* lb */) GC_API int GC_CALL GC_posix_memalign(void ** /* memptr */, size_t /* align */, size_t /* lb */); -/* The following is only defined if the library has been suitably */ -/* compiled: */ -GC_API void * GC_CALL GC_malloc_atomic_uncollectable( - size_t /* size_in_bytes */) - GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1); - /* Explicitly deallocate an object. Dangerous if used incorrectly. */ /* Requires a pointer to the base of an object. */ /* If the argument is stubborn, it should not be changeable when freed. */ @@ -615,6 +609,15 @@ GC_API void * GC_CALL GC_malloc_atomic_ignore_off_page(size_t /* lb */) # define GC_EXTRA_PARAMS const char * s, int i #endif +/* The following is only defined if the library has been suitably */ +/* compiled: */ +GC_API void * GC_CALL GC_malloc_atomic_uncollectable( + size_t /* size_in_bytes */) + GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1); +GC_API void * GC_CALL GC_debug_malloc_atomic_uncollectable(size_t, + GC_EXTRA_PARAMS) + GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1); + /* Debugging (annotated) allocation. GC_gcollect will check */ /* objects allocated in this way for overwrites, etc. */ GC_API void * GC_CALL GC_debug_malloc(size_t /* size_in_bytes */, @@ -682,6 +685,8 @@ GC_API void * GC_CALL GC_debug_realloc_replacement(void * /* object_addr */, # define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) # define GC_STRDUP(s) GC_debug_strdup(s, GC_EXTRAS) # define GC_STRNDUP(s, sz) GC_debug_strndup(s, sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) \ + GC_debug_malloc_atomic_uncollectable(sz, GC_EXTRAS) # define GC_MALLOC_UNCOLLECTABLE(sz) \ GC_debug_malloc_uncollectable(sz, GC_EXTRAS) # define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ @@ -708,6 +713,7 @@ GC_API void * GC_CALL GC_debug_realloc_replacement(void * /* object_addr */, # define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) # define GC_STRDUP(s) GC_strdup(s) # define GC_STRNDUP(s, sz) GC_strndup(s, sz) +# define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz) GC_malloc_atomic_uncollectable(sz) # define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) # define GC_MALLOC_IGNORE_OFF_PAGE(sz) \ GC_malloc_ignore_off_page(sz) @@ -734,7 +740,8 @@ GC_API void * GC_CALL GC_debug_realloc_replacement(void * /* object_addr */, /* The following are included because they are often convenient, and */ /* reduce the chance for a misspecified size argument. But calls may */ /* expand to something syntactically incorrect if t is a complicated */ -/* type expression. */ +/* type expression. Note that, unlike C++ new operator, these ones */ +/* may return NULL (if out of memory). */ #define GC_NEW(t) ((t*)GC_MALLOC(sizeof(t))) #define GC_NEW_ATOMIC(t) ((t*)GC_MALLOC_ATOMIC(sizeof(t))) #define GC_NEW_STUBBORN(t) ((t*)GC_MALLOC_STUBBORN(sizeof(t))) @@ -975,22 +982,19 @@ GC_API void GC_CALLBACK GC_ignore_warn_proc(char *, GC_word); /* Note that putting pointers in atomic objects or in */ /* non-pointer slots of "typed" objects is equivalent to */ /* disguising them in this way, and may have other advantages. */ -#if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS) - typedef GC_word GC_hidden_pointer; -# define HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) -# define REVEAL_POINTER(p) ((void *)HIDE_POINTER(p)) - /* Converting a hidden pointer to a real pointer requires verifying */ - /* that the object still exists. This involves acquiring the */ - /* allocator lock to avoid a race with the collector. */ -#endif /* I_HIDE_POINTERS */ - -/* The GC-prefixed symbols are preferred for new code (I_HIDE_POINTERS, */ -/* HIDE_POINTER and REVEAL_POINTER remain for compatibility). */ -//#ifdef GC_I_HIDE_POINTERS - typedef GC_word GC_hidden_pointer; -# define GC_HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) -# define GC_REVEAL_POINTER(p) ((void*) (~(GC_hidden_pointer)(p))) -//#endif +typedef GC_word GC_hidden_pointer; +#define GC_HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +/* Converting a hidden pointer to a real pointer requires verifying */ +/* that the object still exists. This involves acquiring the */ +/* allocator lock to avoid a race with the collector. */ +#define GC_REVEAL_POINTER(p) ((void *)GC_HIDE_POINTER(p)) + +#ifdef I_HIDE_POINTERS + /* This exists only for compatibility (the GC-prefixed symbols are */ + /* preferred for new code). */ +# define HIDE_POINTER(p) GC_HIDE_POINTER(p) +# define REVEAL_POINTER(p) GC_REVEAL_POINTER(p) +#endif typedef void * (GC_CALLBACK * GC_fn_type)(void * /* client_data */); GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type /* fn */, @@ -1064,11 +1068,15 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func /* fn */, /* always done implicitly. This is normally done implicitly if GC_ */ /* functions are called to create the thread, e.g. by including gc.h */ /* (which redefines some system functions) before calling the system */ - /* thread creation function. */ + /* thread creation function. Nonetheless, thread cleanup routines */ + /* (eg., pthread key destructor) typically require manual thread */ + /* registering (and unregistering) if pointers to GC-allocated */ + /* objects are manipulated inside. */ /* It is also always done implicitly on some platforms if */ /* GC_use_threads_discovery() is called at start-up. Except for the */ /* latter case, the explicit call is normally required for threads */ /* created by third-party libraries. */ + /* A manually registered thread requires manual unregistering. */ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *); /* Unregister the current thread. Only an explicitly registered */ @@ -1507,11 +1515,6 @@ GC_API void GC_CALL GC_win32_free_heap(void); # include "gc_amiga_redirects.h" #endif - /* - * GC_REDIRECT_TO_LOCAL is now redundant; - * that's the default with THREAD_LOCAL_ALLOC. - */ - #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/include/gc_config_macros.h b/include/gc_config_macros.h index 030218e4..a27f8dd7 100644 --- a/include/gc_config_macros.h +++ b/include/gc_config_macros.h @@ -273,8 +273,8 @@ /* This may also be desirable if it is possible but expensive to */ /* retrieve the call chain. */ #if (defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__) \ - || defined(__FreeBSD__) || defined(__DragonFly__)) \ - && !defined(GC_CAN_SAVE_CALL_STACKS) + || defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(PLATFORM_ANDROID)) && !defined(GC_CAN_SAVE_CALL_STACKS) # define GC_ADD_CALLER # if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) /* gcc knows how to retrieve return address, but we don't know */ diff --git a/include/gc_cpp.h b/include/gc_cpp.h index 60eafcf9..4bee8dec 100644 --- a/include/gc_cpp.h +++ b/include/gc_cpp.h @@ -252,9 +252,9 @@ inline void* operator new( classes derived from "gc_cleanup" or containing members derived from "gc_cleanup". */ -# ifdef GC_PLACEMENT_DELETE - inline void operator delete( void*, GCPlacement, GCCleanUpFunc, void * ); -# endif +#ifdef GC_PLACEMENT_DELETE + inline void operator delete( void*, GCPlacement, GCCleanUpFunc, void * ); +#endif #ifdef _MSC_VER /** This ensures that the system default operator new[] doesn't get @@ -307,10 +307,9 @@ _Ret_bytecap_(_Size) inline void* operator new[](size_t size, int nBlockUse, con #endif /* _MSC_VER */ - #ifdef GC_OPERATOR_NEW_ARRAY -inline void* operator new[]( + inline void* operator new[]( size_t size, GCPlacement gcp, GCCleanUpFunc cleanup = 0, @@ -360,30 +359,26 @@ inline void gc::operator delete( void* obj ) { #endif #ifdef GC_OPERATOR_NEW_ARRAY - -inline void* gc::operator new[]( size_t size ) { + inline void* gc::operator new[]( size_t size ) { return gc::operator new( size );} -inline void* gc::operator new[]( size_t size, GCPlacement gcp ) { + inline void* gc::operator new[]( size_t size, GCPlacement gcp ) { return gc::operator new( size, gcp );} -inline void* gc::operator new[]( size_t size, void *p ) { + inline void* gc::operator new[]( size_t size, void *p ) { return p;} -inline void gc::operator delete[]( void* obj ) { + inline void gc::operator delete[]( void* obj ) { gc::operator delete( obj );} -#ifdef GC_PLACEMENT_DELETE - inline void gc::operator delete[]( void*, void* ) {} - - inline void gc::operator delete[]( void* p, GCPlacement gcp ) { - gc::operator delete(p); } - -#endif +# ifdef GC_PLACEMENT_DELETE + inline void gc::operator delete[]( void*, void* ) {} + inline void gc::operator delete[]( void* p, GCPlacement gcp ) { + gc::operator delete(p); } +# endif #endif /* GC_OPERATOR_NEW_ARRAY */ - inline gc_cleanup::~gc_cleanup() { GC_register_finalizer_ignore_self( GC_base(this), 0, 0, 0, 0 );} @@ -397,7 +392,7 @@ inline gc_cleanup::gc_cleanup() { if (0 != base) { // Don't call the debug version, since this is a real base address. GC_register_finalizer_ignore_self( - base, (GC_finalization_proc)cleanup, (void*) ((char*) this - (char*) base), + base, (GC_finalization_proc)cleanup, (void*)((char*)this - (char*)base), &oldProc, &oldData ); if (0 != oldProc) { GC_register_finalizer_ignore_self( base, oldProc, oldData, 0, 0 );}}} @@ -435,27 +430,26 @@ inline void* operator new( return obj; } -# ifdef GC_PLACEMENT_DELETE -inline void operator delete ( +#ifdef GC_PLACEMENT_DELETE + inline void operator delete ( void *p, GCPlacement gcp, GCCleanUpFunc cleanup, void* clientData ) -{ + { GC_FREE(p); -} -# endif + } +#endif /* GC_PLACEMENT_DELETE */ #ifdef GC_OPERATOR_NEW_ARRAY - -inline void* operator new[]( + inline void* operator new[]( size_t size, GCPlacement gcp, GCCleanUpFunc cleanup, void* clientData ) -{ - return ::operator new( size, gcp, cleanup, clientData );} - + { + return ::operator new( size, gcp, cleanup, clientData ); + } #endif /* GC_OPERATOR_NEW_ARRAY */ // oooohh... big hack (mainly for vnl which explicitly references mem-stuff via std namespace) @@ -466,6 +460,13 @@ namespace std using ::GC_realloc; using ::GC_malloc_uncollectable; } +#if defined(__CYGWIN__) +# include <new> // for delete throw() + inline void operator delete(void *p) + { + GC_FREE(p); + } +#endif #endif // __cplusplus diff --git a/include/gc_inline.h b/include/gc_inline.h index 312ed61b..cc49da22 100644 --- a/include/gc_inline.h +++ b/include/gc_inline.h @@ -118,15 +118,15 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */, /* free list array. For single-threaded applications, this may be */ /* a global array. */ # define GC_MALLOC_WORDS(result,n,tiny_fl) \ -{ \ +{ \ size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \ GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \ NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \ - *(void **)result = 0); \ + *(void **)(result) = 0); \ } # define GC_MALLOC_ATOMIC_WORDS(result,n,tiny_fl) \ -{ \ +{ \ size_t grans = GC_WORDS_TO_WHOLE_GRANULES(n); \ GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \ PTRFREE, GC_malloc_atomic(grans*GC_GRANULE_BYTES), \ @@ -135,12 +135,12 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t /* lb */, int /* k */, /* And once more for two word initialized objects: */ # define GC_CONS(result, first, second, tiny_fl) \ -{ \ +{ \ size_t grans = GC_WORDS_TO_WHOLE_GRANULES(2); \ GC_FAST_MALLOC_GRANS(result, grans, tiny_fl, 0, \ NORMAL, GC_malloc(grans*GC_GRANULE_BYTES), \ - *(void **)result = (void *)(first)); \ - ((void **)(result))[1] = (void *)(second); \ + *(void **)(result) = (void *)(first)); \ + ((void **)(result))[1] = (void *)(second); \ } #endif /* !GC_INLINE_H */ diff --git a/include/gc_typed.h b/include/gc_typed.h index 10111cdb..a794fbbe 100644 --- a/include/gc_typed.h +++ b/include/gc_typed.h @@ -39,9 +39,9 @@ typedef GC_word * GC_bitmap; #define GC_WORDSZ (8 * sizeof(GC_word)) #define GC_get_bit(bm, index) \ - (((bm)[index / GC_WORDSZ] >> (index % GC_WORDSZ)) & 1) + (((bm)[(index) / GC_WORDSZ] >> ((index) % GC_WORDSZ)) & 1) #define GC_set_bit(bm, index) \ - (bm)[index / GC_WORDSZ] |= ((GC_word)1 << (index % GC_WORDSZ)) + ((bm)[(index) / GC_WORDSZ] |= (GC_word)1 << ((index) % GC_WORDSZ)) #define GC_WORD_OFFSET(t, f) (offsetof(t,f) / sizeof(GC_word)) #define GC_WORD_LEN(t) (sizeof(t) / sizeof(GC_word)) #define GC_BITMAP_SIZE(t) ((GC_WORD_LEN(t) + GC_WORDSZ - 1) / GC_WORDSZ) @@ -99,7 +99,7 @@ GC_API void * GC_CALL GC_calloc_explicitly_typed(size_t /* nelements */, #ifdef GC_DEBUG # define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) GC_MALLOC(bytes) -# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) GC_MALLOC(n * bytes) +# define GC_CALLOC_EXPLICITLY_TYPED(n, bytes, d) GC_MALLOC((n) * (bytes)) #else # define GC_MALLOC_EXPLICITLY_TYPED(bytes, d) \ GC_malloc_explicitly_typed(bytes, d) diff --git a/include/gc_version.h b/include/gc_version.h index ee3bb22c..4ea1a897 100644 --- a/include/gc_version.h +++ b/include/gc_version.h @@ -23,7 +23,7 @@ /* it to keep the old-style build process working. */ #define GC_TMP_VERSION_MAJOR 7 #define GC_TMP_VERSION_MINOR 2 -#define GC_TMP_ALPHA_VERSION 5 +#define GC_TMP_ALPHA_VERSION 7 #ifndef GC_NOT_ALPHA # define GC_NOT_ALPHA 0xff diff --git a/include/private/config.h.in b/include/private/config.h.in index 6af87d14..a14c0f34 100755..100644 --- a/include/private/config.h.in +++ b/include/private/config.h.in @@ -69,7 +69,7 @@ /* Define to support Solaris pthreads. */ #undef GC_SOLARIS_THREADS -/* Define to support threads. */ +/* Define to support platform-specific threads. */ #undef GC_THREADS /* See doc/README.macros. */ @@ -81,7 +81,10 @@ /* The minor version number of this GC release. */ #undef GC_VERSION_MINOR -/* Define to support win32 threads. */ +/* Define to support win32-pthreads. */ +#undef GC_WIN32_PTHREADS + +/* Define to support Win32 threads. */ #undef GC_WIN32_THREADS /* Define to 1 if you have the <dlfcn.h> header file. */ diff --git a/include/private/darwin_stop_world.h b/include/private/darwin_stop_world.h index ee601144..399304ea 100644 --- a/include/private/darwin_stop_world.h +++ b/include/private/darwin_stop_world.h @@ -30,4 +30,17 @@ struct thread_stop_info { ptr_t stack_ptr; /* Valid only when thread is in a "blocked" state. */ }; +#ifndef DARWIN_DONT_PARSE_STACK + GC_INNER ptr_t GC_FindTopOfStack(unsigned long); +#endif + +#ifdef MPROTECT_VDB + GC_INNER void GC_mprotect_stop(void); + GC_INNER void GC_mprotect_resume(void); +#endif + +#if defined(PARALLEL_MARK) && !defined(GC_NO_THREADS_DISCOVERY) + GC_INNER GC_bool GC_is_mach_marker(thread_act_t); +#endif + #endif diff --git a/include/private/dbg_mlc.h b/include/private/dbg_mlc.h index 45dc1812..7b2969d2 100644 --- a/include/private/dbg_mlc.h +++ b/include/private/dbg_mlc.h @@ -25,92 +25,96 @@ #ifndef _DBG_MLC_H #define _DBG_MLC_H -# include "gc_priv.h" -# ifdef KEEP_BACK_PTRS -# include "gc_backptr.h" -# endif +#include "gc_priv.h" +#ifdef KEEP_BACK_PTRS +# include "gc_backptr.h" +#endif -# define START_FLAG ((word)0xfedcedcb) -# define END_FLAG ((word)0xbcdecdef) +#if CPP_WORDSZ == 32 +# define START_FLAG (word)0xfedcedcb +# define END_FLAG (word)0xbcdecdef +#else +# define START_FLAG GC_WORD_C(0xFEDCEDCBfedcedcb) +# define END_FLAG GC_WORD_C(0xBCDECDEFbcdecdef) +#endif /* Stored both one past the end of user object, and one before */ /* the end of the object as seen by the allocator. */ -# if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) \ - || defined(MAKE_BACK_GRAPH) - /* Pointer "source"s that aren't real locations. */ - /* Used in oh_back_ptr fields and as "source" */ - /* argument to some marking functions. */ -# define NOT_MARKED (ptr_t)(0) -# define MARKED_FOR_FINALIZATION ((ptr_t)(word)2) - /* Object was marked because it is finalizable. */ -# define MARKED_FROM_REGISTER ((ptr_t)(word)4) - /* Object was marked from a register. Hence the */ - /* source of the reference doesn't have an address. */ -# endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */ +#if defined(KEEP_BACK_PTRS) || defined(PRINT_BLACK_LIST) \ + || defined(MAKE_BACK_GRAPH) + /* Pointer "source"s that aren't real locations. */ + /* Used in oh_back_ptr fields and as "source" */ + /* argument to some marking functions. */ +# define NOT_MARKED (ptr_t)0 +# define MARKED_FOR_FINALIZATION ((ptr_t)(word)2) + /* Object was marked because it is finalizable. */ +# define MARKED_FROM_REGISTER ((ptr_t)(word)4) + /* Object was marked from a register. Hence the */ + /* source of the reference doesn't have an address. */ +#endif /* KEEP_BACK_PTRS || PRINT_BLACK_LIST */ /* Object header */ typedef struct { -# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) - /* We potentially keep two different kinds of back */ - /* pointers. KEEP_BACK_PTRS stores a single back */ - /* pointer in each reachable object to allow reporting */ - /* of why an object was retained. MAKE_BACK_GRAPH */ - /* builds a graph containing the inverse of all */ - /* "points-to" edges including those involving */ - /* objects that have just become unreachable. This */ - /* allows detection of growing chains of unreachable */ - /* objects. It may be possible to eventually combine */ - /* both, but for now we keep them separate. Both */ - /* kinds of back pointers are hidden using the */ - /* following macros. In both cases, the plain version */ - /* is constrained to have an least significant bit of 1,*/ - /* to allow it to be distinguished from a free list */ - /* link. This means the plain version must have an */ - /* lsb of 0. */ - /* Note that blocks dropped by black-listing will */ - /* also have the lsb clear once debugging has */ - /* started. */ - /* We're careful never to overwrite a value with lsb 0. */ -# if ALIGNMENT == 1 - /* Fudge back pointer to be even. */ -# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~1 & (GC_word)(p)) -# else -# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(p) -# endif - -# ifdef KEEP_BACK_PTRS - GC_hidden_pointer oh_back_ptr; -# endif -# ifdef MAKE_BACK_GRAPH - GC_hidden_pointer oh_bg_ptr; -# endif -# if defined(KEEP_BACK_PTRS) != defined(MAKE_BACK_GRAPH) - /* Keep double-pointer-sized alignment. */ - word oh_dummy; -# endif +# if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) + /* We potentially keep two different kinds of back */ + /* pointers. KEEP_BACK_PTRS stores a single back */ + /* pointer in each reachable object to allow reporting */ + /* of why an object was retained. MAKE_BACK_GRAPH */ + /* builds a graph containing the inverse of all */ + /* "points-to" edges including those involving */ + /* objects that have just become unreachable. This */ + /* allows detection of growing chains of unreachable */ + /* objects. It may be possible to eventually combine */ + /* both, but for now we keep them separate. Both */ + /* kinds of back pointers are hidden using the */ + /* following macros. In both cases, the plain version */ + /* is constrained to have an least significant bit of 1, */ + /* to allow it to be distinguished from a free list */ + /* link. This means the plain version must have an */ + /* lsb of 0. */ + /* Note that blocks dropped by black-listing will */ + /* also have the lsb clear once debugging has */ + /* started. */ + /* We're careful never to overwrite a value with lsb 0. */ +# if ALIGNMENT == 1 + /* Fudge back pointer to be even. */ +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(~1 & (GC_word)(p)) +# else +# define HIDE_BACK_PTR(p) GC_HIDE_POINTER(p) +# endif +# ifdef KEEP_BACK_PTRS + GC_hidden_pointer oh_back_ptr; +# endif +# ifdef MAKE_BACK_GRAPH + GC_hidden_pointer oh_bg_ptr; # endif - const char * oh_string; /* object descriptor string */ - word oh_int; /* object descriptor integers */ -# ifdef NEED_CALLINFO - struct callinfo oh_ci[NFRAMES]; +# if defined(KEEP_BACK_PTRS) != defined(MAKE_BACK_GRAPH) + /* Keep double-pointer-sized alignment. */ + word oh_dummy; # endif -# ifndef SHORT_DBG_HDRS - word oh_sz; /* Original malloc arg. */ - word oh_sf; /* start flag */ -# endif /* SHORT_DBG_HDRS */ +# endif + const char * oh_string; /* object descriptor string */ + word oh_int; /* object descriptor integers */ +# ifdef NEED_CALLINFO + struct callinfo oh_ci[NFRAMES]; +# endif +# ifndef SHORT_DBG_HDRS + word oh_sz; /* Original malloc arg. */ + word oh_sf; /* start flag */ +# endif /* SHORT_DBG_HDRS */ } oh; /* The size of the above structure is assumed not to de-align things, */ /* and to be a multiple of the word length. */ #ifdef SHORT_DBG_HDRS -# define DEBUG_BYTES (sizeof (oh)) -# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES +# define DEBUG_BYTES (sizeof (oh)) +# define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES #else - /* Add space for END_FLAG, but use any extra space that was already */ - /* added to catch off-the-end pointers. */ - /* For uncollectable objects, the extra byte is not added. */ -# define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh) + sizeof (word)) -# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES) + /* Add space for END_FLAG, but use any extra space that was already */ + /* added to catch off-the-end pointers. */ + /* For uncollectable objects, the extra byte is not added. */ +# define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh) + sizeof (word)) +# define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES) #endif /* Round bytes to words without adding extra byte at end. */ @@ -122,47 +126,41 @@ typedef struct { /* PRINT_CALL_CHAIN prints the call chain stored in an object */ /* to stderr. It requires that we do not hold the lock. */ #if defined(SAVE_CALL_CHAIN) - struct callinfo; - GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); - GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); -# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) -# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) + struct callinfo; + GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) #elif defined(GC_ADD_CALLER) - struct callinfo; - GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); -# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra) -# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) + struct callinfo; + GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) #else -# define ADD_CALL_CHAIN(base, ra) -# define PRINT_CALL_CHAIN(base) +# define ADD_CALL_CHAIN(base, ra) +# define PRINT_CALL_CHAIN(base) #endif -# ifdef GC_ADD_CALLER -# define OPT_RA ra, -# else -# define OPT_RA -# endif - +#ifdef GC_ADD_CALLER +# define OPT_RA ra, +#else +# define OPT_RA +#endif /* Check whether object with base pointer p has debugging info */ /* p is assumed to point to a legitimate object in our part */ /* of the heap. */ #ifdef SHORT_DBG_HDRS -# define GC_has_other_debug_info(p) TRUE +# define GC_has_other_debug_info(p) 1 #else - GC_INNER GC_bool GC_has_other_debug_info(ptr_t p); + GC_INNER int GC_has_other_debug_info(ptr_t p); #endif #if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) # define GC_HAS_DEBUG_INFO(p) \ - ((*((word *)p) & 1) && GC_has_other_debug_info(p)) + ((*((word *)p) & 1) && GC_has_other_debug_info(p) > 0) #else -# define GC_HAS_DEBUG_INFO(p) GC_has_other_debug_info(p) +# define GC_HAS_DEBUG_INFO(p) (GC_has_other_debug_info(p) > 0) #endif -/* Store debugging info into p. Return displaced pointer. */ -/* Assumes we don't hold allocation lock. */ -GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str, - word integer); - #endif /* _DBG_MLC_H */ diff --git a/include/private/gc_hdrs.h b/include/private/gc_hdrs.h index 777b016a..0360adbc 100644 --- a/include/private/gc_hdrs.h +++ b/include/private/gc_hdrs.h @@ -176,7 +176,6 @@ typedef struct bi { register word hi = \ (word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \ register bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \ - \ while (_bi -> key != hi && _bi != GC_all_nils) \ _bi = _bi -> hash_link; \ (bottom_indx) = _bi; \ @@ -184,8 +183,7 @@ typedef struct bi { # define GET_HDR_ADDR(p, ha) \ { \ register bottom_index * bi; \ - \ - GET_BI(p, bi); \ + GET_BI(p, bi); \ (ha) = &(HDR_FROM_BI(bi, p)); \ } # define GET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \ diff --git a/include/private/gc_locks.h b/include/private/gc_locks.h index 3b0d1c3a..113b4aea 100644 --- a/include/private/gc_locks.h +++ b/include/private/gc_locks.h @@ -52,6 +52,10 @@ # endif # if defined(GC_WIN32_THREADS) && !defined(USE_PTHREAD_LOCKS) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE # include <windows.h> # define NO_THREAD (DWORD)(-1) GC_EXTERN DWORD GC_lock_holder; @@ -64,8 +68,8 @@ { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ LeaveCriticalSection(&GC_allocate_ml); } # else -# define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml); -# define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml); +# define UNCOND_LOCK() EnterCriticalSection(&GC_allocate_ml) +# define UNCOND_UNLOCK() LeaveCriticalSection(&GC_allocate_ml) # endif /* !GC_ASSERTIONS */ # define SET_LOCK_HOLDER() GC_lock_holder = GetCurrentThreadId() # define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD @@ -95,20 +99,15 @@ # define THREAD_EQUAL(id1, id2) ((id1) == (id2)) # define NUMERIC_THREAD_ID_UNIQUE # else -# if defined(GC_WIN32_PTHREADS) -# define NUMERIC_THREAD_ID(id) ((unsigned long)(id.p)) - /* Using documented internal details of win32_pthread library. */ - /* Faster than pthread_equal(). Should not change with */ - /* future versions of win32_pthread library. */ -# define THREAD_EQUAL(id1, id2) ((id1.p == id2.p) && (id1.x == id2.x)) -# undef NUMERIC_THREAD_ID_UNIQUE -# else - /* Generic definitions that always work, but will result in */ - /* poor performance and weak assertion checking. */ -# define NUMERIC_THREAD_ID(id) 1l -# define THREAD_EQUAL(id1, id2) pthread_equal(id1, id2) -# undef NUMERIC_THREAD_ID_UNIQUE -# endif +# define NUMERIC_THREAD_ID(id) ((unsigned long)(id.p)) + /* Using documented internal details of win32-pthread library. */ + /* Faster than pthread_equal(). Should not change with */ + /* future versions of win32-pthread library. */ +# define THREAD_EQUAL(id1, id2) ((id1.p == id2.p) && (id1.x == id2.x)) +# undef NUMERIC_THREAD_ID_UNIQUE + /* Generic definitions based on pthread_equal() always work but */ + /* will result in poor performance (as NUMERIC_THREAD_ID is */ + /* defined to just a constant) and weak assertion checking. */ # endif # define NO_THREAD ((unsigned long)(-1l)) /* != NUMERIC_THREAD_ID(pthread_self()) for any thread */ @@ -125,18 +124,17 @@ /* GC_call_with_alloc_lock. */ # ifdef GC_ASSERTIONS # define UNCOND_LOCK() \ - { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ - GC_lock(); \ - SET_LOCK_HOLDER(); } + { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ + GC_lock(); \ + SET_LOCK_HOLDER(); } # define UNCOND_UNLOCK() \ - { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ - AO_CLEAR(&GC_allocate_lock); } + { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ + AO_CLEAR(&GC_allocate_lock); } # else # define UNCOND_LOCK() \ - { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ - GC_lock(); } -# define UNCOND_UNLOCK() \ - AO_CLEAR(&GC_allocate_lock) + { if (AO_test_and_set_acquire(&GC_allocate_lock) == AO_TS_SET) \ + GC_lock(); } +# define UNCOND_UNLOCK() AO_CLEAR(&GC_allocate_lock) # endif /* !GC_ASSERTIONS */ # else /* THREAD_LOCAL_ALLOC || USE_PTHREAD_LOCKS */ # ifndef USE_PTHREAD_LOCKS @@ -147,18 +145,17 @@ # include <pthread.h> GC_EXTERN pthread_mutex_t GC_allocate_ml; # ifdef GC_ASSERTIONS -# define UNCOND_LOCK() \ - { GC_lock(); \ - SET_LOCK_HOLDER(); } +# define UNCOND_LOCK() { GC_lock(); SET_LOCK_HOLDER(); } # define UNCOND_UNLOCK() \ { GC_ASSERT(I_HOLD_LOCK()); UNSET_LOCK_HOLDER(); \ pthread_mutex_unlock(&GC_allocate_ml); } # else /* !GC_ASSERTIONS */ # if defined(NO_PTHREAD_TRYLOCK) -# define UNCOND_LOCK() GC_lock(); +# define UNCOND_LOCK() GC_lock() # else /* !defined(NO_PTHREAD_TRYLOCK) */ # define UNCOND_LOCK() \ - { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) GC_lock(); } + { if (0 != pthread_mutex_trylock(&GC_allocate_ml)) \ + GC_lock(); } # endif # define UNCOND_UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) # endif /* !GC_ASSERTIONS */ @@ -201,8 +198,8 @@ #if defined(UNCOND_LOCK) && !defined(LOCK) /* At least two thread running; need to lock. */ -# define LOCK() if (GC_need_to_lock) { UNCOND_LOCK(); } -# define UNLOCK() if (GC_need_to_lock) { UNCOND_UNLOCK(); } +# define LOCK() { if (GC_need_to_lock) UNCOND_LOCK(); } +# define UNLOCK() { if (GC_need_to_lock) UNCOND_UNLOCK(); } #endif # ifndef ENTER_GC diff --git a/include/private/gc_pmark.h b/include/private/gc_pmark.h index c0232cc4..42fbb204 100644 --- a/include/private/gc_pmark.h +++ b/include/private/gc_pmark.h @@ -35,9 +35,6 @@ #endif #ifndef GC_MARK_H -# ifndef GC_H -# define GC_I_HIDE_POINTERS /* to get GC_HIDE_POINTER() and friends */ -# endif # include "../gc_mark.h" #endif @@ -136,7 +133,6 @@ GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp); #define PUSH_OBJ(obj, hhdr, mark_stack_top, mark_stack_limit) \ { \ register word _descr = (hhdr) -> hb_descr; \ - \ GC_ASSERT(!HBLK_IS_FREE(hhdr)); \ if (_descr != 0) { \ mark_stack_top++; \ @@ -156,17 +152,26 @@ GC_INNER mse * GC_signal_mark_stack_overflow(mse *msp); source, exit_label) \ { \ hdr * my_hhdr; \ - \ HC_GET_HDR(current, my_hhdr, source, exit_label); \ PUSH_CONTENTS_HDR(current, mark_stack_top, mark_stack_limit, \ - source, exit_label, my_hhdr, TRUE); \ + source, exit_label, my_hhdr, TRUE); \ exit_label: ; \ } /* Set mark bit, exit if it was already set. */ - -#ifdef USE_MARK_BITS +#ifdef USE_MARK_BYTES + /* There is a race here, and we may set */ + /* the bit twice in the concurrent case. This can result in the */ + /* object being pushed twice. But that's only a performance issue. */ +# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \ + { \ + char * mark_byte_addr = (char *)hhdr -> hb_marks + (bit_no); \ + if (*mark_byte_addr) goto exit_label; \ + *mark_byte_addr = 1; \ + } +#else # ifdef PARALLEL_MARK + /* This is used only if we explicitly set USE_MARK_BITS. */ /* The following may fail to exit even if the bit was already set. */ /* For our uses, that's benign: */ # define OR_WORD_EXIT_IF_SET(addr, bits, exit_label) \ @@ -189,44 +194,16 @@ exit_label: ; \ # define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \ { \ word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(bit_no); \ - \ OR_WORD_EXIT_IF_SET(mark_word_addr, (word)1 << modWORDSZ(bit_no), \ exit_label); \ } -#endif - -#if defined(I386) && defined(__GNUC__) -# define LONG_MULT(hprod, lprod, x, y) { \ - asm("mull %2" : "=a"(lprod), "=d"(hprod) : "g"(y), "0"(x)); \ - } -#else /* No in-line X86 assembly code */ -# define LONG_MULT(hprod, lprod, x, y) { \ - unsigned long long prod = (unsigned long long)x \ - * (unsigned long long)y; \ - hprod = prod >> 32; \ - lprod = (unsigned32)prod; \ - } -#endif - -#ifdef USE_MARK_BYTES - /* There is a race here, and we may set */ - /* the bit twice in the concurrent case. This can result in the */ - /* object being pushed twice. But that's only a performance issue. */ -# define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no,exit_label) \ - { \ - char * mark_byte_addr = (char *)hhdr -> hb_marks + (bit_no); \ - char mark_byte = *mark_byte_addr; \ - \ - if (mark_byte) goto exit_label; \ - *mark_byte_addr = 1; \ - } -#endif /* USE_MARK_BYTES */ +#endif /* !USE_MARK_BYTES */ #ifdef PARALLEL_MARK # define INCR_MARKS(hhdr) \ - AO_store(&(hhdr -> hb_n_marks), AO_load(&(hhdr -> hb_n_marks))+1); + AO_store(&hhdr->hb_n_marks, AO_load(&hhdr->hb_n_marks) + 1) #else -# define INCR_MARKS(hhdr) ++(hhdr -> hb_n_marks) +# define INCR_MARKS(hhdr) (void)(++hhdr->hb_n_marks) #endif #ifdef ENABLE_TRACE @@ -239,6 +216,20 @@ exit_label: ; \ # define TRACE_TARGET(source, cmd) #endif +#if defined(I386) && defined(__GNUC__) +# define LONG_MULT(hprod, lprod, x, y) { \ + __asm__ __volatile__("mull %2" : "=a"(lprod), "=d"(hprod) \ + : "g"(y), "0"(x)); \ + } +#else +# define LONG_MULT(hprod, lprod, x, y) { \ + unsigned long long prod = (unsigned long long)(x) \ + * (unsigned long long)(y); \ + hprod = prod >> 32; \ + lprod = (unsigned32)prod; \ + } +#endif /* !I386 */ + /* If the mark bit corresponding to current is not set, set it, and */ /* push the contents of the object on the mark stack. Current points */ /* to the beginning of the object. We rely on the fact that the */ @@ -257,16 +248,16 @@ exit_label: ; \ /* first block, then we are in the all_interior_pointers case, and */ \ /* it is safe to use any displacement value. */ \ size_t gran_displ = BYTES_TO_GRANULES(displ); \ - size_t gran_offset = hhdr -> hb_map[gran_displ]; \ + size_t gran_offset = hhdr -> hb_map[gran_displ]; \ size_t byte_offset = displ & (GRANULE_BYTES - 1); \ - ptr_t base = current; \ + ptr_t base = current; \ /* The following always fails for large block references. */ \ if (EXPECT((gran_offset | byte_offset) != 0, FALSE)) { \ if (hhdr -> hb_large_block) { \ /* gran_offset is bogus. */ \ size_t obj_displ; \ base = (ptr_t)(hhdr -> hb_block); \ - obj_displ = (ptr_t)(current) - base; \ + obj_displ = (ptr_t)(current) - base; \ if (obj_displ != displ) { \ GC_ASSERT(obj_displ < hhdr -> hb_sz); \ /* Must be in all_interior_pointer case, not first block */ \ @@ -315,7 +306,7 @@ exit_label: ; \ size_t displ = HBLKDISPL(current); /* Displacement in block; in bytes. */\ unsigned32 low_prod, high_prod; \ unsigned32 inv_sz = hhdr -> hb_inv_sz; \ - ptr_t base = current; \ + ptr_t base = current; \ LONG_MULT(high_prod, low_prod, displ, inv_sz); \ /* product is > and within sz_in_bytes of displ * sz_in_bytes * 2**32 */ \ if (EXPECT(low_prod >> 16 != 0, FALSE)) { \ @@ -323,7 +314,7 @@ exit_label: ; \ if (inv_sz == LARGE_INV_SZ) { \ size_t obj_displ; \ base = (ptr_t)(hhdr -> hb_block); \ - obj_displ = (ptr_t)(current) - base; \ + obj_displ = (ptr_t)(current) - base; \ if (obj_displ != displ) { \ GC_ASSERT(obj_displ < hhdr -> hb_sz); \ /* Must be in all_interior_pointer case, not first block */ \ @@ -358,8 +349,7 @@ exit_label: ; \ (unsigned)GC_gc_no)); \ TRACE_TARGET(base, \ GC_log_printf("GC:%u: marking %p from %p instead\n", \ - (unsigned)GC_gc_no, \ - base, source)); \ + (unsigned)GC_gc_no, base, source)); \ INCR_MARKS(hhdr); \ GC_STORE_BACK_PTR((ptr_t)source, base); \ PUSH_OBJ(base, hhdr, mark_stack_top, mark_stack_limit); \ @@ -385,20 +375,20 @@ exit_label: ; \ #if NEED_FIXUP_POINTER /* Try both the raw version and the fixed up one. */ # define GC_PUSH_ONE_STACK(p, source) \ - if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ - && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ - PUSH_ONE_CHECKED_STACK(p, source); \ + if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ + && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ } \ FIXUP_POINTER(p); \ - if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ - && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ - PUSH_ONE_CHECKED_STACK(p, source); \ + if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ + && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ } #else /* !NEED_FIXUP_POINTER */ # define GC_PUSH_ONE_STACK(p, source) \ - if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ - && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ - PUSH_ONE_CHECKED_STACK(p, source); \ + if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ + && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED_STACK(p, source); \ } #endif @@ -409,9 +399,9 @@ exit_label: ; \ */ #define GC_PUSH_ONE_HEAP(p,source) \ FIXUP_POINTER(p); \ - if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ - && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ - GC_mark_stack_top = GC_mark_and_push( \ + if ((ptr_t)(p) >= (ptr_t)GC_least_plausible_heap_addr \ + && (ptr_t)(p) < (ptr_t)GC_greatest_plausible_heap_addr) { \ + GC_mark_stack_top = GC_mark_and_push( \ (void *)(p), GC_mark_stack_top, \ GC_mark_stack_limit, (void * *)(source)); \ } diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 816bb0f0..d3d14ca3 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -29,7 +29,7 @@ #if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \ && !defined(_GNU_SOURCE) /* Can't test LINUX, since this must be defined before other includes. */ -# define _GNU_SOURCE +# define _GNU_SOURCE 1 #endif #if (defined(DGUX) && defined(GC_THREADS) || defined(DGUX386_THREADS) \ @@ -43,7 +43,6 @@ # endif #ifndef GC_H -# define GC_I_HIDE_POINTERS /* to get GC_HIDE_POINTER() and friends */ # include "../gc.h" #endif @@ -172,6 +171,8 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # include "gc_locks.h" #endif +#define ONES ((word)(signed_word)(-1)) + # ifdef STACK_GROWS_DOWN # define COOLER_THAN > # define HOTTER_THAN < @@ -310,51 +311,54 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ /*********************************/ #ifdef BSD_TIME -# undef CLOCK_TYPE -# undef GET_TIME -# undef MS_TIME_DIFF -# define CLOCK_TYPE struct timeval -# define GET_TIME(x) { struct rusage rusage; \ - getrusage (RUSAGE_SELF, &rusage); \ - x = rusage.ru_utime; } -# define MS_TIME_DIFF(a,b) \ - ((unsigned long)((double) (a.tv_sec - b.tv_sec) * 1000.0 \ - + (double) (a.tv_usec - b.tv_usec) / 1000.0)) -#else /* !BSD_TIME */ -# if defined(MSWIN32) || defined(MSWINCE) -# include <windows.h> -# include <winbase.h> -# define CLOCK_TYPE DWORD -# define GET_TIME(x) x = GetTickCount() -# define MS_TIME_DIFF(a,b) ((long)((a)-(b))) -# else /* !MSWIN32, !MSWINCE, !BSD_TIME */ -# include <time.h> -# if !defined(__STDC__) && defined(SPARC) && defined(SUNOS4) - clock_t clock(void); /* Not in time.h, where it belongs */ -# endif -# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) -# include <machine/limits.h> -# define CLOCKS_PER_SEC CLK_TCK -# endif -# if !defined(CLOCKS_PER_SEC) -# define CLOCKS_PER_SEC 1000000 -/* - * This is technically a bug in the implementation. ANSI requires that - * CLOCKS_PER_SEC be defined. But at least under SunOS4.1.1, it isn't. - * Also note that the combination of ANSI C and POSIX is incredibly gross - * here. The type clock_t is used by both clock() and times(). But on - * some machines these use different notions of a clock tick, CLOCKS_PER_SEC - * seems to apply only to clock. Hence we use it here. On many machines, - * including SunOS, clock actually uses units of microseconds (which are - * not really clock ticks). - */ -# endif -# define CLOCK_TYPE clock_t -# define GET_TIME(x) x = clock() -# define MS_TIME_DIFF(a,b) ((unsigned long) \ - (1000.0*(double)((a)-(b))/(double)CLOCKS_PER_SEC)) -# endif /* !MSWIN32 */ -#endif /* !BSD_TIME */ +# undef CLOCK_TYPE +# undef GET_TIME +# undef MS_TIME_DIFF +# define CLOCK_TYPE struct timeval +# define GET_TIME(x) { struct rusage rusage; \ + getrusage (RUSAGE_SELF, &rusage); \ + x = rusage.ru_utime; } +# define MS_TIME_DIFF(a,b) ((unsigned long)(a.tv_sec - b.tv_sec) * 1000 \ + + (unsigned long)(a.tv_usec - b.tv_usec) / 1000) +#elif defined(MSWIN32) || defined(MSWINCE) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include <windows.h> +# include <winbase.h> +# define CLOCK_TYPE DWORD +# define GET_TIME(x) x = GetTickCount() +# define MS_TIME_DIFF(a,b) ((long)((a)-(b))) +#else /* !MSWIN32, !MSWINCE, !BSD_TIME */ +# include <time.h> +# if !defined(__STDC__) && defined(SPARC) && defined(SUNOS4) + clock_t clock(void); /* Not in time.h, where it belongs */ +# endif +# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) +# include <machine/limits.h> +# define CLOCKS_PER_SEC CLK_TCK +# endif +# if !defined(CLOCKS_PER_SEC) +# define CLOCKS_PER_SEC 1000000 + /* This is technically a bug in the implementation. */ + /* ANSI requires that CLOCKS_PER_SEC be defined. But at least */ + /* under SunOS 4.1.1, it isn't. Also note that the combination of */ + /* ANSI C and POSIX is incredibly gross here. The type clock_t */ + /* is used by both clock() and times(). But on some machines */ + /* these use different notions of a clock tick, CLOCKS_PER_SEC */ + /* seems to apply only to clock. Hence we use it here. On many */ + /* machines, including SunOS, clock actually uses units of */ + /* microseconds (which are not really clock ticks). */ +# endif +# define CLOCK_TYPE clock_t +# define GET_TIME(x) x = clock() +# define MS_TIME_DIFF(a,b) (CLOCKS_PER_SEC % 1000 == 0 ? \ + (unsigned long)((a) - (b)) / (unsigned long)(CLOCKS_PER_SEC / 1000) \ + : ((unsigned long)((a) - (b)) * 1000) / (unsigned long)CLOCKS_PER_SEC) + /* Avoid using double type since some targets (like ARM) might */ + /* require -lm option for double-to-long conversion. */ +#endif /* !BSD_TIME && !MSWIN32 */ /* We use bzero and bcopy internally. They may not be available. */ # if defined(SPARC) && defined(SUNOS4) @@ -399,7 +403,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # define START_WORLD() \ PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \ PCR_allSigsBlocked, \ - PCR_waitForever); + PCR_waitForever) # else # if defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) GC_INNER void GC_stop_world(void); @@ -427,7 +431,7 @@ typedef char * ptr_t; /* A generic pointer to which we can add */ # define DebugBreak() _exit(-1) /* there is no abort() in WinCE */ # endif # ifdef SMALL_CONFIG -# if defined(MSWIN32) || defined(MSWINCE) +# if (defined(MSWIN32) && !defined(LINT2)) || defined(MSWINCE) # define ABORT(msg) DebugBreak() # else # define ABORT(msg) abort() @@ -579,7 +583,6 @@ GC_EXTERN GC_warn_proc GC_current_warn_proc; #define WORDSZ ((word)CPP_WORDSZ) #define SIGNB ((word)1 << (WORDSZ-1)) #define BYTES_PER_WORD ((word)(sizeof (word))) -#define ONES ((word)(signed_word)(-1)) #define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ #if GRANULE_BYTES == 8 @@ -768,17 +771,6 @@ typedef word page_hash_table[PHT_SIZE]; /* initial group of mark bits, and it is safe */ /* to allocate smaller header for large objects. */ -# ifdef USE_MARK_BYTES -# define MARK_BITS_SZ (MARK_BITS_PER_HBLK + 1) - /* Unlike the other case, this is in units of bytes. */ - /* Since we force doubleword alignment, we need at most one */ - /* mark bit per 2 words. But we do allocate and set one */ - /* extra mark bit to avoid an explicit check for the */ - /* partial object at the end of each block. */ -# else -# define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ + 1) -# endif - #ifdef PARALLEL_MARK # include "atomic_ops.h" typedef AO_t counter_t; @@ -787,7 +779,7 @@ typedef word page_hash_table[PHT_SIZE]; # if defined(THREADS) && defined(MPROTECT_VDB) # include "atomic_ops.h" # endif -#endif +#endif /* !PARALLEL_MARK */ /* We maintain layout maps for heap blocks containing objects of a given */ /* size. Each entry in this map describes a byte offset and has the */ @@ -860,6 +852,12 @@ struct hblkhdr { /* Without parallel marking, the count */ /* is accurate. */ # ifdef USE_MARK_BYTES +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK + 1) + /* Unlike the other case, this is in units of bytes. */ + /* Since we force double-word alignment, we need at most one */ + /* mark bit per 2 words. But we do allocate and set one */ + /* extra mark bit to avoid an explicit check for the */ + /* partial object at the end of each block. */ union { char _hb_marks[MARK_BITS_SZ]; /* The i'th byte is 1 if the object */ @@ -872,6 +870,7 @@ struct hblkhdr { } _mark_byte_union; # define hb_marks _mark_byte_union._hb_marks # else +# define MARK_BITS_SZ (MARK_BITS_PER_HBLK/CPP_WORDSZ + 1) word hb_marks[MARK_BITS_SZ]; # endif /* !USE_MARK_BYTES */ }; @@ -953,7 +952,7 @@ struct roots { # else # define MAX_HEAP_SECTS 768 /* Separately added heap sections. */ # endif -# elif defined(SMALL_CONFIG) +# elif defined(SMALL_CONFIG) && !defined(USE_PROC_FOR_LIBRARIES) # define MAX_HEAP_SECTS 128 /* Roughly 256MB (128*2048*1K) */ # elif CPP_WORDSZ > 32 # define MAX_HEAP_SECTS 1024 /* Roughly 8GB */ @@ -1268,8 +1267,10 @@ GC_EXTERN word GC_page_size; #if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) struct _SYSTEM_INFO; GC_EXTERN struct _SYSTEM_INFO GC_sysinfo; + GC_INNER GC_bool GC_is_heap_base(ptr_t p); #endif + GC_EXTERN word GC_black_list_spacing; /* Average number of bytes between blacklisted */ /* blocks. Approximate. */ @@ -1335,15 +1336,6 @@ struct GC_traced_stack_sect_s { /* with it. Only those corresponding to the beginning of an */ /* object are used. */ -/* Set mark bit correctly, even if mark bits may be concurrently */ -/* accessed. */ -#ifdef PARALLEL_MARK -# define OR_WORD(addr, bits) \ - { AO_or((volatile AO_t *)(addr), (AO_t)bits); } -#else -# define OR_WORD(addr, bits) *(addr) |= (bits) -#endif - /* Mark bit operations */ /* @@ -1355,16 +1347,23 @@ struct GC_traced_stack_sect_s { #ifdef USE_MARK_BYTES # define mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n]) -# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n]) = 1 -# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n]) = 0 -#else /* !USE_MARK_BYTES */ -# define mark_bit_from_hdr(hhdr,n) (((hhdr)->hb_marks[divWORDSZ(n)] \ - >> (modWORDSZ(n))) & (word)1) +# define set_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 1) +# define clear_mark_bit_from_hdr(hhdr,n) ((hhdr)->hb_marks[n] = 0) +#else +/* Set mark bit correctly, even if mark bits may be concurrently */ +/* accessed. */ +# ifdef PARALLEL_MARK + /* This is used only if we explicitly set USE_MARK_BITS. */ +# define OR_WORD(addr, bits) AO_or((volatile AO_t *)(addr), (AO_t)(bits)) +# else +# define OR_WORD(addr, bits) (void)(*(addr) |= (bits)) +# endif +# define mark_bit_from_hdr(hhdr,n) \ + (((hhdr)->hb_marks[divWORDSZ(n)] >> modWORDSZ(n)) & (word)1) # define set_mark_bit_from_hdr(hhdr,n) \ - OR_WORD((hhdr)->hb_marks+divWORDSZ(n), \ - (word)1 << modWORDSZ(n)) -# define clear_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ - &= ~((word)1 << modWORDSZ(n)) + OR_WORD((hhdr)->hb_marks+divWORDSZ(n), (word)1 << modWORDSZ(n)) +# define clear_mark_bit_from_hdr(hhdr,n) \ + ((hhdr)->hb_marks[divWORDSZ(n)] &= ~((word)1 << modWORDSZ(n))) #endif /* !USE_MARK_BYTES */ #ifdef MARK_BIT_PER_OBJ @@ -1381,8 +1380,8 @@ struct GC_traced_stack_sect_s { # define MARK_BIT_OFFSET(sz) BYTES_TO_GRANULES(sz) # define IF_PER_OBJ(x) # define FINAL_MARK_BIT(sz) \ - ((sz) > MAXOBJBYTES? MARK_BITS_PER_HBLK \ - : BYTES_TO_GRANULES((sz) * HBLK_OBJS(sz))) + ((sz) > MAXOBJBYTES ? MARK_BITS_PER_HBLK \ + : BYTES_TO_GRANULES((sz) * HBLK_OBJS(sz))) #endif /* Important internal collector routines */ @@ -1628,7 +1627,7 @@ GC_INNER void GC_freehblk(struct hblk * p); /* Misc GC: */ GC_INNER GC_bool GC_expand_hp_inner(word n); -GC_INNER void GC_start_reclaim(int abort_if_found); +GC_INNER void GC_start_reclaim(GC_bool abort_if_found); /* Restore unmarked objects to free */ /* lists, or (if abort_if_found is */ /* TRUE) report them. */ @@ -1709,6 +1708,12 @@ GC_INNER ptr_t GC_allocobj(size_t sz, int kind); GC_INNER void * GC_clear_stack(void *); /* in misc.c, behaves like identity. */ +#ifdef GC_ADD_CALLER +# define GC_DBG_RA GC_RETURN_ADDR, +#else +# define GC_DBG_RA /* empty */ +#endif + /* We make the GC_clear_stack() call a tail one, hoping to get more of */ /* the stack. */ #define GENERAL_MALLOC(lb,k) \ @@ -1786,9 +1791,18 @@ GC_EXTERN void (*GC_print_heap_obj)(ptr_t p); /* Print an address map of the process. */ #endif +#ifndef SHORT_DBG_HDRS + GC_EXTERN GC_bool GC_findleak_delay_free; + /* Do not immediately deallocate object on */ + /* free() in the leak-finding mode, just mark */ + /* it as freed (and deallocate it after GC). */ + GC_INNER GC_bool GC_check_leaked(ptr_t base); /* from dbg_mlc.c */ +#endif + GC_EXTERN GC_bool GC_have_errors; /* We saw a smashed or leaked object. */ /* Call error printing routine */ - /* occasionally. */ + /* occasionally. It is ok to read it */ + /* without acquiring the lock. */ #ifndef SMALL_CONFIG /* GC_print_stats should be visible outside the GC in some cases. */ @@ -1804,9 +1818,9 @@ GC_EXTERN GC_bool GC_have_errors; /* We saw a smashed or leaked object. */ #ifndef NO_DEBUGGING GC_EXTERN GC_bool GC_dump_regularly; /* Generate regular debugging dumps. */ -# define COND_DUMP if (GC_dump_regularly) GC_dump(); +# define COND_DUMP if (GC_dump_regularly) GC_dump() #else -# define COND_DUMP +# define COND_DUMP /* empty */ #endif #ifdef KEEP_BACK_PTRS @@ -1915,7 +1929,7 @@ void GC_print_static_roots(void); #endif /* Make arguments appear live to compiler */ -#if defined(__BORLANDC__) || defined(__WATCOMC__) +#if defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__CC_ARM) void GC_noop(void*, ...); #else # ifdef __DMC__ @@ -1951,12 +1965,6 @@ void GC_err_puts(const char *s); /* Write s to stderr, don't buffer, don't add */ /* newlines, don't ... */ -#if defined(LINUX) && !defined(SMALL_CONFIG) - GC_INNER void GC_err_write(const char *buf, size_t len); - /* Write buf to stderr, don't buffer, don't add */ - /* newlines, don't ... */ -#endif - GC_EXTERN unsigned GC_fail_count; /* How many consecutive GC/expansion failures? */ /* Reset by GC_allochblk(); defined in alloc.c. */ @@ -2003,18 +2011,121 @@ GC_EXTERN signed_word GC_bytes_found; #ifdef THREAD_LOCAL_ALLOC GC_EXTERN GC_bool GC_world_stopped; /* defined in alloc.c */ + GC_INNER void GC_mark_thread_local_free_lists(void); #endif #ifdef GC_GCJ_SUPPORT - GC_EXTERN GC_bool GC_gcj_malloc_initialized; /* defined in gcj_mlc.c */ +# ifdef GC_ASSERTIONS + GC_EXTERN GC_bool GC_gcj_malloc_initialized; /* defined in gcj_mlc.c */ +# endif GC_EXTERN ptr_t * GC_gcjobjfreelist; #endif +#if defined(GWW_VDB) && defined(MPROTECT_VDB) + GC_INNER GC_bool GC_gww_dirty_init(void); + /* Defined in os_dep.c. Returns TRUE if GetWriteWatch is available. */ + /* May be called repeatedly. */ +#endif + +#if defined(CHECKSUMS) || defined(PROC_VDB) + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h); + /* Could the page contain valid heap pointers? */ +#endif + +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p); + +GC_INNER void GC_extend_size_map(size_t); /* in misc.c */ + +GC_INNER void GC_setpagesize(void); + +GC_INNER void GC_initialize_offsets(void); /* defined in obj_map.c */ + +GC_INNER void GC_bl_init(void); +GC_INNER void GC_bl_init_no_interiors(void); /* defined in blacklst.c */ + +GC_INNER void GC_start_debugging(void); /* defined in dbg_mlc.c */ + +/* Store debugging info into p. Return displaced pointer. */ +/* Assumes we don't hold allocation lock. */ +GC_INNER ptr_t GC_store_debug_info(ptr_t p, word sz, const char *str, + int linenum); + +#ifdef REDIRECT_MALLOC +# ifdef GC_LINUX_THREADS + GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp); + /* from os_dep.c */ +# endif +#elif defined(MSWIN32) || defined(MSWINCE) + GC_INNER void GC_add_current_malloc_heap(void); +#endif /* !REDIRECT_MALLOC */ + +#ifdef MAKE_BACK_GRAPH + GC_INNER void GC_build_back_graph(void); + GC_INNER void GC_traverse_back_graph(void); +#endif + +#ifdef MSWIN32 + GC_INNER void GC_init_win32(void); +#endif + +#if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) + GC_INNER void * GC_roots_present(ptr_t); + /* The type is a lie, since the real type doesn't make sense here, */ + /* and we only test for NULL. */ +#endif + +#ifdef GC_WIN32_THREADS + GC_INNER void GC_get_next_stack(char *start, char * limit, char **lo, + char **hi); +# ifdef MPROTECT_VDB + GC_INNER void GC_set_write_fault_handler(void); +# endif +#endif /* GC_WIN32_THREADS */ + +#ifdef THREADS + GC_INNER void GC_reset_finalizer_nested(void); + GC_INNER unsigned char *GC_check_finalizer_nested(void); + GC_INNER void GC_do_blocking_inner(ptr_t data, void * context); + GC_INNER void GC_push_all_stacks(void); +# ifdef USE_PROC_FOR_LIBRARIES + GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi); +# endif +# ifdef IA64 + GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound); +# endif +#endif /* THREADS */ + +#ifdef DYNAMIC_LOADING + GC_INNER GC_bool GC_register_main_static_data(void); +# ifdef DARWIN + GC_INNER void GC_init_dyld(void); +# endif +#endif /* DYNAMIC_LOADING */ + +#ifdef SEARCH_FOR_DATA_START + GC_INNER void GC_init_linux_data_start(void); +#endif + +#if defined(NETBSD) && defined(__ELF__) + GC_INNER void GC_init_netbsd_elf(void); +#endif + +#ifdef UNIX_LIKE + GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int)); +#endif + +#ifdef NEED_PROC_MAPS + GC_INNER char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, + char **prot, unsigned int *maj_dev, + char **mapping_name); + GC_INNER char *GC_get_maps(void); /* from os_dep.c */ +#endif + #ifdef GC_ASSERTIONS # define GC_ASSERT(expr) \ if (!(expr)) { \ - GC_err_printf("Assertion failure: %s:%ld\n", \ - __FILE__, (unsigned long)__LINE__); \ + GC_err_printf("Assertion failure: %s:%d\n", \ + __FILE__, __LINE__); \ ABORT("assertion failure"); \ } #else @@ -2096,13 +2207,17 @@ GC_EXTERN signed_word GC_bytes_found; # endif #endif /* GC_PTHREADS && !SIG_SUSPEND */ +#if defined(GC_PTHREADS) && !defined(GC_SEM_INIT_PSHARED) +# define GC_SEM_INIT_PSHARED 0 +#endif + /* Some macros for setjmp that works across signal handlers */ /* were possible, and a couple of routines to facilitate */ /* catching accesses to bad addresses when that's */ /* possible/needed. */ #if defined(UNIX_LIKE) || (defined(NEED_FIND_LIMIT) && defined(CYGWIN32)) # include <setjmp.h> -# if defined(SUNOS5SIGS) && !defined(FREEBSD) +# if defined(SUNOS5SIGS) && !defined(FREEBSD) && !defined(LINUX) # include <sys/siginfo.h> # endif /* Define SETJMP and friends to be the version that restores */ diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index e914391b..81aa4b52 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -54,8 +54,8 @@ # endif /* And one for FreeBSD: */ -# if (defined(__FreeBSD__) || defined(__DragonFly__) || \ - defined(__FreeBSD_kernel__)) && !defined(FREEBSD) +# if (defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__FreeBSD_kernel__)) && !defined(FREEBSD) # define FREEBSD # endif @@ -70,7 +70,7 @@ # define I386 # define mach_type_known # endif -# if defined(__arm__) || defined(__thumb__) +# if defined(__arm) || defined(__arm__) || defined(__thumb__) # define ARM32 # if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \ && !defined(DARWIN) && !defined(_WIN32) && !defined(__CEGCC__) @@ -186,8 +186,8 @@ # define mach_type_known # endif # if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ - && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__) \ - && !defined(__DragonFly__) + && !defined(__OpenBSD__) && !defined(__NetBSD__) \ + && !defined(__FreeBSD__) && !defined(__DragonFly__) # define SPARC # define DRSNX # define mach_type_known @@ -250,7 +250,7 @@ # define IA64 # define mach_type_known # endif -# if defined(LINUX) && defined(__arm__) +# if defined(LINUX) && (defined(__arm) || defined(__arm__)) # define ARM32 # define mach_type_known # endif @@ -260,12 +260,8 @@ # endif # define mach_type_known # endif -# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) || \ - defined(powerpc64) || defined(__powerpc64__)) -# define POWERPC -# define mach_type_known -# endif -# if defined(FREEBSD) && (defined(powerpc) || defined(__powerpc__)) +# if defined(LINUX) && (defined(powerpc) || defined(__powerpc__) \ + || defined(powerpc64) || defined(__powerpc64__)) # define POWERPC # define mach_type_known # endif @@ -277,10 +273,6 @@ # define SPARC # define mach_type_known # endif -# if defined(LINUX) && defined(__arm__) -# define ARM32 -# define mach_type_known -# endif # if defined(LINUX) && defined(__sh__) # define SH # define mach_type_known @@ -293,9 +285,14 @@ # define M32R # define mach_type_known # endif +# if defined(FREEBSD) && (defined(powerpc) || defined(__powerpc__)) +# define POWERPC +# define mach_type_known +# endif # if defined(__alpha) || defined(__alpha__) # define ALPHA -# if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) && !defined(FREEBSD) +# if !defined(LINUX) && !defined(NETBSD) && !defined(OPENBSD) \ + && !defined(FREEBSD) # define OSF1 /* a.k.a Digital Unix */ # endif # define mach_type_known @@ -338,6 +335,11 @@ # define DARWIN_DONT_PARSE_STACK # endif # endif +# if defined(__rtems__) && (defined(i386) || defined(__i386__)) +# define I386 +# define RTEMS +# define mach_type_known +# endif # if defined(NeXT) && defined(mc68000) # define M68K # define NEXT @@ -669,11 +671,10 @@ * allocation. */ -/* If we are using a recent version of gcc, we can use __builtin_unwind_init() - * to push the relevant registers onto the stack. - */ -# if defined(__GNUC__) && ((__GNUC__ >= 3) || \ - (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) \ +/* If we are using a recent version of gcc, we can use */ +/* __builtin_unwind_init() to push the relevant registers onto the stack. */ +# if defined(__GNUC__) && ((__GNUC__ >= 3) \ + || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)) \ && !defined(__INTEL_COMPILER) && !defined(__PATHCC__) # define HAVE_BUILTIN_UNWIND_INIT # endif @@ -750,7 +751,7 @@ # include <LowMem.h> # endif # define OS_TYPE "MACOS" - /* see os_dep.c for details of global data segments. */ + /* see os_dep.c for details of global data segments. */ # define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) # define DATAEND /* not needed */ # define GETPAGESIZE() 4096 @@ -810,8 +811,8 @@ # define ALIGNMENT 4 # define STACKBOTTOM ((ptr_t) 0xc0000000) # endif - /* XXX: see get_end(3), get_etext() and get_end() should not be used. - These aren't used when dyld support is enabled (it is by default) */ + /* XXX: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default). */ # define DATASTART ((ptr_t) get_etext()) # define DATAEND ((ptr_t) get_end()) # ifndef USE_MMAP @@ -828,8 +829,8 @@ # define PREFETCH_FOR_WRITE(x) \ __asm__ __volatile__ ("dcbtst 0,%0" : : "r" ((const void *) (x))) # endif - /* There seems to be some issues with trylock hanging on darwin. This - should be looked into some more */ + /* There seems to be some issues with trylock hanging on darwin. */ + /* This should be looked into some more. */ # define NO_PTHREAD_TRYLOCK # endif # ifdef OPENBSD @@ -877,7 +878,6 @@ # define ALIGNMENT 4 # define OS_TYPE "NETBSD" # define HEURISTIC2 - extern char etext[]; extern ptr_t GC_data_start; # define DATASTART GC_data_start # define DYNAMIC_LOADING @@ -1128,6 +1128,9 @@ # ifdef SOLARIS25_PROC_VDB_BUG_FIXED # define PROC_VDB # endif +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif # define DYNAMIC_LOADING # if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) # define USE_MMAP @@ -1247,26 +1250,26 @@ # define DATASTART ((ptr_t)((((word) (etext)) + 0xfff) & ~0xfff)) # endif # ifdef USE_I686_PREFETCH - /* FIXME: Thus should use __builtin_prefetch, but we'll leave that */ - /* for the next rtelease. */ + /* FIXME: Thus should use __builtin_prefetch, but we'll leave that */ + /* for the next rtelease. */ # define PREFETCH(x) \ - __asm__ __volatile__ (" prefetchnta %0": : "m"(*(char *)(x))) - /* Empirically prefetcht0 is much more effective at reducing */ - /* cache miss stalls for the targeted load instructions. But it */ - /* seems to interfere enough with other cache traffic that the net */ - /* result is worse than prefetchnta. */ -# if 0 - /* Using prefetches for write seems to have a slight negative */ - /* impact on performance, at least for a PIII/500. */ + __asm__ __volatile__ ("prefetchnta %0" : : "m"(*(char *)(x))) + /* Empirically prefetcht0 is much more effective at reducing */ + /* cache miss stalls for the targeted load instructions. But it */ + /* seems to interfere enough with other cache traffic that the */ + /* net result is worse than prefetchnta. */ +# ifdef FORCE_WRITE_PREFETCH + /* Using prefetches for write seems to have a slight negative */ + /* impact on performance, at least for a PIII/500. */ # define PREFETCH_FOR_WRITE(x) \ - __asm__ __volatile__ (" prefetcht0 %0": : "m"(*(char *)(x))) + __asm__ __volatile__ ("prefetcht0 %0" : : "m"(*(char *)(x))) # endif # endif # ifdef USE_3DNOW_PREFETCH # define PREFETCH(x) \ - __asm__ __volatile__ (" prefetch %0": : "m"(*(char *)(x))) + __asm__ __volatile__ ("prefetch %0" : : "m"(*(char *)(x))) # define PREFETCH_FOR_WRITE(x) \ - __asm__ __volatile__ (" prefetchw %0": : "m"(*(char *)(x))) + __asm__ __volatile__ ("prefetchw %0" : : "m"(*(char *)(x))) # endif # endif # ifdef CYGWIN32 @@ -1306,8 +1309,7 @@ extern int _stklen; extern int __djgpp_stack_limit; # define DATASTART ((ptr_t)((((word) (etext)) + 0x1ff) & ~0x1ff)) -/* # define STACKBOTTOM ((ptr_t)((word) _stubinfo + _stubinfo->size \ - + _stklen)) */ +/* #define STACKBOTTOM ((ptr_t)((word)_stubinfo+_stubinfo->size+_stklen)) */ # define STACKBOTTOM ((ptr_t)((word) __djgpp_stack_limit + _stklen)) /* This may not be right. */ # endif @@ -1373,14 +1375,24 @@ # define STACKBOTTOM ((ptr_t)0xc0000000) # define DATAEND /* not needed */ # endif +# ifdef RTEMS +# define OS_TYPE "RTEMS" +# include <sys/unistd.h> + extern int etext[]; + extern int end[]; + extern void *InitStackBottom; +# define DATASTART ((ptr_t)etext) +# define DATAEND ((ptr_t)end) +# define STACKBOTTOM ((ptr_t)InitStackBottom) +# endif # ifdef DOS4GW # define OS_TYPE "DOS4GW" extern long __nullarea; extern char _end; extern char *_STACKTOP; - /* Depending on calling conventions Watcom C either precedes - or does not precedes with underscore names of C-variables. - Make sure startup code variables always have the same names. */ + /* Depending on calling conventions Watcom C either precedes */ + /* or does not precedes with underscore names of C-variables. */ + /* Make sure startup code variables always have the same names. */ #pragma aux __nullarea "*"; #pragma aux _end "*"; # define STACKBOTTOM ((ptr_t) _STACKTOP) @@ -1404,8 +1416,8 @@ # define OS_TYPE "DARWIN" # define DARWIN_DONT_PARSE_STACK # define DYNAMIC_LOADING - /* XXX: see get_end(3), get_etext() and get_end() should not be used. - These aren't used when dyld support is enabled (it is by default) */ + /* XXX: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default). */ # define DATASTART ((ptr_t) get_etext()) # define DATAEND ((ptr_t) get_end()) # define STACKBOTTOM ((ptr_t) 0xc0000000) @@ -1416,8 +1428,8 @@ # define MPROTECT_VDB # include <unistd.h> # define GETPAGESIZE() getpagesize() - /* There seems to be some issues with trylock hanging on darwin. This - should be looked into some more */ + /* There seems to be some issues with trylock hanging on darwin. */ + /* This should be looked into some more. */ # define NO_PTHREAD_TRYLOCK # endif /* DARWIN */ # endif @@ -1516,7 +1528,6 @@ # define ALIGNMENT 4 # define HEURISTIC2 # ifdef __ELF__ - extern int etext[]; extern ptr_t GC_data_start; # define DATASTART GC_data_start # define NEED_FIND_LIMIT @@ -1563,22 +1574,15 @@ # define ALIGNMENT 4 # endif # if !defined(GC_HPUX_THREADS) && !defined(GC_LINUX_THREADS) \ - && !defined(OPENBSD) -# ifndef LINUX /* For now. */ -# define MPROTECT_VDB -# endif -# else -# ifdef PARALLEL_MARK -# define USE_MARK_BYTES - /* Minimize compare-and-swap usage. */ -# endif + && !defined(OPENBSD) && !defined(LINUX) /* For now. */ +# define MPROTECT_VDB # endif # define STACK_GROWS_UP # ifdef HPUX # define OS_TYPE "HPUX" extern int __data_start[]; # define DATASTART ((ptr_t)(__data_start)) -# if 0 +# ifdef USE_HPUX_FIXED_STACKBOTTOM /* The following appears to work for 7xx systems running HP/UX */ /* 9.xx Furthermore, it might result in much faster */ /* collections than HEURISTIC2, which may involve scanning */ @@ -1729,7 +1733,7 @@ # define ALIGNMENT 4 # else # ifndef _LP64 - ---> unknown ABI +# error --> unknown ABI # endif # define CPP_WORDSZ 64 /* Requires 16 byte alignment for malloc */ @@ -2124,8 +2128,8 @@ # define OS_TYPE "DARWIN" # define DARWIN_DONT_PARSE_STACK # define DYNAMIC_LOADING - /* XXX: see get_end(3), get_etext() and get_end() should not be used. - These aren't used when dyld support is enabled (it is by default) */ + /* XXX: see get_end(3), get_etext() and get_end() should not be used. */ + /* These aren't used when dyld support is enabled (it is by default) */ # define DATASTART ((ptr_t) get_etext()) # define DATAEND ((ptr_t) get_end()) # define STACKBOTTOM ((ptr_t) 0x7fff5fc00000) @@ -2136,8 +2140,8 @@ # define MPROTECT_VDB # include <unistd.h> # define GETPAGESIZE() getpagesize() - /* There seems to be some issues with trylock hanging on darwin. This - should be looked into some more */ + /* There seems to be some issues with trylock hanging on darwin. */ + /* This should be looked into some more. */ # define NO_PTHREAD_TRYLOCK # endif # ifdef FREEBSD @@ -2165,12 +2169,14 @@ # endif # ifdef NETBSD # define OS_TYPE "NETBSD" +# define HEURISTIC2 # ifdef __ELF__ + extern ptr_t GC_data_start; +# define DATASTART GC_data_start # define DYNAMIC_LOADING +# else +# define SEARCH_FOR_DATA_START # endif -# define HEURISTIC2 - extern char etext[]; -# define SEARCH_FOR_DATA_START # endif # ifdef SOLARIS # define OS_TYPE "SOLARIS" @@ -2200,6 +2206,9 @@ # ifdef SOLARIS25_PROC_VDB_BUG_FIXED # define PROC_VDB # endif +# ifndef GC_THREADS +# define MPROTECT_VDB +# endif # define DYNAMIC_LOADING # if !defined(USE_MMAP) && defined(REDIRECT_MALLOC) # define USE_MMAP @@ -2244,7 +2253,8 @@ # define USE_MMAP_ANON #endif -#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(USE_PROC_FOR_LIBRARIES) /* Nptl allocates thread stacks with mmap, which is fine. But it */ /* keeps a cache of thread stacks. Thread stacks contain the */ /* thread control blocks. These in turn contain a pointer to */ @@ -2270,354 +2280,387 @@ # define USE_PROC_FOR_LIBRARIES #endif -# ifndef STACK_GROWS_UP -# define STACK_GROWS_DOWN -# endif +#ifndef STACK_GROWS_UP +# define STACK_GROWS_DOWN +#endif -# ifndef CPP_WORDSZ -# define CPP_WORDSZ 32 -# endif +#ifndef CPP_WORDSZ +# define CPP_WORDSZ 32 +#endif -# ifndef OS_TYPE -# define OS_TYPE "" -# endif +#ifndef OS_TYPE +# define OS_TYPE "" +#endif -# ifndef DATAEND - extern int end[]; -# define DATAEND (ptr_t)(end) -# endif +#ifndef DATAEND + extern int end[]; +# define DATAEND (ptr_t)(end) +#endif -# if defined(SVR4) && !defined(GETPAGESIZE) -# include <unistd.h> -# define GETPAGESIZE() sysconf(_SC_PAGESIZE) -# endif +#if (defined(SVR4) || defined(PLATFORM_ANDROID)) && !defined(GETPAGESIZE) +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) +#endif -# ifndef GETPAGESIZE -# if defined(SOLARIS) || defined(IRIX5) || defined(LINUX) \ - || defined(NETBSD) || defined(FREEBSD) || defined(HPUX) -# include <unistd.h> -# endif -# define GETPAGESIZE() getpagesize() +#ifndef GETPAGESIZE +# if defined(SOLARIS) || defined(IRIX5) || defined(LINUX) \ + || defined(NETBSD) || defined(FREEBSD) || defined(HPUX) +# include <unistd.h> # endif +# define GETPAGESIZE() getpagesize() +#endif -# if defined(SOLARIS) || defined(DRSNX) || defined(UTS4) - /* OS has SVR4 generic features. */ - /* Probably others also qualify. */ -# define SVR4 -# endif +#if defined(SOLARIS) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. */ + /* Probably others also qualify. */ +# define SVR4 +#endif -# if defined(SOLARIS) || defined(DRSNX) - /* OS has SOLARIS style semi-undocumented interface */ - /* to dynamic loader. */ -# define SOLARISDL - /* OS has SOLARIS style signal handlers. */ -# define SUNOS5SIGS -# endif +#if defined(SOLARIS) || defined(DRSNX) + /* OS has SOLARIS style semi-undocumented interface */ + /* to dynamic loader. */ +# define SOLARISDL + /* OS has SOLARIS style signal handlers. */ +# define SUNOS5SIGS +#endif -# if defined(HPUX) -# define SUNOS5SIGS -# endif +#if defined(HPUX) +# define SUNOS5SIGS +#endif -# if defined(FREEBSD) && \ - (defined(__DragonFly__) || __FreeBSD__ >= 4 || (__FreeBSD_kernel__ >= 4)) -# define SUNOS5SIGS -# endif +#if defined(FREEBSD) && (defined(__DragonFly__) || __FreeBSD__ >= 4 \ + || (__FreeBSD_kernel__ >= 4)) +# define SUNOS5SIGS +#endif -# ifdef GC_NETBSD_THREADS -# define SIGRTMIN 33 -# define SIGRTMAX 63 -# endif +#if !defined(GC_EXPLICIT_SIGNALS_UNBLOCK) && defined(SUNOS5SIGS) \ + && !defined(GC_NO_PTHREAD_SIGMASK) +# define GC_EXPLICIT_SIGNALS_UNBLOCK +#endif -# if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \ - || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ - || defined(DGUX) || defined(BSD) || defined(HURD) \ - || defined(AIX) || defined(DARWIN) || defined(OSF1) -# define UNIX_LIKE /* Basic Unix-like system calls work. */ -# endif +#ifdef GC_NETBSD_THREADS +# define SIGRTMIN 33 +# define SIGRTMAX 63 +#endif -# if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 - -> bad word size -# endif +#if defined(SVR4) || defined(LINUX) || defined(IRIX5) || defined(HPUX) \ + || defined(OPENBSD) || defined(NETBSD) || defined(FREEBSD) \ + || defined(DGUX) || defined(BSD) || defined(HURD) \ + || defined(AIX) || defined(DARWIN) || defined(OSF1) +# define UNIX_LIKE /* Basic Unix-like system calls work. */ +#endif -# ifndef ALIGNMENT - --> undefined ALIGNMENT -# endif +#if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 +# error --> bad word size +#endif -# ifdef PCR -# undef DYNAMIC_LOADING -# undef STACKBOTTOM -# undef HEURISTIC1 -# undef HEURISTIC2 -# undef PROC_VDB -# undef MPROTECT_VDB -# define PCR_VDB -# endif +#ifndef ALIGNMENT +# error --> undefined ALIGNMENT +#endif -# if !defined(STACKBOTTOM) && (defined(ECOS) || defined(NOSYS)) -# error --> undefined STACKBOTTOM -# endif +#ifdef PCR +# undef DYNAMIC_LOADING +# undef STACKBOTTOM +# undef HEURISTIC1 +# undef HEURISTIC2 +# undef PROC_VDB +# undef MPROTECT_VDB +# define PCR_VDB +#endif -# ifdef IGNORE_DYNAMIC_LOADING -# undef DYNAMIC_LOADING -# endif +#if !defined(STACKBOTTOM) && (defined(ECOS) || defined(NOSYS)) +# error --> undefined STACKBOTTOM +#endif -# if defined(SMALL_CONFIG) && !defined(GC_DISABLE_INCREMENTAL) - /* Presumably not worth the space it takes. */ -# define GC_DISABLE_INCREMENTAL -# endif +#ifdef IGNORE_DYNAMIC_LOADING +# undef DYNAMIC_LOADING +#endif -# ifdef GC_DISABLE_INCREMENTAL -# undef GWW_VDB -# undef MPROTECT_VDB -# undef PCR_VDB -# undef PROC_VDB -# undef CHECKSUMS -# endif +#if defined(SMALL_CONFIG) && !defined(GC_DISABLE_INCREMENTAL) + /* Presumably not worth the space it takes. */ +# define GC_DISABLE_INCREMENTAL +#endif -# ifdef USE_GLOBAL_ALLOC - /* Cannot pass MEM_WRITE_WATCH to GlobalAlloc(). */ -# undef GWW_VDB -# endif +#if defined(GC_DISABLE_INCREMENTAL) || defined(MANUAL_VDB) +# undef GWW_VDB +# undef MPROTECT_VDB +# undef PCR_VDB +# undef PROC_VDB +#endif -# ifdef USE_MUNMAP - /* FIXME: Remove this undef if possible. */ -# undef MPROTECT_VDB /* Can't deal with address space holes. */ -# endif +#ifdef GC_DISABLE_INCREMENTAL +# undef CHECKSUMS +#endif + +#ifdef USE_GLOBAL_ALLOC + /* Cannot pass MEM_WRITE_WATCH to GlobalAlloc(). */ +# undef GWW_VDB +#endif + +#ifdef USE_MUNMAP + /* FIXME: Remove this undef if possible. */ +# undef MPROTECT_VDB /* Can't deal with address space holes. */ +#endif /* PARALLEL_MARK does not cause undef MPROTECT_VDB any longer. */ -# if defined(MPROTECT_VDB) && defined(GC_PREFER_MPROTECT_VDB) - /* Choose MPROTECT_VDB manually (if multiple strategies available). */ -# undef PCR_VDB -# undef PROC_VDB - /* #undef GWW_VDB - handled in os_dep.c */ -# endif +#if defined(MPROTECT_VDB) && defined(GC_PREFER_MPROTECT_VDB) + /* Choose MPROTECT_VDB manually (if multiple strategies available). */ +# undef PCR_VDB +# undef PROC_VDB + /* #undef GWW_VDB - handled in os_dep.c */ +#endif -# if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) \ - && !defined(GWW_VDB) && !defined(GC_DISABLE_INCREMENTAL) -# define DEFAULT_VDB -# endif +#ifdef PROC_VDB + /* Multi-VDB mode is not implemented. */ +# undef MPROTECT_VDB +#endif -# ifndef PREFETCH -# define PREFETCH(x) -# define NO_PREFETCH -# endif +#if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) \ + && !defined(GWW_VDB) && !defined(MANUAL_VDB) \ + && !defined(GC_DISABLE_INCREMENTAL) +# define DEFAULT_VDB +#endif -# ifndef PREFETCH_FOR_WRITE -# define PREFETCH_FOR_WRITE(x) -# define NO_PREFETCH_FOR_WRITE -# endif +#if ((defined(UNIX_LIKE) && (defined(DARWIN) || defined(HURD) \ + || defined(OPENBSD) || defined(ARM32) \ + || defined(MIPS) || defined(AVR32))) \ + || (defined(LINUX) && (defined(SPARC) || defined(M68K))) \ + || (defined(RTEMS) && defined(I386))) && !defined(NO_GETCONTEXT) +# define NO_GETCONTEXT +#endif -# ifndef CACHE_LINE_SIZE -# define CACHE_LINE_SIZE 32 /* Wild guess */ -# endif +#ifndef PREFETCH +# define PREFETCH(x) +# define NO_PREFETCH +#endif -# ifndef STATIC -# ifndef NO_DEBUGGING -# define STATIC /* ignore to aid profiling and possibly debugging */ -# else -# define STATIC static -# endif -# endif +#ifndef PREFETCH_FOR_WRITE +# define PREFETCH_FOR_WRITE(x) +# define NO_PREFETCH_FOR_WRITE +#endif -# if defined(LINUX) || defined(HURD) || defined(__GLIBC__) -# define REGISTER_LIBRARIES_EARLY - /* We sometimes use dl_iterate_phdr, which may acquire an internal */ - /* lock. This isn't safe after the world has stopped. So we must */ - /* call GC_register_dynamic_libraries before stopping the world. */ - /* For performance reasons, this may be beneficial on other */ - /* platforms as well, though it should be avoided in win32. */ -# endif /* LINUX */ +#ifndef CACHE_LINE_SIZE +# define CACHE_LINE_SIZE 32 /* Wild guess */ +#endif -# if defined(SEARCH_FOR_DATA_START) - extern ptr_t GC_data_start; -# define DATASTART GC_data_start +#ifndef STATIC +# ifndef NO_DEBUGGING +# define STATIC /* ignore to aid profiling and possibly debugging */ +# else +# define STATIC static # endif +#endif -# ifndef CLEAR_DOUBLE -# define CLEAR_DOUBLE(x) \ - ((word*)x)[0] = 0; \ - ((word*)x)[1] = 0; -# endif /* CLEAR_DOUBLE */ +#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) \ + || !defined(SMALL_CONFIG)) +# define NEED_PROC_MAPS +#endif -# if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ - && !defined(INCLUDE_LINUX_THREAD_DESCR) - /* Will not work, since libc and the dynamic loader use thread */ - /* locals, sometimes as the only reference. */ -# define INCLUDE_LINUX_THREAD_DESCR -# endif +#if defined(LINUX) || defined(HURD) || defined(__GLIBC__) +# define REGISTER_LIBRARIES_EARLY + /* We sometimes use dl_iterate_phdr, which may acquire an internal */ + /* lock. This isn't safe after the world has stopped. So we must */ + /* call GC_register_dynamic_libraries before stopping the world. */ + /* For performance reasons, this may be beneficial on other */ + /* platforms as well, though it should be avoided in win32. */ +#endif /* LINUX */ + +#if defined(SEARCH_FOR_DATA_START) + extern ptr_t GC_data_start; +# define DATASTART GC_data_start +#endif -# if defined(GC_IRIX_THREADS) && !defined(IRIX5) - --> inconsistent configuration -# endif -# if defined(GC_LINUX_THREADS) && !defined(LINUX) && !defined(NACL) - --> inconsistent configuration -# endif -# if defined(GC_NETBSD_THREADS) && !defined(NETBSD) - --> inconsistent configuration -# endif -# if defined(GC_FREEBSD_THREADS) && !defined(FREEBSD) - --> inconsistent configuration -# endif -# if defined(GC_SOLARIS_THREADS) && !defined(SOLARIS) - --> inconsistent configuration -# endif -# if defined(GC_HPUX_THREADS) && !defined(HPUX) - --> inconsistent configuration -# endif -# if defined(GC_AIX_THREADS) && !defined(_AIX) - --> inconsistent configuration -# endif -# if defined(GC_GNU_THREADS) && !defined(HURD) - --> inconsistent configuration -# endif -# if defined(GC_WIN32_THREADS) && !defined(MSWIN32) && !defined(CYGWIN32) \ - && !defined(MSWINCE) - --> inconsistent configuration -# endif +#ifndef CLEAR_DOUBLE +# define CLEAR_DOUBLE(x) (((word*)(x))[0] = 0, ((word*)(x))[1] = 0) +#endif -# if defined(PCR) || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) \ - || defined(SN_TARGET_PS3) -# define THREADS -# endif +#if defined(GC_LINUX_THREADS) && defined(REDIRECT_MALLOC) \ + && !defined(INCLUDE_LINUX_THREAD_DESCR) + /* Will not work, since libc and the dynamic loader use thread */ + /* locals, sometimes as the only reference. */ +# define INCLUDE_LINUX_THREAD_DESCR +#endif -# if defined(UNIX_LIKE) && defined(THREADS) && !defined(NO_CANCEL_SAFE) \ - && !defined(PLATFORM_ANDROID) - /* Make the code cancellation-safe. This basically means that we */ - /* ensure that cancellation requests are ignored while we are in */ - /* the collector. This applies only to Posix deferred cancellation;*/ - /* we don't handle Posix asynchronous cancellation. */ - /* Note that this only works if pthread_setcancelstate is */ - /* async-signal-safe, at least in the absence of asynchronous */ - /* cancellation. This appears to be true for the glibc version, */ - /* though it is not documented. Without that assumption, there */ - /* seems to be no way to safely wait in a signal handler, which */ - /* we need to do for thread suspension. */ - /* Also note that little other code appears to be cancellation-safe.*/ - /* Hence it may make sense to turn this off for performance. */ -# define CANCEL_SAFE -# endif +#if defined(GC_IRIX_THREADS) && !defined(IRIX5) +# error --> inconsistent configuration +#endif +#if defined(GC_LINUX_THREADS) && !defined(LINUX) && !defined(NACL) +# error --> inconsistent configuration +#endif +#if defined(GC_NETBSD_THREADS) && !defined(NETBSD) +# error --> inconsistent configuration +#endif +#if defined(GC_FREEBSD_THREADS) && !defined(FREEBSD) +# error --> inconsistent configuration +#endif +#if defined(GC_SOLARIS_THREADS) && !defined(SOLARIS) +# error --> inconsistent configuration +#endif +#if defined(GC_HPUX_THREADS) && !defined(HPUX) +# error --> inconsistent configuration +#endif +#if defined(GC_AIX_THREADS) && !defined(_AIX) +# error --> inconsistent configuration +#endif +#if defined(GC_GNU_THREADS) && !defined(HURD) +# error --> inconsistent configuration +#endif +#if defined(GC_WIN32_THREADS) && !defined(MSWIN32) && !defined(CYGWIN32) \ + && !defined(MSWINCE) +# error --> inconsistent configuration +#endif -# ifdef CANCEL_SAFE -# define IF_CANCEL(x) x -# else -# define IF_CANCEL(x) -# endif +#if defined(PCR) || defined(GC_WIN32_THREADS) || defined(GC_PTHREADS) \ + || defined(SN_TARGET_PS3) +# define THREADS +#endif -# if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) -# if defined(THREADS) && defined(PARALLEL_MARK) -# define USE_MARK_BYTES -# else -# define USE_MARK_BITS -# endif -# endif +#if defined(UNIX_LIKE) && defined(THREADS) && !defined(NO_CANCEL_SAFE) \ + && !defined(PLATFORM_ANDROID) + /* Make the code cancellation-safe. This basically means that we */ + /* ensure that cancellation requests are ignored while we are in */ + /* the collector. This applies only to Posix deferred cancellation; */ + /* we don't handle Posix asynchronous cancellation. */ + /* Note that this only works if pthread_setcancelstate is */ + /* async-signal-safe, at least in the absence of asynchronous */ + /* cancellation. This appears to be true for the glibc version, */ + /* though it is not documented. Without that assumption, there */ + /* seems to be no way to safely wait in a signal handler, which */ + /* we need to do for thread suspension. */ + /* Also note that little other code appears to be cancellation-safe. */ + /* Hence it may make sense to turn this off for performance. */ +# define CANCEL_SAFE +#endif -# if defined(MSWINCE) && !defined(__CEGCC__) && !defined(NO_GETENV) -# define NO_GETENV -# endif +#ifdef CANCEL_SAFE +# define IF_CANCEL(x) x +#else +# define IF_CANCEL(x) /* empty */ +#endif -# if (defined(NO_GETENV) || defined(MSWINCE)) && !defined(NO_GETENV_WIN32) -# define NO_GETENV_WIN32 -# endif +#if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \ + && defined(PARALLEL_MARK) + /* Minimize compare-and-swap usage. */ +# define USE_MARK_BYTES +#endif -# ifndef STRTOULL -# if defined(_WIN64) && !defined(__GNUC__) -# define STRTOULL _strtoui64 -# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) -# define STRTOULL strtoull -# else - /* strtoul() fits since sizeof(long) >= sizeof(word). */ -# define STRTOULL strtoul -# endif +#if defined(MSWINCE) && !defined(__CEGCC__) && !defined(NO_GETENV) +# define NO_GETENV +#endif + +#if (defined(NO_GETENV) || defined(MSWINCE)) && !defined(NO_GETENV_WIN32) +# define NO_GETENV_WIN32 +#endif + +#ifndef STRTOULL +# if defined(_WIN64) && !defined(__GNUC__) +# define STRTOULL _strtoui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define STRTOULL strtoull +# else + /* strtoul() fits since sizeof(long) >= sizeof(word). */ +# define STRTOULL strtoul # endif +#endif /* !STRTOULL */ -# if defined(SPARC) -# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ - /* include assembly code to do it well. */ +#ifndef GC_WORD_C +# if defined(_WIN64) && !defined(__GNUC__) +# define GC_WORD_C(val) val##ui64 +# elif defined(_LLP64) || defined(__LLP64__) || defined(_WIN64) +# define GC_WORD_C(val) val##ULL +# else +# define GC_WORD_C(val) ((word)val##UL) # endif +#endif /* !GC_WORD_C */ - /* Can we save call chain in objects for debugging? */ - /* SET NFRAMES (# of saved frames) and NARGS (#of args for each */ - /* frame) to reasonable values for the platform. */ - /* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified */ - /* at build time, though we feel free to adjust it slightly. */ - /* Define NEED_CALLINFO if we either save the call stack or */ - /* GC_ADD_CALLER is defined. */ - /* GC_CAN_SAVE_CALL_STACKS is set in gc.h. */ +#if defined(SPARC) +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ +#endif +/* Can we save call chain in objects for debugging? */ +/* SET NFRAMES (# of saved frames) and NARGS (#of args for each */ +/* frame) to reasonable values for the platform. */ +/* Set SAVE_CALL_CHAIN if we can. SAVE_CALL_COUNT can be specified */ +/* at build time, though we feel free to adjust it slightly. */ +/* Define NEED_CALLINFO if we either save the call stack or */ +/* GC_ADD_CALLER is defined. */ +/* GC_CAN_SAVE_CALL_STACKS is set in gc.h. */ #if defined(SPARC) # define CAN_SAVE_CALL_ARGS #endif -#if (defined(I386) || defined(X86_64)) && (defined(LINUX) || defined(__GLIBC__)) - /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ - /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ +#if (defined(I386) || defined(X86_64)) \ + && (defined(LINUX) || defined(__GLIBC__)) + /* SAVE_CALL_CHAIN is supported if the code is compiled to save */ + /* frame pointers by default, i.e. no -fomit-frame-pointer flag. */ # define CAN_SAVE_CALL_ARGS #endif -# if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \ - && defined(GC_CAN_SAVE_CALL_STACKS) -# define SAVE_CALL_CHAIN -# endif -# ifdef SAVE_CALL_CHAIN -# if defined(SAVE_CALL_NARGS) && defined(CAN_SAVE_CALL_ARGS) -# define NARGS SAVE_CALL_NARGS -# else -# define NARGS 0 /* Number of arguments to save for each call. */ -# endif -# endif -# ifdef SAVE_CALL_CHAIN -# ifndef SAVE_CALL_COUNT -# define NFRAMES 6 /* Number of frames to save. Even for */ - /* alignment reasons. */ -# else -# define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) -# endif -# define NEED_CALLINFO -# endif /* SAVE_CALL_CHAIN */ -# ifdef GC_ADD_CALLER -# define NFRAMES 1 -# define NARGS 0 -# define NEED_CALLINFO +#if defined(SAVE_CALL_COUNT) && !defined(GC_ADD_CALLER) \ + && defined(GC_CAN_SAVE_CALL_STACKS) +# define SAVE_CALL_CHAIN +#endif +#ifdef SAVE_CALL_CHAIN +# if defined(SAVE_CALL_NARGS) && defined(CAN_SAVE_CALL_ARGS) +# define NARGS SAVE_CALL_NARGS +# else +# define NARGS 0 /* Number of arguments to save for each call. */ # endif +#endif +#ifdef SAVE_CALL_CHAIN +# ifndef SAVE_CALL_COUNT +# define NFRAMES 6 /* Number of frames to save. Even for */ + /* alignment reasons. */ +# else +# define NFRAMES ((SAVE_CALL_COUNT + 1) & ~1) +# endif +# define NEED_CALLINFO +#endif /* SAVE_CALL_CHAIN */ +#ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +#endif -# if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL) -# define DBG_HDRS_ALL -# endif +#if defined(MAKE_BACK_GRAPH) && !defined(DBG_HDRS_ALL) +# define DBG_HDRS_ALL +#endif -# if defined(POINTER_MASK) && !defined(POINTER_SHIFT) -# define POINTER_SHIFT 0 -# endif +#if defined(POINTER_MASK) && !defined(POINTER_SHIFT) +# define POINTER_SHIFT 0 +#endif -# if defined(POINTER_SHIFT) && !defined(POINTER_MASK) -# define POINTER_MASK ((GC_word)(-1)) -# endif +#if defined(POINTER_SHIFT) && !defined(POINTER_MASK) +# define POINTER_MASK ((GC_word)(-1)) +#endif -# if !defined(FIXUP_POINTER) && defined(POINTER_MASK) -# define FIXUP_POINTER(p) (p = ((p) & POINTER_MASK) << POINTER_SHIFT) -# endif +#if !defined(FIXUP_POINTER) && defined(POINTER_MASK) +# define FIXUP_POINTER(p) (p = ((p) & POINTER_MASK) << POINTER_SHIFT) +#endif -# if defined(FIXUP_POINTER) -# define NEED_FIXUP_POINTER 1 -# else -# define NEED_FIXUP_POINTER 0 -# define FIXUP_POINTER(p) -# endif +#if defined(FIXUP_POINTER) +# define NEED_FIXUP_POINTER 1 +#else +# define NEED_FIXUP_POINTER 0 +# define FIXUP_POINTER(p) +#endif -# if !defined(MARK_BIT_PER_GRANULE) && !defined(MARK_BIT_PER_OBJ) -# define MARK_BIT_PER_GRANULE /* Usually faster */ -# endif +#if !defined(MARK_BIT_PER_GRANULE) && !defined(MARK_BIT_PER_OBJ) +# define MARK_BIT_PER_GRANULE /* Usually faster */ +#endif /* Some static sanity tests. */ -# if defined(MARK_BIT_PER_GRANULE) && defined(MARK_BIT_PER_OBJ) -# error Define only one of MARK_BIT_PER_GRANULE and MARK_BIT_PER_OBJ. -# endif +#if defined(MARK_BIT_PER_GRANULE) && defined(MARK_BIT_PER_OBJ) +# error Define only one of MARK_BIT_PER_GRANULE and MARK_BIT_PER_OBJ. +#endif -# if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN) -# error "Only one of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd." -# endif -# if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN) -# error "One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd." -# endif +#if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN) +# error "Only one of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd." +#endif +#if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN) +# error "One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd." +#endif # if defined(REDIRECT_MALLOC) && defined(THREADS) && !defined(LINUX) // FIXME: no idea if this is really true! @@ -2643,18 +2686,19 @@ struct hblk; /* See gc_priv.h. */ # if defined(PCR) char * real_malloc(size_t bytes); -# define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ +# define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)(bytes) + GC_page_size) \ + GC_page_size-1) # elif defined(OS2) void * os2_alloc(size_t bytes); -# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)bytes \ - + GC_page_size) \ - + GC_page_size-1) -# elif defined(NEXT) || defined(DOS4GW) || defined(NONSTOP) || \ - (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) || \ - (defined(SOLARIS) && !defined(USE_MMAP)) -# define GET_MEM(bytes) HBLKPTR((size_t) calloc(1, (size_t)bytes + GC_page_size) \ - + GC_page_size-1) +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)(bytes) \ + + GC_page_size) + GC_page_size-1) +# elif defined(NEXT) || defined(DOS4GW) || defined(NONSTOP) \ + || (defined(AMIGA) && !defined(GC_AMIGA_FASTALLOC)) \ + || (defined(SOLARIS) && !defined(USE_MMAP)) || defined(RTEMS) \ + || defined(__CC_ARM) +# define GET_MEM(bytes) HBLKPTR((size_t)calloc(1, \ + (size_t)(bytes) + GC_page_size) \ + + GC_page_size - 1) # elif defined(MSWIN32) || defined(CYGWIN32) ptr_t GC_win32_get_mem(GC_word bytes); # define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) @@ -2662,10 +2706,10 @@ # if defined(USE_TEMPORARY_MEMORY) Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory); # define GET_MEM(bytes) HBLKPTR( \ - GC_MacTemporaryNewPtr(bytes + GC_page_size, true) \ - + GC_page_size-1) + GC_MacTemporaryNewPtr((bytes) + GC_page_size, true) \ + + GC_page_size-1) # else -# define GET_MEM(bytes) HBLKPTR(NewPtrClear(bytes + GC_page_size) \ +# define GET_MEM(bytes) HBLKPTR(NewPtrClear((bytes) + GC_page_size) \ + GC_page_size-1) # endif # elif defined(MSWINCE) @@ -2674,7 +2718,7 @@ # elif defined(AMIGA) && defined(GC_AMIGA_FASTALLOC) void *GC_amiga_get_mem(size_t size); # define GET_MEM(bytes) HBLKPTR((size_t) \ - GC_amiga_get_mem((size_t)bytes + GC_page_size) \ + GC_amiga_get_mem((size_t)(bytes) + GC_page_size) \ + GC_page_size-1) # elif defined(SN_TARGET_PS3) void *ps3_get_mem(size_t size); diff --git a/include/private/pthread_stop_world.h b/include/private/pthread_stop_world.h index c883c3c8..cb67d230 100644 --- a/include/private/pthread_stop_world.h +++ b/include/private/pthread_stop_world.h @@ -39,4 +39,6 @@ struct thread_stop_info { # endif }; +GC_INNER void GC_stop_init(void); + #endif diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h index 73fa8fee..ba0f6936 100644 --- a/include/private/pthread_support.h +++ b/include/private/pthread_support.h @@ -30,7 +30,7 @@ #ifdef THREAD_LOCAL_ALLOC # include "thread_local_alloc.h" -#endif /* THREAD_LOCAL_ALLOC */ +#endif /* We use the allocation lock to protect thread-related data structures. */ @@ -53,7 +53,7 @@ typedef struct GC_Thread_Rep { struct thread_stop_info stop_info; unsigned char flags; -# define FINISHED 1 /* Thread has exited. */ +# define FINISHED 1 /* Thread has exited. */ # define DETACHED 2 /* Thread is treated as detached. */ /* Thread may really be detached, or */ /* it may have have been explicitly */ @@ -62,7 +62,10 @@ typedef struct GC_Thread_Rep { /* it unregisters itself, since it */ /* may not return a GC pointer. */ # define MAIN_THREAD 4 /* True for the original thread only. */ -# define DISABLED_GC 8 /* Collections are disabled while the */ +# define SUSPENDED_EXT 8 /* Thread was suspended externally */ + /* (this is not used by the unmodified */ + /* GC itself at present). */ +# define DISABLED_GC 0x10 /* Collections are disabled while the */ /* thread is exiting. */ unsigned char thread_blocked; @@ -123,9 +126,20 @@ GC_EXTERN GC_bool GC_in_thread_creation; /* Only set to TRUE while allocation lock is held. */ /* When set, it is OK to run GC from unknown thread. */ -# ifdef NACL - GC_EXTERN __thread GC_thread GC_nacl_gc_thread_self; -# endif +#ifdef NACL + GC_EXTERN __thread GC_thread GC_nacl_gc_thread_self; + GC_INNER void GC_nacl_initialize_gc_thread(void); + GC_INNER void GC_nacl_shutdown_gc_thread(void); +#endif + +#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + GC_INNER void GC_unblock_gc_signals(void); +#endif + +GC_INNER GC_thread GC_start_rtn_prepare_thread(void *(**pstart)(void *), + void **pstart_arg, + struct GC_stack_base *sb, void *arg); +GC_INNER void GC_thread_exit_proc(void *); #endif /* GC_PTHREADS && !GC_WIN32_THREADS */ diff --git a/include/private/specific.h b/include/private/specific.h index 372238dc..8b5cf847 100644 --- a/include/private/specific.h +++ b/include/private/specific.h @@ -51,8 +51,8 @@ typedef struct thread_specific_entry { /* Return the "quick thread id". Default version. Assumes page size, */ /* or at least thread stack separation, is at least 4K. */ -/* Must be defined so that it never returns 0. (Page 0 can't really */ -/* be part of any stack, since that would make 0 a valid stack pointer.)*/ +/* Must be defined so that it never returns 0. (Page 0 can't really be */ +/* part of any stack, since that would make 0 a valid stack pointer.) */ #define quick_thread_id() (((unsigned long)GC_approx_sp()) >> 12) #define INVALID_QTID ((unsigned long)0) @@ -67,11 +67,9 @@ typedef struct thread_specific_data { typedef tsd * PREFIXED(key_t); -extern int PREFIXED(key_create) (tsd ** key_ptr, void (* destructor)(void *)); - -extern int PREFIXED(setspecific) (tsd * key, void * value); - -extern void PREFIXED(remove_specific) (tsd * key); +int PREFIXED(key_create) (tsd ** key_ptr, void (* destructor)(void *)); +int PREFIXED(setspecific) (tsd * key, void * value); +void PREFIXED(remove_specific) (tsd * key); /* An internal version of getspecific that assumes a cache miss. */ void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid, @@ -80,7 +78,7 @@ void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid, /* GC_INLINE is defined in gc_priv.h. */ GC_INLINE void * PREFIXED(getspecific) (tsd * key) { - long qtid = quick_thread_id(); + unsigned long qtid = quick_thread_id(); unsigned hash_val = CACHE_HASH(qtid); tse * volatile * entry_ptr = key -> cache + hash_val; tse * entry = *entry_ptr; /* Must be loaded only once. */ diff --git a/include/private/thread_local_alloc.h b/include/private/thread_local_alloc.h index ab14fdbf..55d8fd6e 100644 --- a/include/private/thread_local_alloc.h +++ b/include/private/thread_local_alloc.h @@ -24,107 +24,107 @@ #include "private/gc_priv.h" -#if defined(THREAD_LOCAL_ALLOC) +#ifdef THREAD_LOCAL_ALLOC #include "gc_inline.h" -# if defined(USE_HPUX_TLS) -# error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS -# endif +#if defined(USE_HPUX_TLS) +# error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS +#endif -# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) && \ - !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) && \ - !defined(USE_CUSTOM_SPECIFIC) -# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) -# if defined(__GNUC__) /* Fixed for versions past 2.95? */ \ - || defined(MSWINCE) -# define USE_WIN32_SPECIFIC -# else -# define USE_WIN32_COMPILER_TLS -# endif /* !GNU */ -# elif defined(LINUX) && !defined(ARM32) && !defined(AVR32) && \ - (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >=3)) -# define USE_COMPILER_TLS -# elif (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \ - defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS)) || \ - defined(GC_NETBSD_THREADS) -# define USE_PTHREAD_SPECIFIC -# elif defined(GC_HPUX_THREADS) -# ifdef __GNUC__ -# define USE_PTHREAD_SPECIFIC - /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */ -# else -# define USE_COMPILER_TLS -# endif +#if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC) \ + && !defined(USE_WIN32_COMPILER_TLS) && !defined(USE_COMPILER_TLS) \ + && !defined(USE_CUSTOM_SPECIFIC) +# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) +# if defined(__GNUC__) /* Fixed for versions past 2.95? */ \ + || defined(MSWINCE) +# define USE_WIN32_SPECIFIC # else -# define USE_CUSTOM_SPECIFIC /* Use our own. */ +# define USE_WIN32_COMPILER_TLS +# endif /* !GNU */ +# elif defined(LINUX) && !defined(ARM32) && !defined(AVR32) \ + && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >=3)) +# define USE_COMPILER_TLS +# elif defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) \ + || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) \ + || defined(GC_NETBSD_THREADS) +# define USE_PTHREAD_SPECIFIC +# elif defined(GC_HPUX_THREADS) +# ifdef __GNUC__ +# define USE_PTHREAD_SPECIFIC + /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */ +# else +# define USE_COMPILER_TLS # endif +# else +# define USE_CUSTOM_SPECIFIC /* Use our own. */ # endif +#endif -# include <stdlib.h> +#include <stdlib.h> /* One of these should be declared as the tlfs field in the */ /* structure pointed to by a GC_thread. */ typedef struct thread_local_freelists { -# ifdef THREAD_LOCAL_ALLOC - void * ptrfree_freelists[TINY_FREELISTS]; - void * normal_freelists[TINY_FREELISTS]; -# ifdef GC_GCJ_SUPPORT - void * gcj_freelists[TINY_FREELISTS]; -# define ERROR_FL ((void *)(word)-1) - /* Value used for gcj_freelist[-1]; allocation is */ - /* erroneous. */ -# endif - /* Free lists contain either a pointer or a small count */ - /* reflecting the number of granules allocated at that */ - /* size. */ - /* 0 ==> thread-local allocation in use, free list */ - /* empty. */ - /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */ - /* too few objects of this size have been */ - /* allocated by this thread. */ - /* >= HBLKSIZE => pointer to nonempty free list. */ - /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */ - /* local alloc, equivalent to 0. */ -# define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES) - /* Don't use local free lists for up to this much */ - /* allocation. */ - -# endif + void * ptrfree_freelists[TINY_FREELISTS]; + void * normal_freelists[TINY_FREELISTS]; +# ifdef GC_GCJ_SUPPORT + void * gcj_freelists[TINY_FREELISTS]; +# define ERROR_FL ((void *)(word)-1) + /* Value used for gcj_freelist[-1]; allocation is */ + /* erroneous. */ +# endif + /* Free lists contain either a pointer or a small count */ + /* reflecting the number of granules allocated at that */ + /* size. */ + /* 0 ==> thread-local allocation in use, free list */ + /* empty. */ + /* > 0, <= DIRECT_GRANULES ==> Using global allocation, */ + /* too few objects of this size have been */ + /* allocated by this thread. */ + /* >= HBLKSIZE => pointer to nonempty free list. */ + /* > DIRECT_GRANULES, < HBLKSIZE ==> transition to */ + /* local alloc, equivalent to 0. */ +# define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES) + /* Don't use local free lists for up to this much */ + /* allocation. */ } *GC_tlfs; -# if defined(USE_PTHREAD_SPECIFIC) -# define GC_getspecific pthread_getspecific -# define GC_setspecific pthread_setspecific -# define GC_key_create pthread_key_create -# define GC_remove_specific(key) /* No need for cleanup on exit. */ - typedef pthread_key_t GC_key_t; -# elif defined(USE_COMPILER_TLS) || defined(USE_WIN32_COMPILER_TLS) -# define GC_getspecific(x) (x) -# define GC_setspecific(key, v) ((key) = (v), 0) -# define GC_key_create(key, d) 0 -# define GC_remove_specific(key) /* No need for cleanup on exit. */ - typedef void * GC_key_t; -# elif defined(USE_WIN32_SPECIFIC) -# include <windows.h> -# define GC_getspecific TlsGetValue -# define GC_setspecific(key, v) !TlsSetValue(key, v) +#if defined(USE_PTHREAD_SPECIFIC) +# define GC_getspecific pthread_getspecific +# define GC_setspecific pthread_setspecific +# define GC_key_create pthread_key_create +# define GC_remove_specific(key) /* No need for cleanup on exit. */ + typedef pthread_key_t GC_key_t; +#elif defined(USE_COMPILER_TLS) || defined(USE_WIN32_COMPILER_TLS) +# define GC_getspecific(x) (x) +# define GC_setspecific(key, v) ((key) = (v), 0) +# define GC_key_create(key, d) 0 +# define GC_remove_specific(key) /* No need for cleanup on exit. */ + typedef void * GC_key_t; +#elif defined(USE_WIN32_SPECIFIC) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# define NOSERVICE +# include <windows.h> +# define GC_getspecific TlsGetValue +# define GC_setspecific(key, v) !TlsSetValue(key, v) /* We assume 0 == success, msft does the opposite. */ -# ifndef TLS_OUT_OF_INDEXES - /* this is currently missing in WinCE */ -# define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF -# endif -# define GC_key_create(key, d) \ - ((d) != 0 || (*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? -1 : 0) -# define GC_remove_specific(key) /* No need for cleanup on thread exit. */ - /* Need TlsFree on process exit/detach ? */ - typedef DWORD GC_key_t; -# elif defined(USE_CUSTOM_SPECIFIC) -# include "private/specific.h" -# else -# error implement me +# ifndef TLS_OUT_OF_INDEXES + /* this is currently missing in WinCE */ +# define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF # endif - +# define GC_key_create(key, d) \ + ((d) != 0 || (*(key) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? -1 : 0) +# define GC_remove_specific(key) /* No need for cleanup on exit. */ + /* Need TlsFree on process exit/detach? */ + typedef DWORD GC_key_t; +#elif defined(USE_CUSTOM_SPECIFIC) +# include "private/specific.h" +#else +# error implement me +#endif /* Each thread structure must be initialized. */ /* This call must be made from the new thread. */ @@ -148,12 +148,9 @@ extern __declspec(thread) #endif GC_key_t GC_thread_key; - -/* This is set up by the thread_local_alloc implementation. But the */ -/* thread support layer calls GC_remove_specific(GC_thread_key) */ -/* before a thread exits. */ -/* And the thread support layer makes sure that GC_thread_key is traced,*/ -/* if necessary. */ +/* This is set up by the thread_local_alloc implementation. No need */ +/* for cleanup on thread exit. But the thread support layer makes sure */ +/* that GC_thread_key is traced, if necessary. */ #endif /* THREAD_LOCAL_ALLOC */ @@ -17,7 +17,7 @@ #include <stdio.h> #include <setjmp.h> -#if defined(OS2) || defined(CX_UX) +#if defined(OS2) || defined(CX_UX) || defined(__CC_ARM) # define _setjmp(b) setjmp(b) # define _longjmp(b,v) longjmp(b,v) #endif @@ -171,16 +171,6 @@ asm static void PushMacRegisters() # undef HAVE_PUSH_REGS #endif -#if defined(UNIX_LIKE) && !defined(NO_GETCONTEXT) && \ - (defined(DARWIN) || defined(HURD) || defined(OPENBSD) \ - || defined(ARM32) || defined(MIPS) || defined(AVR32)) -# define NO_GETCONTEXT -#endif - -#if defined(LINUX) && defined(SPARC) && !defined(NO_GETCONTEXT) -# define NO_GETCONTEXT -#endif - #if !defined(HAVE_PUSH_REGS) && defined(UNIX_LIKE) # include <signal.h> # ifndef NO_GETCONTEXT @@ -244,8 +234,9 @@ GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), /* subsumed by the getcontext() call. */ GC_save_regs_ret_val = GC_save_regs_in_stack(); # endif /* register windows. */ -# elif defined(HAVE_BUILTIN_UNWIND_INIT) && \ - !(defined(POWERPC) && defined(DARWIN)) +# elif defined(HAVE_BUILTIN_UNWIND_INIT) \ + && !(defined(POWERPC) && defined(DARWIN)) \ + && !(defined(I386) && defined(RTEMS)) /* This was suggested by Richard Henderson as the way to */ /* force callee-save registers and register windows onto */ /* the stack. */ @@ -267,8 +258,8 @@ GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *), for (; (char *)i < lim; i++) { *i = 0; } -# if defined(MSWIN32) || defined(MSWINCE) \ - || defined(UTS4) || defined(LINUX) || defined(EWS4800) +# if defined(MSWIN32) || defined(MSWINCE) || defined(UTS4) \ + || defined(LINUX) || defined(EWS4800) || defined(RTEMS) (void) setjmp(regs); # else (void) _setjmp(regs); @@ -17,11 +17,6 @@ #include <stdio.h> #include <string.h> -#ifndef MSWINCE -# include <errno.h> -#endif - -GC_INNER void GC_extend_size_map(size_t); /* in misc.c */ /* Allocate reclaim list for kind: */ /* Return TRUE on success */ @@ -232,65 +227,6 @@ GC_API void * GC_CALL GC_generic_malloc(size_t lb, int k) } } -/* provide a version of strdup() that uses the collector to allocate the - copy of the string */ -GC_API char * GC_CALL GC_strdup(const char *s) -{ - char *copy; - size_t lb; - if (s == NULL) return NULL; - lb = strlen(s) + 1; - if ((copy = GC_malloc_atomic(lb)) == NULL) { -# ifndef MSWINCE - errno = ENOMEM; -# endif - return NULL; - } -# ifndef MSWINCE - strcpy(copy, s); -# else - /* strcpy() is deprecated in WinCE */ - memcpy(copy, s, lb); -# endif - return copy; -} - -GC_API char * GC_CALL GC_strndup(const char *str, size_t size) -{ - char *copy; - size_t len = strlen(str); /* str is expected to be non-NULL */ - if (len > size) - len = size; - copy = GC_malloc_atomic(len + 1); - if (copy == NULL) { -# ifndef MSWINCE - errno = ENOMEM; -# endif - return NULL; - } - BCOPY(str, copy, len); - copy[len] = '\0'; - return copy; -} - -#ifdef GC_REQUIRE_WCSDUP -# include <wchar.h> /* for wcslen() */ - - GC_API wchar_t * GC_CALL GC_wcsdup(const wchar_t *str) - { - size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); - wchar_t *copy = GC_malloc_atomic(lb); - if (copy == NULL) { -# ifndef MSWINCE - errno = ENOMEM; -# endif - return NULL; - } - BCOPY(str, copy, lb); - return copy; - } -#endif /* GC_REQUIRE_WCSDUP */ - /* Allocate lb bytes of composite (pointerful) data */ #ifdef THREAD_LOCAL_ALLOC GC_INNER void * GC_core_malloc(size_t lb) @@ -326,19 +262,70 @@ GC_API char * GC_CALL GC_strndup(const char *str, size_t size) } } -# ifdef REDIRECT_MALLOC +/* Allocate lb bytes of pointerful, traced, but not collectable data */ +GC_API void * GC_CALL GC_malloc_uncollectable(size_t lb) +{ + void *op; + void **opp; + size_t lg; + DCL_LOCK_STATE; + + if( SMALL_OBJ(lb) ) { + if (EXTRA_BYTES != 0 && lb != 0) lb--; + /* We don't need the extra byte, since this won't be */ + /* collected anyway. */ + lg = GC_size_map[lb]; + opp = &(GC_uobjfreelist[lg]); + LOCK(); + if( (op = *opp) != 0 ) { + *opp = obj_link(op); + obj_link(op) = 0; + GC_bytes_allocd += GRANULES_TO_BYTES(lg); + /* Mark bit ws already set on free list. It will be */ + /* cleared only temporarily during a collection, as a */ + /* result of the normal free list mark bit clearing. */ + GC_non_gc_bytes += GRANULES_TO_BYTES(lg); + UNLOCK(); + } else { + UNLOCK(); + op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE); + /* For small objects, the free lists are completely marked. */ + } + GC_ASSERT(0 == op || GC_is_marked(op)); + return((void *) op); + } else { + hdr * hhdr; + + op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE); + if (0 == op) return(0); + + GC_ASSERT(((word)op & (HBLKSIZE - 1)) == 0); /* large block */ + hhdr = HDR(op); + /* We don't need the lock here, since we have an undisguised */ + /* pointer. We do need to hold the lock while we adjust */ + /* mark bits. */ + LOCK(); + set_mark_bit_from_hdr(hhdr, 0); /* Only object. */ + GC_ASSERT(hhdr -> hb_n_marks == 0); + hhdr -> hb_n_marks = 1; + UNLOCK(); + return((void *) op); + } +} + +#ifdef REDIRECT_MALLOC + +# ifndef MSWINCE +# include <errno.h> +# endif /* Avoid unnecessary nested procedure calls here, by #defining some */ /* malloc replacements. Otherwise we end up saving a */ /* meaningless return address in the object. It also speeds things up, */ /* but it is admittedly quite ugly. */ -# ifdef GC_ADD_CALLER -# define RA GC_RETURN_ADDR, -# else -# define RA -# endif + # define GC_debug_malloc_replacement(lb) \ - GC_debug_malloc(lb, RA "unknown", 0) + GC_debug_malloc(lb, GC_DBG_RA "unknown", 0) #if 0 void * malloc(size_t lb) @@ -365,12 +352,11 @@ void * malloc(size_t lb) STATIC ptr_t GC_libpthread_end = 0; STATIC ptr_t GC_libld_start = 0; STATIC ptr_t GC_libld_end = 0; - GC_INNER GC_bool GC_text_mapping(char *nm, ptr_t *startp, ptr_t *endp); - /* From os_dep.c */ STATIC void GC_init_lib_bounds(void) { if (GC_libpthread_start != 0) return; + GC_init(); /* if not called yet */ if (!GC_text_mapping("libpthread-", &GC_libpthread_start, &GC_libpthread_end)) { WARN("Failed to find libpthread.so text mapping: Expect crash\n", 0); @@ -383,7 +369,7 @@ void * malloc(size_t lb) WARN("Failed to find ld.so text mapping: Expect crash\n", 0); } } -#endif +#endif /* GC_LINUX_THREADS */ #if 0 void * calloc(size_t n, size_t lb) @@ -400,7 +386,7 @@ void * calloc(size_t n, size_t lb) GC_init_lib_bounds(); lib_bounds_set = TRUE; } - if (caller >= GC_libpthread_start && caller < GC_libpthread_end + if ((caller >= GC_libpthread_start && caller < GC_libpthread_end) || (caller >= GC_libld_start && caller < GC_libld_end)) return GC_malloc_uncollectable(n*lb); /* The two ranges are actually usually adjacent, so there may */ @@ -413,7 +399,6 @@ void * calloc(size_t n, size_t lb) #if 0 #ifndef strdup -# include <string.h> char *strdup(const char *s) { size_t lb = strlen(s) + 1; @@ -433,7 +418,6 @@ void * calloc(size_t n, size_t lb) #ifndef strndup /* This is similar to strdup(). */ -# include <string.h> char *strndup(const char *str, size_t size) { char *copy; @@ -453,7 +437,7 @@ void * calloc(size_t n, size_t lb) #undef GC_debug_malloc_replacement -# endif /* REDIRECT_MALLOC */ +#endif /* REDIRECT_MALLOC */ /* Explicitly deallocate an object p. */ GC_API void GC_CALL GC_free(void * p) @@ -24,6 +24,7 @@ */ #include <stdio.h> +#include <string.h> #ifdef MSWINCE # ifndef WIN32_LEAN_AND_MEAN @@ -152,13 +153,9 @@ GC_API void * GC_CALL GC_realloc(void * p, size_t lb) # ifdef REDIRECT_REALLOC /* As with malloc, avoid two levels of extra calls here. */ -# ifdef GC_ADD_CALLER -# define RA GC_RETURN_ADDR, -# else -# define RA -# endif + # define GC_debug_realloc_replacement(p, lb) \ - GC_debug_realloc(p, lb, RA "unknown", 0) + GC_debug_realloc(p, lb, GC_DBG_RA "unknown", 0) #if 0 void * realloc(void * p, size_t lb) @@ -443,57 +440,6 @@ GC_API void * GC_CALL GC_malloc_many(size_t lb) return result; } -/* Allocate lb bytes of pointerful, traced, but not collectable data */ -GC_API void * GC_CALL GC_malloc_uncollectable(size_t lb) -{ - void *op; - void **opp; - size_t lg; - DCL_LOCK_STATE; - - if( SMALL_OBJ(lb) ) { - if (EXTRA_BYTES != 0 && lb != 0) lb--; - /* We don't need the extra byte, since this won't be */ - /* collected anyway. */ - lg = GC_size_map[lb]; - opp = &(GC_uobjfreelist[lg]); - LOCK(); - if( (op = *opp) != 0 ) { - *opp = obj_link(op); - obj_link(op) = 0; - GC_bytes_allocd += GRANULES_TO_BYTES(lg); - /* Mark bit ws already set on free list. It will be */ - /* cleared only temporarily during a collection, as a */ - /* result of the normal free list mark bit clearing. */ - GC_non_gc_bytes += GRANULES_TO_BYTES(lg); - UNLOCK(); - } else { - UNLOCK(); - op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE); - /* For small objects, the free lists are completely marked. */ - } - GC_ASSERT(0 == op || GC_is_marked(op)); - return((void *) op); - } else { - hdr * hhdr; - - op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE); - if (0 == op) return(0); - - GC_ASSERT(((word)op & (HBLKSIZE - 1)) == 0); /* large block */ - hhdr = HDR(op); - /* We don't need the lock here, since we have an undisguised */ - /* pointer. We do need to hold the lock while we adjust */ - /* mark bits. */ - LOCK(); - set_mark_bit_from_hdr(hhdr, 0); /* Only object. */ - GC_ASSERT(hhdr -> hb_n_marks == 0); - hhdr -> hb_n_marks = 1; - UNLOCK(); - return((void *) op); - } -} - /* Not well tested nor integrated. */ /* Debug version is tricky and currently missing. */ #include <limits.h> @@ -600,3 +546,62 @@ GC_API int GC_CALL GC_posix_memalign(void **memptr, size_t align, size_t lb) } } #endif /* ATOMIC_UNCOLLECTABLE */ + +/* provide a version of strdup() that uses the collector to allocate the + copy of the string */ +GC_API char * GC_CALL GC_strdup(const char *s) +{ + char *copy; + size_t lb; + if (s == NULL) return NULL; + lb = strlen(s) + 1; + if ((copy = GC_malloc_atomic(lb)) == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } +# ifndef MSWINCE + strcpy(copy, s); +# else + /* strcpy() is deprecated in WinCE */ + memcpy(copy, s, lb); +# endif + return copy; +} + +GC_API char * GC_CALL GC_strndup(const char *str, size_t size) +{ + char *copy; + size_t len = strlen(str); /* str is expected to be non-NULL */ + if (len > size) + len = size; + copy = GC_malloc_atomic(len + 1); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, len); + copy[len] = '\0'; + return copy; +} + +#ifdef GC_REQUIRE_WCSDUP +# include <wchar.h> /* for wcslen() */ + + GC_API wchar_t * GC_CALL GC_wcsdup(const wchar_t *str) + { + size_t lb = (wcslen(str) + 1) * sizeof(wchar_t); + wchar_t *copy = GC_malloc_atomic(lb); + if (copy == NULL) { +# ifndef MSWINCE + errno = ENOMEM; +# endif + return NULL; + } + BCOPY(str, copy, lb); + return copy; + } +#endif /* GC_REQUIRE_WCSDUP */ @@ -24,7 +24,7 @@ /* We put this here to minimize the risk of inlining. */ /*VARARGS*/ -#if defined(__BORLANDC__) || defined(__WATCOMC__) +#if defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__CC_ARM) /*ARGSUSED*/ void GC_noop(void *p, ...) {} #else @@ -36,11 +36,10 @@ #endif /* Single argument version, robust against whole program analysis. */ +volatile word GC_noop_sink; GC_API void GC_CALL GC_noop1(word x) { - static volatile word sink; - - sink = x; + GC_noop_sink = x; } /* mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0} -- declared in gc_priv.h */ @@ -144,14 +143,8 @@ GC_INNER GC_bool GC_collection_in_progress(void) GC_INNER void GC_clear_hdr_marks(hdr *hhdr) { size_t last_bit = FINAL_MARK_BIT(hhdr -> hb_sz); - -# ifdef USE_MARK_BYTES - BZERO(hhdr -> hb_marks, MARK_BITS_SZ); - hhdr -> hb_marks[last_bit] = 1; -# else - BZERO(hhdr -> hb_marks, MARK_BITS_SZ*sizeof(word)); - set_mark_bit_from_hdr(hhdr, last_bit); -# endif + BZERO(hhdr -> hb_marks, sizeof(hhdr->hb_marks)); + set_mark_bit_from_hdr(hhdr, last_bit); hhdr -> hb_n_marks = 0; } @@ -272,7 +265,7 @@ GC_INNER void GC_initiate_gc(void) if (GC_mark_state == MS_NONE) { GC_mark_state = MS_PUSH_RESCUERS; } else if (GC_mark_state != MS_INVALID) { - ABORT("unexpected state"); + ABORT("Unexpected state"); } /* else this is really a full collection, and mark */ /* bits are invalid. */ scan_ptr = 0; @@ -576,8 +569,8 @@ static void alloc_mark_stack(size_t); handle_ex: /* Exception handler starts here for all cases. */ if (GC_print_stats) { - GC_log_printf("Caught ACCESS_VIOLATION in marker. " - "Memory mapping disappeared.\n"); + GC_log_printf( + "Caught ACCESS_VIOLATION in marker; memory mapping disappeared\n"); } /* We have bad roots on the stack. Discard mark stack. */ @@ -670,9 +663,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, # ifdef ENABLE_TRACE if (GC_trace_addr >= current_p && GC_trace_addr < current_p + descr) { - GC_log_printf("GC:%u Large section; start %p len %lu\n", - (unsigned)GC_gc_no, current_p, - (unsigned long) descr); + GC_log_printf("GC:%u Large section; start %p len %lu\n", + (unsigned)GC_gc_no, current_p, (unsigned long)descr); } # endif /* ENABLE_TRACE */ # ifdef PARALLEL_MARK @@ -688,9 +680,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, # ifdef ENABLE_TRACE if (GC_trace_addr >= current_p && GC_trace_addr < current_p + descr) { - GC_log_printf("GC:%u splitting (parallel) %p at %p\n", - (unsigned)GC_gc_no, current_p, - current_p + new_size); + GC_log_printf("GC:%u Splitting (parallel) %p at %p\n", + (unsigned)GC_gc_no, current_p, current_p + new_size); } # endif /* ENABLE_TRACE */ current_p += new_size; @@ -705,8 +696,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, # ifdef ENABLE_TRACE if (GC_trace_addr >= current_p && GC_trace_addr < current_p + descr) { - GC_log_printf("GC:%u splitting %p at %p\n", - (unsigned)GC_gc_no, current_p, limit); + GC_log_printf("GC:%u Splitting %p at %p\n", + (unsigned)GC_gc_no, current_p, limit); } # endif /* ENABLE_TRACE */ /* Make sure that pointers overlapping the two ranges are */ @@ -718,9 +709,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, # ifdef ENABLE_TRACE if (GC_trace_addr >= current_p && GC_trace_addr < current_p + WORDS_TO_BYTES(WORDSZ-2)) { - GC_log_printf("GC:%u Tracing from %p bitmap descr %lu\n", - (unsigned)GC_gc_no, current_p, - (unsigned long) descr); + GC_log_printf("GC:%u Tracing from %p bitmap descr %lu\n", + (unsigned)GC_gc_no, current_p, (unsigned long)descr); } # endif /* ENABLE_TRACE */ descr &= ~GC_DS_TAGS; @@ -734,8 +724,7 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, # ifdef ENABLE_TRACE if (GC_trace_addr == current_p) { GC_log_printf("GC:%u Considering(3) %p -> %p\n", - (unsigned)GC_gc_no, current_p, - (ptr_t) current); + (unsigned)GC_gc_no, current_p, (ptr_t)current); } # endif /* ENABLE_TRACE */ PUSH_CONTENTS((ptr_t)current, mark_stack_top, @@ -752,9 +741,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, if (GC_trace_addr >= current_p && GC_base(current_p) != 0 && GC_base(current_p) == GC_base(GC_trace_addr)) { - GC_log_printf("GC:%u Tracing from %p proc descr %lu\n", - (unsigned)GC_gc_no, current_p, - (unsigned long) descr); + GC_log_printf("GC:%u Tracing from %p proc descr %lu\n", + (unsigned)GC_gc_no, current_p, (unsigned long)descr); } # endif /* ENABLE_TRACE */ credit -= GC_PROC_BYTES; @@ -805,8 +793,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, # ifdef ENABLE_TRACE if (GC_trace_addr >= current_p && GC_trace_addr < limit) { - GC_log_printf("GC:%u Tracing from %p len %lu\n", - (int)GC_gc_no, current_p, (unsigned long) descr); + GC_log_printf("GC:%u Tracing from %p len %lu\n", + (int)GC_gc_no, current_p, (unsigned long)descr); } # endif /* ENABLE_TRACE */ /* The simple case in which we're scanning a range. */ @@ -862,8 +850,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, PREFETCH((ptr_t)current); # ifdef ENABLE_TRACE if (GC_trace_addr == current_p) { - GC_log_printf("GC:%u Considering(1) %p -> %p\n", - (unsigned)GC_gc_no, current_p, (ptr_t) current); + GC_log_printf("GC:%u Considering(1) %p -> %p\n", + (unsigned)GC_gc_no, current_p, (ptr_t)current); } # endif /* ENABLE_TRACE */ PUSH_CONTENTS((ptr_t)current, mark_stack_top, @@ -878,8 +866,8 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack, /* validity test. */ # ifdef ENABLE_TRACE if (GC_trace_addr == current_p) { - GC_log_printf("GC:%u Considering(2) %p -> %p\n", - (unsigned)GC_gc_no, current_p, (ptr_t) deferred); + GC_log_printf("GC:%u Considering(2) %p -> %p\n", + (unsigned)GC_gc_no, current_p, (ptr_t)deferred); } # endif /* ENABLE_TRACE */ PUSH_CONTENTS((ptr_t)deferred, mark_stack_top, @@ -1106,8 +1094,8 @@ STATIC void GC_mark_local(mse *local_mark_stack, int id) GC_helper_count--; if (0 == GC_helper_count) need_to_notify = TRUE; if (GC_print_stats == VERBOSE) - GC_log_printf( - "Finished mark helper %lu\n", (unsigned long)id); + GC_log_printf("Finished mark helper %lu\n", + (unsigned long)id); GC_release_mark_lock(); if (need_to_notify) GC_notify_all_marker(); return; @@ -1150,7 +1138,7 @@ STATIC void GC_do_parallel_mark(void) ABORT("Tried to start parallel mark in bad state"); if (GC_print_stats == VERBOSE) GC_log_printf("Starting marking for mark phase number %lu\n", - (unsigned long)GC_mark_no); + (unsigned long)GC_mark_no); GC_first_nonempty = (AO_t)GC_mark_stack; GC_active_count = 0; GC_helper_count = 1; @@ -1165,9 +1153,8 @@ STATIC void GC_do_parallel_mark(void) while (GC_helper_count > 0) GC_wait_marker(); /* GC_helper_count cannot be incremented while GC_help_wanted == FALSE */ if (GC_print_stats == VERBOSE) - GC_log_printf( - "Finished marking for mark phase number %lu\n", - (unsigned long)GC_mark_no); + GC_log_printf("Finished marking for mark phase number %lu\n", + (unsigned long)GC_mark_no); GC_mark_no++; GC_release_mark_lock(); GC_notify_all_marker(); @@ -1210,12 +1197,12 @@ static void alloc_mark_stack(size_t n) # ifdef GWW_VDB /* Don't recycle a stack segment obtained with the wrong flags. */ /* Win32 GetWriteWatch requires the right kind of memory. */ - static GC_bool GC_incremental_at_stack_alloc = 0; + static GC_bool GC_incremental_at_stack_alloc = FALSE; GC_bool recycle_old = (!GC_incremental || GC_incremental_at_stack_alloc); GC_incremental_at_stack_alloc = GC_incremental; # else -# define recycle_old 1 +# define recycle_old TRUE # endif GC_mark_stack_too_small = FALSE; @@ -1280,7 +1267,7 @@ GC_INNER void GC_push_all(ptr_t bottom, ptr_t top) if (top == 0 || bottom == top) return; GC_mark_stack_top++; if (GC_mark_stack_top >= GC_mark_stack_limit) { - ABORT("unexpected mark stack overflow"); + ABORT("Unexpected mark stack overflow"); } length = top - bottom; # if GC_DS_TAGS > ALIGNMENT - 1 @@ -1293,80 +1280,70 @@ GC_INNER void GC_push_all(ptr_t bottom, ptr_t top) #ifndef GC_DISABLE_INCREMENTAL - /* - * Analogous to the above, but push only those pages h with - * dirty_fn(h) != 0. We use push_fn to actually push the block. - * Used both to selectively push dirty pages, or to push a block - * in piecemeal fashion, to allow for more marking concurrency. - * Will not overflow mark stack if push_fn pushes a small fixed number - * of entries. (This is invoked only if push_fn pushes a single entry, - * or if it marks each object before pushing it, thus ensuring progress - * in the event of a stack overflow.) - */ + /* Analogous to the above, but push only those pages h with */ + /* dirty_fn(h) != 0. We use GC_push_all to actually push the block. */ + /* Used both to selectively push dirty pages, or to push a block in */ + /* piecemeal fashion, to allow for more marking concurrency. */ + /* Will not overflow mark stack if GC_push_all pushes a small fixed */ + /* number of entries. (This is invoked only if GC_push_all pushes */ + /* a single entry, or if it marks each object before pushing it, thus */ + /* ensuring progress in the event of a stack overflow.) */ STATIC void GC_push_selected(ptr_t bottom, ptr_t top, - int (*dirty_fn)(struct hblk *), - void (*push_fn)(ptr_t, ptr_t)) + GC_bool (*dirty_fn)(struct hblk *)) { struct hblk * h; bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); top = (ptr_t)(((word) top) & ~(ALIGNMENT-1)); - if (top == 0 || bottom == top) return; + h = HBLKPTR(bottom + HBLKSIZE); if (top <= (ptr_t) h) { if ((*dirty_fn)(h-1)) { - (*push_fn)(bottom, top); + GC_push_all(bottom, top); } return; } if ((*dirty_fn)(h-1)) { - (*push_fn)(bottom, (ptr_t)h); + GC_push_all(bottom, (ptr_t)h); } + while ((ptr_t)(h+1) <= top) { if ((*dirty_fn)(h)) { if ((word)(GC_mark_stack_top - GC_mark_stack) > 3 * GC_mark_stack_size / 4) { /* Danger of mark stack overflow */ - (*push_fn)((ptr_t)h, top); + GC_push_all((ptr_t)h, top); return; } else { - (*push_fn)((ptr_t)h, (ptr_t)(h+1)); + GC_push_all((ptr_t)h, (ptr_t)(h+1)); } } h++; } - if ((ptr_t)h != top) { - if ((*dirty_fn)(h)) { - (*push_fn)((ptr_t)h, top); - } + + if ((ptr_t)h != top && (*dirty_fn)(h)) { + GC_push_all((ptr_t)h, top); } if (GC_mark_stack_top >= GC_mark_stack_limit) { - ABORT("unexpected mark stack overflow"); + ABORT("Unexpected mark stack overflow"); } } -# ifdef PROC_VDB - GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk *h); - /* Could the page contain valid heap pointers? */ -# endif - GC_INNER void GC_push_conditional(ptr_t bottom, ptr_t top, GC_bool all) { - if (all) { - if (GC_dirty_maintained) { -# ifdef PROC_VDB - /* Pages that were never dirtied cannot contain pointers */ - GC_push_selected(bottom, top, GC_page_was_ever_dirty, - GC_push_all); -# else - GC_push_all(bottom, top); -# endif - } else { + if (!all) { + GC_push_selected(bottom, top, GC_page_was_dirty); + } else { +# ifdef PROC_VDB + if (GC_dirty_maintained) { + /* Pages that were never dirtied cannot contain pointers. */ + GC_push_selected(bottom, top, GC_page_was_ever_dirty); + } else +# endif + /* else */ { GC_push_all(bottom, top); } - } else { - GC_push_selected(bottom, top, GC_page_was_dirty, GC_push_all); } } #endif /* !GC_DISABLE_INCREMENTAL */ @@ -1413,6 +1390,10 @@ GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void *obj, return mark_stack_ptr; } +#if defined(MANUAL_VDB) && defined(THREADS) + void GC_dirty(ptr_t p); +#endif + /* Mark and push (i.e. gray) a single object p onto the main */ /* mark stack. Consider p to be valid if it is an interior */ /* pointer. */ @@ -1448,8 +1429,8 @@ GC_API struct GC_ms_entry * GC_CALL GC_mark_and_push(void *obj, } # if defined(MANUAL_VDB) && defined(THREADS) /* Pointer is on the stack. We may have dirtied the object */ - /* it points to, but not yet have called GC_dirty(); */ - GC_dirty(p); /* Implicitly affects entire object. */ + /* it points to, but not yet have called GC_dirty(); */ + GC_dirty(p); /* Implicitly affects entire object. */ # endif PUSH_CONTENTS_HDR(r, GC_mark_stack_top, GC_mark_stack_limit, source, mark_and_push_exit, hhdr, FALSE); @@ -1576,7 +1557,7 @@ GC_INNER void GC_push_all_stack(ptr_t bottom, ptr_t top) qcontents = (q)[3]; \ GC_PUSH_ONE_HEAP(qcontents, (q)+3); } # endif -#endif +#endif /* !USE_MARK_BYTES && MARK_BIT_PER_GRANULE */ #ifdef USE_PUSH_MARKED_ACCELERATORS /* Push all objects reachable from marked objects in the given block */ @@ -1820,7 +1801,7 @@ STATIC struct hblk * GC_push_next_marked(struct hblk *h) { hdr * hhdr = HDR(h); - if (!GC_dirty_maintained) { ABORT("dirty bits not set up"); } + if (!GC_dirty_maintained) ABORT("Dirty bits not set up"); for (;;) { if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) || HBLK_IS_FREE(hhdr), FALSE)) { @@ -111,7 +111,7 @@ static int n_root_sets = 0; /* Is a range starting at b already in the table? If so return a */ /* pointer to it, else NULL. */ - GC_INNER struct roots * GC_roots_present(ptr_t b) + GC_INNER void * GC_roots_present(ptr_t b) { int h = rt_hash(b); struct roots *p = GC_root_index[h]; @@ -120,7 +120,7 @@ static int n_root_sets = 0; if (p -> r_start == (ptr_t)b) return(p); p = p -> r_next; } - return(FALSE); + return NULL; } /* Add the given root structure to the index. */ @@ -216,7 +216,7 @@ void GC_add_roots_inner(ptr_t b, ptr_t e, GC_bool tmp) } } # else - old = GC_roots_present(b); + old = (struct roots *)GC_roots_present(b); if (old != 0) { if (e <= old -> r_end) /* already there */ return; /* else extend */ @@ -251,10 +251,7 @@ GC_API void GC_CALL GC_clear_roots(void) n_root_sets = 0; GC_root_size = 0; # if !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) - { - int i; - for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; - } + BZERO(GC_root_index, RT_SIZE * sizeof(void *)); # endif UNLOCK(); } @@ -273,8 +270,7 @@ STATIC void GC_remove_root_at_pos(int i) STATIC void GC_rebuild_root_index(void) { int i; - - for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; + BZERO(GC_root_index, RT_SIZE * sizeof(void *)); for (i = 0; i < n_root_sets; i++) add_roots_to_index(GC_static_roots + i); } @@ -426,7 +422,7 @@ GC_INNER void GC_exclude_static_roots_inner(void *start, void *finish) if (0 != next) { if ((word)(next -> e_start) < (word) finish) { /* incomplete error check. */ - ABORT("exclusion ranges overlap"); + ABORT("Exclusion ranges overlap"); } if ((word)(next -> e_start) == (word) finish) { /* extend old range backwards */ @@ -694,10 +690,6 @@ STATIC void GC_push_gc_structures(void) GC_push_typed_structures(); } -#ifdef THREAD_LOCAL_ALLOC - GC_INNER void GC_mark_thread_local_free_lists(void); -#endif - GC_INNER void GC_cond_register_dynamic_libraries(void) { # if defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE) \ @@ -59,7 +59,6 @@ #ifdef DYNAMIC_LOADING /* We need to register the main data segment. Returns TRUE unless */ /* this is done implicitly as part of dynamic library registration. */ - GC_INNER GC_bool GC_register_main_static_data(void); # define GC_REGISTER_MAIN_STATIC_DATA() GC_register_main_static_data() #else /* Don't unnecessarily call GC_register_main_static_data() in case */ @@ -76,9 +75,6 @@ GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */; GC_INNER GC_bool GC_debugging_started = FALSE; /* defined here so we don't have to load debug_malloc.o */ -GC_INNER void (*GC_check_heap)(void) = 0; -GC_INNER void (*GC_print_all_smashed)(void) = 0; - ptr_t GC_stackbottom = 0; #ifdef IA64 @@ -95,10 +91,14 @@ GC_bool GC_quiet = 0; /* used also in pcr_interface.c */ GC_bool GC_print_stats = 0; #endif -GC_INNER GC_bool GC_print_back_height = 0; +#ifdef GC_PRINT_BACK_HEIGHT + GC_INNER GC_bool GC_print_back_height = TRUE; +#else + GC_INNER GC_bool GC_print_back_height = FALSE; +#endif #ifndef NO_DEBUGGING - GC_INNER GC_bool GC_dump_regularly = 0; + GC_INNER GC_bool GC_dump_regularly = FALSE; /* Generate regular debugging dumps. */ #endif @@ -113,6 +113,14 @@ GC_INNER GC_bool GC_print_back_height = 0; int GC_find_leak = 0; #endif +#ifndef SHORT_DBG_HDRS +# ifdef GC_FINDLEAK_DELAY_FREE + GC_INNER GC_bool GC_findleak_delay_free = TRUE; +# else + GC_INNER GC_bool GC_findleak_delay_free = FALSE; +# endif +#endif /* !SHORT_DBG_HDRS */ + #ifdef ALL_INTERIOR_POINTERS int GC_all_interior_pointers = 1; #else @@ -577,25 +585,12 @@ GC_INNER GC_bool GC_is_initialized = FALSE; GC_INNER CRITICAL_SECTION GC_write_cs; #endif -#ifdef MSWIN32 - GC_INNER void GC_init_win32(void); -#endif - -GC_INNER void GC_setpagesize(void); - STATIC void GC_exit_check(void) { GC_gcollect(); } -#ifdef SEARCH_FOR_DATA_START - GC_INNER void GC_init_linux_data_start(void); -#endif - #ifdef UNIX_LIKE - - GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int)); - static void looping_handler(int sig) { GC_err_printf("Caught signal %d: looping in handler\n", sig); @@ -618,20 +613,45 @@ STATIC void GC_exit_check(void) # define maybe_install_looping_handler() #endif -#if defined(DYNAMIC_LOADING) && defined(DARWIN) - GC_INNER void GC_init_dyld(void); -#endif - -#if defined(NETBSD) && defined(__ELF__) - GC_INNER void GC_init_netbsd_elf(void); -#endif - #if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE) - STATIC int GC_log = 2; + STATIC int GC_stdout = 1; + STATIC int GC_stderr = 2; + STATIC int GC_log = 2; /* stderr */ #endif -GC_INNER void GC_initialize_offsets(void); /* defined in obj_map.c */ -GC_INNER void GC_bl_init(void); /* defined in blacklst.c */ +STATIC word GC_parse_mem_size_arg(const char *str) +{ + char *endptr; + word result = 0; /* bad value */ + char ch; + + if (*str != '\0') { + result = (word)STRTOULL(str, &endptr, 10); + ch = *endptr; + if (ch != '\0') { + if (*(endptr + 1) != '\0') + return 0; + /* Allow k, M or G suffix. */ + switch (ch) { + case 'K': + case 'k': + result <<= 10; + break; + case 'M': + case 'm': + result <<= 20; + break; + case 'G': + case 'g': + result <<= 30; + break; + default: + result = 0; + } + } + } + return result; +} GC_API void GC_CALL GC_init(void) { @@ -643,6 +663,14 @@ GC_API void GC_CALL GC_init(void) IF_CANCEL(int cancel_state;) if (GC_is_initialized) return; +# ifdef REDIRECT_MALLOC + { + static GC_bool init_started = FALSE; + if (init_started) + ABORT("Redirected malloc() called during GC init"); + init_started = TRUE; + } +# endif # ifdef GC_INITIAL_HEAP_SIZE initial_heap_sz = divHBLKSZ(GC_INITIAL_HEAP_SIZE); @@ -741,7 +769,23 @@ GC_API void GC_CALL GC_init(void) if (log_d < 0) { GC_err_printf("Failed to open %s as log file\n", file_name); } else { + char *str; GC_log = log_d; + str = GETENV("GC_ONLY_LOG_TO_FILE"); +# ifdef GC_ONLY_LOG_TO_FILE + /* The similar environment variable set to "0" */ + /* overrides the effect of the macro defined. */ + if (str != NULL && *str == '0' && *(str + 1) == '\0') +# else + /* Otherwise setting the environment variable */ + /* to anything other than "0" will prevent from */ + /* redirecting stdout/err to the log file. */ + if (str == NULL || (*str == '0' && *(str + 1) == '\0')) +# endif + { + GC_stdout = log_d; + GC_stderr = log_d; + } } } } @@ -749,7 +793,7 @@ GC_API void GC_CALL GC_init(void) # endif /* !SMALL_CONFIG */ # ifndef NO_DEBUGGING if (0 != GETENV("GC_DUMP_REGULARLY")) { - GC_dump_regularly = 1; + GC_dump_regularly = TRUE; } # endif # ifdef KEEP_BACK_PTRS @@ -764,6 +808,11 @@ GC_API void GC_CALL GC_init(void) if (0 != GETENV("GC_FIND_LEAK")) { GC_find_leak = 1; } +# ifndef SHORT_DBG_HDRS + if (0 != GETENV("GC_FINDLEAK_DELAY_FREE")) { + GC_findleak_delay_free = TRUE; + } +# endif if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) { GC_all_interior_pointers = 1; } @@ -771,7 +820,7 @@ GC_API void GC_CALL GC_init(void) GC_dont_gc = 1; } if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) { - GC_print_back_height = 1; + GC_print_back_height = TRUE; } if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) { GC_large_alloc_warn_interval = LONG_MAX; @@ -893,8 +942,8 @@ GC_API void GC_CALL GC_init(void) # if defined(NETBSD) && defined(__ELF__) GC_init_netbsd_elf(); # endif -# if !defined(THREADS) || defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) \ - || defined(GC_SOLARIS_THREADS) +# if !defined(THREADS) || defined(GC_PTHREADS) \ + || defined(GC_WIN32_THREADS) || defined(GC_SOLARIS_THREADS) if (GC_stackbottom == 0) { GC_stackbottom = GC_get_main_stack_base(); # if (defined(LINUX) || defined(HPUX)) && defined(IA64) @@ -916,19 +965,15 @@ GC_API void GC_CALL GC_init(void) GC_STATIC_ASSERT(sizeof (signed_word) == sizeof(word)); GC_STATIC_ASSERT(sizeof (struct hblk) == HBLKSIZE); # ifndef THREADS -# ifdef STACK_GROWS_DOWN - GC_ASSERT((word)(&dummy) <= (word)GC_stackbottom); -# else - GC_ASSERT((word)(&dummy) >= (word)GC_stackbottom); -# endif + GC_ASSERT(!((word)GC_stackbottom HOTTER_THAN (word)(&dummy))); # endif # if !defined(_AUX_SOURCE) || defined(__GNUC__) GC_STATIC_ASSERT((word)(-1) > (word)0); /* word should be unsigned */ # endif -# if !defined(__BORLANDC__) /* Workaround for Borland C */ - GC_STATIC_ASSERT((ptr_t)(word)(-1) > (ptr_t)0); - /* Ptr_t comparisons should behave as unsigned comparisons. */ +# if !defined(__BORLANDC__) && !defined(__CC_ARM) /* Workaround */ + GC_STATIC_ASSERT((ptr_t)(word)(-1) > (ptr_t)0); + /* Ptr_t comparisons should behave as unsigned comparisons. */ # endif GC_STATIC_ASSERT((signed_word)(-1) < (signed_word)0); # ifndef GC_DISABLE_INCREMENTAL @@ -950,10 +995,9 @@ GC_API void GC_CALL GC_init(void) { char * sz_str = GETENV("GC_INITIAL_HEAP_SIZE"); if (sz_str != NULL) { - initial_heap_sz = (word)STRTOULL(sz_str, NULL, 10); + initial_heap_sz = GC_parse_mem_size_arg(sz_str); if (initial_heap_sz <= MINHINCR * HBLKSIZE) { - WARN("Bad initial heap size %s - ignoring it.\n", - sz_str); + WARN("Bad initial heap size %s - ignoring it.\n", sz_str); } initial_heap_sz = divHBLKSZ(initial_heap_sz); } @@ -961,10 +1005,9 @@ GC_API void GC_CALL GC_init(void) { char * sz_str = GETENV("GC_MAXIMUM_HEAP_SIZE"); if (sz_str != NULL) { - word max_heap_sz = (word)STRTOULL(sz_str, NULL, 10); + word max_heap_sz = GC_parse_mem_size_arg(sz_str); if (max_heap_sz < initial_heap_sz * HBLKSIZE) { - WARN("Bad maximum heap size %s - ignoring it.\n", - sz_str); + WARN("Bad maximum heap size %s - ignoring it.\n", sz_str); } if (0 == GC_max_retries) GC_max_retries = 2; GC_set_max_heap_size(max_heap_sz); @@ -1078,14 +1121,13 @@ GC_API void GC_CALL GC_enable_incremental(void) GC_init(); } - #if defined(MSWIN32) || defined(MSWINCE) # if defined(_MSC_VER) && defined(_DEBUG) && !defined(MSWINCE) # include <crtdbg.h> # endif - STATIC HANDLE GC_stdout = 0; + STATIC HANDLE GC_log = 0; void GC_deinit(void) { @@ -1096,19 +1138,19 @@ GC_API void GC_CALL GC_enable_incremental(void) # endif } -#ifdef THREADS -# ifdef PARALLEL_MARK -# define IF_NEED_TO_LOCK(x) if (GC_parallel || GC_need_to_lock) x +# ifdef THREADS +# ifdef PARALLEL_MARK +# define IF_NEED_TO_LOCK(x) if (GC_parallel || GC_need_to_lock) x +# else +# define IF_NEED_TO_LOCK(x) if (GC_need_to_lock) x +# endif # else -# define IF_NEED_TO_LOCK(x) if (GC_need_to_lock) x -# endif -#else -# define IF_NEED_TO_LOCK(x) -#endif +# define IF_NEED_TO_LOCK(x) +# endif /* !THREADS */ -#ifndef _MAX_PATH -# define _MAX_PATH MAX_PATH -#endif +# ifndef _MAX_PATH +# define _MAX_PATH MAX_PATH +# endif STATIC HANDLE GC_CreateLogFile(void) { @@ -1157,19 +1199,19 @@ GC_API void GC_CALL GC_enable_incremental(void) # ifdef THREADS GC_ASSERT(!GC_write_disabled); # endif - if (GC_stdout == INVALID_HANDLE_VALUE) { + if (GC_log == INVALID_HANDLE_VALUE) { IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); return -1; - } else if (GC_stdout == 0) { - GC_stdout = GC_CreateLogFile(); + } else if (GC_log == 0) { + GC_log = GC_CreateLogFile(); /* Ignore open log failure if the collector is built with */ /* print_stats always set on. */ # ifndef GC_PRINT_VERBOSE_STATS - if (GC_stdout == INVALID_HANDLE_VALUE) + if (GC_log == INVALID_HANDLE_VALUE) ABORT("Open of log file failed"); # endif } - tmp = WriteFile(GC_stdout, buf, (DWORD)len, &written, NULL); + tmp = WriteFile(GC_log, buf, (DWORD)len, &written, NULL); if (!tmp) DebugBreak(); # if defined(_MSC_VER) && defined(_DEBUG) @@ -1191,89 +1233,85 @@ GC_API void GC_CALL GC_enable_incremental(void) return tmp ? (int)written : -1; } -#endif /* MSWIN32 */ + /* FIXME: This is pretty ugly ... */ +# define WRITE(f, buf, len) GC_write(buf, len) -#if defined(OS2) || defined(MACOS) +#elif defined(OS2) || defined(MACOS) STATIC FILE * GC_stdout = NULL; STATIC FILE * GC_stderr = NULL; STATIC FILE * GC_log = NULL; + /* Initialize GC_log (and the friends) passed to GC_write(). */ STATIC void GC_set_files(void) { - if (GC_stdout == NULL) { - GC_stdout = stdout; + if (GC_stdout == NULL) { + GC_stdout = stdout; } if (GC_stderr == NULL) { - GC_stderr = stderr; + GC_stderr = stderr; } if (GC_log == NULL) { - GC_log = stderr; + GC_log = stderr; } } -#endif -#if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE) - STATIC int GC_stdout = 1; - STATIC int GC_stderr = 2; -# if !defined(AMIGA) + GC_INLINE int GC_write(FILE *f, const char *buf, size_t len) + { + int res = fwrite(buf, 1, len, f); + fflush(f); + return res; + } + +# define WRITE(f, buf, len) (GC_set_files(), GC_write(f, buf, len)) + +#else +# if !defined(AMIGA) && !defined(__CC_ARM) # include <unistd.h> # endif -#endif -#if defined(ECOS) || defined(NOSYS) STATIC int GC_write(int fd, const char *buf, size_t len) { -# ifdef ECOS - /* FIXME: This seems to be defined nowhere at present. */ - /* _Jv_diag_write(buf, len); */ +# if defined(ECOS) || defined(NOSYS) +# ifdef ECOS + /* FIXME: This seems to be defined nowhere at present. */ + /* _Jv_diag_write(buf, len); */ +# else + /* No writing. */ +# endif + return len; # else - /* No writing. */ + int bytes_written = 0; + int result; + IF_CANCEL(int cancel_state;) + + DISABLE_CANCEL(cancel_state); + while (bytes_written < len) { +# ifdef GC_SOLARIS_THREADS + result = syscall(SYS_write, fd, buf + bytes_written, + len - bytes_written); +# else + result = write(fd, buf + bytes_written, len - bytes_written); +# endif + if (-1 == result) { + RESTORE_CANCEL(cancel_state); + return(result); + } + bytes_written += result; + } + RESTORE_CANCEL(cancel_state); + return(bytes_written); # endif - return len; - } -#elif !defined(MSWIN32) && !defined(MSWINCE) && !defined(OS2) \ - && !defined(MACOS) - STATIC int GC_write(int fd, const char *buf, size_t len) - { - int bytes_written = 0; - int result; - IF_CANCEL(int cancel_state;) - - DISABLE_CANCEL(cancel_state); - while (bytes_written < len) { -# ifdef GC_SOLARIS_THREADS - result = syscall(SYS_write, fd, buf + bytes_written, - len - bytes_written); -# else - result = write(fd, buf + bytes_written, len - bytes_written); -# endif - if (-1 == result) { - RESTORE_CANCEL(cancel_state); - return(result); - } - bytes_written += result; - } - RESTORE_CANCEL(cancel_state); - return(bytes_written); } -#endif /* UN*X */ -#if defined(MSWIN32) || defined(MSWINCE) - /* FIXME: This is pretty ugly ... */ -# define WRITE(f, buf, len) GC_write(buf, len) -#else -# if defined(OS2) || defined(MACOS) - static int fwrite_gc_res; /* Should really be local ... */ -# define WRITE(f, buf, len) (GC_set_files(), \ - fwrite_gc_res = fwrite((buf), 1, (len), (f)), \ - fflush(f), fwrite_gc_res) -# else -# define WRITE(f, buf, len) GC_write((f), (buf), (len)) -# endif -#endif +# define WRITE(f, buf, len) GC_write(f, buf, len) +#endif /* !MSWIN32 && !OS2 && !MACOS */ #define BUFSZ 1024 -#ifdef _MSC_VER + +#ifdef NO_VSNPRINTF + /* In case this function is missing (eg., in DJGPP v2.0.3). */ +# define vsnprintf(buf, bufsz, format, args) vsprintf(buf, format, args) +#elif defined(_MSC_VER) # ifdef MSWINCE /* _vsnprintf is deprecated in WinCE */ # define vsnprintf StringCchVPrintfA @@ -1291,8 +1329,8 @@ void GC_printf(const char *format, ...) va_list args; char buf[BUFSZ+1]; - va_start(args, format); if (GC_quiet) return; + va_start(args, format); buf[BUFSZ] = 0x15; (void) vsnprintf(buf, BUFSZ, format, args); va_end(args); @@ -1329,18 +1367,12 @@ void GC_log_printf(const char *format, ...) ABORT("write to log failed"); } +/* This is equivalent to GC_err_printf("%s",s). */ void GC_err_puts(const char *s) { if (WRITE(GC_stderr, s, strlen(s)) < 0) ABORT("write to stderr failed"); } -#if defined(LINUX) && !defined(SMALL_CONFIG) - GC_INNER void GC_err_write(const char *buf, size_t len) - { - if (WRITE(GC_stderr, buf, len) < 0) ABORT("write to stderr failed"); - } -#endif - STATIC void GC_CALLBACK GC_default_warn_proc(char *msg, GC_word arg) { GC_err_printf(msg, arg); @@ -1531,12 +1563,7 @@ GC_API void * GC_CALL GC_call_with_stack_base(GC_stack_base_func fn, void *arg) return fn(&base, arg); } -#ifdef THREADS - - /* Defined in pthread_support.c or win32_threads.c. */ - GC_INNER void GC_do_blocking_inner(ptr_t data, void * context); - -#else +#ifndef THREADS GC_INNER ptr_t GC_blocked_sp = NULL; /* NULL value means we are not inside GC_do_blocking() call. */ @@ -1729,8 +1756,6 @@ GC_API int GC_CALL GC_get_find_leak(void) return GC_find_leak; } -GC_INNER void GC_bl_init_no_interiors(void); /* defined in blacklst.c */ - GC_API void GC_CALL GC_set_all_interior_pointers(int value) { DCL_LOCK_STATE; @@ -158,12 +158,10 @@ GC_INNER ptr_t GC_build_fl(struct hblk *h, size_t sz, GC_bool clear, } p -= sz; /* p now points to last object */ - /* - * put p (which is now head of list of objects in *h) as first - * pointer in the appropriate free list for this size. - */ - obj_link(h -> hb_body) = list; - return ((ptr_t)p); + /* Put p (which is now head of list of objects in *h) as first */ + /* pointer in the appropriate free list for this size. */ + *(ptr_t *)h = list; + return ((ptr_t)p); } /* @@ -19,10 +19,10 @@ #if defined(LINUX) && !defined(POWERPC) # include <linux/version.h> # if (LINUX_VERSION_CODE <= 0x10400) - /* Ugly hack to get struct sigcontext_struct definition. Required */ - /* for some early 1.3.X releases. Will hopefully go away soon. */ - /* in some later Linux releases, asm/sigcontext.h may have to */ - /* be included instead. */ + /* Ugly hack to get struct sigcontext_struct definition. Required */ + /* for some early 1.3.X releases. Will hopefully go away soon. */ + /* in some later Linux releases, asm/sigcontext.h may have to */ + /* be included instead. */ # define __KERNEL__ # include <asm/signal.h> # undef __KERNEL__ @@ -34,8 +34,8 @@ # include <features.h> # if 2 <= __GLIBC__ # if 2 == __GLIBC__ && 0 == __GLIBC_MINOR__ - /* glibc 2.1 no longer has sigcontext.h. But signal.h */ - /* has the right declaration for glibc 2.1. */ + /* glibc 2.1 no longer has sigcontext.h. But signal.h */ + /* has the right declaration for glibc 2.1. */ # include <sigcontext.h> # endif /* 0 == __GLIBC_MINOR__ */ # else /* not 2 <= __GLIBC__ */ @@ -47,7 +47,7 @@ #endif #if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) \ - && !defined(MSWINCE) + && !defined(MSWINCE) && !defined(__CC_ARM) # include <sys/types.h> # if !defined(MSWIN32) # include <unistd.h> @@ -131,22 +131,17 @@ #endif #if !defined(NO_EXECUTE_PERMISSION) - static GC_bool pages_executable = TRUE; + STATIC GC_bool GC_pages_executable = TRUE; #else - static GC_bool pages_executable = FALSE; + STATIC GC_bool GC_pages_executable = FALSE; #endif #define IGNORE_PAGES_EXECUTABLE 1 - /* Undefined on pages_executable real use. */ - -#if defined(LINUX) && (defined(USE_PROC_FOR_LIBRARIES) || defined(IA64) \ - || !defined(SMALL_CONFIG)) -# define NEED_PROC_MAPS -#endif + /* Undefined on GC_pages_executable real use. */ #ifdef NEED_PROC_MAPS -/* We need to parse /proc/self/maps, either to find dynamic libraries, */ -/* and/or to find the register backing store base (IA64). Do it once */ -/* here. */ +/* We need to parse /proc/self/maps, either to find dynamic libraries, */ +/* and/or to find the register backing store base (IA64). Do it once */ +/* here. */ #define READ read @@ -197,12 +192,10 @@ STATIC ssize_t GC_repeat_read(int fd, char *buf, size_t count) } #endif /* THREADS */ -/* - * Copy the contents of /proc/self/maps to a buffer in our address space. - * Return the address of the buffer, or zero on failure. - * This code could be simplified if we could determine its size - * ahead of time. - */ +/* Copy the contents of /proc/self/maps to a buffer in our address */ +/* space. Return the address of the buffer, or zero on failure. */ +/* This code could be simplified if we could determine its size ahead */ +/* of time. */ GC_INNER char * GC_get_maps(void) { int f; @@ -243,7 +236,7 @@ GC_INNER char * GC_get_maps(void) /* thus can't use stdio. */ do { while (maps_size >= maps_buf_sz) { - /* Grow only by powers of 2, since we leak "too small" buffers. */ + /* Grow only by powers of 2, since we leak "too small" buffers.*/ while (maps_size >= maps_buf_sz) maps_buf_sz *= 2; maps_buf = GC_scratch_alloc(maps_buf_sz); # ifdef THREADS @@ -264,25 +257,29 @@ GC_INNER char * GC_get_maps(void) maps_size = 0; do { result = GC_repeat_read(f, maps_buf, maps_buf_sz-1); - if (result <= 0) return 0; + if (result <= 0) + break; maps_size += result; } while (result == maps_buf_sz-1); close(f); + if (result <= 0) + return 0; # ifdef THREADS if (maps_size > old_maps_size) { if (GC_print_stats) - GC_printf("Unexpected maps size growth from %lu to %lu\n", - (unsigned long)old_maps_size, - (unsigned long)maps_size); + GC_log_printf( + "Unexpected maps size growth from %lu to %lu\n", + (unsigned long)old_maps_size, + (unsigned long)maps_size); ABORT("Unexpected asynchronous /proc/self/maps growth: " "unregistered thread?"); } # endif } while (maps_size >= maps_buf_sz || maps_size < old_maps_size); - /* In the single-threaded case, the second clause is false. */ + /* In the single-threaded case, the second clause is false. */ maps_buf[maps_size] = '\0'; - /* Apply fn to result. */ + /* Apply fn to result. */ return maps_buf; } @@ -300,12 +297,10 @@ GC_INNER char * GC_get_maps(void) * anywhere, which is safer anyway. */ -/* - * Assign various fields of the first line in buf_ptr to *start, *end, - * *prot, *maj_dev and *mapping_name. Mapping_name may be NULL. - * *prot and *mapping_name are assigned pointers into the original - * buffer. - */ +/* Assign various fields of the first line in buf_ptr to (*start), */ +/* (*end), (*prot), (*maj_dev) and (*mapping_name). mapping_name may */ +/* be NULL. (*prot) and (*mapping_name) are assigned pointers into the */ +/* original buffer. */ GC_INNER char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, char **prot, unsigned int *maj_dev, char **mapping_name) @@ -354,34 +349,37 @@ GC_INNER char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end, return p; } -/* Try to read the backing store base from /proc/self/maps. */ -/* Return the bounds of the writable mapping with a 0 major device, */ -/* which includes the address passed as data. */ -/* Return FALSE if there is no such mapping. */ -GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp) -{ - char *prot; - ptr_t my_start, my_end; - unsigned int maj_dev; - char *maps = GC_get_maps(); - char *buf_ptr = maps; +#if defined(IA64) || defined(INCLUDE_LINUX_THREAD_DESCR) + /* Try to read the backing store base from /proc/self/maps. */ + /* Return the bounds of the writable mapping with a 0 major device, */ + /* which includes the address passed as data. */ + /* Return FALSE if there is no such mapping. */ + GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, + ptr_t *endp) + { + char *prot; + ptr_t my_start, my_end; + unsigned int maj_dev; + char *maps = GC_get_maps(); + char *buf_ptr = maps; - if (0 == maps) return(FALSE); - for (;;) { - buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, - &prot, &maj_dev, 0); - - if (buf_ptr == NULL) return FALSE; - if (prot[1] == 'w' && maj_dev == 0) { - if (my_end > addr && my_start <= addr) { - *startp = my_start; - *endp = my_end; - return TRUE; - } + if (0 == maps) return(FALSE); + for (;;) { + buf_ptr = GC_parse_map_entry(buf_ptr, &my_start, &my_end, + &prot, &maj_dev, 0); + + if (buf_ptr == NULL) return FALSE; + if (prot[1] == 'w' && maj_dev == 0) { + if (my_end > addr && my_start <= addr) { + *startp = my_start; + *endp = my_end; + return TRUE; + } + } } + return FALSE; } - return FALSE; -} +#endif /* IA64 || INCLUDE_LINUX_THREAD_DESCR */ #if defined(REDIRECT_MALLOC) /* Find the text(code) mapping for the library whose name, after */ @@ -425,7 +423,7 @@ GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp) ptr_t my_start, my_end; if (!GC_enclosing_mapping(GC_save_regs_in_stack(), &my_start, &my_end)) { if (GC_print_stats) { - GC_log_printf("Failed to find backing store base from /proc\n"); + GC_log_printf("Failed to find backing store base from /proc\n"); } return 0; } @@ -474,7 +472,7 @@ GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp) # endif /* LINUX */ GC_data_start = GC_find_limit((ptr_t)(_end), FALSE); } -#endif +#endif /* SEARCH_FOR_DATA_START */ #ifdef ECOS @@ -568,7 +566,7 @@ GC_bool GC_enclosing_mapping(ptr_t addr, ptr_t *startp, ptr_t *endp) } # ifdef THREADS - /* Due to the siglongjump we need to manually unmask SIGPROF. */ + /* Due to the siglongjump we need to manually unmask SIGPROF. */ __syscall(SYS_sigprocmask, SIG_UNBLOCK, sigmask(SIGPROF)); # endif @@ -770,7 +768,6 @@ GC_INNER word GC_page_size = 0; sb -> mem_base = trunc_sp + size; return GC_SUCCESS; } - # else /* CYGWIN32 */ /* An alternate version for Cygwin (adapted from Dave Korn's */ /* gcc version of boehm-gc). */ @@ -783,20 +780,12 @@ GC_INNER word GC_page_size = 0; # endif /* CYGWIN32 */ # define HAVE_GET_STACK_BASE - /* This is always called from the main thread. */ - ptr_t GC_get_main_stack_base(void) - { - struct GC_stack_base sb; - GC_get_stack_base(&sb); - GC_ASSERT((void *)&sb HOTTER_THAN sb.mem_base); - return (ptr_t)sb.mem_base; - } - #else /* !MSWIN32 */ GC_INNER void GC_setpagesize(void) { # if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) GC_page_size = GETPAGESIZE(); + if (!GC_page_size) ABORT("getpagesize() failed"); # else /* It's acceptable to fake it. */ GC_page_size = HBLKSIZE; @@ -806,25 +795,29 @@ GC_INNER word GC_page_size = 0; #ifdef BEOS # include <kernel/OS.h> - ptr_t GC_get_main_stack_base(void) + + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) { thread_info th; get_thread_info(find_thread(NULL),&th); - return th.stack_end; + sb->mem_base = th.stack_end; + return GC_SUCCESS; } +# define HAVE_GET_STACK_BASE #endif /* BEOS */ #ifdef OS2 - ptr_t GC_get_main_stack_base(void) + GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *sb) { - PTIB ptib; + PTIB ptib; /* thread information block */ PPIB ppib; - if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { ABORT("DosGetInfoBlocks failed"); } - return((ptr_t)(ptib -> tib_pstacklimit)); + sb->mem_base = ptib->tib_pstacklimit; + return GC_SUCCESS; } +# define HAVE_GET_STACK_BASE #endif /* OS2 */ # ifdef AMIGA @@ -850,22 +843,23 @@ GC_INNER word GC_page_size = 0; GC_INNER void GC_set_and_save_fault_handler(GC_fault_handler_t h) { -# if defined(SUNOS5SIGS) || defined(IRIX5) \ +# if defined(SUNOS5SIGS) || defined(IRIX5) \ || defined(OSF1) || defined(HURD) || defined(NETBSD) - struct sigaction act; + struct sigaction act; - act.sa_handler = h; -# if 0 /* Was necessary for Solaris 2.3 and very temporary */ - /* NetBSD bugs. */ - act.sa_flags = SA_RESTART | SA_NODEFER; + act.sa_handler = h; +# ifdef SIGACTION_FLAGS_NODEFER_HACK + /* Was necessary for Solaris 2.3 and very temporary */ + /* NetBSD bugs. */ + act.sa_flags = SA_RESTART | SA_NODEFER; # else - act.sa_flags = SA_RESTART; + act.sa_flags = SA_RESTART; # endif (void) sigemptyset(&act.sa_mask); # ifdef GC_IRIX_THREADS - /* Older versions have a bug related to retrieving and */ - /* and setting a handler at the same time. */ + /* Older versions have a bug related to retrieving and */ + /* and setting a handler at the same time. */ (void) sigaction(SIGSEGV, 0, &old_segv_act); (void) sigaction(SIGSEGV, &act, 0); # else @@ -873,9 +867,9 @@ GC_INNER word GC_page_size = 0; # if defined(IRIX5) && defined(_sigargs) /* Irix 5.x, not 6.x */ \ || defined(HPUX) || defined(HURD) || defined(NETBSD) \ || defined(FREEBSD) - /* Under Irix 5.x or HP/UX, we may get SIGBUS. */ - /* Pthreads doesn't exist under Irix 5.x, so we */ - /* don't have to worry in the threads case. */ + /* Under Irix 5.x or HP/UX, we may get SIGBUS. */ + /* Pthreads doesn't exist under Irix 5.x, so we */ + /* don't have to worry in the threads case. */ (void) sigaction(SIGBUS, &act, &old_bus_act); # endif # endif /* GC_IRIX_THREADS */ @@ -888,8 +882,8 @@ GC_INNER word GC_page_size = 0; } # endif /* NEED_FIND_LIMIT || UNIX_LIKE */ -# if defined(NEED_FIND_LIMIT) || \ - defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS) +# if defined(NEED_FIND_LIMIT) \ + || (defined(USE_PROC_FOR_LIBRARIES) && defined(THREADS)) /* Some tools to implement HEURISTIC2 */ # define MIN_PAGE_SIZE 256 /* Smallest conceivable page size, bytes */ @@ -901,8 +895,8 @@ GC_INNER word GC_page_size = 0; GC_INNER void GC_setup_temporary_fault_handler(void) { - /* Handler is process-wide, so this should only happen in */ - /* one thread at a time. */ + /* Handler is process-wide, so this should only happen in */ + /* one thread at a time. */ GC_ASSERT(I_HOLD_LOCK()); GC_set_and_save_fault_handler(GC_fault_handler); } @@ -1033,7 +1027,7 @@ GC_INNER word GC_page_size = 0; } # endif /* IA64 */ - STATIC ptr_t GC_linux_stack_base(void) + STATIC ptr_t GC_linux_main_stack_base(void) { /* We read the stack base value from /proc/self/stat. We do this */ /* using direct I/O system calls in order to avoid calling malloc */ @@ -1107,11 +1101,9 @@ GC_INNER word GC_page_size = 0; ABORT("Absurd stack bottom value"); return (ptr_t)result; } - #endif /* LINUX_STACKBOTTOM */ #ifdef FREEBSD_STACKBOTTOM - /* This uses an undocumented sysctl call, but at least one expert */ /* believes it will stay. */ @@ -1119,15 +1111,13 @@ GC_INNER word GC_page_size = 0; # include <sys/types.h> # include <sys/sysctl.h> - STATIC ptr_t GC_freebsd_stack_base(void) + STATIC ptr_t GC_freebsd_main_stack_base(void) { int nm[2] = {CTL_KERN, KERN_USRSTACK}; ptr_t base; size_t len = sizeof(ptr_t); int r = sysctl(nm, 2, &base, &len, NULL, 0); - - if (r) ABORT("Error getting stack base"); - + if (r) ABORT("Error getting main stack base"); return base; } #endif /* FREEBSD_STACKBOTTOM */ @@ -1137,6 +1127,7 @@ GC_INNER word GC_page_size = 0; { return STACKBOTTOM; } +# define GET_MAIN_STACKBASE_SPECIAL #elif !defined(BEOS) && !defined(AMIGA) && !defined(OS2) \ && !defined(MSWIN32) && !defined(MSWINCE) && !defined(CYGWIN32) \ && !defined(GC_OPENBSD_THREADS) \ @@ -1155,7 +1146,9 @@ GC_INNER word GC_page_size = 0; ptr_t GC_get_main_stack_base(void) { ptr_t result; /* also used as "dummy" to get the approx. sp value */ -# if defined(LINUX) && defined(USE_GET_STACKBASE_FOR_MAIN) && !defined(NACL) +# if defined(LINUX) && !defined(NACL) \ + && (defined(USE_GET_STACKBASE_FOR_MAIN) \ + || (defined(THREADS) && !defined(REDIRECT_MALLOC))) pthread_attr_t attr; void *stackaddr; size_t size; @@ -1186,10 +1179,10 @@ GC_INNER word GC_page_size = 0; # endif # endif /* HEURISTIC1 */ # ifdef LINUX_STACKBOTTOM - result = GC_linux_stack_base(); + result = GC_linux_main_stack_base(); # endif # ifdef FREEBSD_STACKBOTTOM - result = GC_freebsd_stack_base(); + result = GC_freebsd_main_stack_base(); # endif # ifdef HEURISTIC2 # ifdef STACK_GROWS_DOWN @@ -1218,19 +1211,14 @@ GC_INNER word GC_page_size = 0; GC_ASSERT((ptr_t)(&result) HOTTER_THAN result); return(result); } +# define GET_MAIN_STACKBASE_SPECIAL #endif /* !AMIGA, !BEOS, !OPENBSD, !OS2, !Windows */ -#if defined(GC_LINUX_THREADS) && !defined(HAVE_GET_STACK_BASE) \ - && !defined(NACL) +#if (defined(GC_LINUX_THREADS) || defined(PLATFORM_ANDROID)) && !defined(NACL) # include <pthread.h> /* extern int pthread_getattr_np(pthread_t, pthread_attr_t *); */ -# ifdef IA64 - GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound); - /* From pthread_support.c */ -# endif - GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base *b) { pthread_attr_t attr; @@ -1277,7 +1265,6 @@ GC_INNER word GC_page_size = 0; # endif return GC_SUCCESS; } - # define HAVE_GET_STACK_BASE #endif /* GC_LINUX_THREADS */ @@ -1311,17 +1298,7 @@ GC_INNER word GC_page_size = 0; sb->mem_base = stack.ss_sp; return GC_SUCCESS; } - # define HAVE_GET_STACK_BASE - - /* This is always called from the main thread. */ - ptr_t GC_get_main_stack_base(void) - { - struct GC_stack_base sb; - GC_get_stack_base(&sb); - GC_ASSERT((void *)&sb HOTTER_THAN sb.mem_base); - return (ptr_t)sb.mem_base; - } #endif /* GC_OPENBSD_THREADS */ #if defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC) @@ -1360,7 +1337,7 @@ GC_INNER word GC_page_size = 0; /* s.ss_sp holds the pointer to the stack bottom. */ GC_ASSERT((void *)&s HOTTER_THAN s.ss_sp); - if (!stackbase_main_self) + if (!stackbase_main_self && thr_main() != 0) { /* Cache the stack base value for the primordial thread (this */ /* is done during GC_init, so there is no race). */ @@ -1371,19 +1348,7 @@ GC_INNER word GC_page_size = 0; b -> mem_base = s.ss_sp; return GC_SUCCESS; } - # define HAVE_GET_STACK_BASE - - /* This is always called from the main thread. The above */ - /* implementation of GC_get_stack_base() requires the latter to be */ - /* first called from GC_get_main_stack_base() (to cache the proper */ - /* ss_sp value). */ - ptr_t GC_get_main_stack_base(void) - { - struct GC_stack_base sb; - GC_get_stack_base(&sb); - return (ptr_t)sb.mem_base; - } #endif /* GC_SOLARIS_THREADS */ #ifndef HAVE_GET_STACK_BASE @@ -1420,14 +1385,22 @@ GC_INNER word GC_page_size = 0; } #endif /* !HAVE_GET_STACK_BASE */ -/* - * Register static data segment(s) as roots. - * If more data segments are added later then they need to be registered - * add that point (as we do with SunOS dynamic loading), - * or GC_mark_roots needs to check for them (as we do with PCR). - * Called with allocator lock held. - */ +#ifndef GET_MAIN_STACKBASE_SPECIAL + /* This is always called from the main thread. Default implementation. */ + ptr_t GC_get_main_stack_base(void) + { + struct GC_stack_base sb; + if (GC_get_stack_base(&sb) != GC_SUCCESS) + ABORT("GC_get_stack_base failed"); + GC_ASSERT((void *)&sb HOTTER_THAN sb.mem_base); + return (ptr_t)sb.mem_base; + } +#endif /* !GET_MAIN_STACKBASE_SPECIAL */ +/* Register static data segment(s) as roots. If more data segments are */ +/* added later then they need to be registered at that point (as we do */ +/* with SunOS dynamic loading), or GC_mark_roots needs to check for */ +/* them (as we do with PCR). Called with allocator lock held. */ # ifdef OS2 void GC_register_data_segments(void) @@ -1553,11 +1526,9 @@ void GC_register_data_segments(void) # else /* !OS2 */ # if defined(GWW_VDB) - # ifndef MEM_WRITE_WATCH # define MEM_WRITE_WATCH 0x200000 # endif - # ifndef WRITE_WATCH_FLAG_RESET # define WRITE_WATCH_FLAG_RESET 1 # endif @@ -1644,9 +1615,9 @@ void GC_register_data_segments(void) done = TRUE; } -# else /* !GWW_VDB */ +# else # define GetWriteWatch_alloc_flag 0 -# endif /* GWW_VDB */ +# endif /* !GWW_VDB */ # if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32) @@ -1716,7 +1687,7 @@ void GC_register_data_segments(void) /* the malloc heap with HeapWalk on the default heap. But that */ /* apparently works only for NT-based Windows. */ - STATIC size_t GC_max_root_size = 100000; /* Appr. largest root size. */ + STATIC size_t GC_max_root_size = 100000; /* Appr. largest root size. */ # ifndef CYGWIN32 /* In the long run, a better data structure would also be nice ... */ @@ -1775,8 +1746,8 @@ void GC_register_data_segments(void) } } if (GC_print_stats) - GC_log_printf("Found new system malloc AllocationBase at %p\n", - candidate); + GC_log_printf("Found new system malloc AllocationBase at %p\n", + candidate); new_l -> allocation_base = candidate; new_l -> next = GC_malloc_heap_l; GC_malloc_heap_l = new_l; @@ -2009,7 +1980,8 @@ void GC_register_data_segments(void) # if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \ && !defined(MSWINCE) && !defined(MACOS) && !defined(DOS4GW) \ - && !defined(NONSTOP) && !defined(SN_TARGET_PS3) + && !defined(NONSTOP) && !defined(SN_TARGET_PS3) && !defined(RTEMS) \ + && !defined(__CC_ARM) # define SBRK_ARG_T ptrdiff_t @@ -2056,7 +2028,7 @@ STATIC ptr_t GC_unix_mmap_get_mem(word bytes) if (bytes & (GC_page_size - 1)) ABORT("Bad GET_MEM arg"); result = mmap(last_addr, bytes, (PROT_READ | PROT_WRITE) - | (pages_executable ? PROT_EXEC : 0), + | (GC_pages_executable ? PROT_EXEC : 0), GC_MMAP_FLAGS | OPT_MAP_ANON, zero_fd, 0/* offset */); # undef IGNORE_PAGES_EXECUTABLE @@ -2069,7 +2041,7 @@ STATIC ptr_t GC_unix_mmap_get_mem(word bytes) /* usable by arbitrary C code, since one-past-end pointers */ /* don't work, so we discard it and try again. */ munmap(result, (size_t)(-GC_page_size) - (size_t)result); - /* Leave last page mapped, so we can't repeat. */ + /* Leave last page mapped, so we can't repeat. */ return GC_unix_mmap_get_mem(bytes); } # else @@ -2161,7 +2133,7 @@ void * os2_alloc(size_t bytes) void * result; if (DosAllocMem(&result, bytes, (PAG_READ | PAG_WRITE | PAG_COMMIT) - | (pages_executable ? PAG_EXECUTE : 0)) + | (GC_pages_executable ? PAG_EXECUTE : 0)) != NO_ERROR) { return(0); } @@ -2240,8 +2212,8 @@ void * os2_alloc(size_t bytes) GetWriteWatch_alloc_flag | (MEM_COMMIT | MEM_RESERVE) | GC_mem_top_down, - pages_executable ? PAGE_EXECUTE_READWRITE : - PAGE_READWRITE); + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); # undef IGNORE_PAGES_EXECUTABLE } # endif /* !CYGWIN32 */ @@ -2256,13 +2228,18 @@ void * os2_alloc(size_t bytes) GC_API void GC_CALL GC_win32_free_heap(void) { # ifndef CYGWIN32 - if (GC_no_win32_dlls) { - while (GC_n_heap_bases > 0) { - GlobalFree (GC_heap_bases[--GC_n_heap_bases]); - GC_heap_bases[GC_n_heap_bases] = 0; - } - } + if (GC_no_win32_dlls) # endif + { + while (GC_n_heap_bases-- > 0) { +# ifdef CYGWIN32 + /* FIXME: Is it ok to use non-GC free() here? */ +# else + GlobalFree(GC_heap_bases[GC_n_heap_bases]); +# endif + GC_heap_bases[GC_n_heap_bases] = 0; + } + } } #endif /* MSWIN32 || CYGWIN32 */ @@ -2302,8 +2279,8 @@ void * os2_alloc(size_t bytes) /* argument to span regions, so we should be OK for now. */ result = (ptr_t) VirtualAlloc(NULL, res_bytes, MEM_RESERVE | MEM_TOP_DOWN, - pages_executable ? PAGE_EXECUTE_READWRITE : - PAGE_READWRITE); + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); /* If I read the documentation correctly, this can */ /* only happen if HBLKSIZE > 64k or not a power of 2. */ @@ -2316,8 +2293,8 @@ void * os2_alloc(size_t bytes) /* Commit pages */ result = (ptr_t) VirtualAlloc(result, bytes, MEM_COMMIT, - pages_executable ? PAGE_EXECUTE_READWRITE : - PAGE_READWRITE); + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); # undef IGNORE_PAGES_EXECUTABLE if (result != NULL) { @@ -2428,8 +2405,8 @@ GC_INNER void GC_remap(ptr_t start, size_t bytes) ABORT("Weird VirtualQuery result"); alloc_len = (len < mem_info.RegionSize) ? len : mem_info.RegionSize; result = VirtualAlloc(start_addr, alloc_len, MEM_COMMIT, - pages_executable ? PAGE_EXECUTE_READWRITE : - PAGE_READWRITE); + GC_pages_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE); if (result != start_addr) { if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY || GetLastError() == ERROR_OUTOFMEMORY) { @@ -2449,12 +2426,12 @@ GC_INNER void GC_remap(ptr_t start, size_t bytes) # ifndef NACL result = mprotect(start_addr, len, (PROT_READ | PROT_WRITE) - | (pages_executable ? PROT_EXEC : 0)); + | (GC_pages_executable ? PROT_EXEC : 0)); # else { /* NaCl does not expose mprotect, but mmap should work fine. */ void *mmap_result = mmap(start_addr, len, (PROT_READ | PROT_WRITE) - | (pages_executable ? PROT_EXEC : 0), + | (GC_pages_executable ? PROT_EXEC : 0), MAP_PRIVATE | MAP_FIXED | OPT_MAP_ANON, zero_fd, 0 /* offset */); if (mmap_result != (void *)start_addr) @@ -2467,9 +2444,9 @@ GC_INNER void GC_remap(ptr_t start, size_t bytes) if (result != 0) { if (GC_print_stats) - GC_printf("Mprotect failed at %p (length %lu) with errno %d\n", - start_addr, (unsigned long)len, errno); - ABORT("Mprotect remapping failed"); + GC_log_printf("Mprotect failed at %p (length %lu) with errno %d\n", + start_addr, (unsigned long)len, errno); + ABORT("mprotect remapping failed"); } GC_unmapped_bytes -= len; # endif @@ -2565,39 +2542,33 @@ STATIC void GC_default_push_other_roots(void) /* Traverse all thread stacks. */ if (PCR_ERes_IsErr( PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0)) - || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) { - ABORT("Thread stack marking failed\n"); + || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) { + ABORT("Thread stack marking failed"); } } # endif /* PCR */ - # if defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) - -GC_INNER void GC_push_all_stacks(void); - -STATIC void GC_default_push_other_roots(void) -{ - GC_push_all_stacks(); -} - + STATIC void GC_default_push_other_roots(void) + { + GC_push_all_stacks(); + } # endif /* GC_WIN32_THREADS || GC_PTHREADS */ # ifdef SN_TARGET_PS3 STATIC void GC_default_push_other_roots(void) { - ABORT("GC_default_push_other_roots is not implemented\n"); + ABORT("GC_default_push_other_roots is not implemented"); } void GC_push_thread_structures(void) { - ABORT("GC_push_thread_structures is not implemented\n"); + ABORT("GC_push_thread_structures is not implemented"); } # endif /* SN_TARGET_PS3 */ GC_INNER void (*GC_push_other_roots)(void) = GC_default_push_other_roots; - #endif /* THREADS */ /* @@ -2648,9 +2619,45 @@ STATIC void GC_default_push_other_roots(void) STATIC void GC_or_pages(page_hash_table pht1, page_hash_table pht2) { register int i; - for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i]; } + +# ifdef MPROTECT_VDB + STATIC GC_bool GC_gww_page_was_dirty(struct hblk * h) +# else + GC_INNER GC_bool GC_page_was_dirty(struct hblk * h) +# endif + { + register word index; + if (HDR(h) == 0) + return TRUE; + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_grungy_pages, index); + } + +# if defined(CHECKSUMS) || defined(PROC_VDB) + /* Used only if GWW_VDB. */ +# ifdef MPROTECT_VDB + STATIC GC_bool GC_gww_page_was_ever_dirty(struct hblk * h) +# else + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h) +# endif + { + register word index; + if (HDR(h) == 0) + return TRUE; + index = PHT_HASH(h); + return get_pht_entry_from_index(GC_written_pages, index); + } +# endif /* CHECKSUMS || PROC_VDB */ + +# ifndef MPROTECT_VDB + /* Ignore write hints. They don't help us here. */ + /*ARGSUSED*/ + GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, + GC_bool is_ptrfree) {} +# endif + #endif /* PROC_VDB || GWW_VDB */ #ifdef GWW_VDB @@ -2672,7 +2679,7 @@ STATIC void GC_default_push_other_roots(void) detect_GetWriteWatch(); GC_dirty_maintained = GC_GWW_AVAILABLE(); } -# endif +# endif /* !MPROTECT_VDB */ # ifdef MPROTECT_VDB STATIC void GC_gww_read_dirty(void) @@ -2693,23 +2700,20 @@ STATIC void GC_default_push_other_roots(void) pages = gww_buf; count = GC_GWW_BUF_LEN; - /* - * GetWriteWatch is documented as returning non-zero when it fails, - * but the documentation doesn't explicitly say why it would fail or - * what its behaviour will be if it fails. - * It does appear to fail, at least on recent W2K instances, if - * the underlying memory was not allocated with the appropriate - * flag. This is common if GC_enable_incremental is called - * shortly after GC initialization. To avoid modifying the - * interface, we silently work around such a failure, it it only - * affects the initial (small) heap allocation. - * If there are more dirty - * pages than will fit in the buffer, this is not treated as a - * failure; we must check the page count in the loop condition. - * Since each partial call will reset the status of some - * pages, this should eventually terminate even in the overflow - * case. - */ + /* GetWriteWatch is documented as returning non-zero when it */ + /* fails, but the documentation doesn't explicitly say why it */ + /* would fail or what its behaviour will be if it fails. */ + /* It does appear to fail, at least on recent W2K instances, if */ + /* the underlying memory was not allocated with the appropriate */ + /* flag. This is common if GC_enable_incremental is called */ + /* shortly after GC initialization. To avoid modifying the */ + /* interface, we silently work around such a failure, it only */ + /* affects the initial (small) heap allocation. If there are */ + /* more dirty pages than will fit in the buffer, this is not */ + /* treated as a failure; we must check the page count in the */ + /* loop condition. Since each partial call will reset the */ + /* status of some pages, this should eventually terminate even */ + /* in the overflow case. */ if (GetWriteWatch_func(WRITE_WATCH_FLAG_RESET, GC_heap_sects[i].hs_start, GC_heap_sects[i].hs_bytes, @@ -2733,7 +2737,7 @@ STATIC void GC_default_push_other_roots(void) set_pht_entry_from_index(GC_grungy_pages, hash); } count = 1; /* Done with this section. */ - } else /* succeeded */{ + } else /* succeeded */ { pages_end = pages + count; while (pages != pages_end) { struct hblk * h = (struct hblk *) *pages++; @@ -2744,47 +2748,16 @@ STATIC void GC_default_push_other_roots(void) } } } while (count == GC_GWW_BUF_LEN); - /* FIXME: It's unclear from Microsoft's documentation if this loop */ - /* is useful. We suspect the call just fails if the buffer fills */ - /* up. But that should still be handled correctly. */ + /* FIXME: It's unclear from Microsoft's documentation if this loop */ + /* is useful. We suspect the call just fails if the buffer fills */ + /* up. But that should still be handled correctly. */ } GC_or_pages(GC_written_pages, GC_grungy_pages); } - -# ifdef MPROTECT_VDB - STATIC GC_bool GC_gww_page_was_dirty(struct hblk * h) -# else - GC_INNER GC_bool GC_page_was_dirty(struct hblk * h) -# endif - { - return HDR(h) == 0 || - get_pht_entry_from_index(GC_grungy_pages, PHT_HASH(h)); - } - -# ifdef CHECKSUMS - /* Used only if PROC_VDB. */ -# ifdef MPROTECT_VDB - STATIC GC_bool GC_gww_page_was_ever_dirty(struct hblk * h) -# else - GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk * h) -# endif - { - return HDR(h) == 0 || - get_pht_entry_from_index(GC_written_pages, PHT_HASH(h)); - } -# endif /* CHECKSUMS */ - -# ifndef MPROTECT_VDB - /*ARGSUSED*/ - GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, - GC_bool is_ptrfree) {} -# endif - #endif /* GWW_VDB */ #ifdef DEFAULT_VDB - /* All of the following assume the allocation lock is held. */ /* The client asserts that unallocated pages in the heap are never */ @@ -2812,12 +2785,10 @@ STATIC void GC_default_push_other_roots(void) return(TRUE); } - /* - * The following two routines are typically less crucial. They matter - * most with large dynamic libraries, or if we can't accurately identify - * stacks, e.g. under Solaris 2.X. Otherwise the following default - * versions are adequate. - */ + /* The following two routines are typically less crucial. */ + /* They matter most with large dynamic libraries, or if we can't */ + /* accurately identify stacks, e.g. under Solaris 2.X. Otherwise the */ + /* following default versions are adequate. */ # ifdef CHECKSUMS /* Could any valid GC heap pointer ever have been written to this page? */ /*ARGSUSED*/ @@ -2837,17 +2808,15 @@ STATIC void GC_default_push_other_roots(void) /*ARGSUSED*/ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree) {} - #endif /* DEFAULT_VDB */ #ifdef MANUAL_VDB - /* Initialize virtual dirty bit implementation. */ GC_INNER void GC_dirty_init(void) { if (GC_print_stats == VERBOSE) GC_log_printf("Initializing MANUAL_VDB...\n"); - /* GC_dirty_pages and GC_grungy_pages are already cleared. */ + /* GC_dirty_pages and GC_grungy_pages are already cleared. */ GC_dirty_maintained = TRUE; } @@ -2870,6 +2839,9 @@ STATIC void GC_default_push_other_roots(void) return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index)); } +# define async_set_pht_entry_from_index(db, index) \ + set_pht_entry_from_index(db, index) /* for now */ + /* Mark the page containing p as dirty. Logically, this dirties the */ /* entire object. */ void GC_dirty(ptr_t p) @@ -2885,7 +2857,7 @@ STATIC void GC_default_push_other_roots(void) # ifdef CHECKSUMS /* Could any valid GC heap pointer ever have been written to this page? */ /*ARGSUSED*/ - GC_bool GC_page_was_ever_dirty(struct hblk *h) + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk *h) { /* FIXME - implement me. */ return(TRUE); @@ -2894,9 +2866,7 @@ STATIC void GC_default_push_other_roots(void) #endif /* MANUAL_VDB */ - #ifdef MPROTECT_VDB - /* See DEFAULT_VDB for interface descriptions. */ /* @@ -2922,16 +2892,16 @@ STATIC void GC_default_push_other_roots(void) # define PROTECT(addr,len) \ if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \ FALSE, VM_PROT_READ \ - | (pages_executable ? VM_PROT_EXECUTE : 0)) \ + | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \ != KERN_SUCCESS) { \ - ABORT("vm_protect (PROTECT) failed"); \ + ABORT("vm_protect(PROTECT) failed"); \ } # define UNPROTECT(addr,len) \ if(vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len), \ FALSE, (VM_PROT_READ | VM_PROT_WRITE) \ - | (pages_executable ? VM_PROT_EXECUTE : 0)) \ + | (GC_pages_executable ? VM_PROT_EXECUTE : 0)) \ != KERN_SUCCESS) { \ - ABORT("vm_protect (UNPROTECT) failed"); \ + ABORT("vm_protect(UNPROTECT) failed"); \ } # elif !defined(MSWIN32) && !defined(MSWINCE) @@ -2940,19 +2910,19 @@ STATIC void GC_default_push_other_roots(void) # include <sys/syscall.h> # define PROTECT(addr, len) \ - if (mprotect((caddr_t)(addr), (size_t)(len), \ - PROT_READ \ - | (pages_executable ? PROT_EXEC : 0)) < 0) { \ - ABORT("mprotect failed"); \ - } + if (mprotect((caddr_t)(addr), (size_t)(len), \ + PROT_READ \ + | (GC_pages_executable ? PROT_EXEC : 0)) < 0) { \ + ABORT("mprotect failed"); \ + } # define UNPROTECT(addr, len) \ - if (mprotect((caddr_t)(addr), (size_t)(len), \ - (PROT_READ | PROT_WRITE) \ - | (pages_executable ? PROT_EXEC : 0)) < 0) { \ - ABORT(pages_executable ? "un-mprotect executable page" \ - " failed (probably disabled by OS)" : \ - "un-mprotect failed"); \ - } + if (mprotect((caddr_t)(addr), (size_t)(len), \ + (PROT_READ | PROT_WRITE) \ + | (GC_pages_executable ? PROT_EXEC : 0)) < 0) { \ + ABORT(GC_pages_executable ? "un-mprotect executable page" \ + " failed (probably disabled by OS)" : \ + "un-mprotect failed"); \ + } # undef IGNORE_PAGES_EXECUTABLE # else /* MSWIN32 */ @@ -2962,21 +2932,21 @@ STATIC void GC_default_push_other_roots(void) static DWORD protect_junk; # define PROTECT(addr, len) \ - if (!VirtualProtect((addr), (len), \ - pages_executable ? PAGE_EXECUTE_READ : \ - PAGE_READONLY, \ - &protect_junk)) { \ - if (GC_print_stats) \ - GC_printf("Last error code: 0x%lx\n", (long)GetLastError()); \ - ABORT("VirtualProtect failed"); \ - } + if (!VirtualProtect((addr), (len), \ + GC_pages_executable ? PAGE_EXECUTE_READ : \ + PAGE_READONLY, \ + &protect_junk)) { \ + if (GC_print_stats) \ + GC_log_printf("Last error code: 0x%lx\n", (long)GetLastError()); \ + ABORT("VirtualProtect failed"); \ + } # define UNPROTECT(addr, len) \ - if (!VirtualProtect((addr), (len), \ - pages_executable ? PAGE_EXECUTE_READWRITE : \ - PAGE_READWRITE, \ - &protect_junk)) { \ - ABORT("un-VirtualProtect failed"); \ - } + if (!VirtualProtect((addr), (len), \ + GC_pages_executable ? PAGE_EXECUTE_READWRITE : \ + PAGE_READWRITE, \ + &protect_junk)) { \ + ABORT("un-VirtualProtect failed"); \ + } # endif /* MSWIN32 || MSWINCE || DARWIN */ # if defined(MSWIN32) @@ -3105,7 +3075,9 @@ STATIC void GC_default_push_other_roots(void) # elif defined(SUNOS5SIGS) # define CODE_OK (si -> si_code == SEGV_ACCERR) # endif -# include <ucontext.h> +# ifndef NO_GETCONTEXT +# include <ucontext.h> +# endif /*ARGSUSED*/ STATIC void GC_write_fault_handler(int sig, siginfo_t *si, void *raw_sc) # else @@ -3113,7 +3085,7 @@ STATIC void GC_default_push_other_roots(void) == STATUS_ACCESS_VIOLATION) # define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] \ == 1) /* Write fault */ - GC_INNER LONG WINAPI GC_write_fault_handler( + STATIC LONG WINAPI GC_write_fault_handler( struct _EXCEPTION_POINTERS *exc_info) # endif /* MSWIN32 || MSWINCE */ { @@ -3139,6 +3111,7 @@ STATIC void GC_default_push_other_roots(void) for (i = 0; i < divHBLKSZ(GC_page_size); i++) { if (HDR(h+i) != 0) { in_allocd_block = TRUE; + break; } } # else @@ -3169,7 +3142,7 @@ STATIC void GC_default_push_other_roots(void) if (old_handler == (SIG_HNDLR_PTR)SIG_DFL) { # if !defined(MSWIN32) && !defined(MSWINCE) if (GC_print_stats) - GC_printf("Unexpected segfault at %p\n", addr); + GC_log_printf("Unexpected segfault at %p\n", addr); ABORT("Unexpected bus error or segmentation fault"); # else return(EXCEPTION_CONTINUE_SEARCH); @@ -3221,20 +3194,25 @@ STATIC void GC_default_push_other_roots(void) return EXCEPTION_CONTINUE_SEARCH; # else if (GC_print_stats) - GC_printf("Unexpected segfault at %p\n", addr); + GC_log_printf("Unexpected segfault at %p\n", addr); ABORT("Unexpected bus error or segmentation fault"); # endif } + +# ifdef GC_WIN32_THREADS + GC_INNER void GC_set_write_fault_handler(void) + { + SetUnhandledExceptionFilter(GC_write_fault_handler); + } +# endif #endif /* !DARWIN */ -/* - * We hold the allocation lock. We expect block h to be written - * shortly. Ensure that all pages containing any part of the n hblks - * starting at h are no longer protected. If is_ptrfree is false, - * also ensure that they will subsequently appear to be dirty. - * Not allowed to call GC_printf (and the friends) here, see Win32 - * GC_stop_world() for the information. - */ +/* We hold the allocation lock. We expect block h to be written */ +/* shortly. Ensure that all pages containing any part of the n hblks */ +/* starting at h are no longer protected. If is_ptrfree is false, also */ +/* ensure that they will subsequently appear to be dirty. Not allowed */ +/* to call GC_printf (and the friends) here, see Win32 GC_stop_world() */ +/* for the information. */ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, GC_bool is_ptrfree) { @@ -3279,7 +3257,7 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, # endif /* SIG_SUSPEND */ # endif if (GC_print_stats == VERBOSE) - GC_log_printf( + GC_log_printf( "Initializing mprotect virtual dirty bit implementation\n"); GC_dirty_maintained = TRUE; if (GC_page_size % HBLKSIZE != 0) { @@ -3578,66 +3556,57 @@ ssize_t read(int fd, void *buf, size_t nbyte) /* We should probably also do this for __read, or whatever stdio */ /* actually calls. */ #endif - #endif /* 0 */ -#ifdef CHECKSUMS - /*ARGSUSED*/ - GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk *h) - { -# if defined(GWW_VDB) - if (GC_GWW_AVAILABLE()) - return GC_gww_page_was_ever_dirty(h); -# endif - return(TRUE); - } -#endif /* CHECKSUMS */ +# ifdef CHECKSUMS + /*ARGSUSED*/ + GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk *h) + { +# if defined(GWW_VDB) + if (GC_GWW_AVAILABLE()) + return GC_gww_page_was_ever_dirty(h); +# endif + return(TRUE); + } +# endif /* CHECKSUMS */ -# endif /* MPROTECT_VDB */ +#endif /* MPROTECT_VDB */ -# ifdef PROC_VDB +#ifdef PROC_VDB +/* See DEFAULT_VDB for interface descriptions. */ -/* - * See DEFAULT_VDB for interface descriptions. - */ +/* This implementation assumes a Solaris 2.X like /proc */ +/* pseudo-file-system from which we can read page modified bits. This */ +/* facility is far from optimal (e.g. we would like to get the info for */ +/* only some of the address space), but it avoids intercepting system */ +/* calls. */ -/* - * This implementation assumes a Solaris 2.X like /proc pseudo-file-system - * from which we can read page modified bits. This facility is far from - * optimal (e.g. we would like to get the info for only some of the - * address space), but it avoids intercepting system calls. - */ - -#include <errno.h> -#include <sys/types.h> -#include <sys/signal.h> -#include <sys/fault.h> -#include <sys/syscall.h> -#include <sys/procfs.h> -#include <sys/stat.h> - -#define INITIAL_BUF_SZ 16384 -STATIC word GC_proc_buf_size = INITIAL_BUF_SZ; -STATIC char *GC_proc_buf = NULL; +# include <errno.h> +# include <sys/types.h> +# include <sys/signal.h> +# include <sys/fault.h> +# include <sys/syscall.h> +# include <sys/procfs.h> +# include <sys/stat.h> -STATIC int GC_proc_fd = 0; +# define INITIAL_BUF_SZ 16384 + STATIC word GC_proc_buf_size = INITIAL_BUF_SZ; + STATIC char *GC_proc_buf = NULL; + STATIC int GC_proc_fd = 0; GC_INNER void GC_dirty_init(void) { int fd; char buf[30]; - GC_dirty_maintained = TRUE; if (GC_bytes_allocd != 0 || GC_bytes_allocd_before_gc != 0) { - register int i; - - for (i = 0; i < PHT_SIZE; i++) GC_written_pages[i] = (word)(-1); - if (GC_print_stats == VERBOSE) - GC_log_printf( - "Allocated bytes:%lu:all pages may have been written\n", - (unsigned long) - (GC_bytes_allocd + GC_bytes_allocd_before_gc)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + if (GC_print_stats == VERBOSE) + GC_log_printf("Allocated bytes:%lu:all pages may have been written\n", + (unsigned long)(GC_bytes_allocd + + GC_bytes_allocd_before_gc)); } + sprintf(buf, "/proc/%ld", (long)getpid()); fd = open(buf, O_RDONLY); if (fd < 0) { @@ -3647,104 +3616,98 @@ GC_INNER void GC_dirty_init(void) close(fd); syscall(SYS_fcntl, GC_proc_fd, F_SETFD, FD_CLOEXEC); if (GC_proc_fd < 0) { - ABORT("/proc ioctl failed"); + WARN("/proc ioctl(PIOCOPENPD) failed", 0); + return; } + + GC_dirty_maintained = TRUE; GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size); } -/* Ignore write hints. They don't help us here. */ -/*ARGSUSED*/ -GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, - GC_bool is_ptrfree) {} - -# define READ(fd,buf,nbytes) read(fd, buf, nbytes) +# define READ read GC_INNER void GC_read_dirty(void) { - unsigned long ps, np; int nmaps; - ptr_t vaddr; + unsigned long npages; + unsigned pagesize; + ptr_t vaddr, limit; struct prasmap * map; char * bufp; - ptr_t current_addr, limit; int i; - BZERO(GC_grungy_pages, (sizeof GC_grungy_pages)); - + BZERO(GC_grungy_pages, sizeof(GC_grungy_pages)); bufp = GC_proc_buf; if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + /* Retry with larger buffer. */ + word new_size = 2 * GC_proc_buf_size; + char *new_buf; if (GC_print_stats) - GC_log_printf("/proc read failed: GC_proc_buf_size = %lu\n", - (unsigned long)GC_proc_buf_size); - { - /* Retry with larger buffer. */ - word new_size = 2 * GC_proc_buf_size; - char * new_buf = GC_scratch_alloc(new_size); + GC_err_printf("/proc read failed: GC_proc_buf_size = %lu\n", + (unsigned long)GC_proc_buf_size); - if (new_buf != 0) { - GC_proc_buf = bufp = new_buf; - GC_proc_buf_size = new_size; - } - if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { - WARN("Insufficient space for /proc read\n", 0); - /* Punt: */ - memset(GC_grungy_pages, 0xff, sizeof (page_hash_table)); - memset(GC_written_pages, 0xff, sizeof(page_hash_table)); - return; - } + new_buf = GC_scratch_alloc(new_size); + if (new_buf != 0) { + GC_proc_buf = bufp = new_buf; + GC_proc_buf_size = new_size; + } + if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + WARN("Insufficient space for /proc read\n", 0); + /* Punt: */ + memset(GC_grungy_pages, 0xff, sizeof (page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); + return; } } - /* Copy dirty bits into GC_grungy_pages */ - nmaps = ((struct prpageheader *)bufp) -> pr_nmap; - /* printf( "nmaps = %d, PG_REFERENCED = %d, PG_MODIFIED = %d\n", - nmaps, PG_REFERENCED, PG_MODIFIED); */ - bufp = bufp + sizeof(struct prpageheader); - for (i = 0; i < nmaps; i++) { - map = (struct prasmap *)bufp; - vaddr = (ptr_t)(map -> pr_vaddr); - ps = map -> pr_pagesize; - np = map -> pr_npage; - /* printf("vaddr = 0x%X, ps = 0x%X, np = 0x%X\n", vaddr, ps, np); */ - limit = vaddr + ps * np; - bufp += sizeof (struct prasmap); - for (current_addr = vaddr; - current_addr < limit; current_addr += ps) { - if ((*bufp++) & PG_MODIFIED) { - register struct hblk * h = (struct hblk *) current_addr; - - while ((ptr_t)h < current_addr + ps) { - register word index = PHT_HASH(h); - - set_pht_entry_from_index(GC_grungy_pages, index); - h++; - } + + /* Copy dirty bits into GC_grungy_pages */ + nmaps = ((struct prpageheader *)bufp) -> pr_nmap; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read: pr_nmap= %u, pr_npage= %lu\n", + nmaps, ((struct prpageheader *)bufp)->pr_npage); + +# endif + bufp += sizeof(struct prpageheader); + for (i = 0; i < nmaps; i++) { + map = (struct prasmap *)bufp; + vaddr = (ptr_t)(map -> pr_vaddr); + npages = map -> pr_npage; + pagesize = map -> pr_pagesize; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf( + "pr_vaddr= %p, npage= %lu, mflags= 0x%x, pagesize= 0x%x\n", + vaddr, npages, map->pr_mflags, pagesize); +# endif + + bufp += sizeof(struct prasmap); + limit = vaddr + pagesize * npages; + for (; vaddr < limit; vaddr += pagesize) { + if ((*bufp++) & PG_MODIFIED) { + register struct hblk * h; + ptr_t next_vaddr = vaddr + pagesize; +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("dirty page at: %p\n", vaddr); +# endif + for (h = (struct hblk *)vaddr; (ptr_t)h < next_vaddr; h++) { + register word index = PHT_HASH(h); + set_pht_entry_from_index(GC_grungy_pages, index); } } - bufp += sizeof(long) - 1; - bufp = (char *)((unsigned long)bufp & ~(sizeof(long)-1)); } - /* Update GC_written_pages. */ - GC_or_pages(GC_written_pages, GC_grungy_pages); -} - -#undef READ - -GC_INNER GC_bool GC_page_was_dirty(struct hblk *h) -{ - register word index = PHT_HASH(h); - return get_pht_entry_from_index(GC_grungy_pages, index); -} + bufp = (char *)(((word)bufp + (sizeof(long)-1)) & ~(sizeof(long)-1)); + } +# ifdef DEBUG_DIRTY_BITS + GC_log_printf("Proc VDB read done.\n"); +# endif -GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk *h) -{ - register word index = PHT_HASH(h); - return get_pht_entry_from_index(GC_written_pages, index); + /* Update GC_written_pages. */ + GC_or_pages(GC_written_pages, GC_grungy_pages); } -# endif /* PROC_VDB */ - +# undef READ +#endif /* PROC_VDB */ -# ifdef PCR_VDB +#ifdef PCR_VDB # include "vd/PCR_VD.h" @@ -3766,7 +3729,7 @@ GC_INNER void GC_dirty_init(void) } if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE) != PCR_ERes_okay) { - ABORT("dirty bit initialization failed"); + ABORT("Dirty bit initialization failed"); } } @@ -3785,7 +3748,7 @@ GC_INNER void GC_read_dirty(void) if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits) != PCR_ERes_okay) { - ABORT("dirty bit read failed"); + ABORT("Dirty bit read failed"); } } @@ -3805,7 +3768,7 @@ GC_INNER void GC_remove_protection(struct hblk *h, word nblocks, PCR_VD_WriteProtectEnable(h, nblocks*HBLKSIZE); } -# endif /* PCR_VDB */ +#endif /* PCR_VDB */ #if defined(MPROTECT_VDB) && defined(DARWIN) /* The following sources were used as a "reference" for this exception @@ -3864,7 +3827,7 @@ catch_exception_raise_state(mach_port_name_t exception_port, int exception, thread_state_t old_state, int old_stateCnt, thread_state_t new_state, int new_stateCnt) { - ABORT("catch_exception_raise_state"); + ABORT("Unexpected catch_exception_raise_state invocation"); return(KERN_INVALID_ARGUMENT); } @@ -3876,7 +3839,7 @@ catch_exception_raise_state_identity(mach_port_name_t exception_port, thread_state_t old_state, int old_stateCnt, thread_state_t new_state, int new_stateCnt) { - ABORT("catch_exception_raise_state_identity"); + ABORT("Unexpected catch_exception_raise_state_identity invocation"); return(KERN_INVALID_ARGUMENT); } @@ -3950,7 +3913,7 @@ typedef enum { if (r != MACH_MSG_SUCCESS) ABORT("mach_msg failed in GC_mprotect_thread_notify"); if (buf.msg.head.msgh_id != ID_ACK) - ABORT("invalid ack in GC_mprotect_thread_notify"); + ABORT("Invalid ack in GC_mprotect_thread_notify"); } /* Should only be called by the mprotect thread */ @@ -3986,17 +3949,17 @@ typedef enum { GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread); # endif -#else /* !THREADS */ +#else /* The compiler should optimize away any GC_mprotect_state computations */ # define GC_mprotect_state GC_MP_NORMAL -#endif +#endif /* !THREADS */ STATIC void *GC_mprotect_thread(void *arg) { mach_msg_return_t r; - /* These two structures contain some private kernel data. We don't need to - access any of it so we don't bother defining a proper struct. The - correct definitions are in the xnu source code. */ + /* These two structures contain some private kernel data. We don't */ + /* need to access any of it so we don't bother defining a proper */ + /* struct. The correct definitions are in the xnu source code. */ struct { mach_msg_header_t head; char data[256]; @@ -4028,14 +3991,14 @@ STATIC void *GC_mprotect_thread(void *arg) continue; } if(r == MACH_MSG_SUCCESS && (id == ID_STOP || id == ID_RESUME)) - ABORT("out of order mprotect thread request"); + ABORT("Out of order mprotect thread request"); } # endif /* THREADS */ if (r != MACH_MSG_SUCCESS) { if (GC_print_stats) - GC_printf("mach_msg failed with code %d: %s\n", (int)r, - mach_error_string(r)); + GC_log_printf("mach_msg failed with code %d: %s\n", (int)r, + mach_error_string(r)); ABORT("mach_msg failed"); } @@ -4113,8 +4076,8 @@ GC_INNER void GC_dirty_init(void) exception_mask_t mask; if (GC_print_stats == VERBOSE) - GC_log_printf("Initializing mach/darwin mprotect virtual dirty bit " - "implementation\n"); + GC_log_printf( + "Initializing mach/darwin mprotect virtual dirty bit implementation\n"); # ifdef BROKEN_EXCEPTION_HANDLING WARN("Enabling workarounds for various darwin " "exception handling bugs.\n", 0); @@ -4174,7 +4137,7 @@ GC_INNER void GC_dirty_init(void) sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART|SA_SIGINFO; if (sigaction(SIGBUS, &sa, &oldsa) < 0) - ABORT("sigaction"); + ABORT("sigaction failed"); if ((SIG_HNDLR_PTR)oldsa.sa_handler != SIG_DFL) { if (GC_print_stats == VERBOSE) GC_err_printf("Replaced other SIGBUS handler\n"); @@ -4183,9 +4146,9 @@ GC_INNER void GC_dirty_init(void) # endif /* BROKEN_EXCEPTION_HANDLING */ } -/* The source code for Apple's GDB was used as a reference for the exception - forwarding code. This code is similar to be GDB code only because there is - only one way to do it. */ +/* The source code for Apple's GDB was used as a reference for the */ +/* exception forwarding code. This code is similar to be GDB code only */ +/* because there is only one way to do it. */ STATIC kern_return_t GC_forward_exception(mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t data, @@ -4294,8 +4257,9 @@ catch_exception_raise(mach_port_t exception_port, mach_port_t thread, if (exception != EXC_BAD_ACCESS || code[0] != KERN_PROTECTION_FAILURE) { # ifdef DEBUG_EXCEPTION_HANDLING /* We aren't interested, pass it on to the old handler */ - GC_printf("Exception: 0x%x Code: 0x%x 0x%x in catch...\n", exception, - code_count > 0 ? code[0] : -1, code_count > 1 ? code[1] : -1); + GC_log_printf("Exception: 0x%x Code: 0x%x 0x%x in catch...\n", + exception, code_count > 0 ? code[0] : -1, + code_count > 1 ? code[1] : -1); # endif return FWD(); } @@ -4303,8 +4267,8 @@ catch_exception_raise(mach_port_t exception_port, mach_port_t thread, r = thread_get_state(thread, flavor, (natural_t*)&exc_state, &exc_state_count); if(r != KERN_SUCCESS) { - /* The thread is supposed to be suspended while the exception handler - is called. This shouldn't fail. */ + /* The thread is supposed to be suspended while the exception */ + /* handler is called. This shouldn't fail. */ # ifdef BROKEN_EXCEPTION_HANDLING GC_err_printf("thread_get_state failed in catch_exception_raise\n"); return KERN_SUCCESS; @@ -4316,11 +4280,11 @@ catch_exception_raise(mach_port_t exception_port, mach_port_t thread, /* This is the address that caused the fault */ addr = (char*) exc_state.DARWIN_EXC_STATE_DAR; if (HDR(addr) == 0) { - /* Ugh... just like the SIGBUS problem above, it seems we get a bogus - KERN_PROTECTION_FAILURE every once and a while. We wait till we get - a bunch in a row before doing anything about it. If a "real" fault - ever occurs it'll just keep faulting over and over and we'll hit - the limit pretty quickly. */ + /* Ugh... just like the SIGBUS problem above, it seems we get */ + /* a bogus KERN_PROTECTION_FAILURE every once and a while. We wait */ + /* till we get a bunch in a row before doing anything about it. */ + /* If a "real" fault ever occurs it'll just keep faulting over and */ + /* over and we'll hit the limit pretty quickly. */ # ifdef BROKEN_EXCEPTION_HANDLING static char *last_fault; static int last_fault_count; @@ -4335,11 +4299,11 @@ catch_exception_raise(mach_port_t exception_port, mach_port_t thread, return KERN_SUCCESS; } - GC_err_printf("Unexpected KERN_PROTECTION_FAILURE at %p\n" - "Aborting...\n", addr); - /* Can't pass it along to the signal handler because that is - ignoring SIGBUS signals. We also shouldn't call ABORT here as - signals don't always work too well from the exception handler. */ + GC_err_printf( + "Unexpected KERN_PROTECTION_FAILURE at %p; aborting...\n", addr); + /* Can't pass it along to the signal handler because that is */ + /* ignoring SIGBUS signals. We also shouldn't call ABORT here as */ + /* signals don't always work too well from the exception handler. */ exit(EXIT_FAILURE); # else /* BROKEN_EXCEPTION_HANDLING */ /* Pass it along to the next exception handler @@ -4399,9 +4363,9 @@ catch_exception_raise(mach_port_t exception_port, mach_port_t thread, GC_API void GC_CALL GC_set_pages_executable(int value) { GC_ASSERT(!GC_is_initialized); - /* Even if IGNORE_PAGES_EXECUTABLE is defined, pages_executable is */ + /* Even if IGNORE_PAGES_EXECUTABLE is defined, GC_pages_executable is */ /* touched here to prevent a compiler warning. */ - pages_executable = (GC_bool)(value != 0); + GC_pages_executable = (GC_bool)(value != 0); } /* Returns non-zero if the GC-allocated memory is executable. */ @@ -4412,14 +4376,12 @@ GC_API int GC_CALL GC_get_pages_executable(void) # ifdef IGNORE_PAGES_EXECUTABLE return 1; /* Always allocate executable memory. */ # else - return (int)pages_executable; + return (int)GC_pages_executable; # endif } -/* - * Call stack save code for debugging. - * Should probably be in mach_dep.c, but that requires reorganization. - */ +/* Call stack save code for debugging. Should probably be in */ +/* mach_dep.c, but that requires reorganization. */ /* I suspect the following works for most X86 *nix variants, so */ /* long as the frame pointer is explicitly stored. In the case of gcc, */ @@ -4459,7 +4421,7 @@ GC_API int GC_CALL GC_get_pages_executable(void) # include <sys/frame.h> # endif # if NARGS > 6 -# error We only know how to to get the first 6 arguments +# error We only know how to get the first 6 arguments # endif #endif /* SPARC */ @@ -4724,21 +4686,12 @@ GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]) #endif /* NEED_CALLINFO */ #if defined(LINUX) && defined(__ELF__) && !defined(SMALL_CONFIG) - -/* Dump /proc/self/maps to GC_stderr, to enable looking up names for - addresses in FIND_LEAK output. */ - -static word dump_maps(char *maps) -{ - GC_err_write(maps, strlen(maps)); - return 1; -} - -void GC_print_address_map(void) -{ + /* Dump /proc/self/maps to GC_stderr, to enable looking up names for */ + /* addresses in FIND_LEAK output. */ + void GC_print_address_map(void) + { GC_err_printf("---------- Begin address map ----------\n"); - dump_maps(GC_get_maps()); + GC_err_puts(GC_get_maps()); GC_err_printf("---------- End address map ----------\n"); -} - + } #endif /* LINUX && ELF */ diff --git a/pthread_start.c b/pthread_start.c index cbe5a857..9d3556b4 100644 --- a/pthread_start.c +++ b/pthread_start.c @@ -41,12 +41,6 @@ #include <pthread.h> #include <sched.h> -GC_INNER void * GC_start_rtn_prepare_thread(void *(**pstart)(void *), - void **pstart_arg, - struct GC_stack_base *sb, void *arg); -GC_INNER void GC_thread_exit_proc(void *arg); - /* defined in pthread_support.c */ - /* Invoked from GC_start_routine(). */ void * GC_CALLBACK GC_inner_start_routine(struct GC_stack_base *sb, void *arg) { @@ -56,11 +50,11 @@ void * GC_CALLBACK GC_inner_start_routine(struct GC_stack_base *sb, void *arg) GC_thread me = GC_start_rtn_prepare_thread(&start, &start_arg, sb, arg); # ifndef NACL - pthread_cleanup_push(GC_thread_exit_proc, 0); + pthread_cleanup_push(GC_thread_exit_proc, me); # endif result = (*start)(start_arg); # ifdef DEBUG_THREADS - GC_printf("Finishing thread 0x%x\n", (unsigned)pthread_self()); + GC_log_printf("Finishing thread 0x%x\n", (unsigned)pthread_self()); # endif me -> status = result; # ifndef NACL diff --git a/pthread_stop_world.c b/pthread_stop_world.c index 0ef0d221..9302f93f 100644 --- a/pthread_stop_world.c +++ b/pthread_stop_world.c @@ -43,6 +43,9 @@ int GC_nacl_thread_used[MAX_NACL_GC_THREADS]; #include <unistd.h> #include "atomic_ops.h" +/* It's safe to call original pthread_sigmask() here. */ +#undef pthread_sigmask + #ifdef DEBUG_THREADS # ifndef NSIG # if defined(MAXSIG) @@ -56,19 +59,17 @@ int GC_nacl_thread_used[MAX_NACL_GC_THREADS]; # endif # endif /* NSIG */ - /* It's safe to call original pthread_sigmask() here. */ -# undef pthread_sigmask - void GC_print_sig_mask(void) { sigset_t blocked; int i; if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) - ABORT("pthread_sigmask"); + ABORT("pthread_sigmask failed"); GC_printf("Blocked: "); for (i = 1; i < NSIG; i++) { - if (sigismember(&blocked, i)) { GC_printf("%d ", i); } + if (sigismember(&blocked, i)) + GC_printf("%d ", i); } GC_printf("\n"); } @@ -128,7 +129,8 @@ STATIC volatile AO_t GC_world_is_stopped = FALSE; */ #ifndef SIG_THR_RESTART -# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) || defined(GC_NETBSD_THREADS) +# if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS) \ + || defined(GC_NETBSD_THREADS) # ifdef _SIGRTMIN # define SIG_THR_RESTART _SIGRTMIN + 5 # else @@ -139,6 +141,20 @@ STATIC volatile AO_t GC_world_is_stopped = FALSE; # endif #endif +#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Some targets (eg., Solaris) might require this to be called when */ + /* doing thread registering from the thread destructor. */ + GC_INNER void GC_unblock_gc_signals(void) + { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIG_SUSPEND); + sigaddset(&set, SIG_THR_RESTART); + if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) + ABORT("pthread_sigmask failed"); + } +#endif /* GC_EXPLICIT_SIGNALS_UNBLOCK */ + STATIC sem_t GC_suspend_ack_sem; #ifdef GC_NETBSD_THREADS @@ -176,15 +192,13 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context); /*ARGSUSED*/ STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context) { - int sig = (int)(word)sig_arg; - int dummy; - pthread_t my_thread = pthread_self(); + pthread_t self = pthread_self(); GC_thread me; IF_CANCEL(int cancel_state;) - AO_t my_stop_count = AO_load(&GC_stop_count); - if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); + if ((signed_word)sig_arg != SIG_SUSPEND) + ABORT("Bad signal in suspend_handler"); DISABLE_CANCEL(cancel_state); /* pthread_setcancelstate is not defined to be async-signal-safe. */ @@ -196,10 +210,10 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context) /* cancellation point is inherently a problem, unless there is */ /* some way to disable cancellation in the handler. */ # ifdef DEBUG_THREADS - GC_printf("Suspending 0x%x\n", (unsigned)my_thread); + GC_log_printf("Suspending 0x%x\n", (unsigned)self); # endif - me = GC_lookup_thread(my_thread); + me = GC_lookup_thread(self); /* The lookup here is safe, since I'm doing this on behalf */ /* of a thread which holds the allocation lock in order */ /* to stop the world. Thus concurrent modification of the */ @@ -207,7 +221,7 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context) if (me -> stop_info.last_stop_count == my_stop_count) { /* Duplicate signal. OK if we are retrying. */ if (!GC_retry_signals) { - WARN("Duplicate suspend signal in thread %p\n", pthread_self()); + WARN("Duplicate suspend signal in thread %p\n", self); } RESTORE_CANCEL(cancel_state); return; @@ -215,7 +229,7 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context) # ifdef SPARC me -> stop_info.stack_ptr = GC_save_regs_in_stack(); # else - me -> stop_info.stack_ptr = (ptr_t)(&dummy); + me -> stop_info.stack_ptr = (ptr_t)(&me); # endif # ifdef IA64 me -> backing_store_ptr = GC_save_regs_in_stack(); @@ -250,7 +264,7 @@ STATIC void GC_suspend_handler_inner(ptr_t sig_arg, void *context) /* unlikely to be efficient. */ # ifdef DEBUG_THREADS - GC_printf("Continuing 0x%x\n", (unsigned)my_thread); + GC_log_printf("Continuing 0x%x\n", (unsigned)self); # endif RESTORE_CANCEL(cancel_state); } @@ -272,7 +286,8 @@ STATIC void GC_restart_handler(int sig) */ # ifdef DEBUG_THREADS - GC_printf("In GC_restart_handler for 0x%x\n", (unsigned)pthread_self()); + GC_log_printf("In GC_restart_handler for 0x%x\n", + (unsigned)pthread_self()); # endif } @@ -294,18 +309,18 @@ GC_INNER void GC_push_all_stacks(void) ptr_t lo, hi; /* On IA64, we also need to scan the register backing store. */ IF_IA64(ptr_t bs_lo; ptr_t bs_hi;) - pthread_t me = pthread_self(); + pthread_t self = pthread_self(); word total_size = 0; if (!GC_thr_initialized) GC_thr_init(); # ifdef DEBUG_THREADS - GC_printf("Pushing stacks from thread 0x%x\n", (unsigned) me); + GC_log_printf("Pushing stacks from thread 0x%x\n", (unsigned)self); # endif for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { if (p -> flags & FINISHED) continue; ++nthreads; - if (THREAD_EQUAL(p -> id, me)) { + if (THREAD_EQUAL(p -> id, self)) { GC_ASSERT(!p->thread_blocked); # ifdef SPARC lo = (ptr_t)GC_save_regs_in_stack(); @@ -327,8 +342,8 @@ GC_INNER void GC_push_all_stacks(void) IF_IA64(bs_lo = BACKING_STORE_BASE;) } # ifdef DEBUG_THREADS - GC_printf("Stack for thread 0x%x = [%p,%p)\n", - (unsigned)(p -> id), lo, hi); + GC_log_printf("Stack for thread 0x%x = [%p,%p)\n", + (unsigned)(p -> id), lo, hi); # endif if (0 == lo) ABORT("GC_push_all_stacks: sp not set!"); GC_push_all_stack_sections(lo, hi, p -> traced_stack_sect); @@ -345,23 +360,23 @@ GC_INNER void GC_push_all_stacks(void) # endif # ifdef IA64 # ifdef DEBUG_THREADS - GC_printf("Reg stack for thread 0x%x = [%p,%p)\n", - (unsigned)p -> id, bs_lo, bs_hi); + GC_log_printf("Reg stack for thread 0x%x = [%p,%p)\n", + (unsigned)p -> id, bs_lo, bs_hi); # endif - /* FIXME: This (if p->id==me) may add an unbounded number of */ - /* entries, and hence overflow the mark stack, which is bad. */ + /* FIXME: This (if p->id==self) may add an unbounded number of */ + /* entries, and hence overflow the mark stack, which is bad. */ GC_push_all_register_sections(bs_lo, bs_hi, - THREAD_EQUAL(p -> id, me), + THREAD_EQUAL(p -> id, self), p -> traced_stack_sect); total_size += bs_hi - bs_lo; /* bs_lo <= bs_hi */ # endif } } if (GC_print_stats == VERBOSE) { - GC_log_printf("Pushed %d thread stacks\n", (int)nthreads); + GC_log_printf("Pushed %d thread stacks\n", (int)nthreads); } if (!found_me && !GC_in_thread_creation) - ABORT("Collecting from unknown thread."); + ABORT("Collecting from unknown thread"); GC_total_stacksize = total_size; } @@ -373,6 +388,8 @@ GC_INNER void GC_push_all_stacks(void) #endif #ifdef PLATFORM_ANDROID + extern int tkill(pid_t tid, int sig); /* from sys/linux-unistd.h */ + static int android_thread_kill(pid_t tid, int sig) { int ret; @@ -401,15 +418,15 @@ STATIC int GC_suspend_all(void) # ifndef GC_OPENBSD_THREADS int result; # endif - pthread_t my_thread = pthread_self(); + pthread_t self = pthread_self(); # ifdef DEBUG_THREADS - GC_stopping_thread = my_thread; + GC_stopping_thread = self; GC_stopping_pid = getpid(); # endif for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { - if (!THREAD_EQUAL(p -> id, my_thread)) { + if (!THREAD_EQUAL(p -> id, self)) { if (p -> flags & FINISHED) continue; if (p -> thread_blocked) /* Will wait */ continue; # ifndef GC_OPENBSD_THREADS @@ -417,8 +434,8 @@ STATIC int GC_suspend_all(void) n_live_threads++; # endif # ifdef DEBUG_THREADS - GC_printf("Sending suspend signal to 0x%x\n", - (unsigned)(p -> id)); + GC_log_printf("Sending suspend signal to 0x%x\n", + (unsigned)(p -> id)); # endif # ifdef GC_OPENBSD_THREADS @@ -457,8 +474,8 @@ STATIC int GC_suspend_all(void) # define NACL_PARK_WAIT_NANOSECONDS (100 * 1000) # endif # ifdef DEBUG_THREADS - GC_printf("pthread_stop_world: num_threads %d\n", - GC_nacl_num_gc_threads - 1); + GC_log_printf("pthread_stop_world: num_threads %d\n", + GC_nacl_num_gc_threads - 1); # endif GC_nacl_thread_parker = pthread_self(); GC_nacl_park_threads_now = 1; @@ -488,8 +505,8 @@ STATIC int GC_suspend_all(void) ts.tv_sec = 0; ts.tv_nsec = NACL_PARK_WAIT_NANOSECONDS; # ifdef DEBUG_THREADS - GC_printf("Sleep waiting for %d threads to park...\n", - GC_nacl_num_gc_threads - num_threads_parked - 1); + GC_log_printf("Sleep waiting for %d threads to park...\n", + GC_nacl_num_gc_threads - num_threads_parked - 1); # endif /* This requires _POSIX_TIMERS feature. */ nanosleep(&ts, 0); @@ -507,7 +524,7 @@ GC_INNER void GC_stop_world(void) # endif GC_ASSERT(I_HOLD_LOCK()); # ifdef DEBUG_THREADS - GC_printf("Stopping the world from 0x%x\n", (unsigned)pthread_self()); + GC_log_printf("Stopping the world from 0x%x\n", (unsigned)pthread_self()); # endif /* Make sure all free list construction has stopped before we start. */ @@ -543,8 +560,7 @@ GC_INNER void GC_stop_world(void) int newly_sent = GC_suspend_all(); if (GC_print_stats) { - GC_log_printf("Resent %d signals after timeout\n", - newly_sent); + GC_log_printf("Resent %d signals after timeout\n", newly_sent); } sem_getvalue(&GC_suspend_ack_sem, &ack_count); if (newly_sent < n_live_threads - ack_count) { @@ -577,7 +593,7 @@ GC_INNER void GC_stop_world(void) GC_release_mark_lock(); # endif # ifdef DEBUG_THREADS - GC_printf("World stopped from 0x%x\n", (unsigned)pthread_self()); + GC_log_printf("World stopped from 0x%x\n", (unsigned)pthread_self()); GC_stopping_thread = 0; # endif } @@ -712,7 +728,7 @@ GC_INNER void GC_stop_world(void) GC_INNER void GC_start_world(void) { # ifndef NACL - pthread_t my_thread = pthread_self(); + pthread_t self = pthread_self(); register int i; register GC_thread p; # ifndef GC_OPENBSD_THREADS @@ -724,7 +740,7 @@ GC_INNER void GC_start_world(void) # endif # ifdef DEBUG_THREADS - GC_printf("World starting\n"); + GC_log_printf("World starting\n"); # endif # ifndef GC_OPENBSD_THREADS @@ -732,15 +748,15 @@ GC_INNER void GC_start_world(void) # endif for (i = 0; i < THREAD_TABLE_SZ; i++) { for (p = GC_threads[i]; p != 0; p = p -> next) { - if (!THREAD_EQUAL(p -> id, my_thread)) { + if (!THREAD_EQUAL(p -> id, self)) { if (p -> flags & FINISHED) continue; if (p -> thread_blocked) continue; # ifndef GC_OPENBSD_THREADS n_live_threads++; # endif # ifdef DEBUG_THREADS - GC_printf("Sending restart signal to 0x%x\n", - (unsigned)(p -> id)); + GC_log_printf("Sending restart signal to 0x%x\n", + (unsigned)(p -> id)); # endif # ifdef GC_OPENBSD_THREADS @@ -767,20 +783,22 @@ GC_INNER void GC_start_world(void) } } # ifdef GC_NETBSD_THREADS_WORKAROUND - for (i = 0; i < n_live_threads; i++) - while (0 != (code = sem_wait(&GC_restart_ack_sem))) - if (errno != EINTR) { - if (GC_print_stats) - GC_printf("sem_wait() returned %d\n", code); - ABORT("sem_wait() for restart handler failed"); - } + for (i = 0; i < n_live_threads; i++) { + while (0 != (code = sem_wait(&GC_restart_ack_sem))) { + if (errno != EINTR) { + if (GC_print_stats) + GC_log_printf("sem_wait() returned %d\n", code); + ABORT("sem_wait() for restart handler failed"); + } + } + } # endif # ifdef DEBUG_THREADS - GC_printf("World started\n"); + GC_log_printf("World started\n"); # endif # else /* NACL */ # ifdef DEBUG_THREADS - GC_printf("World starting...\n"); + GC_log_printf("World starting...\n"); # endif GC_nacl_park_threads_now = 0; # endif @@ -791,10 +809,10 @@ GC_INNER void GC_stop_init(void) # if !defined(GC_OPENBSD_THREADS) && !defined(NACL) struct sigaction act; - if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0) + if (sem_init(&GC_suspend_ack_sem, GC_SEM_INIT_PSHARED, 0) != 0) ABORT("sem_init failed"); # ifdef GC_NETBSD_THREADS_WORKAROUND - if (sem_init(&GC_restart_ack_sem, 0, 0) != 0) + if (sem_init(&GC_restart_ack_sem, GC_SEM_INIT_PSHARED, 0) != 0) ABORT("sem_init failed"); # endif @@ -840,7 +858,7 @@ GC_INNER void GC_stop_init(void) GC_retry_signals = FALSE; } if (GC_print_stats && GC_retry_signals) { - GC_log_printf("Will retry suspend signal if necessary.\n"); + GC_log_printf("Will retry suspend signal if necessary\n"); } # endif /* !GC_OPENBSD_THREADS && !NACL */ } diff --git a/pthread_support.c b/pthread_support.c index 68384bc6..bcdfebd1 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -288,12 +288,12 @@ STATIC long GC_nprocs = 1; /* a guess as any ... */ #ifdef THREAD_LOCAL_ALLOC -/* We must explicitly mark ptrfree and gcj free lists, since the free */ -/* list links wouldn't otherwise be found. We also set them in the */ -/* normal free lists, since that involves touching less memory than if */ -/* we scanned them normally. */ -GC_INNER void GC_mark_thread_local_free_lists(void) -{ + /* We must explicitly mark ptrfree and gcj free lists, since the free */ + /* list links wouldn't otherwise be found. We also set them in the */ + /* normal free lists, since that involves touching less memory than */ + /* if we scanned them normally. */ + GC_INNER void GC_mark_thread_local_free_lists(void) + { int i; GC_thread p; @@ -303,9 +303,9 @@ GC_INNER void GC_mark_thread_local_free_lists(void) GC_mark_thread_local_fls_for(&(p->tlfs)); } } -} + } -#if defined(GC_ASSERTIONS) +# if defined(GC_ASSERTIONS) void GC_check_tls_for(GC_tlfs p); # if defined(USE_CUSTOM_SPECIFIC) void GC_check_tsd_marks(tsd *key); @@ -328,9 +328,8 @@ GC_INNER void GC_mark_thread_local_free_lists(void) GC_check_tsd_marks(GC_thread_key); # endif } -#endif /* GC_ASSERTIONS */ - -#endif /* Thread_local_alloc */ +# endif /* GC_ASSERTIONS */ +#endif /* THREAD_LOCAL_ALLOC */ #ifdef PARALLEL_MARK @@ -388,8 +387,8 @@ STATIC void * GC_mark_thread(void * id) my_mark_no = GC_mark_no; } # ifdef DEBUG_THREADS - GC_printf("Starting mark helper for mark number %lu\n", - (unsigned long)my_mark_no); + GC_log_printf("Starting mark helper for mark number %lu\n", + (unsigned long)my_mark_no); # endif GC_help_marker(my_mark_no); } @@ -423,7 +422,7 @@ static void start_mark_threads(void) ABORT("pthread_attr_getstacksize failed"); if (old_size < MIN_STACK_SIZE) { if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0) - ABORT("pthread_attr_setstacksize failed"); + ABORT("pthread_attr_setstacksize failed"); } } # endif /* HPUX || GC_DGUX386_THREADS */ @@ -439,7 +438,7 @@ static void start_mark_threads(void) } } if (GC_print_stats) { - GC_log_printf("Started %ld mark helper threads\n", GC_markers - 1); + GC_log_printf("Started %ld mark helper threads\n", GC_markers - 1); } pthread_attr_destroy(&attr); } @@ -456,18 +455,13 @@ void GC_push_thread_structures(void) GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads)); # if defined(THREAD_LOCAL_ALLOC) GC_push_all((ptr_t)(&GC_thread_key), - (ptr_t)(&GC_thread_key)+sizeof(&GC_thread_key)); + (ptr_t)(&GC_thread_key) + sizeof(&GC_thread_key)); # endif } /* It may not be safe to allocate when we register the first thread. */ static struct GC_Thread_Rep first_thread; -#ifdef NACL - GC_INNER void GC_nacl_initialize_gc_thread(void); - GC_INNER void GC_nacl_shutdown_gc_thread(void); -#endif - /* Add a thread to GC_threads. We assume it wasn't already there. */ /* Caller holds allocation lock. */ STATIC GC_thread GC_new_thread(pthread_t id) @@ -535,15 +529,15 @@ STATIC void GC_delete_thread(pthread_t id) /* been notified, then there may be more than one thread */ /* in the table with the same pthread id. */ /* This is OK, but we need a way to delete a specific one. */ -STATIC void GC_delete_gc_thread(GC_thread gc_id) +STATIC void GC_delete_gc_thread(GC_thread t) { - pthread_t id = gc_id -> id; + pthread_t id = t -> id; int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ; register GC_thread p = GC_threads[hv]; register GC_thread prev = 0; GC_ASSERT(I_HOLD_LOCK()); - while (p != gc_id) { + while (p != t) { prev = p; p = p -> next; } @@ -615,7 +609,7 @@ GC_INNER unsigned char *GC_check_finalizer_nested(void) /* thread_local_alloc.c) checks only that it's close. */ return((char *)tsd > me && (char *)tsd < me + 1000); } -#endif +#endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */ #ifdef HANDLE_FORK /* Remove all entries from the GC_threads table, except the */ @@ -640,7 +634,7 @@ STATIC void GC_remove_all_threads_but_me(void) if (!(p -> flags & FINISHED)) { GC_destroy_thread_local(&(p->tlfs)); } -# endif /* THREAD_LOCAL_ALLOC */ +# endif if (p != &first_thread) GC_INTERNAL_FREE(p); } } @@ -658,9 +652,9 @@ STATIC void GC_remove_all_threads_but_me(void) GC_ASSERT(I_HOLD_LOCK()); # ifdef PARALLEL_MARK for (i = 0; i < GC_markers - 1; ++i) { - if (marker_sp[i] > lo & marker_sp[i] < hi) return TRUE; + if (marker_sp[i] > lo && marker_sp[i] < hi) return TRUE; # ifdef IA64 - if (marker_bsp[i] > lo & marker_bsp[i] < hi) return TRUE; + if (marker_bsp[i] > lo && marker_bsp[i] < hi) return TRUE; # endif } # endif @@ -707,8 +701,8 @@ STATIC void GC_remove_all_threads_but_me(void) } #endif /* IA64 */ -#if defined(GC_LINUX_THREADS) && !defined(NACL) - /* Return the number of processors, or i<= 0 if it can't be determined. */ +#if defined(GC_LINUX_THREADS) && !defined(PLATFORM_ANDROID) && !defined(NACL) + /* Return the number of processors. */ STATIC int GC_get_nprocs(void) { /* Should be "return sysconf(_SC_NPROCESSORS_ONLN);" but that */ @@ -727,21 +721,24 @@ STATIC void GC_remove_all_threads_but_me(void) /* Some old kernels only have a single "cpu nnnn ..." */ /* entry in /proc/stat. We identify those as */ /* uniprocessors. */ - size_t i, len = 0; + int i, len; f = open("/proc/stat", O_RDONLY); - if (f < 0 || (len = STAT_READ(f, stat_buf, STAT_BUF_SIZE)) < 100) { + if (f < 0) { WARN("Couldn't read /proc/stat\n", 0); - return -1; + return 1; /* assume an uniprocessor */ } + len = STAT_READ(f, stat_buf, STAT_BUF_SIZE); + close(f); + for (i = 0; i < len - 100; ++i) { if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c' && stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') { - int cpu_no = atoi(stat_buf + i + 4); - if (cpu_no >= result) result = cpu_no + 1; + int cpu_no = atoi(&stat_buf[i + 4]); + if (cpu_no >= result) + result = cpu_no + 1; } } - close(f); return result; } #endif /* GC_LINUX_THREADS && !NACL */ @@ -849,7 +846,7 @@ STATIC void GC_fork_child_proc(void) /* <takis@XFree86.Org> */ int numCpus; struct dg_sys_info_pm_info pm_sysinfo; - int status =0; + int status = 0; status = dg_sys_info((long int *) &pm_sysinfo, DG_SYS_INFO_PM_INFO_TYPE, DG_SYS_INFO_PM_CURRENT_VERSION); @@ -861,7 +858,7 @@ STATIC void GC_fork_child_proc(void) numCpus = pm_sysinfo.idle_vp_count; # ifdef DEBUG_THREADS - GC_printf("Number of active CPUs in this system: %d\n", numCpus); + GC_log_printf("Number of active CPUs in this system: %d\n", numCpus); # endif return(numCpus); } @@ -880,12 +877,10 @@ STATIC void GC_fork_child_proc(void) } #endif /* GC_DARWIN_THREADS || ... */ -#if defined(GC_LINUX_THREADS) && defined(INCLUDE_LINUX_THREAD_DESCR) +#ifdef INCLUDE_LINUX_THREAD_DESCR __thread int GC_dummy_thread_local; -#endif - -#ifndef GC_DARWIN_THREADS - GC_INNER void GC_stop_init(void); /* defined in pthread_stop_world.c */ + GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, + ptr_t *startp, ptr_t *endp); #endif /* We hold the allocation lock. */ @@ -913,15 +908,17 @@ GC_INNER void GC_thr_init(void) if (!GC_enclosing_mapping(thread_local_addr, &main_thread_start, &main_thread_end)) { ABORT("Failed to find mapping for main thread thread locals"); + } else { + /* main_thread_start and main_thread_end are initialized. */ + GC_add_roots_inner(main_thread_start, main_thread_end, FALSE); } - GC_add_roots_inner(main_thread_start, main_thread_end, FALSE); } # endif /* Add the initial thread, so we can stop it. */ { GC_thread t = GC_new_thread(pthread_self()); if (t == NULL) - ABORT("Failed to allocate memory for the initial thread."); + ABORT("Failed to allocate memory for the initial thread"); # ifdef GC_DARWIN_THREADS t -> stop_info.mach_thread = mach_thread_self(); # else @@ -945,7 +942,7 @@ GC_INNER void GC_thr_init(void) GC_nprocs = pthread_num_processors_np(); # elif defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \ || defined(GC_SOLARIS_THREADS) || defined(GC_GNU_THREADS) \ - || defined(NACL) + || defined(PLATFORM_ANDROID) || defined(NACL) GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN); if (GC_nprocs <= 0) GC_nprocs = 1; # elif defined(GC_IRIX_THREADS) @@ -960,7 +957,7 @@ GC_INNER void GC_thr_init(void) } if (GC_nprocs <= 0) { WARN("GC_get_nprocs() returned %" GC_PRIdPTR "\n", GC_nprocs); - GC_nprocs = 2; + GC_nprocs = 2; /* assume dual-core */ # ifdef PARALLEL_MARK GC_markers = 1; # endif @@ -984,8 +981,9 @@ GC_INNER void GC_thr_init(void) } # ifdef PARALLEL_MARK if (GC_print_stats) { - GC_log_printf("Number of processors = %ld, " - "number of marker threads = %ld\n", GC_nprocs, GC_markers); + GC_log_printf( + "Number of processors = %ld, number of marker threads = %ld\n", + GC_nprocs, GC_markers); } if (GC_markers <= 1) { GC_parallel = FALSE; @@ -1043,10 +1041,6 @@ GC_INNER void GC_init_parallel(void) } #endif /* !GC_NO_PTHREAD_SIGMASK */ -#if defined(GC_DARWIN_THREADS) && !defined(DARWIN_DONT_PARSE_STACK) - GC_INNER ptr_t GC_FindTopOfStack(unsigned long); -#endif - /* Wrapper for functions that are likely to block for an appreciable */ /* length of time. */ @@ -1158,18 +1152,11 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, return client_data; /* result */ } -GC_API int GC_CALL GC_unregister_my_thread(void) +STATIC void GC_unregister_my_thread_inner(GC_thread me) { - GC_thread me; - IF_CANCEL(int cancel_state;) - DCL_LOCK_STATE; - - LOCK(); - DISABLE_CANCEL(cancel_state); - /* Wait for any GC that may be marking from our stack to */ - /* complete before we remove this thread. */ - GC_wait_for_gc_completion(FALSE); - me = GC_lookup_thread(pthread_self()); +# ifdef DEBUG_THREADS + GC_log_printf("Unregistering thread 0x%x\n", (unsigned)pthread_self()); +# endif GC_ASSERT(!(me -> flags & FINISHED)); # if defined(THREAD_LOCAL_ALLOC) GC_destroy_thread_local(&(me->tlfs)); @@ -1187,8 +1174,23 @@ GC_API int GC_CALL GC_unregister_my_thread(void) me -> flags |= FINISHED; } # if defined(THREAD_LOCAL_ALLOC) + /* It is required to call remove_specific defined in specific.c. */ GC_remove_specific(GC_thread_key); # endif +} + +GC_API int GC_CALL GC_unregister_my_thread(void) +{ + pthread_t self = pthread_self(); + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + LOCK(); + DISABLE_CANCEL(cancel_state); + /* Wait for any GC that may be marking from our stack to */ + /* complete before we remove this thread. */ + GC_wait_for_gc_completion(FALSE); + GC_unregister_my_thread_inner(GC_lookup_thread(self)); RESTORE_CANCEL(cancel_state); UNLOCK(); return GC_SUCCESS; @@ -1201,18 +1203,26 @@ GC_API int GC_CALL GC_unregister_my_thread(void) /* resources or id anyway. */ GC_INNER void GC_thread_exit_proc(void *arg) { - GC_unregister_my_thread(); + IF_CANCEL(int cancel_state;) + DCL_LOCK_STATE; + + LOCK(); + DISABLE_CANCEL(cancel_state); + GC_wait_for_gc_completion(FALSE); + GC_unregister_my_thread_inner((GC_thread)arg); + RESTORE_CANCEL(cancel_state); + UNLOCK(); } GC_API int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval) { int result; - GC_thread thread_gc_id; + GC_thread t; DCL_LOCK_STATE; INIT_REAL_SYMS(); LOCK(); - thread_gc_id = GC_lookup_thread(thread); + t = GC_lookup_thread(thread); /* This is guaranteed to be the intended one, since the thread id */ /* can't have been recycled by pthreads. */ UNLOCK(); @@ -1231,7 +1241,8 @@ GC_API int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval) if (result == 0) { LOCK(); /* Here the pthread thread id may have been recycled. */ - GC_delete_gc_thread(thread_gc_id); + GC_ASSERT((t -> flags & FINISHED) != 0); + GC_delete_gc_thread(t); UNLOCK(); } return result; @@ -1240,20 +1251,20 @@ GC_API int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval) GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread) { int result; - GC_thread thread_gc_id; + GC_thread t; DCL_LOCK_STATE; INIT_REAL_SYMS(); LOCK(); - thread_gc_id = GC_lookup_thread(thread); + t = GC_lookup_thread(thread); UNLOCK(); result = REAL_FUNC(pthread_detach)(thread); if (result == 0) { LOCK(); - thread_gc_id -> flags |= DETACHED; + t -> flags |= DETACHED; /* Here the pthread thread id may have been recycled. */ - if (thread_gc_id -> flags & FINISHED) { - GC_delete_gc_thread(thread_gc_id); + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread(t); } UNLOCK(); } @@ -1273,20 +1284,19 @@ GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread) GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread) { # ifdef CANCEL_SAFE - GC_thread thread_gc_id; + GC_thread t; DCL_LOCK_STATE; # endif INIT_REAL_SYMS(); # ifdef CANCEL_SAFE LOCK(); - thread_gc_id = GC_lookup_thread(thread); + t = GC_lookup_thread(thread); /* We test DISABLED_GC because pthread_exit could be called at */ - /* the same time. (If thread_gc_id is NULL then pthread_cancel */ - /* should return ESRCH.) */ - if (thread_gc_id != 0 - && (thread_gc_id -> flags & DISABLED_GC) == 0) { - thread_gc_id -> flags |= DISABLED_GC; + /* the same time. (If t is NULL then pthread_cancel should */ + /* return ESRCH.) */ + if (t != NULL && (t -> flags & DISABLED_GC) == 0) { + t -> flags |= DISABLED_GC; GC_dont_gc++; } UNLOCK(); @@ -1325,6 +1335,20 @@ GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread) GC_INNER GC_bool GC_in_thread_creation = FALSE; /* Protected by allocation lock. */ +GC_INLINE void GC_record_stack_base(GC_thread me, + const struct GC_stack_base *sb) +{ +# ifndef GC_DARWIN_THREADS + me -> stop_info.stack_ptr = sb -> mem_base; +# endif + me -> stack_end = sb -> mem_base; + if (me -> stack_end == NULL) + ABORT("Bad stack base in GC_register_my_thread"); +# ifdef IA64 + me -> backing_store_end = sb -> reg_base; +# endif +} + STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, pthread_t my_pthread) { @@ -1334,18 +1358,16 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, me = GC_new_thread(my_pthread); GC_in_thread_creation = FALSE; if (me == 0) - ABORT("Failed to allocate memory for thread registering."); + ABORT("Failed to allocate memory for thread registering"); # ifdef GC_DARWIN_THREADS me -> stop_info.mach_thread = mach_thread_self(); -# else - me -> stop_info.stack_ptr = sb -> mem_base; # endif - me -> stack_end = sb -> mem_base; - if (me -> stack_end == NULL) - ABORT("Bad stack base in GC_register_my_thread"); -# ifdef IA64 - me -> backing_store_end = sb -> reg_base; -# endif /* IA64 */ + GC_record_stack_base(me, sb); +# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Since this could be executed from a detached thread */ + /* destructor, our signals might already be blocked. */ + GC_unblock_gc_signals(); +# endif return me; } @@ -1359,7 +1381,7 @@ GC_API void GC_CALL GC_allow_register_threads(void) GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) { - pthread_t my_pthread = pthread_self(); + pthread_t self = pthread_self(); GC_thread me; DCL_LOCK_STATE; @@ -1367,9 +1389,9 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) ABORT("Threads explicit registering is not previously enabled"); LOCK(); - me = GC_lookup_thread(my_pthread); + me = GC_lookup_thread(self); if (0 == me) { - me = GC_register_my_thread_inner(sb, my_pthread); + me = GC_register_my_thread_inner(sb, self); me -> flags |= DETACHED; /* Treat as detached, since we do not need to worry about */ /* pointer results. */ @@ -1378,6 +1400,21 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) # endif UNLOCK(); return GC_SUCCESS; + } else if ((me -> flags & FINISHED) != 0) { + /* This code is executed when a thread is registered from the */ + /* client thread key destructor. */ + GC_record_stack_base(me, sb); + me -> flags &= ~FINISHED; /* but not DETACHED */ +# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK + /* Since this could be executed from a thread destructor, */ + /* our signals might be blocked. */ + GC_unblock_gc_signals(); +# endif +# if defined(THREAD_LOCAL_ALLOC) + GC_init_thread_local(&(me->tlfs)); +# endif + UNLOCK(); + return GC_SUCCESS; } else { UNLOCK(); return GC_DUPLICATE; @@ -1395,30 +1432,29 @@ struct start_info { /* Called from GC_inner_start_routine(). Defined in this file to */ /* minimize the number of include files in pthread_start.c (because */ /* sem_t and sem_post() are not used that file directly). */ -GC_INNER void * GC_start_rtn_prepare_thread(void *(**pstart)(void *), +GC_INNER GC_thread GC_start_rtn_prepare_thread(void *(**pstart)(void *), void **pstart_arg, struct GC_stack_base *sb, void *arg) { struct start_info * si = arg; + pthread_t self = pthread_self(); GC_thread me; - pthread_t my_pthread; DCL_LOCK_STATE; - my_pthread = pthread_self(); # ifdef DEBUG_THREADS - GC_printf("Starting thread 0x%x, pid = %ld, sp = %p\n", - (unsigned)my_pthread, (long) getpid(), &arg); + GC_log_printf("Starting thread 0x%x, pid = %ld, sp = %p\n", + (unsigned)self, (long)getpid(), &arg); # endif LOCK(); - me = GC_register_my_thread_inner(sb, my_pthread); + me = GC_register_my_thread_inner(sb, self); me -> flags = si -> flags; # if defined(THREAD_LOCAL_ALLOC) - GC_init_thread_local(&(me->tlfs)); + GC_init_thread_local(&(me->tlfs)); # endif UNLOCK(); *pstart = si -> start_routine; # ifdef DEBUG_THREADS - GC_printf("start_routine = %p\n", (void *)(signed_word)(*pstart)); + GC_log_printf("start_routine = %p\n", (void *)(signed_word)(*pstart)); # endif *pstart_arg = si -> arg; sem_post(&(si -> registered)); /* Last action on si. */ @@ -1442,7 +1478,7 @@ STATIC void * GC_start_routine(void * arg) GC_disable(); # endif if (GC_get_stack_base(&sb) != GC_SUCCESS) - ABORT("Failed to get thread stack base."); + ABORT("Failed to get thread stack base"); # ifdef REDIRECT_MALLOC GC_enable(); # endif @@ -1478,7 +1514,9 @@ GC_API int WRAP_FUNC(pthread_create)(pthread_t *new_thread, (si = (struct start_info *) (*GC_get_oom_fn())(sizeof(struct start_info))) == 0) return(ENOMEM); - sem_init(&(si -> registered), 0, 0); + if (sem_init(&(si -> registered), GC_SEM_INIT_PSHARED, 0) != 0) + ABORT("sem_init failed"); + si -> start_routine = start_routine; si -> arg = arg; LOCK(); @@ -1523,15 +1561,15 @@ GC_API int WRAP_FUNC(pthread_create)(pthread_t *new_thread, si -> flags = my_flags; UNLOCK(); # ifdef DEBUG_THREADS - GC_printf("About to start new thread from thread 0x%x\n", - (unsigned)pthread_self()); + GC_log_printf("About to start new thread from thread 0x%x\n", + (unsigned)pthread_self()); # endif GC_need_to_lock = TRUE; result = REAL_FUNC(pthread_create)(new_thread, attr, GC_start_routine, si); # ifdef DEBUG_THREADS - GC_printf("Started thread 0x%x\n", (unsigned)(*new_thread)); + GC_log_printf("Started thread 0x%x\n", (unsigned)(*new_thread)); # endif /* Wait until child has been added to the thread table. */ /* This also ensures that we hold onto si until the child is done */ @@ -1738,7 +1776,7 @@ GC_INNER void GC_lock(void) GC_INNER unsigned long GC_mark_lock_holder = NO_THREAD; #endif -#if 0 +#ifdef GLIBC_2_1_MUTEX_HACK /* Ugly workaround for a linux threads bug in the final versions */ /* of glibc2.1. Pthread_mutex_trylock sets the mutex owner */ /* field even when it fails to acquire the mutex. This causes */ @@ -1756,11 +1794,6 @@ static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER; GC_INNER void GC_acquire_mark_lock(void) { -/* - if (pthread_mutex_lock(&mark_mutex) != 0) { - ABORT("pthread_mutex_lock failed"); - } -*/ GC_generic_lock(&mark_mutex); # ifdef GC_ASSERTIONS GC_mark_lock_holder = NUMERIC_THREAD_ID(pthread_self()); @@ -1843,4 +1876,4 @@ GC_INNER void GC_notify_all_marker(void) #endif /* PARALLEL_MARK */ -#endif /* GC_LINUX_THREADS and friends */ +#endif /* GC_PTHREADS */ @@ -242,7 +242,7 @@ GC_API void * GC_CALL GC_is_visible(void *p) } else { ptr_t type_descr = *(ptr_t *)base; descr = *(word *)(type_descr - - (descr - (GC_DS_PER_OBJECT + - (descr - (word)(GC_DS_PER_OBJECT - GC_INDIR_PER_OBJ_BIAS))); } goto retry; @@ -33,27 +33,36 @@ GC_INNER signed_word GC_bytes_found = 0; /* We defer printing of leaked objects until we're done with the GC */ /* cycle, since the routine for printing objects needs to run outside */ /* the collector, e.g. without the allocation lock. */ -#define MAX_LEAKED 40 +#ifndef MAX_LEAKED +# define MAX_LEAKED 40 +#endif STATIC ptr_t GC_leaked[MAX_LEAKED] = { NULL }; STATIC unsigned GC_n_leaked = 0; GC_INNER GC_bool GC_have_errors = FALSE; -STATIC void GC_add_leaked(ptr_t leaked) +GC_INLINE void GC_add_leaked(ptr_t leaked) { +# ifndef SHORT_DBG_HDRS + if (GC_findleak_delay_free && !GC_check_leaked(leaked)) + return; +# endif + + GC_have_errors = TRUE; + /* FIXME: Prevent adding an object while printing leaked ones. */ if (GC_n_leaked < MAX_LEAKED) { - GC_have_errors = TRUE; GC_leaked[GC_n_leaked++] = leaked; /* Make sure it's not reclaimed this cycle */ - GC_set_mark_bit(leaked); + GC_set_mark_bit(leaked); } } /* Print all objects on the list after printing any smashed objects. */ -/* Clear both lists. */ +/* Clear both lists. Called without the allocation lock held. */ GC_INNER void GC_print_all_errors(void) { static GC_bool printing_errors = FALSE; + GC_bool have_errors; unsigned i; DCL_LOCK_STATE; @@ -62,9 +71,16 @@ GC_INNER void GC_print_all_errors(void) UNLOCK(); return; } + have_errors = GC_have_errors; printing_errors = TRUE; UNLOCK(); - if (GC_debugging_started) GC_print_all_smashed(); + + if (GC_debugging_started) { + GC_print_all_smashed(); + } else { + have_errors = FALSE; + } + for (i = 0; i < GC_n_leaked; ++i) { ptr_t p = GC_leaked[i]; if (HDR(p) -> hb_obj_kind == PTRFREE) { @@ -76,8 +92,18 @@ GC_INNER void GC_print_all_errors(void) GC_err_printf("\n"); GC_free(p); GC_leaked[i] = 0; + have_errors = TRUE; } GC_n_leaked = 0; + + if (have_errors +# ifndef GC_ABORT_ON_LEAK + && GETENV("GC_ABORT_ON_LEAK") != NULL +# endif + ) { + ABORT("Leaked or smashed objects encountered"); + } + printing_errors = FALSE; } @@ -183,24 +209,20 @@ STATIC ptr_t GC_reclaim_uninit(struct hblk *hbp, hdr *hhdr, size_t sz, /* Don't really reclaim objects, just check for unmarked ones: */ STATIC void GC_reclaim_check(struct hblk *hbp, hdr *hhdr, word sz) { - word bit_no = 0; + word bit_no; ptr_t p, plim; - GC_ASSERT(sz == hhdr -> hb_sz); - p = hbp->hb_body; - plim = p + HBLKSIZE - sz; /* go through all words in block */ - while (p <= plim) { - if( !mark_bit_from_hdr(hhdr, bit_no) ) { - GC_add_leaked(p); - } - p += sz; - bit_no += MARK_BIT_OFFSET(sz); - } + p = hbp->hb_body; + plim = p + HBLKSIZE - sz; + for (bit_no = 0; p <= plim; p += sz, bit_no += MARK_BIT_OFFSET(sz)) { + if (!mark_bit_from_hdr(hhdr, bit_no)) { + GC_add_leaked(p); + } + } } - /* * Generic procedure to rebuild a free list in hbp. * Also called directly from GC_malloc_many. @@ -233,12 +255,11 @@ GC_INNER ptr_t GC_reclaim_generic(struct hblk * hbp, hdr *hhdr, size_t sz, * caller should perform that check. */ STATIC void GC_reclaim_small_nonempty_block(struct hblk *hbp, - int report_if_found) + GC_bool report_if_found) { hdr *hhdr = HDR(hbp); size_t sz = hhdr -> hb_sz; - int kind = hhdr -> hb_obj_kind; - struct obj_kind * ok = &GC_obj_kinds[kind]; + struct obj_kind * ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; void **flh = &(ok -> ok_freelist[BYTES_TO_GRANULES(sz)]); hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; @@ -246,8 +267,7 @@ STATIC void GC_reclaim_small_nonempty_block(struct hblk *hbp, if (report_if_found) { GC_reclaim_check(hbp, hhdr, sz); } else { - *flh = GC_reclaim_generic(hbp, hhdr, sz, - ok -> ok_init, + *flh = GC_reclaim_generic(hbp, hhdr, sz, ok -> ok_init, *flh, &GC_bytes_found); } } @@ -305,7 +325,7 @@ STATIC void GC_reclaim_block(struct hblk *hbp, word report_if_found) GC_atomic_in_use += sz * hhdr -> hb_n_marks; } if (report_if_found) { - GC_reclaim_small_nonempty_block(hbp, (int)report_if_found); + GC_reclaim_small_nonempty_block(hbp, TRUE /* report_if_found */); } else if (empty) { GC_bytes_found += HBLKSIZE; GC_freehblk(hbp); @@ -404,10 +424,10 @@ STATIC void GC_print_block_descr(struct hblk *h, if (hhdr -> hb_n_marks != n_marks) { GC_printf("(%u:%u,%u!=%u)", hhdr -> hb_obj_kind, (unsigned)bytes, - (unsigned)hhdr -> hb_n_marks, n_marks); + (unsigned)hhdr -> hb_n_marks, n_marks); } else { GC_printf("(%u:%u,%u)", hhdr -> hb_obj_kind, - (unsigned)bytes, n_marks); + (unsigned)bytes, n_marks); } bytes += HBLKSIZE-1; bytes &= ~(HBLKSIZE-1); @@ -436,15 +456,15 @@ void GC_print_free_list(int kind, size_t sz_in_granules) struct obj_kind * ok = &GC_obj_kinds[kind]; ptr_t flh = ok -> ok_freelist[sz_in_granules]; struct hblk *lastBlock = 0; - int n = 0; + int n; - while (flh) { + for (n = 1; flh; n++) { struct hblk *block = HBLKPTR(flh); if (block != lastBlock) { - GC_printf("\nIn heap block at %p:\n\t", block); - lastBlock = block; + GC_printf("\nIn heap block at %p:\n\t", block); + lastBlock = block; } - GC_printf("%d: %p;", ++n, flh); + GC_printf("%d: %p;", n, flh); flh = obj_link(flh); } } @@ -487,8 +507,6 @@ GC_INNER void GC_start_reclaim(GC_bool report_if_found) for (kind = 0; kind < GC_n_kinds; kind++) { void **fop; void **lim; - struct hblk ** rlp; - struct hblk ** rlim; struct hblk ** rlist = GC_obj_kinds[kind].ok_reclaim_list; GC_bool should_clobber = (GC_obj_kinds[kind].ok_descriptor != 0); @@ -506,10 +524,7 @@ GC_INNER void GC_start_reclaim(GC_bool report_if_found) } } /* otherwise free list objects are marked, */ /* and its safe to leave them */ - rlim = rlist + MAXOBJGRANULES+1; - for( rlp = rlist; rlp < rlim; rlp++ ) { - *rlp = 0; - } + BZERO(rlist, (MAXOBJGRANULES + 1) * sizeof(void *)); } @@ -602,7 +617,7 @@ GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func, GC_bool ignore_old) if (GC_print_stats == VERBOSE) { GET_TIME(done_time); GC_log_printf("Disposing of reclaim lists took %lu msecs\n", - MS_TIME_DIFF(done_time,start_time)); + MS_TIME_DIFF(done_time,start_time)); } # endif return(TRUE); @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED @@ -11,35 +11,35 @@ * modified is included with the above copyright notice. */ -#include "private/gc_priv.h" /* For configuration, pthreads.h. */ +#include "private/gc_priv.h" /* For configuration, pthreads.h. */ #include "private/thread_local_alloc.h" - /* To determine type of tsd impl. */ - /* Includes private/specific.h */ - /* if needed. */ + /* To determine type of tsd impl. */ + /* Includes private/specific.h */ + /* if needed. */ #if defined(USE_CUSTOM_SPECIFIC) #include "atomic_ops.h" static tse invalid_tse = {INVALID_QTID, 0, 0, INVALID_THREADID}; - /* A thread-specific data entry which will never */ - /* appear valid to a reader. Used to fill in empty */ - /* cache entries to avoid a check for 0. */ + /* A thread-specific data entry which will never */ + /* appear valid to a reader. Used to fill in empty */ + /* cache entries to avoid a check for 0. */ int PREFIXED(key_create) (tsd ** key_ptr, void (* destructor)(void *)) { int i; - tsd * result = (tsd *)MALLOC_CLEAR(sizeof (tsd)); + tsd * result = (tsd *)MALLOC_CLEAR(sizeof(tsd)); /* A quick alignment check, since we need atomic stores */ - GC_ASSERT((unsigned long)(&invalid_tse.next) % sizeof(tse *) == 0); + GC_ASSERT((unsigned long)(&invalid_tse.next) % sizeof(tse *) == 0); if (0 == result) return ENOMEM; pthread_mutex_init(&(result -> lock), NULL); for (i = 0; i < TS_CACHE_SIZE; ++i) { - result -> cache[i] = &invalid_tse; + result -> cache[i] = &invalid_tse; } # ifdef GC_ASSERTIONS for (i = 0; i < TS_HASH_SIZE; ++i) { - GC_ASSERT(result -> hash[i] == 0); + GC_ASSERT(result -> hash[i] == 0); } # endif *key_ptr = result; @@ -50,24 +50,24 @@ int PREFIXED(setspecific) (tsd * key, void * value) { pthread_t self = pthread_self(); int hash_val = HASH(self); volatile tse * entry = (volatile tse *)MALLOC_CLEAR(sizeof (tse)); - + GC_ASSERT(self != INVALID_THREADID); if (0 == entry) return ENOMEM; pthread_mutex_lock(&(key -> lock)); - /* Could easily check for an existing entry here. */ + /* Could easily check for an existing entry here. */ entry -> next = key -> hash[hash_val]; entry -> thread = self; entry -> value = value; GC_ASSERT(entry -> qtid == INVALID_QTID); - /* There can only be one writer at a time, but this needs to be */ - /* atomic with respect to concurrent readers. */ + /* There can only be one writer at a time, but this needs to be */ + /* atomic with respect to concurrent readers. */ AO_store_release((volatile AO_t *)(key -> hash + hash_val), (AO_t)entry); pthread_mutex_unlock(&(key -> lock)); return 0; } -/* Remove thread-specific data for this thread. Should be called on */ -/* thread exit. */ +/* Remove thread-specific data for this thread. Should be called on */ +/* thread exit. */ void PREFIXED(remove_specific) (tsd * key) { pthread_t self = pthread_self(); unsigned hash_val = HASH(self); @@ -77,89 +77,86 @@ void PREFIXED(remove_specific) (tsd * key) { pthread_mutex_lock(&(key -> lock)); entry = *link; while (entry != NULL && entry -> thread != self) { - link = &(entry -> next); - entry = *link; + link = &(entry -> next); + entry = *link; } - /* Invalidate qtid field, since qtids may be reused, and a later */ - /* cache lookup could otherwise find this entry. */ - entry -> qtid = INVALID_QTID; + /* Invalidate qtid field, since qtids may be reused, and a later */ + /* cache lookup could otherwise find this entry. */ if (entry != NULL) { - *link = entry -> next; - /* Atomic! concurrent accesses still work. */ - /* They must, since readers don't lock. */ - /* We shouldn't need a volatile access here, */ - /* since both this and the preceding write */ - /* should become visible no later than */ - /* the pthread_mutex_unlock() call. */ + entry -> qtid = INVALID_QTID; + *link = entry -> next; + /* Atomic! concurrent accesses still work. */ + /* They must, since readers don't lock. */ + /* We shouldn't need a volatile access here, */ + /* since both this and the preceding write */ + /* should become visible no later than */ + /* the pthread_mutex_unlock() call. */ } - /* If we wanted to deallocate the entry, we'd first have to clear */ - /* any cache entries pointing to it. That probably requires */ - /* additional synchronization, since we can't prevent a concurrent */ + /* If we wanted to deallocate the entry, we'd first have to clear */ + /* any cache entries pointing to it. That probably requires */ + /* additional synchronization, since we can't prevent a concurrent */ /* cache lookup, which should still be examining deallocated memory.*/ - /* This can only happen if the concurrent access is from another */ - /* thread, and hence has missed the cache, but still... */ + /* This can only happen if the concurrent access is from another */ + /* thread, and hence has missed the cache, but still... */ - /* With GC, we're done, since the pointers from the cache will */ - /* be overwritten, all local pointers to the entries will be */ - /* dropped, and the entry will then be reclaimed. */ + /* With GC, we're done, since the pointers from the cache will */ + /* be overwritten, all local pointers to the entries will be */ + /* dropped, and the entry will then be reclaimed. */ pthread_mutex_unlock(&(key -> lock)); } -/* Note that even the slow path doesn't lock. */ -void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid, - tse * volatile * cache_ptr) { +/* Note that even the slow path doesn't lock. */ +void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid, + tse * volatile * cache_ptr) { pthread_t self = pthread_self(); unsigned hash_val = HASH(self); tse *entry = key -> hash[hash_val]; GC_ASSERT(qtid != INVALID_QTID); while (entry != NULL && entry -> thread != self) { - entry = entry -> next; - } + entry = entry -> next; + } if (entry == NULL) return NULL; - /* Set cache_entry. */ - entry -> qtid = qtid; - /* It's safe to do this asynchronously. Either value */ - /* is safe, though may produce spurious misses. */ - /* We're replacing one qtid with another one for the */ - /* same thread. */ - *cache_ptr = entry; - /* Again this is safe since pointer assignments are */ - /* presumed atomic, and either pointer is valid. */ + /* Set cache_entry. */ + entry -> qtid = (AO_t)qtid; + /* It's safe to do this asynchronously. Either value */ + /* is safe, though may produce spurious misses. */ + /* We're replacing one qtid with another one for the */ + /* same thread. */ + *cache_ptr = entry; + /* Again this is safe since pointer assignments are */ + /* presumed atomic, and either pointer is valid. */ return entry -> value; } #ifdef GC_ASSERTIONS - -/* Check that that all elements of the data structure associated */ -/* with key are marked. */ -void PREFIXED(check_tsd_marks) (tsd *key) -{ + /* Check that that all elements of the data structure associated */ + /* with key are marked. */ + void PREFIXED(check_tsd_marks) (tsd *key) + { int i; tse *p; if (!GC_is_marked(GC_base(key))) { - ABORT("Unmarked thread-specific-data table"); + ABORT("Unmarked thread-specific-data table"); } for (i = 0; i < TS_HASH_SIZE; ++i) { - for (p = key -> hash[i]; p != 0; p = p -> next) { - if (!GC_is_marked(GC_base(p))) { - GC_err_printf( - "Thread-specific-data entry at %p not marked\n",p); - ABORT("Unmarked tse"); - } - } + for (p = key -> hash[i]; p != 0; p = p -> next) { + if (!GC_is_marked(GC_base(p))) { + GC_err_printf("Thread-specific-data entry at %p not marked\n", p); + ABORT("Unmarked tse"); + } + } } for (i = 0; i < TS_CACHE_SIZE; ++i) { - p = key -> cache[i]; - if (p != &invalid_tse && !GC_is_marked(GC_base(p))) { - GC_err_printf( - "Cached thread-specific-data entry at %p not marked\n",p); - ABORT("Unmarked cached tse"); - } + p = key -> cache[i]; + if (p != &invalid_tse && !GC_is_marked(GC_base(p))) { + GC_err_printf("Cached thread-specific-data entry at %p not marked\n", + p); + ABORT("Unmarked cached tse"); + } } -} - -#endif + } +#endif /* GC_ASSERTIONS */ #endif /* USE_CUSTOM_SPECIFIC */ @@ -22,6 +22,8 @@ /* written, but not yet GC_dirty()ed objects must be referenced */ /* by a stack. */ + void GC_dirty(ptr_t p); + GC_API void * GC_CALL GC_malloc_stubborn(size_t lb) { return(GC_malloc(lb)); diff --git a/tests/huge_test.c b/tests/huge_test.c index b09802fd..dd58a8e7 100644 --- a/tests/huge_test.c +++ b/tests/huge_test.c @@ -14,7 +14,7 @@ /* * Check that very large allocation requests fail. "Success" would usually - * indicate that the the size was somehow converted to a negative + * indicate that the size was somehow converted to a negative * number. Clients shouldn't do this, but we should fail in the * expected manner. */ diff --git a/tests/initsecondarythread.c b/tests/initsecondarythread.c new file mode 100755 index 00000000..f89252e2 --- /dev/null +++ b/tests/initsecondarythread.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 Ludovic Courtes <ludo@gnu.org> + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* Make sure 'GC_INIT' can be called from threads other than the initial + * thread. + */ + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#define GC_NO_THREAD_REDIRECTS 1 + +#include "gc.h" + +#include <pthread.h> +#include <stdlib.h> + +static void *thread(void *arg) +{ + GC_INIT(); + GC_MALLOC(123); + GC_MALLOC(12345); + return NULL; +} + +#include "private/gcconfig.h" + +int main(void) +{ + pthread_t t; +# if !(defined(BEOS) || defined(MSWIN32) || defined(MSWINCE) \ + || defined(CYGWIN32) || defined(GC_OPENBSD_THREADS) \ + || (defined(DARWIN) && !defined(NO_PTHREAD_GET_STACKADDR_NP)) \ + || (defined(LINUX) && !defined(NACL)) \ + || (defined(GC_SOLARIS_THREADS) && !defined(_STRICT_STDC)) \ + || (!defined(STACKBOTTOM) && (defined(HEURISTIC1) \ + || (!defined(LINUX_STACKBOTTOM) && !defined(FREEBSD_STACKBOTTOM))))) + /* GC_INIT() must be called from main thread only. */ + GC_INIT(); +# endif + pthread_create (&t, NULL, thread, NULL); + pthread_join (t, NULL); + return 0; +} diff --git a/tests/realloc_test.c b/tests/realloc_test.c new file mode 100644 index 00000000..370ba466 --- /dev/null +++ b/tests/realloc_test.c @@ -0,0 +1,34 @@ + +#include <stdio.h> +#include <stdlib.h> +#include "gc.h" + +#define COUNT 10000000 + +int main(void) { + int i; + unsigned long last_heap_size = 0; + + GC_INIT(); + + for (i = 0; i < COUNT; i++) { + int **p = GC_MALLOC(sizeof(int *)); + int *q = GC_MALLOC_ATOMIC(sizeof(int)); + + if (*p != 0) { + fprintf(stderr, "GC_malloc returned garbage\n"); + exit(1); + } + + *p = GC_REALLOC(q, 2 * sizeof(int)); + + if (i % 10 == 0) { + unsigned long heap_size = (unsigned long)GC_get_heap_size(); + if (heap_size != last_heap_size) { + printf("Heap size: %lu\n", heap_size); + last_heap_size = heap_size; + } + } + } + return 0; +} diff --git a/tests/smash_test.c b/tests/smash_test.c index 86e081bd..0e8b1f08 100644 --- a/tests/smash_test.c +++ b/tests/smash_test.c @@ -22,7 +22,7 @@ int main(void) A[i] = p = GC_MALLOC(SIZE); if (i%3000 == 0) GC_gcollect(); - if (i%5678 == 0) p[SIZE + i/2000] = 42; + if (i%5678 == 0 && p != 0) p[SIZE + i/2000] = 42; } return 0; } diff --git a/tests/staticrootslib.c b/tests/staticrootslib.c index 16f3352b..2a8fcd49 100755..100644 --- a/tests/staticrootslib.c +++ b/tests/staticrootslib.c @@ -19,8 +19,10 @@ struct treenode * libsrl_mktree(int i) struct treenode * r = GC_MALLOC(sizeof(struct treenode)); if (0 == i) return 0; if (1 == i) r = GC_MALLOC_ATOMIC(sizeof(struct treenode)); - r -> x = libsrl_mktree(i-1); - r -> y = libsrl_mktree(i-1); + if (r) { + r -> x = libsrl_mktree(i-1); + r -> y = libsrl_mktree(i-1); + } return r; } diff --git a/tests/test.c b/tests/test.c index d8cf4f68..fa2eda17 100644 --- a/tests/test.c +++ b/tests/test.c @@ -81,16 +81,30 @@ # include <stdarg.h> +#define CHECH_GCLIB_VERSION \ + if (GC_get_version() != ((GC_VERSION_MAJOR<<16) \ + | (GC_VERSION_MINOR<<8) \ + | GC_ALPHA_VERSION)) { \ + GC_printf("libgc version mismatch\n"); \ + exit(1); \ + } + /* Call GC_INIT only on platforms on which we think we really need it, */ /* so that we can test automatic initialization on the rest. */ #if defined(CYGWIN32) || defined (AIX) || defined(DARWIN) \ || defined(THREAD_LOCAL_ALLOC) \ || (defined(MSWINCE) && !defined(GC_WINMAIN_REDIRECT)) -# define GC_COND_INIT() GC_INIT() +# define GC_COND_INIT() GC_INIT(); CHECH_GCLIB_VERSION #else -# define GC_COND_INIT() +# define GC_COND_INIT() CHECH_GCLIB_VERSION #endif +#define CHECK_OUT_OF_MEMORY(p) \ + if ((p) == NULL) { \ + GC_printf("Out of memory\n"); \ + exit(1); \ + } + /* Allocation Statistics. Incremented without synchronization. */ /* FIXME: We should be using synchronization. */ int stubborn_count = 0; @@ -113,7 +127,7 @@ int realloc_count = 0; } if(ret==NULL){ GC_printf("Out of memory, (typed allocations are not directly " - "supported with the GC_AMIGA_FASTALLOC option.)\n"); + "supported with the GC_AMIGA_FASTALLOC option.)\n"); FAIL; } } @@ -128,7 +142,7 @@ int realloc_count = 0; } if(ret==NULL){ GC_printf("Out of memory, (typed allocations are not directly " - "supported with the GC_AMIGA_FASTALLOC option.)\n"); + "supported with the GC_AMIGA_FASTALLOC option.)\n"); FAIL; } } @@ -139,7 +153,7 @@ int realloc_count = 0; #else /* !AMIGA_FASTALLOC */ -# ifdef PCR +# if defined(PCR) || defined(LINT2) # define FAIL (void)abort() # else # define FAIL ABORT("Test failed") @@ -186,14 +200,11 @@ sexpr cons (sexpr x, sexpr y) stubborn_count++; r = (sexpr) GC_MALLOC_STUBBORN(sizeof(struct SEXPR) + my_extra); - if (r == 0) { - (void)GC_printf("Out of memory\n"); - exit(1); - } + CHECK_OUT_OF_MEMORY(r); for (p = (int *)r; ((char *)p) < ((char *)r) + my_extra + sizeof(struct SEXPR); p++) { if (*p) { - (void)GC_printf("Found nonzero at %p - allocator is broken\n", p); + GC_printf("Found nonzero at %p - allocator is broken\n", p); FAIL; } *p = (int)((13 << 12) + ((p - (int *)r) & 0xfff)); @@ -262,10 +273,7 @@ sexpr small_cons (sexpr x, sexpr y) collectable_count++; r = (sexpr) GC_MALLOC(sizeof(struct SEXPR)); - if (r == 0) { - (void)GC_printf("Out of memory\n"); - exit(1); - } + CHECK_OUT_OF_MEMORY(r); r -> sexpr_car = x; r -> sexpr_cdr = y; return(r); @@ -277,10 +285,7 @@ sexpr small_cons_uncollectable (sexpr x, sexpr y) uncollectable_count++; r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); - if (r == 0) { - (void)GC_printf("Out of memory\n"); - exit(1); - } + CHECK_OUT_OF_MEMORY(r); r -> sexpr_car = x; r -> sexpr_cdr = (sexpr)(~(GC_word)y); return(r); @@ -297,10 +302,7 @@ sexpr gcj_cons(sexpr x, sexpr y) r = (GC_word *) GC_GCJ_MALLOC(sizeof(struct SEXPR) + sizeof(struct fake_vtable*), &gcj_class_struct2); - if (r == 0) { - (void)GC_printf("Out of memory\n"); - exit(1); - } + CHECK_OUT_OF_MEMORY(r); result = (sexpr)(r + 1); result -> sexpr_car = x; result -> sexpr_cdr = y; @@ -376,13 +378,13 @@ sexpr uncollectable_ints(int low, int up) void check_ints(sexpr list, int low, int up) { if (SEXPR_TO_INT(car(car(list))) != low) { - (void)GC_printf( + GC_printf( "List reversal produced incorrect list - collector is broken\n"); FAIL; } if (low == up) { if (cdr(list) != nil) { - (void)GC_printf("List too long - collector is broken\n"); + GC_printf("List too long - collector is broken\n"); FAIL; } } else { @@ -395,15 +397,14 @@ void check_ints(sexpr list, int low, int up) void check_uncollectable_ints(sexpr list, int low, int up) { if (SEXPR_TO_INT(car(car(list))) != low) { - (void)GC_printf( - "Uncollectable list corrupted - collector is broken\n"); + GC_printf("Uncollectable list corrupted - collector is broken\n"); FAIL; } if (low == up) { - if (UNCOLLECTABLE_CDR(list) != nil) { - (void)GC_printf("Uncollectable list too long - collector is broken\n"); - FAIL; - } + if (UNCOLLECTABLE_CDR(list) != nil) { + GC_printf("Uncollectable list too long - collector is broken\n"); + FAIL; + } } else { check_uncollectable_ints(UNCOLLECTABLE_CDR(list), low+1, up); } @@ -413,14 +414,14 @@ void check_uncollectable_ints(sexpr list, int low, int up) void print_int_list(sexpr x) { if (is_nil(x)) { - (void)GC_printf("NIL\n"); + GC_printf("NIL\n"); } else { - (void)GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); + GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); if (!is_nil(cdr(x))) { - (void)GC_printf(", "); - (void)print_int_list(cdr(x)); + GC_printf(", "); + print_int_list(cdr(x)); } else { - (void)GC_printf("\n"); + GC_printf("\n"); } } } @@ -433,15 +434,16 @@ void check_marks_int_list(sexpr x) else GC_printf("[mkd:%p]", x); if (is_nil(x)) { - (void)GC_printf("NIL\n"); + GC_printf("NIL\n"); } else { - if (!GC_is_marked((ptr_t)car(x))) GC_printf("[unm car:%p]", car(x)); - (void)GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); + if (!GC_is_marked((ptr_t)car(x))) + GC_printf("[unm car:%p]", car(x)); + GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); if (!is_nil(cdr(x))) { - (void)GC_printf(", "); - (void)check_marks_int_list(cdr(x)); + GC_printf(", "); + check_marks_int_list(cdr(x)); } else { - (void)GC_printf("\n"); + GC_printf("\n"); } } } @@ -477,11 +479,11 @@ void check_marks_int_list(sexpr x) pthread_t t; int code; if ((code = pthread_create(&t, 0, tiny_reverse_test, 0)) != 0) { - (void)GC_printf("Small thread creation failed %d\n", code); + GC_printf("Small thread creation failed %d\n", code); FAIL; } if ((code = pthread_join(t, 0)) != 0) { - (void)GC_printf("Small thread join failed %d\n", code); + GC_printf("Small thread join failed %d\n", code); FAIL; } } @@ -493,13 +495,13 @@ void check_marks_int_list(sexpr x) HANDLE h; h = GC_CreateThread(NULL, 0, tiny_reverse_test, 0, 0, &thread_id); if (h == (HANDLE)NULL) { - (void)GC_printf("Small thread creation failed %d\n", - (int)GetLastError()); + GC_printf("Small thread creation failed %d\n", + (int)GetLastError()); FAIL; } if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { - (void)GC_printf("Small thread wait failed %d\n", - (int)GetLastError()); + GC_printf("Small thread wait failed %d\n", + (int)GetLastError()); FAIL; } } @@ -536,23 +538,19 @@ void *GC_CALLBACK reverse_test_inner(void *data) # if /*defined(MSWIN32) ||*/ defined(MACOS) /* Win32S only allows 128K stacks */ # define BIG 1000 +# elif defined(PCR) + /* PCR default stack is 100K. Stack frames are up to 120 bytes. */ +# define BIG 700 +# elif defined(MSWINCE) || defined(RTEMS) + /* WinCE only allows 64K stacks */ +# define BIG 500 +# elif defined(OSF1) + /* OSF has limited stack space by default, and large frames. */ +# define BIG 200 +# elif defined(__MACH__) && defined(__ppc64__) +# define BIG 2500 # else -# if defined(PCR) - /* PCR default stack is 100K. Stack frames are up to 120 bytes. */ -# define BIG 700 -# else -# if defined(MSWINCE) - /* WinCE only allows 64K stacks */ -# define BIG 500 -# else -# if defined(OSF1) - /* OSF has limited stack space by default, and large frames. */ -# define BIG 200 -# else -# define BIG 4500 -# endif -# endif -# endif +# define BIG 4500 # endif A.dummy = 17; @@ -566,16 +564,19 @@ void *GC_CALLBACK reverse_test_inner(void *data) f = (sexpr *)GC_MALLOC(4 * sizeof(sexpr)); realloc_count++; f = (sexpr *)GC_REALLOC((void *)f, 6 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(f); f[5] = ints(1,17); collectable_count++; g = (sexpr *)GC_MALLOC(513 * sizeof(sexpr)); realloc_count++; g = (sexpr *)GC_REALLOC((void *)g, 800 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(g); g[799] = ints(1,18); collectable_count++; h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr)); realloc_count++; h = (sexpr *)GC_REALLOC((void *)h, 2000 * sizeof(sexpr)); + CHECK_OUT_OF_MEMORY(h); # ifdef GC_GCJ_SUPPORT h[1999] = gcj_ints(1,200); for (i = 0; i < 51; ++i) @@ -585,12 +586,12 @@ void *GC_CALLBACK reverse_test_inner(void *data) h[1999] = ints(1,200); # endif /* Try to force some collections and reuse of small list elements */ - for (i = 0; i < 10; i++) { - (void)ints(1, BIG); - } + for (i = 0; i < 10; i++) { + (void)ints(1, BIG); + } /* Superficially test interior pointer recognition on stack */ - c = (sexpr)((char *)c + sizeof(char *)); - d = (sexpr)((char *)d + sizeof(char *)); + c = (sexpr)((char *)c + sizeof(char *)); + d = (sexpr)((char *)d + sizeof(char *)); GC_FREE((void *)e); @@ -621,8 +622,11 @@ void *GC_CALLBACK reverse_test_inner(void *data) } check_ints(a,1,49); check_ints(b,1,50); + + /* Restore c and d values. */ c = (sexpr)((char *)c - sizeof(char *)); d = (sexpr)((char *)d - sizeof(char *)); + check_ints(c,1,BIG); check_uncollectable_ints(d, 1, 100); check_ints(f[5], 1,17); @@ -634,8 +638,8 @@ void *GC_CALLBACK reverse_test_inner(void *data) # ifndef THREADS a = 0; # endif - *(volatile void **)&b = 0; - *(volatile void **)&c = 0; + *(sexpr volatile *)&b = 0; + *(sexpr volatile *)&c = 0; return 0; } @@ -675,7 +679,7 @@ void GC_CALLBACK finalizer(void * obj, void * client_data) EnterCriticalSection(&incr_cs); # endif if ((int)(GC_word)client_data != t -> level) { - (void)GC_printf("Wrong finalization data - collector is broken\n"); + GC_printf("Wrong finalization data - collector is broken\n"); FAIL; } finalized_count++; @@ -710,19 +714,14 @@ tn * mktree(int n) collectable_count++; # if defined(MACOS) /* get around static data limitations. */ - if (!live_indicators) - live_indicators = - (GC_word*)NewPtrClear(MAX_FINALIZED * sizeof(GC_word)); if (!live_indicators) { - (void)GC_printf("Out of memory\n"); - exit(1); + live_indicators = + (GC_word*)NewPtrClear(MAX_FINALIZED * sizeof(GC_word)); + CHECK_OUT_OF_MEMORY(live_indicators); } # endif if (n == 0) return(0); - if (result == 0) { - (void)GC_printf("Out of memory\n"); - exit(1); - } + CHECK_OUT_OF_MEMORY(result); result -> level = n; result -> lchild = mktree(n-1); result -> rchild = mktree(n-1); @@ -791,12 +790,12 @@ tn * mktree(int n) void chktree(tn *t, int n) { if (n == 0 && t != 0) { - (void)GC_printf("Clobbered a leaf - collector is broken\n"); + GC_printf("Clobbered a leaf - collector is broken\n"); FAIL; } if (n == 0) return; if (t -> level != n) { - (void)GC_printf("Lost a node at level %d - collector is broken\n", n); + GC_printf("Lost a node at level %d - collector is broken\n", n); FAIL; } if (counter++ % 373 == 0) { @@ -828,18 +827,16 @@ void * alloc8bytes(void) if (my_free_list_ptr == 0) { uncollectable_count++; my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); + CHECK_OUT_OF_MEMORY(my_free_list_ptr); if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { - (void)GC_printf("pthread_setspecific failed\n"); + GC_printf("pthread_setspecific failed\n"); FAIL; } } my_free_list = *my_free_list_ptr; if (my_free_list == 0) { my_free_list = GC_malloc_many(8); - if (my_free_list == 0) { - (void)GC_printf("alloc8bytes out of memory\n"); - FAIL; - } + CHECK_OUT_OF_MEMORY(my_free_list); } *my_free_list_ptr = GC_NEXT(my_free_list); GC_NEXT(my_free_list) = 0; @@ -859,7 +856,7 @@ void alloc_small(int n) for (i = 0; i < n; i += 8) { atomic_count++; if (alloc8bytes() == 0) { - (void)GC_printf("Out of memory\n"); + GC_printf("Out of memory\n"); FAIL; } } @@ -889,7 +886,7 @@ void tree_test(void) # endif chktree(root, TREE_HEIGHT); if (finalized_count && ! dropped_something) { - (void)GC_printf("Premature finalization - collector is broken\n"); + GC_printf("Premature finalization - collector is broken\n"); FAIL; } dropped_something = 1; @@ -943,6 +940,7 @@ void typed_test(void) for (i = 0; i < 4000; i++) { collectable_count++; new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d1); + CHECK_OUT_OF_MEMORY(new); if (0 != new[0] || 0 != new[1]) { GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); FAIL; @@ -952,17 +950,20 @@ void typed_test(void) old = new; collectable_count++; new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d2); + CHECK_OUT_OF_MEMORY(new); new[0] = 17; new[1] = (GC_word)old; old = new; collectable_count++; new = (GC_word *) GC_malloc_explicitly_typed(33 * sizeof(GC_word), d3); + CHECK_OUT_OF_MEMORY(new); new[0] = 17; new[1] = (GC_word)old; old = new; collectable_count++; new = (GC_word *) GC_calloc_explicitly_typed(4, 2 * sizeof(GC_word), d1); + CHECK_OUT_OF_MEMORY(new); new[0] = 17; new[1] = (GC_word)old; old = new; @@ -974,19 +975,19 @@ void typed_test(void) new = (GC_word *) GC_calloc_explicitly_typed(1001, 3 * sizeof(GC_word), d2); - if (0 != new[0] || 0 != new[1]) { + if (new && (0 != new[0] || 0 != new[1])) { GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); FAIL; } } + CHECK_OUT_OF_MEMORY(new); new[0] = 17; new[1] = (GC_word)old; old = new; } for (i = 0; i < 20000; i++) { if (new[0] != 17) { - (void)GC_printf("typed alloc failed at %lu\n", - (unsigned long)i); + GC_printf("typed alloc failed at %lu\n", (unsigned long)i); FAIL; } new[0] = 0; @@ -1055,22 +1056,22 @@ void run_one_test(void) # ifdef FIND_LEAK GC_printf( - "This test program is not designed for leak detection mode\n"); - GC_printf("Expect lots of problems.\n"); + "This test program is not designed for leak detection mode\n"); + GC_printf("Expect lots of problems\n"); # endif GC_FREE(0); # ifndef DBG_HDRS_ALL collectable_count += 3; if ((GC_size(GC_malloc(7)) != 8 && GC_size(GC_malloc(7)) != MIN_WORDS * sizeof(GC_word)) - || GC_size(GC_malloc(15)) != 16) { - GC_printf("GC_size produced unexpected results\n"); - FAIL; + || GC_size(GC_malloc(15)) != 16) { + GC_printf("GC_size produced unexpected results\n"); + FAIL; } collectable_count += 1; if (GC_size(GC_malloc(0)) != MIN_WORDS * sizeof(GC_word)) { GC_printf("GC_malloc(0) failed: GC_size returns %ld\n", - (unsigned long)GC_size(GC_malloc(0))); + (unsigned long)GC_size(GC_malloc(0))); FAIL; } collectable_count += 1; @@ -1126,8 +1127,7 @@ void run_one_test(void) if (GC_is_valid_displacement(y) != y || GC_is_valid_displacement(x) != x || GC_is_valid_displacement(x + 3) != x + 3) { - GC_printf( - "GC_is_valid_displacement produced incorrect result\n"); + GC_printf("GC_is_valid_displacement produced incorrect result\n"); FAIL; } { @@ -1143,19 +1143,25 @@ void run_one_test(void) # if defined(RS6000) || defined(POWERPC) if (!TEST_FAIL_COUNT(1)) # else - if ((GC_all_interior_pointers && !TEST_FAIL_COUNT(1)) - || (!GC_all_interior_pointers && !TEST_FAIL_COUNT(2))) + if (!TEST_FAIL_COUNT(GC_get_all_interior_pointers() ? 1 : 2)) # endif { - GC_printf("GC_is_valid_displacement produced wrong failure indication\n"); + GC_printf( + "GC_is_valid_displacement produced wrong failure indication\n"); FAIL; } # endif # endif /* DBG_HDRS_ALL */ /* Test floating point alignment */ collectable_count += 2; - *(double *)GC_MALLOC(sizeof(double)) = 1.0; - *(double *)GC_MALLOC(sizeof(double)) = 1.0; + { + double *dp = GC_MALLOC(sizeof(double)); + CHECK_OUT_OF_MEMORY(dp); + *dp = 1.0; + dp = GC_MALLOC(sizeof(double)); + CHECK_OUT_OF_MEMORY(dp); + *dp = 1.0; + } /* Test size 0 allocation a bit more */ { size_t i; @@ -1211,15 +1217,16 @@ void run_one_test(void) GET_TIME(tree_time); time_diff = MS_TIME_DIFF(tree_time, start_time); GC_log_printf("-------------Finished tree_test at time %u (%p)\n", - (unsigned) time_diff, &start_time); + (unsigned) time_diff, &start_time); } /* Run reverse_test a second time, so we hopefully notice corruption. */ reverse_test(); if (GC_print_stats) { - GET_TIME(reverse_time); - time_diff = MS_TIME_DIFF(reverse_time, start_time); - GC_log_printf("-------------Finished second reverse_test at time %u (%p)\n", - (unsigned) time_diff, &start_time); + GET_TIME(reverse_time); + time_diff = MS_TIME_DIFF(reverse_time, start_time); + GC_log_printf( + "-------------Finished second reverse_test at time %u (%p)\n", + (unsigned)time_diff, &start_time); } /* GC_allocate_ml and GC_need_to_lock are no longer exported, and */ /* AO_fetch_and_add1() may be unavailable to update a counter. */ @@ -1238,6 +1245,8 @@ void run_one_test(void) GC_log_printf("Finished %p\n", &start_time); } +#define NUMBER_ROUND_UP(v, bound) ((((v) + (bound) - 1) / (bound)) * (bound)) + void check_heap_stats(void) { size_t max_heap_sz; @@ -1274,6 +1283,10 @@ void check_heap_stats(void) # endif # endif # endif + max_heap_sz *= n_tests; +# if defined(USE_MMAP) || defined(MSWIN32) + max_heap_sz = NUMBER_ROUND_UP(max_heap_sz, 4 * 1024 * 1024); +# endif /* Garbage collect repeatedly so that all inaccessible objects */ /* can be finalized. */ while (GC_collect_a_little()) { } @@ -1288,26 +1301,26 @@ void check_heap_stats(void) GC_log_printf("Primordial thread stack bottom: %p\n", GC_stackbottom); } - (void)GC_printf("Completed %u tests\n", n_tests); - (void)GC_printf("Allocated %d collectable objects\n", collectable_count); - (void)GC_printf("Allocated %d uncollectable objects\n", - uncollectable_count); - (void)GC_printf("Allocated %d atomic objects\n", atomic_count); - (void)GC_printf("Allocated %d stubborn objects\n", stubborn_count); - (void)GC_printf("Finalized %d/%d objects - ", - finalized_count, finalizable_count); + GC_printf("Completed %u tests\n", n_tests); + GC_printf("Allocated %d collectable objects\n", collectable_count); + GC_printf("Allocated %d uncollectable objects\n", + uncollectable_count); + GC_printf("Allocated %d atomic objects\n", atomic_count); + GC_printf("Allocated %d stubborn objects\n", stubborn_count); + GC_printf("Finalized %d/%d objects - ", + finalized_count, finalizable_count); # ifdef FINALIZE_ON_DEMAND if (finalized_count != late_finalize_count) { - (void)GC_printf("Demand finalization error\n"); + GC_printf("Demand finalization error\n"); FAIL; } # endif if (finalized_count > finalizable_count || finalized_count < finalizable_count/2) { - (void)GC_printf("finalization is probably broken\n"); + GC_printf("finalization is probably broken\n"); FAIL; } else { - (void)GC_printf("finalization is probably ok\n"); + GC_printf("finalization is probably ok\n"); } still_live = 0; for (i = 0; i < MAX_FINALIZED; i++) { @@ -1318,36 +1331,38 @@ void check_heap_stats(void) i = finalizable_count - finalized_count - still_live; if (0 != i) { GC_printf("%d disappearing links remain and %d more objects " - "were not finalized\n", still_live, i); + "were not finalized\n", still_live, i); if (i > 10) { GC_printf("\tVery suspicious!\n"); } else { - GC_printf("\tSlightly suspicious, but probably OK.\n"); + GC_printf("\tSlightly suspicious, but probably OK\n"); } } - (void)GC_printf("Total number of bytes allocated is %lu\n", - (unsigned long) - (GC_bytes_allocd + GC_bytes_allocd_before_gc)); - (void)GC_printf("Final heap size is %lu bytes\n", - (unsigned long)GC_get_heap_size()); - if (GC_bytes_allocd + GC_bytes_allocd_before_gc < n_tests * + GC_printf("Total number of bytes allocated is %lu\n", + (unsigned long)GC_get_total_bytes()); + GC_printf("Final heap size is %lu bytes\n", + (unsigned long)GC_get_heap_size()); + if (GC_get_total_bytes() < n_tests * # ifdef VERY_SMALL_CONFIG 2700000 # else 33500000 # endif ) { - (void)GC_printf("Incorrect execution - missed some allocations\n"); - FAIL; + GC_printf("Incorrect execution - missed some allocations\n"); + FAIL; } - if (GC_get_heap_size() + GC_get_unmapped_bytes() > max_heap_sz*n_tests) { - (void)GC_printf("Unexpected heap growth - collector may be broken\n"); + if (GC_get_heap_size() + GC_get_unmapped_bytes() > max_heap_sz) { + GC_printf("Unexpected heap growth - collector may be broken" + " (heapsize: %lu, expected: %lu)\n", + (unsigned long)(GC_get_heap_size() + GC_get_unmapped_bytes()), + (unsigned long)max_heap_sz); FAIL; } # ifdef THREADS GC_unregister_my_thread(); /* just to check it works (for main) */ # endif - (void)GC_printf("Collector appears to work\n"); + GC_printf("Collector appears to work\n"); } #if defined(MACOS) @@ -1380,12 +1395,21 @@ void GC_CALLBACK warn_proc(char *msg, GC_word p) # define WINMAIN_LPTSTR LPSTR #endif -#if !defined(PCR) \ - && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) \ +#if !defined(PCR) && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) \ || defined(LINT) #if defined(MSWIN32) && !defined(__MINGW32__) || defined(MSWINCE) int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, WINMAIN_LPTSTR cmd, int n) +#elif defined(RTEMS) +# include <bsp.h> +# define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER +# define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +# define CONFIGURE_RTEMS_INIT_TASKS_TABLE +# define CONFIGURE_MAXIMUM_TASKS 1 +# define CONFIGURE_INIT +# define CONFIGURE_INIT_TASK_STACK_SIZE (64*1024) +# include <rtems/confdefs.h> + rtems_task Init(rtems_task_argument ignord) #else int main(void) #endif @@ -1395,7 +1419,7 @@ void GC_CALLBACK warn_proc(char *msg, GC_word p) /* Make sure we have lots and lots of stack space. */ SetMinimumStack(cMinStackSpace); /* Cheat and let stdio initialize toolbox for us. */ - printf("Testing GC Macintosh port.\n"); + printf("Testing GC Macintosh port\n"); # endif GC_COND_INIT(); GC_set_warn_proc(warn_proc); @@ -1551,12 +1575,12 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, # ifdef MSWINCE win_created_h = CreateEvent(NULL, FALSE, FALSE, NULL); if (win_created_h == (HANDLE)NULL) { - (void)GC_printf("Event creation failed %d\n", (int)GetLastError()); + GC_printf("Event creation failed %d\n", (int)GetLastError()); FAIL; } win_thr_h = GC_CreateThread(NULL, 0, thr_window, 0, 0, &thread_id); if (win_thr_h == (HANDLE)NULL) { - (void)GC_printf("Thread creation failed %d\n", (int)GetLastError()); + GC_printf("Thread creation failed %d\n", (int)GetLastError()); FAIL; } if (WaitForSingleObject(win_created_h, INFINITE) != WAIT_OBJECT_0) @@ -1567,7 +1591,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, for (i = 0; i < NTHREADS; i++) { h[i] = GC_CreateThread(NULL, 0, thr_run_one_test, 0, 0, &thread_id); if (h[i] == (HANDLE)NULL) { - (void)GC_printf("Thread creation failed %d\n", (int)GetLastError()); + GC_printf("Thread creation failed %d\n", (int)GetLastError()); FAIL; } } @@ -1576,7 +1600,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, # if NTHREADS > 0 for (i = 0; i < NTHREADS; i++) { if (WaitForSingleObject(h[i], INFINITE) != WAIT_OBJECT_0) { - (void)GC_printf("Thread wait failed %d\n", (int)GetLastError()); + GC_printf("Thread wait failed %d\n", (int)GetLastError()); FAIL; } } @@ -1608,11 +1632,11 @@ int test(void) run_one_test(); if (PCR_Th_T_Join(th1, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) != PCR_ERes_okay || code != 0) { - (void)GC_printf("Thread 1 failed\n"); + GC_printf("Thread 1 failed\n"); } if (PCR_Th_T_Join(th2, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) != PCR_ERes_okay || code != 0) { - (void)GC_printf("Thread 2 failed\n"); + GC_printf("Thread 2 failed\n"); } check_heap_stats(); return(0); @@ -1645,7 +1669,7 @@ int main(void) /* Default stack size is too small, especially with the 64 bit ABI */ /* Increase it. */ if (pthread_default_stacksize_np(1024*1024, 0) != 0) { - (void)GC_printf("pthread_default_stacksize_np failed.\n"); + GC_printf("pthread_default_stacksize_np failed\n"); } # endif /* GC_HPUX_THREADS */ # ifdef PTW32_STATIC_LIB @@ -1671,39 +1695,39 @@ int main(void) && !defined(MAKE_BACK_GRAPH) && !defined(USE_PROC_FOR_LIBRARIES) \ && !defined(NO_INCREMENTAL) GC_enable_incremental(); - (void) GC_printf("Switched to incremental mode\n"); + GC_printf("Switched to incremental mode\n"); # if defined(MPROTECT_VDB) - (void)GC_printf("Emulating dirty bits with mprotect/signals\n"); + GC_printf("Emulating dirty bits with mprotect/signals\n"); # else # ifdef PROC_VDB - (void)GC_printf("Reading dirty bits from /proc\n"); + GC_printf("Reading dirty bits from /proc\n"); # else - (void)GC_printf("Using DEFAULT_VDB dirty bit implementation\n"); + GC_printf("Using DEFAULT_VDB dirty bit implementation\n"); # endif # endif # endif GC_set_warn_proc(warn_proc); if ((code = pthread_key_create(&fl_key, 0)) != 0) { - (void)GC_printf("Key creation failed %d\n", code); + GC_printf("Key creation failed %d\n", code); FAIL; } for (i = 0; i < NTHREADS; ++i) { if ((code = pthread_create(th+i, &attr, thr_run_one_test, 0)) != 0) { - (void)GC_printf("Thread %d creation failed %d\n", i, code); + GC_printf("Thread %d creation failed %d\n", i, code); FAIL; } } run_one_test(); for (i = 0; i < NTHREADS; ++i) { if ((code = pthread_join(th[i], 0)) != 0) { - (void)GC_printf("Thread %d failed %d\n", i, code); + GC_printf("Thread %d failed %d\n", i, code); FAIL; } } check_heap_stats(); (void)fflush(stdout); pthread_attr_destroy(&attr); - GC_printf("Completed %u collections\n", (unsigned)GC_gc_no); + GC_printf("Completed %u collections\n", (unsigned)GC_get_gc_no()); # ifdef PTW32_STATIC_LIB pthread_win32_thread_detach_np (); pthread_win32_process_detach_np (); diff --git a/tests/test_cpp.cc b/tests/test_cpp.cc index 94a8ca37..de01bf71 100644 --- a/tests/test_cpp.cc +++ b/tests/test_cpp.cc @@ -27,12 +27,17 @@ few minutes to complete. #ifdef HAVE_CONFIG_H # include "private/config.h" #endif + #undef GC_BUILD + #include "gc_cpp.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> + #define USE_STD_ALLOCATOR + #ifdef USE_STD_ALLOCATOR # include "gc_allocator.h" #elif __GNUC__ @@ -40,6 +45,7 @@ few minutes to complete. #else # include "gc_alloc.h" #endif + extern "C" { # include "private/gcconfig.h" GC_API void GC_printf(const char *format, ...); @@ -47,17 +53,18 @@ extern "C" { /* Don't include gc_priv.h, since that may include Windows system */ /* header files that don't take kindly to this context. */ } + #ifdef MSWIN32 -# include <windows.h> +# include <windows.h> #endif + #ifdef GC_NAME_CONFLICT -# define USE_GC UseGC - struct foo * GC; +# define USE_GC UseGC + struct foo * GC; #else -# define USE_GC GC +# define USE_GC GC #endif - #define my_assert( e ) \ if (! (e)) { \ GC_printf( "Assertion failure in " __FILE__ ", line %d: " #e "\n", \ @@ -180,37 +187,32 @@ GC_word Disguise( void* p ) { void* Undisguise( GC_word i ) { return (void*) ~ i;} - -#ifdef MSWIN32 +#if 0//def MSWIN32 int APIENTRY WinMain( HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int cmdShow ) { int argc; char* argv[ 3 ]; - for (argc = 1; argc < sizeof( argv ) / sizeof( argv[ 0 ] ); argc++) { + for (argc = 1; argc < (int)(sizeof(argv) / sizeof(argv[0])); argc++) { argv[ argc ] = strtok( argc == 1 ? cmd : 0, " \t" ); if (0 == argv[ argc ]) break;} - +#elif defined(MACOS) + int main() { + char* argv_[] = {"test_cpp", "10"}; // MacOS doesn't have a commandline + argv = argv_; + argc = sizeof(argv_)/sizeof(argv_[0]); #else -# ifdef MACOS - int main() { -# else - int main( int argc, char* argv[] ) { -# endif + int main( int argc, char* argv[] ) { #endif - GC_INIT(); + GC_INIT(); -# if defined(MACOS) // MacOS - char* argv_[] = {"test_cpp", "10"}; // doesn't - argv = argv_; // have a - argc = sizeof(argv_)/sizeof(argv_[0]); // commandline -# endif int i, iters, n; # ifdef USE_STD_ALLOCATOR int *x = gc_allocator<int>().allocate(1); - int *xio = gc_allocator_ignore_off_page<int>().allocate(1); + int *xio; + xio = gc_allocator_ignore_off_page<int>().allocate(1); int **xptr = traceable_allocator<int *>().allocate(1); # else # ifdef __GNUC__ @@ -237,7 +239,7 @@ int APIENTRY WinMain( GC_word as[ 1000 ]; GC_word bs[ 1000 ]; for (i = 0; i < 1000; i++) { - as[ i ] = Disguise( new (NoGC) A( i ) ); + as[ i ] = Disguise( new (NoGC ) A( i ) ); bs[ i ] = Disguise( new (NoGC) B( i ) );} /* Allocate a fair number of finalizable Cs, Ds, and Fs. @@ -245,15 +247,18 @@ int APIENTRY WinMain( for (i = 0; i < 1000; i++) { C* c = new C( 2 ); C c1( 2 ); /* stack allocation should work too */ - D* d = ::new (USE_GC, D::CleanUp, (void*)(GC_word)i) D( i ); - F* f = new F; + D* d; + F* f; + d = ::new (USE_GC, D::CleanUp, (void*)(GC_word)i) D( i ); + f = new F; if (0 == i % 10) delete c;} /* Allocate a very large number of collectable As and Bs and drop the references to them immediately, forcing many collections. */ for (i = 0; i < 1000000; i++) { - A* a = new (USE_GC) A( i ); + A* a; + a = new (USE_GC) A( i ); B* b = new B( i ); b = new (USE_GC) B( i ); if (0 == i % 10) { @@ -278,7 +283,6 @@ int APIENTRY WinMain( # ifdef FINALIZE_ON_DEMAND GC_invoke_finalizers(); # endif - } /* Make sure most of the finalizable Cs, Ds, and Fs have @@ -292,4 +296,6 @@ int APIENTRY WinMain( # endif my_assert (29 == x[0]); GC_printf( "The test appears to have succeeded.\n" ); - return( 0 );} + return( 0 ); +} + diff --git a/tests/tests.am b/tests/tests.am index b655c2ba..f3505749 100644 --- a/tests/tests.am +++ b/tests/tests.am @@ -45,6 +45,11 @@ check_PROGRAMS += hugetest hugetest_SOURCES = tests/huge_test.c hugetest_LDADD = $(test_ldadd) +TESTS += realloc_test$(EXEEXT) +check_PROGRAMS += realloc_test +realloc_test_SOURCES = tests/realloc_test.c +realloc_test_LDADD = $(test_ldadd) + TESTS += staticrootstest$(EXEEXT) check_PROGRAMS += staticrootstest staticrootstest_SOURCES = tests/staticrootstest.c @@ -67,6 +72,16 @@ TESTS += threadleaktest$(EXEEXT) check_PROGRAMS += threadleaktest threadleaktest_SOURCES = tests/thread_leak_test.c threadleaktest_LDADD = $(test_ldadd) + +TESTS += threadkey_test$(EXEEXT) +check_PROGRAMS += threadkey_test +threadkey_test_SOURCES = tests/threadkey_test.c +threadkey_test_LDADD = $(test_ldadd) + +TESTS += initsecondarythread$(EXEEXT) +check_PROGRAMS += initsecondarythread +initsecondarythread_SOURCES = tests/initsecondarythread.c +initsecondarythread_LDADD = $(test_ldadd) endif if CPLUSPLUS diff --git a/tests/threadkey_test.c b/tests/threadkey_test.c new file mode 100644 index 00000000..d080a7ba --- /dev/null +++ b/tests/threadkey_test.c @@ -0,0 +1,92 @@ + +#ifndef GC_THREADS +# define GC_THREADS +#endif + +#define GC_NO_THREAD_REDIRECTS 1 + +#include "gc.h" + +#if (!defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \ + || defined(__native_client__)) && !defined(SKIP_THREADKEY_TEST) + /* FIXME: Skip this test on Solaris for now. The test may fail on */ + /* other targets as well. Currently, tested only on Linux, Cygwin */ + /* and Darwin. */ +# define SKIP_THREADKEY_TEST +#endif + +#ifdef SKIP_THREADKEY_TEST + +int main (void) +{ + return 0; +} + +#else + +#include <pthread.h> + +pthread_key_t key; + +#ifdef GC_SOLARIS_THREADS + /* pthread_once_t key_once = { PTHREAD_ONCE_INIT }; */ +#else + pthread_once_t key_once = PTHREAD_ONCE_INIT; +#endif + +void * entry (void *arg) +{ + pthread_setspecific(key, + (void *)GC_HIDE_POINTER(GC_STRDUP("hello, world"))); + return arg; +} + +void * GC_CALLBACK on_thread_exit_inner (struct GC_stack_base * sb, void * arg) +{ + int res = GC_register_my_thread (sb); + pthread_t t; + int creation_res; /* Used to suppress a warning about */ + /* unchecked pthread_create() result. */ + + creation_res = GC_pthread_create (&t, NULL, entry, NULL); + if (res == GC_SUCCESS) + GC_unregister_my_thread (); + + return (void*)(GC_word)creation_res; +} + +void on_thread_exit (void *v) +{ + GC_call_with_stack_base (on_thread_exit_inner, NULL); +} + +void make_key (void) +{ + pthread_key_create (&key, on_thread_exit); +} + +#ifndef LIMIT +# define LIMIT 30 +#endif + +int main (void) +{ + int i; + GC_INIT (); + +# ifdef GC_SOLARIS_THREADS + pthread_key_create (&key, on_thread_exit); +# else + pthread_once (&key_once, make_key); +# endif + for (i = 0; i < LIMIT; i++) { + pthread_t t; + void *res; + if (GC_pthread_create (&t, NULL, entry, NULL) == 0 + && (i & 1) != 0) + GC_pthread_join (t, &res); + } + return 0; +} + +#endif diff --git a/thread_local_alloc.c b/thread_local_alloc.c index 2e27331d..610db6fd 100644 --- a/thread_local_alloc.c +++ b/thread_local_alloc.c @@ -147,8 +147,6 @@ GC_API void * GC_CALL GC_malloc(size_t bytes) tsd = GC_getspecific(k); # else tsd = GC_getspecific(GC_thread_key); -# endif -# if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC) if (EXPECT(0 == tsd, FALSE)) { return GC_core_malloc(bytes); } @@ -179,15 +177,13 @@ GC_API void * GC_CALL GC_malloc_atomic(size_t bytes) if (EXPECT(0 == k, FALSE)) { /* We haven't yet run GC_init_parallel. That means */ /* we also aren't locking, so this is fairly cheap. */ - return GC_core_malloc(bytes); + return GC_core_malloc_atomic(bytes); } tsd = GC_getspecific(k); # else tsd = GC_getspecific(GC_thread_key); -# endif -# if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC) if (EXPECT(0 == tsd, FALSE)) { - return GC_core_malloc(bytes); + return GC_core_malloc_atomic(bytes); } # endif GC_ASSERT(GC_is_initialized); @@ -288,7 +284,7 @@ GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p) } #if defined(GC_ASSERTIONS) - /* Check that all thread-local free-lists in p are completely marked. */ + /* Check that all thread-local free-lists in p are completely marked. */ void GC_check_tls_for(GC_tlfs p) { ptr_t q; diff --git a/vc9/test_cpp_libgc.vcproj b/vc9/test_cpp_libgc.vcproj index 0fcc8eed..b15c9e9f 100644 --- a/vc9/test_cpp_libgc.vcproj +++ b/vc9/test_cpp_libgc.vcproj @@ -56,7 +56,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
@@ -81,11 +81,10 @@ />
</Configuration>
<Configuration
- Name="Release|Win32"
+ Name="Debug|x64"
ConfigurationType="1"
InheritedPropertySheets="$(solutiondir)/$(solutionname)_$(platformname).$(configurationname).vsprops"
CharacterSet="0"
- WholeProgramOptimization="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -101,6 +100,7 @@ />
<Tool
Name="VCMIDLTool"
+ TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
@@ -118,7 +118,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
@@ -143,10 +143,11 @@ />
</Configuration>
<Configuration
- Name="Debug|x64"
+ Name="Release|Win32"
ConfigurationType="1"
InheritedPropertySheets="$(solutiondir)/$(solutionname)_$(platformname).$(configurationname).vsprops"
CharacterSet="0"
+ WholeProgramOptimization="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -162,7 +163,6 @@ />
<Tool
Name="VCMIDLTool"
- TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
@@ -180,7 +180,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
@@ -243,7 +243,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
diff --git a/vc9/test_libgc.vcproj b/vc9/test_libgc.vcproj index 7cb89da9..0f40c859 100644 --- a/vc9/test_libgc.vcproj +++ b/vc9/test_libgc.vcproj @@ -55,7 +55,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
@@ -80,11 +80,10 @@ />
</Configuration>
<Configuration
- Name="Release|Win32"
+ Name="Debug|x64"
ConfigurationType="1"
InheritedPropertySheets="$(solutiondir)/$(solutionname)_$(platformname).$(configurationname).vsprops"
CharacterSet="0"
- WholeProgramOptimization="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -100,6 +99,7 @@ />
<Tool
Name="VCMIDLTool"
+ TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
@@ -116,7 +116,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
@@ -141,10 +141,11 @@ />
</Configuration>
<Configuration
- Name="Debug|x64"
+ Name="Release|Win32"
ConfigurationType="1"
InheritedPropertySheets="$(solutiondir)/$(solutionname)_$(platformname).$(configurationname).vsprops"
CharacterSet="0"
+ WholeProgramOptimization="0"
>
<Tool
Name="VCPreBuildEventTool"
@@ -160,7 +161,6 @@ />
<Tool
Name="VCMIDLTool"
- TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
@@ -177,7 +177,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
@@ -239,7 +239,7 @@ />
<Tool
Name="VCLinkerTool"
- SubSystem="2"
+ SubSystem="1"
/>
<Tool
Name="VCALinkTool"
diff --git a/win32_threads.c b/win32_threads.c index 8322eb6b..123668cf 100755..100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -51,19 +51,6 @@ # undef pthread_sigmask # endif -# ifdef DEBUG_THREADS -# ifdef CYGWIN32 -# define DEBUG_CYGWIN_THREADS 1 -# define DEBUG_WIN32_PTHREADS 0 -# else -# define DEBUG_WIN32_PTHREADS 1 -# define DEBUG_CYGWIN_THREADS 0 -# endif -# else -# define DEBUG_CYGWIN_THREADS 0 -# define DEBUG_WIN32_PTHREADS 0 -# endif - STATIC void * GC_pthread_start(void * arg); STATIC void GC_thread_exit_proc(void *arg); @@ -76,12 +63,6 @@ # undef _beginthreadex # undef _endthreadex -# ifdef DEBUG_THREADS -# define DEBUG_WIN32_THREADS 1 -# else -# define DEBUG_WIN32_THREADS 0 -# endif - # ifdef MSWINCE /* Force DONT_USE_SIGNALANDWAIT implementation of PARALLEL_MARK */ /* for WinCE (since Win32 SignalObjectAndWait() is missing). */ @@ -374,20 +355,20 @@ STATIC GC_thread GC_new_thread(DWORD id) return(result); } -#ifdef MPROTECT_VDB - GC_INNER LONG WINAPI GC_write_fault_handler( - struct _EXCEPTION_POINTERS *exc_info); -#endif - -#if defined(GWW_VDB) && defined(MPROTECT_VDB) - GC_INNER GC_bool GC_gww_dirty_init(void); - /* Defined in os_dep.c. Returns TRUE if GetWriteWatch is available. */ - /* may be called repeatedly. */ -#endif - STATIC GC_bool GC_in_thread_creation = FALSE; /* Protected by allocation lock. */ +GC_INLINE void GC_record_stack_base(GC_vthread me, + const struct GC_stack_base *sb) +{ + me -> stack_base = sb -> mem_base; +# ifdef IA64 + me -> backing_store_end = sb -> reg_base; +# endif + if (me -> stack_base == NULL) + ABORT("Bad stack base in GC_register_my_thread"); +} + /* This may be called from DllMain, and hence operates under unusual */ /* constraints. In particular, it must be lock-free if */ /* GC_win32_dll_threads is set. Always called from the thread being */ @@ -402,12 +383,12 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, /* documentation. There is empirical evidence that it */ /* isn't. - HB */ # if defined(MPROTECT_VDB) -# if defined(GWW_VDB) - if (GC_incremental && !GC_gww_dirty_init()) - SetUnhandledExceptionFilter(GC_write_fault_handler); -# else - if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); -# endif + if (GC_incremental +# ifdef GWW_VDB + && !GC_gww_dirty_init() +# endif + ) + GC_set_write_fault_handler(); # endif # ifndef GC_NO_THREADS_DISCOVERY @@ -436,7 +417,7 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, /* FIXME: We should eventually declare Win95 dead and use AO_ */ /* primitives here. */ if (i == MAX_THREADS - 1) - ABORT("too many threads"); + ABORT("Too many threads"); } /* Update GC_max_thread_index if necessary. The following is */ /* safe, and unlike CompareExchange-based solutions seems to work */ @@ -460,7 +441,7 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, me = GC_new_thread(thread_id); GC_in_thread_creation = FALSE; if (me == 0) - ABORT("Failed to allocate memory for thread registering."); + ABORT("Failed to allocate memory for thread registering"); } # ifdef GC_PTHREADS /* me can be NULL -> segfault */ @@ -474,16 +455,13 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, 0 /* dwDesiredAccess */, FALSE /* bInheritHandle */, DUPLICATE_SAME_ACCESS)) { if (GC_print_stats) - GC_printf("DuplicateHandle failed with error code: %d\n", - (int)GetLastError()); + GC_log_printf("DuplicateHandle failed with error code: %d\n", + (int)GetLastError()); ABORT("DuplicateHandle failed"); } # endif me -> last_stack_min = ADDR_LIMIT; - me -> stack_base = sb -> mem_base; -# ifdef IA64 - me -> backing_store_end = sb -> reg_base; -# endif + GC_record_stack_base(me, sb); /* Up until this point, GC_push_all_stacks considers this thread */ /* invalid. */ /* Up until this point, this entry is viewed as reserved but invalid */ @@ -492,8 +470,6 @@ STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base *sb, # if defined(THREAD_LOCAL_ALLOC) GC_init_thread_local((GC_tlfs)(&(me->tlfs))); # endif - if (me -> stack_base == NULL) - ABORT("Bad stack base in GC_register_my_thread_inner"); # ifndef GC_NO_THREADS_DISCOVERY if (GC_win32_dll_threads) { if (GC_please_stop) { @@ -558,11 +534,19 @@ STATIC GC_thread GC_lookup_thread_inner(DWORD thread_id) } } +#ifdef LINT2 +# define CHECK_LOOKUP_MY_THREAD(me) \ + if (!(me)) ABORT("GC_lookup_thread_inner(GetCurrentThreadId) failed") +#else +# define CHECK_LOOKUP_MY_THREAD(me) /* empty */ +#endif + /* Called by GC_finalize() (in case of an allocation failure observed). */ /* GC_reset_finalizer_nested() is the same as in pthread_support.c. */ GC_INNER void GC_reset_finalizer_nested(void) { GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); + CHECK_LOOKUP_MY_THREAD(me); me->finalizer_nested = 0; } @@ -575,7 +559,9 @@ GC_INNER void GC_reset_finalizer_nested(void) GC_INNER unsigned char *GC_check_finalizer_nested(void) { GC_thread me = GC_lookup_thread_inner(GetCurrentThreadId()); - unsigned nesting_level = me->finalizer_nested; + unsigned nesting_level; + CHECK_LOOKUP_MY_THREAD(me); + nesting_level = me->finalizer_nested; if (nesting_level) { /* We are inside another GC_invoke_finalizers(). */ /* Skip some implicitly-called GC_invoke_finalizers() */ @@ -620,6 +606,12 @@ GC_INNER unsigned char *GC_check_finalizer_nested(void) # define UNPROTECT_THREAD(t) #endif +#ifdef CYGWIN32 +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id +#elif defined(GC_WIN32_PTHREADS) +# define GC_PTHREAD_PTRVAL(pthread_id) pthread_id.p +#endif + /* If a thread has been joined, but we have not yet */ /* been notified, then there may be more than one thread */ /* in the table with the same win32 id. */ @@ -628,40 +620,36 @@ GC_INNER unsigned char *GC_check_finalizer_nested(void) /* GC_win32_dll_threads is set. */ /* If GC_win32_dll_threads is set it should be called from the */ /* thread being deleted. */ -STATIC void GC_delete_gc_thread(GC_vthread gc_id) +STATIC void GC_delete_gc_thread(GC_vthread t) { # ifndef MSWINCE - CloseHandle(gc_id->handle); + CloseHandle(t->handle); # endif # ifndef GC_NO_THREADS_DISCOVERY if (GC_win32_dll_threads) { /* This is intended to be lock-free. */ /* It is either called synchronously from the thread being */ /* deleted, or by the joining thread. */ - /* In this branch asynchronous changes to *gc_id are possible. */ + /* In this branch asynchronous changes to (*t) are possible. */ /* It's not allowed to call GC_printf (and the friends) here, */ /* see GC_stop_world() for the information. */ - gc_id -> stack_base = 0; - gc_id -> id = 0; -# ifdef CYGWIN32 - gc_id -> pthread_id = 0; -# endif /* CYGWIN32 */ -# ifdef GC_WIN32_PTHREADS - gc_id -> pthread_id.p = NULL; -# endif /* GC_WIN32_PTHREADS */ - AO_store_release(&gc_id->tm.in_use, FALSE); + t -> stack_base = 0; + t -> id = 0; +# ifdef GC_PTHREADS + GC_PTHREAD_PTRVAL(t->pthread_id) = 0; +# endif + AO_store_release(&t->tm.in_use, FALSE); } else # endif /* else */ { - /* Cast away volatile qualifier, since we have lock. */ - GC_thread gc_nvid = (GC_thread)gc_id; - DWORD id = gc_nvid -> id; + DWORD id = ((GC_thread)t) -> id; + /* Cast away volatile qualifier, since we have lock. */ word hv = THREAD_TABLE_INDEX(id); register GC_thread p = GC_threads[hv]; register GC_thread prev = 0; GC_ASSERT(I_HOLD_LOCK()); - while (p != gc_nvid) { + while (p != (GC_thread)t) { prev = p; p = p -> tm.next; } @@ -728,7 +716,8 @@ GC_API void GC_CALL GC_allow_register_threads(void) GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) { - DWORD t = GetCurrentThreadId(); + GC_thread me; + DWORD thread_id = GetCurrentThreadId(); DCL_LOCK_STATE; if (GC_need_to_lock == FALSE) @@ -736,11 +725,27 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb) /* We lock here, since we want to wait for an ongoing GC. */ LOCK(); - if (0 == GC_lookup_thread_inner(t)) { - GC_register_my_thread_inner(sb, t); + me = GC_lookup_thread_inner(thread_id); + if (me == 0) { + GC_register_my_thread_inner(sb, thread_id); +# ifdef GC_PTHREADS + me -> flags |= DETACHED; +# endif UNLOCK(); return GC_SUCCESS; - } else { + } else +# ifdef GC_PTHREADS + /* else */ if ((me -> flags & FINISHED) != 0) { + GC_record_stack_base(me, sb); + me -> flags &= ~FINISHED; /* but not DETACHED */ +# ifdef THREAD_LOCAL_ALLOC + GC_init_thread_local((GC_tlfs)(&me->tlfs)); +# endif + UNLOCK(); + return GC_SUCCESS; + } else +# endif + /* else */ { UNLOCK(); return GC_DUPLICATE; } @@ -750,25 +755,45 @@ GC_API int GC_CALL GC_unregister_my_thread(void) { DCL_LOCK_STATE; +# ifdef DEBUG_THREADS + GC_log_printf("Unregistering thread 0x%lx\n", (long)GetCurrentThreadId()); +# endif + /* FIXME: is GC_wait_for_gc_completion(FALSE) needed here? */ if (GC_win32_dll_threads) { # if defined(THREAD_LOCAL_ALLOC) /* Can't happen: see GC_use_threads_discovery(). */ GC_ASSERT(FALSE); # else +# ifdef GC_PTHREADS + /* FIXME: If not DETACHED then just set FINISHED. */ +# endif /* FIXME: Should we just ignore this? */ GC_delete_thread(GetCurrentThreadId()); # endif } else { - DWORD t = GetCurrentThreadId(); +# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS) + GC_thread me; +# endif + DWORD thread_id = GetCurrentThreadId(); + LOCK(); +# if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS) + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); + GC_ASSERT(!KNOWN_FINISHED(me)); +# endif # if defined(THREAD_LOCAL_ALLOC) - { - GC_thread me = GC_lookup_thread_inner(t); - GC_destroy_thread_local(&(me->tlfs)); - } + GC_destroy_thread_local(&(me->tlfs)); +# endif +# ifdef GC_PTHREADS + if ((me -> flags & DETACHED) == 0) { + me -> flags |= FINISHED; + } else # endif - GC_delete_thread(t); + /* else */ { + GC_delete_thread(thread_id); + } UNLOCK(); } return GC_SUCCESS; @@ -782,7 +807,7 @@ GC_API int GC_CALL GC_unregister_my_thread(void) GC_INNER void GC_do_blocking_inner(ptr_t data, void * context) { struct blocking_data * d = (struct blocking_data *) data; - DWORD t = GetCurrentThreadId(); + DWORD thread_id = GetCurrentThreadId(); GC_thread me; # ifdef IA64 ptr_t stack_ptr = GC_save_regs_in_stack(); @@ -790,7 +815,8 @@ GC_INNER void GC_do_blocking_inner(ptr_t data, void * context) DCL_LOCK_STATE; LOCK(); - me = GC_lookup_thread_inner(t); + me = GC_lookup_thread_inner(thread_id); + CHECK_LOOKUP_MY_THREAD(me); GC_ASSERT(me -> thread_blocked_sp == NULL); # ifdef IA64 me -> backing_store_ptr = stack_ptr; @@ -817,7 +843,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, LOCK(); /* This will block if the world is stopped. */ me = GC_lookup_thread_inner(GetCurrentThreadId()); - + CHECK_LOOKUP_MY_THREAD(me); /* Adjust our stack base value (this could happen unless */ /* GC_get_stack_base() was used which returned GC_SUCCESS). */ GC_ASSERT(me -> stack_base != NULL); @@ -1293,8 +1319,8 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me) if (sp >= stack_min && sp < thread->stack_base) { # ifdef DEBUG_THREADS - GC_printf("Pushing stack for 0x%x from sp %p to %p from 0x%x\n", - (int)thread -> id, sp, thread -> stack_base, (int)me); + GC_log_printf("Pushing stack for 0x%x from sp %p to %p from 0x%x\n", + (int)thread -> id, sp, thread -> stack_base, (int)me); # endif GC_push_all_stack_sections(sp, thread->stack_base, traced_stack_sect); } else { @@ -1306,9 +1332,8 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me) WARN("Thread stack pointer %p out of range, pushing everything\n", sp); # ifdef DEBUG_THREADS - GC_printf("Pushing stack for 0x%x from (min) %p to %p from 0x%x\n", - (int)thread -> id, stack_min, - thread -> stack_base, (int)me); + GC_log_printf("Pushing stack for 0x%x from (min) %p to %p from 0x%x\n", + (int)thread->id, stack_min, thread->stack_base, (int)me); # endif /* Push everything - ignore "traced stack section" data. */ GC_push_all_stack(stack_min, thread->stack_base); @@ -1318,7 +1343,7 @@ STATIC word GC_push_stack_for(GC_thread thread, DWORD me) GC_INNER void GC_push_all_stacks(void) { - DWORD me = GetCurrentThreadId(); + DWORD thread_id = GetCurrentThreadId(); GC_bool found_me = FALSE; # ifndef SMALL_CONFIG unsigned nthreads = 0; @@ -1335,8 +1360,8 @@ GC_INNER void GC_push_all_stacks(void) # ifndef SMALL_CONFIG ++nthreads; # endif - total_size += GC_push_stack_for(t, me); - if (t -> id == me) found_me = TRUE; + total_size += GC_push_stack_for(t, thread_id); + if (t -> id == thread_id) found_me = TRUE; } } } else @@ -1350,8 +1375,8 @@ GC_INNER void GC_push_all_stacks(void) # ifndef SMALL_CONFIG ++nthreads; # endif - total_size += GC_push_stack_for(t, me); - if (t -> id == me) found_me = TRUE; + total_size += GC_push_stack_for(t, thread_id); + if (t -> id == thread_id) found_me = TRUE; } } } @@ -1363,7 +1388,7 @@ GC_INNER void GC_push_all_stacks(void) } # endif if (!found_me && !GC_in_thread_creation) - ABORT("Collecting from unknown thread."); + ABORT("Collecting from unknown thread"); GC_total_stacksize = total_size; } @@ -1384,7 +1409,7 @@ GC_INNER void GC_push_all_stacks(void) /* in stack (or ADDR_LIMIT if unset) */ /* for markers. */ -#endif +#endif /* PARALLEL_MARK */ /* Find stack with the lowest address which overlaps the */ /* interval [start, limit). */ @@ -1454,7 +1479,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, return; } - GC_ASSERT(current_min > start); + GC_ASSERT(current_min > start && plast_stack_min != NULL); # ifdef MSWINCE if (GC_dont_query_stack_min) { *lo = GC_wince_evaluate_stack_min(current_min); @@ -1522,8 +1547,8 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, my_mark_no = GC_mark_no; } # ifdef DEBUG_THREADS - GC_printf("Starting mark helper for mark number %lu\n", - (unsigned long)my_mark_no); + GC_log_printf("Starting mark helper for mark number %lu\n", + (unsigned long)my_mark_no); # endif GC_help_marker(my_mark_no); } @@ -1539,7 +1564,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, # include <pthread.h> # ifndef NUMERIC_THREAD_ID -# define NUMERIC_THREAD_ID(id) (unsigned long)(id.p) +# define NUMERIC_THREAD_ID(id) (unsigned long)GC_PTHREAD_PTRVAL(id) # endif /* start_mark_threads() is the same as in pthread_support.c except for: */ @@ -1872,10 +1897,10 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, GC_INNER void GC_wait_marker(void) { HANDLE event = mark_cv; - DWORD id = GetCurrentThreadId(); + DWORD thread_id = GetCurrentThreadId(); int i = (int)GC_markers - 1; while (i-- > 0) { - if (GC_marker_Id[i] == id) { + if (GC_marker_Id[i] == thread_id) { event = GC_marker_cv[i]; break; } @@ -1891,11 +1916,11 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, GC_INNER void GC_notify_all_marker(void) { - DWORD id = GetCurrentThreadId(); + DWORD thread_id = GetCurrentThreadId(); int i = (int)GC_markers - 1; while (i-- > 0) { /* Notify every marker ignoring self (for efficiency). */ - if (SetEvent(GC_marker_Id[i] != id ? GC_marker_cv[i] : + if (SetEvent(GC_marker_Id[i] != thread_id ? GC_marker_cv[i] : mark_cv) == FALSE) ABORT("SetEvent() failed"); } @@ -2002,8 +2027,8 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, GC_register_my_thread(sb); /* This waits for an in-progress GC. */ -# if DEBUG_WIN32_THREADS - GC_printf("thread 0x%x starting...\n", (unsigned)GetCurrentThreadId()); +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx starting...\n", (long)GetCurrentThreadId()); # endif GC_free(arg); @@ -2024,9 +2049,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, GC_unregister_my_thread(); } -# if DEBUG_WIN32_THREADS - GC_printf("thread 0x%x returned from start routine.\n", - (unsigned)GetCurrentThreadId()); +# ifdef DEBUG_THREADS + GC_log_printf("thread 0x%lx returned from start routine\n", + (long)GetCurrentThreadId()); # endif return ret; } @@ -2050,9 +2075,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, /* make sure GC is initialized (i.e. main thread is */ /* attached, tls initialized). */ -# if DEBUG_WIN32_THREADS - GC_printf("About to create a thread from 0x%x\n", - (unsigned)GetCurrentThreadId()); +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); # endif if (GC_win32_dll_threads) { return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, @@ -2097,9 +2122,9 @@ GC_INNER void GC_get_next_stack(char *start, char *limit, if (!parallel_initialized) GC_init_parallel(); /* make sure GC is initialized (i.e. main thread is */ /* attached, tls initialized). */ -# if DEBUG_WIN32_THREADS - GC_printf("About to create a thread from 0x%x\n", - (unsigned)GetCurrentThreadId()); +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from 0x%lx\n", + (long)GetCurrentThreadId()); # endif if (GC_win32_dll_threads) { @@ -2344,17 +2369,12 @@ GC_INNER void GC_thr_init(void) GC_API int GC_pthread_join(pthread_t pthread_id, void **retval) { int result; - GC_thread joinee; + GC_thread t; -# if DEBUG_CYGWIN_THREADS - GC_printf("thread 0x%x(0x%x) is joining thread 0x%x.\n", - (int)pthread_self(), (int)GetCurrentThreadId(), - (int)pthread_id); -# endif -# if DEBUG_WIN32_PTHREADS - GC_printf("thread 0x%x(0x%x) is joining thread 0x%x.\n", - (int)(pthread_self()).p, (int)GetCurrentThreadId(), - pthread_id.p); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) is joining thread %p\n", + GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId(), GC_PTHREAD_PTRVAL(pthread_id)); # endif if (!parallel_initialized) GC_init_parallel(); @@ -2364,33 +2384,29 @@ GC_INNER void GC_thr_init(void) /* FIXME: It would be better if this worked more like */ /* pthread_support.c. */ # ifndef GC_WIN32_PTHREADS - while ((joinee = GC_lookup_pthread(pthread_id)) == 0) Sleep(10); + while ((t = GC_lookup_pthread(pthread_id)) == 0) + Sleep(10); # endif result = pthread_join(pthread_id, retval); # ifdef GC_WIN32_PTHREADS /* win32_pthreads id are unique */ - joinee = GC_lookup_pthread(pthread_id); + t = GC_lookup_pthread(pthread_id); # endif if (!GC_win32_dll_threads) { DCL_LOCK_STATE; LOCK(); - GC_delete_gc_thread(joinee); + GC_delete_gc_thread(t); UNLOCK(); } /* otherwise DllMain handles it. */ -# if DEBUG_CYGWIN_THREADS - GC_printf("thread 0x%x(0x%x) completed join with thread 0x%x.\n", - (int)pthread_self(), (int)GetCurrentThreadId(), - (int)pthread_id); -# endif -# if DEBUG_WIN32_PTHREADS - GC_printf("thread 0x%x(0x%x) completed join with thread 0x%x.\n", - (int)(pthread_self()).p, (int)GetCurrentThreadId(), - pthread_id.p); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) completed join with thread %p\n", + GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId(), GC_PTHREAD_PTRVAL(pthread_id)); # endif return result; } @@ -2422,13 +2438,10 @@ GC_INNER void GC_thr_init(void) si->detached = TRUE; } -# if DEBUG_CYGWIN_THREADS - GC_printf("About to create a thread from 0x%x(0x%x)\n", - (int)pthread_self(), (int)GetCurrentThreadId); -# endif -# if DEBUG_WIN32_PTHREADS - GC_printf("About to create a thread from 0x%x(0x%x)\n", - (int)(pthread_self()).p, (int)GetCurrentThreadId()); +# ifdef DEBUG_THREADS + GC_log_printf("About to create a thread from %p(0x%lx)\n", + GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId()); # endif GC_need_to_lock = TRUE; result = pthread_create(new_thread, attr, GC_pthread_start, si); @@ -2452,13 +2465,9 @@ GC_INNER void GC_thr_init(void) GC_thread me; DCL_LOCK_STATE; -# if DEBUG_CYGWIN_THREADS - GC_printf("thread 0x%x(0x%x) starting...\n",(int)pthread_id, - (int)thread_id); -# endif -# if DEBUG_WIN32_PTHREADS - GC_printf("thread 0x%x(0x%x) starting...\n",(int) pthread_id.p, - (int)thread_id); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%x) starting...\n", + GC_PTHREAD_PTRVAL(pthread_id), (int)thread_id); # endif GC_ASSERT(!GC_win32_dll_threads); @@ -2485,13 +2494,9 @@ GC_INNER void GC_thr_init(void) me -> status = result; pthread_cleanup_pop(1); -# if DEBUG_CYGWIN_THREADS - GC_printf("thread 0x%x(0x%x) returned from start routine.\n", - (int)pthread_self(),(int)GetCurrentThreadId()); -# endif -# if DEBUG_WIN32_PTHREADS - GC_printf("thread 0x%x(0x%x) returned from start routine.\n", - (int)(pthread_self()).p, (int)GetCurrentThreadId()); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%x) returned from start routine\n", + GC_PTHREAD_PTRVAL(pthread_id), (int)thread_id); # endif return(result); } @@ -2507,13 +2512,10 @@ GC_INNER void GC_thr_init(void) DCL_LOCK_STATE; GC_ASSERT(!GC_win32_dll_threads); -# if DEBUG_CYGWIN_THREADS - GC_printf("thread 0x%x(0x%x) called pthread_exit().\n", - (int)pthread_self(),(int)GetCurrentThreadId()); -# endif -# if DEBUG_WIN32_PTHREADS - GC_printf("thread 0x%x(0x%x) called pthread_exit().\n", - (int)(pthread_self()).p,(int)GetCurrentThreadId()); +# ifdef DEBUG_THREADS + GC_log_printf("thread %p(0x%lx) called pthread_exit()\n", + GC_PTHREAD_PTRVAL(pthread_self()), + (long)GetCurrentThreadId()); # endif LOCK(); @@ -2543,20 +2545,20 @@ GC_INNER void GC_thr_init(void) GC_API int GC_pthread_detach(pthread_t thread) { int result; - GC_thread thread_gc_id; + GC_thread t; DCL_LOCK_STATE; if (!parallel_initialized) GC_init_parallel(); LOCK(); - thread_gc_id = GC_lookup_pthread(thread); + t = GC_lookup_pthread(thread); UNLOCK(); result = pthread_detach(thread); if (result == 0) { LOCK(); - thread_gc_id -> flags |= DETACHED; + t -> flags |= DETACHED; /* Here the pthread thread id may have been recycled. */ - if (thread_gc_id -> flags & FINISHED) { - GC_delete_gc_thread(thread_gc_id); + if ((t -> flags & FINISHED) != 0) { + GC_delete_gc_thread(t); } UNLOCK(); } @@ -2652,6 +2654,7 @@ GC_INNER void GC_thr_init(void) GC_INNER void GC_init_parallel(void) { # if defined(THREAD_LOCAL_ALLOC) + GC_thread me; DCL_LOCK_STATE; # endif @@ -2670,8 +2673,9 @@ GC_INNER void GC_init_parallel(void) /* Initialize thread local free lists if used. */ # if defined(THREAD_LOCAL_ALLOC) LOCK(); - GC_init_thread_local( - &GC_lookup_thread_inner(GetCurrentThreadId())->tlfs); + me = GC_lookup_thread_inner(GetCurrentThreadId()); + CHECK_LOOKUP_MY_THREAD(me); + GC_init_thread_local(&me->tlfs); UNLOCK(); # endif } @@ -2709,7 +2713,7 @@ GC_INNER void GC_init_parallel(void) for (p = GC_threads[i]; 0 != p; p = p -> tm.next) { if (!KNOWN_FINISHED(p)) { # ifdef DEBUG_THREADS - GC_printf("Marking thread locals for 0x%x\n", (int)p -> id); + GC_log_printf("Marking thread locals for 0x%x\n", (int)p -> id); # endif GC_mark_thread_local_fls_for(&(p->tlfs)); } |