-
Notifications
You must be signed in to change notification settings - Fork 408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Assertion failure in disclaim_test if manual VDB #235
Comments
* tests/disclaim_test.c (my_assert): Call fflush(). * tests/disclaim_test.c (pair_s): Replace is_valid with "magic" field. * tests/disclaim_test.c (pair_magic): New const static variable. * tests/disclaim_test.c (is_pair): New function. * tests/disclaim_test.c (pair_dct, pair_new): Set "magic" field and use is_pair() instead of is_valid for assertion checking.
If compiled with DEBUG_DISCLAIM_DESTRUCT, the output as follows ("car" is destroyed before "p") : Threaded disclaim test. |
I cannot reproduce this myself from
It's a wild guess though. |
To reproduce it, I run ./disclaim_test in a loop in 3 terminals in parallel (and got my_assert violation after around 100 iterations. |
I think the assertion was because I resurrected the object by GC_ptr_store_and_dirty(&p->car, cd) in pair_dct. Now I've removed the store barrier which was not actually needed because we reclaim p after pair_dct completion (now the code is p->car = 0). |
After the change (GC_ptr_store_and_dirty -> p->car=0), I got the following error: (It is reproduced after several dozens of disclaim_test runs even in a more simple configuration: ./configure --enable-gc-assertions --disable-parallel-mark --disable-munmap --disable-handle-fork --disable-thread-local-alloc && make check CFLAGS_EXTRA="-O0 -g -D TEST_MANUAL_VDB") |
And, again, sometimes I see: p is valid but p->cdr is already reclaimed (zero'ed, the 1st word points to the next element in the free list.) |
Not reproduced w/o -D TEST_MANUAL_VDB |
I will check it tomorrow. |
I managed to trigger the error with 3 parallel runs as you suggested. My adjustments to |
I think the issue is that I'm running some long test now with the following:
As you can see there is a FIXME which made me look bit closer. |
I see. I will check it as well but I'm not sure async_set_pht_entry_from_index has enough thread-safety for GC_dirty. It seems to me that we need AO_or here. To @hboehm: what do you think? |
The issue hasn't popped up after the change, but it also eventually deadlocks, so there is probably a double locking involved. |
Yes, async_set_pht_entry_from_index should fix the race except there could be a deadlock if it is called also from the write fault handler but GC_dirty_inner and write fault handler usage should be mutually exclusive - if manual VDB is used then MPROTECT_VDB functionality is off, and vice versa. So, I don't understand the reason of the deadlock. |
AO_or is better but not always available. |
Yes, this happens if thread is suspended in the middle of async_set_pht_entry_from_index. |
Issue #235 (bdwgc). * darwin_stop_world.c [!GC_NO_THREADS_DISCOVERY] (GC_suspend_thread_list): Call GC_acquire_dirty_lock and GC_release_dirty_lock around thread_suspend(). * darwin_stop_world.c (GC_stop_world): Likewise. * include/private/gc_priv.h (set_pht_entry_from_index_safe): Remove unused macro. * include/private/gc_priv.h [THREADS] (GC_acquire_dirty_lock, GC_release_dirty_lock): Define new macro; move comment from win32_threads.c. * include/private/gc_priv.h [THREADS && !GC_DISABLE_INCREMENTAL] (GC_fault_handler_lock): Declare global variable (even if not MPROTECT_VDB). * os_dep.c [!GC_DISABLE_INCREMENTAL] (async_set_pht_entry_from_index): Define even if not MPROTECT_VDB; reformat comments. * os_dep.c [!GC_DISABLE_INCREMENTAL && AO_HAVE_test_and_set_acquire] (GC_fault_handler_lock): Likewise. * os_dep.c [THREADS && AO_HAVE_test_and_set_acquire] (async_set_pht_entry_from_index): Use GC_acquire_dirty_lock and GC_release_dirty_lock; remove wrong comment. * os_dep.c [THREADS && !AO_HAVE_test_and_set_acquire] (currently_updating, async_set_pht_entry_from_index): Remove incorrect implementation. * os_dep.c [!GC_DISABLE_INCREMENTAL] (GC_dirty_inner): Call async_set_pht_entry_from_index instead of set_pht_entry_from_index; remove FIXME. * pthread_stop_world.c [GC_ENABLE_SUSPEND_THREAD] (GC_suspend_thread): Refine comment for AO_store_release call; call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of RAISE_SIGNAL() and sem_wait(). * pthread_stop_world.c [GC_OPENBSD_UTHREADS] (GC_suspend_all): Call GC_acquire_dirty_lock and GC_release_dirty_lock around pthread_suspend_np(). * pthread_stop_world.c [!GC_OPENBSD_UTHREADS] (GC_suspend_all): Add comment. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL] (GC_stop_world): If GC_manual_vdb then call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of GC_suspend_all() and suspend_restart_barrier(); add comment. * win32_threads.c (GC_suspend): Use GC_acquire_dirty_lock and GC_release_dirty_lock (regardless of MPROTECT_VDB).
Fixed by 0c0e4cd. |
(fix of commit 0c0e4cd) Issue #235 (bdwgc). * pthread_stop_world.c [GC_ENABLE_SUSPEND_THREAD] (GC_suspend_thread): Remove unneeded comment for AO_store_release() call; invoke GC_acquire_dirty_lock() and GC_release_dirty_lock() only if GC_manual_vdb; add comment for GC_acquire_dirty_lock() call. * pthread_stop_world.c [NACL] (GC_suspend_all): If GC_manual_vdb then call GC_acquire_dirty_lock() and GC_release_dirty_lock() around the code which ensures parking of threads. * pthread_support.c [CAN_HANDLE_FORK] (fork_prepare_proc): Call GC_acquire_dirty_lock(). * pthread_support.c [CAN_HANDLE_FORK] (fork_parent_proc, fork_child_proc): Call GC_release_dirty_lock().
(a cherry-pick of commits 7305e56, 0c0e4cd, d6db3f0 from 'master') Issue #235 (bdwgc). * darwin_stop_world.c [!GC_NO_THREADS_DISCOVERY] (GC_suspend_thread_list): Call GC_acquire_dirty_lock and GC_release_dirty_lock around thread_suspend(). * darwin_stop_world.c (GC_stop_world): Likewise. * include/private/gc_priv.h (clear_pht_entry_from_index, set_pht_entry_from_index_safe): Remove unused macro. * include/private/gc_priv.h [THREADS] (GC_acquire_dirty_lock, GC_release_dirty_lock): Define new macro; move comment from win32_threads.c. * include/private/gc_priv.h [THREADS && !GC_DISABLE_INCREMENTAL] (GC_fault_handler_lock): Declare global variable (even if not MPROTECT_VDB). * os_dep.c [!GC_DISABLE_INCREMENTAL] (async_set_pht_entry_from_index): Define even if not MPROTECT_VDB; reformat comments. * os_dep.c [!GC_DISABLE_INCREMENTAL && AO_HAVE_test_and_set_acquire] (GC_fault_handler_lock): Likewise. * os_dep.c [THREADS && AO_HAVE_test_and_set_acquire] (async_set_pht_entry_from_index): Use GC_acquire_dirty_lock and GC_release_dirty_lock; remove wrong comment. * os_dep.c [THREADS && !AO_HAVE_test_and_set_acquire] (currently_updating, async_set_pht_entry_from_index): Remove incorrect implementation. * os_dep.c [!GC_DISABLE_INCREMENTAL] (GC_dirty_inner): Call async_set_pht_entry_from_index instead of set_pht_entry_from_index; remove FIXME. * pthread_stop_world.c [GC_ENABLE_SUSPEND_THREAD] (GC_suspend_thread): If GC_manual_vdb then call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of RAISE_SIGNAL() and sem_wait(). * pthread_stop_world.c [GC_OPENBSD_UTHREADS] (GC_suspend_all): Call GC_acquire_dirty_lock and GC_release_dirty_lock around pthread_suspend_np(). * pthread_stop_world.c [NACL] (GC_suspend_all): If GC_manual_vdb then call GC_acquire_dirty_lock() and GC_release_dirty_lock() around the code which ensures parking of threads. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS] (GC_suspend_all): Add comment. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL] (GC_stop_world): If GC_manual_vdb then call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of GC_suspend_all() and suspend_restart_barrier(); add comment. * pthread_support.c [CAN_HANDLE_FORK] (fork_prepare_proc): Call GC_acquire_dirty_lock(). * pthread_support.c [CAN_HANDLE_FORK] (fork_parent_proc, fork_child_proc): Call GC_release_dirty_lock(). * win32_threads.c (GC_suspend): Use GC_acquire_dirty_lock and GC_release_dirty_lock (regardless of MPROTECT_VDB).
(back-port of commit 815dde3 from 'release-8_0') Issue #235 (bdwgc). * darwin_stop_world.c [!GC_NO_THREADS_DISCOVERY] (GC_suspend_thread_list): Call GC_acquire_dirty_lock and GC_release_dirty_lock around thread_suspend(). * darwin_stop_world.c (GC_stop_world): Likewise. * include/private/gc_priv.h (clear_pht_entry_from_index, set_pht_entry_from_index_safe): Remove unused macro. * include/private/gc_priv.h [THREADS] (GC_acquire_dirty_lock, GC_release_dirty_lock): Define new macro; move comment from win32_threads.c. * include/private/gc_priv.h [THREADS && (MANUAL_VDB || MPROTECT_VDB)] (GC_fault_handler_lock): Declare global variable (even if not MPROTECT_VDB). * os_dep.c [MANUAL_VDB] (async_set_pht_entry_from_index): Define as for MPROTECT_VDB case; reformat comments. * os_dep.c [MANUAL_VDB && AO_HAVE_test_and_set_acquire] (GC_fault_handler_lock): Likewise. * os_dep.c [THREADS && AO_HAVE_test_and_set_acquire] (async_set_pht_entry_from_index): Use GC_acquire_dirty_lock and GC_release_dirty_lock; remove wrong comment. * os_dep.c [THREADS && !AO_HAVE_test_and_set_acquire] (currently_updating, async_set_pht_entry_from_index): Remove incorrect implementation. * pthread_stop_world.c [GC_ENABLE_SUSPEND_THREAD && MANUAL_VDB] (GC_suspend_thread): Call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of RAISE_SIGNAL() and sem_wait(). * pthread_stop_world.c [GC_OPENBSD_UTHREADS] (GC_suspend_all): Call GC_acquire_dirty_lock and GC_release_dirty_lock around pthread_suspend_np(). * pthread_stop_world.c [NACL && MANUAL_VDB] (GC_suspend_all): Call GC_acquire_dirty_lock() and GC_release_dirty_lock() around the code which ensures parking of threads. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS] (GC_suspend_all): Add comment. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL && MANUAL_VDB] (GC_stop_world): Call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of GC_suspend_all() and suspend_restart_barrier(); add comment. * pthread_support.c [CAN_HANDLE_FORK] (fork_prepare_proc): Call GC_acquire_dirty_lock(). * pthread_support.c [CAN_HANDLE_FORK] (fork_parent_proc, fork_child_proc): Call GC_release_dirty_lock(). * win32_threads.c (GC_suspend): Use GC_acquire_dirty_lock and GC_release_dirty_lock (regardless of MPROTECT_VDB).
(back-port of commit 26e9e59 from 'release-7_6') Issue #235 (bdwgc). * darwin_stop_world.c [!GC_NO_THREADS_DISCOVERY] (GC_suspend_thread_list): Call GC_acquire_dirty_lock and GC_release_dirty_lock around thread_suspend(). * darwin_stop_world.c (GC_stop_world): Likewise. * include/private/gc_priv.h [THREADS] (GC_acquire_dirty_lock, GC_release_dirty_lock): Define new macro; move comment from win32_threads.c. * include/private/gc_priv.h [THREADS && (MANUAL_VDB || MPROTECT_VDB)] (GC_fault_handler_lock): Declare global variable (even if not MPROTECT_VDB). * os_dep.c [MANUAL_VDB] (async_set_pht_entry_from_index): Define as for MPROTECT_VDB case; reformat comments. * os_dep.c [MANUAL_VDB && AO_HAVE_test_and_set_acquire] (GC_fault_handler_lock): Likewise. * os_dep.c [THREADS && AO_HAVE_test_and_set_acquire] (async_set_pht_entry_from_index): Use GC_acquire_dirty_lock and GC_release_dirty_lock; remove wrong comment. * pthread_stop_world.c [GC_ENABLE_SUSPEND_THREAD && MANUAL_VDB] (GC_suspend_thread): Call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of pthread_kill() and sem_wait(). * pthread_stop_world.c [GC_OPENBSD_UTHREADS] (GC_suspend_all): Call GC_acquire_dirty_lock and GC_release_dirty_lock around pthread_suspend_np(). * pthread_stop_world.c [NACL && MANUAL_VDB] (GC_suspend_all): Call GC_acquire_dirty_lock() and GC_release_dirty_lock() around the code which ensures parking of threads. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS] (GC_suspend_all): Add comment. * pthread_stop_world.c [!GC_OPENBSD_UTHREADS && !NACL && MANUAL_VDB] (GC_stop_world): Call GC_acquire_dirty_lock and GC_release_dirty_lock around a block of GC_suspend_all() and suspend_restart_barrier(); add comment. * pthread_support.c [CAN_HANDLE_FORK] (fork_prepare_proc): Call GC_acquire_dirty_lock(). * pthread_support.c [CAN_HANDLE_FORK] (fork_parent_proc, fork_child_proc): Call GC_release_dirty_lock(). * win32_threads.c (GC_suspend): Use GC_acquire_dirty_lock and GC_release_dirty_lock (regardless of MPROTECT_VDB).
Build link: https://travis-ci.org/ivmai/bdwgc/jobs/425865062
Source: master (or 8.0.0)
Host: Linux/x64
How to build and run: ./configure --enable-gc-assertions --disable-munmap && make check CFLAGS_EXTRA="-D TEST_MANUAL_VDB"
Output (of disclaim_test):
Threaded disclaim test.
Assertion failure, line 119: !p->car || is_pair(p->car)
The text was updated successfully, but these errors were encountered: