-
Type: Task
-
Resolution: Won't Do
-
Priority: Major - P3
-
None
-
Affects Version/s: None
-
Component/s: Testing
Motivation
The mutex_is_locked is a debug test assertion to enforce the expectation that a mutex is locked in functions that expect callers to lock. It is only enabled in tests on Linux when -DENABLE_DEBUG_ASSERTIONS=ON is configured.
It breaks down in the case where:
- Thread T1 locks L
- Thread T1 waits on a condition variable CV with lock L
- While T1 is waiting, thread T2 locks and unlocks L
- T1 wakes up, and does hold the lock L, but mutex_is_locked returns false.
Scope
To keep mutex_is_locked working correctly, the condition variable functions mongoc_cond_wait and mongoc_cond_timedwait should be overridden to work with the debug form of bson_mutex_t.
Repro
The following is a test written to show the bug:
#include "mongoc/mongoc-thread-private.h" #include "TestSuite.h" #include "test-libmongoc.h" /* Locks and unlocks the passed mutex */ BSON_THREAD_FUN (locker_fn, arg) { bson_mutex_t *lock = (bson_mutex_t *) arg; /* Wait 10ms. */ _mongoc_usleep (10 * 1000); bson_mutex_lock (lock); bson_mutex_unlock (lock); return NULL; } /* Test the mutex_is_locked assertion when a mutex is locked/unlocked by another * thread while waiting for a condition variable. * * This requires compiling on Linux and configuring cmake with * -DENABLE_DEBUG_ASSERTIONS */ static void test_mutex_assertion (void *unused) { bson_mutex_t mutex; mongoc_cond_t cond; bson_thread_t locker_thread; bson_mutex_init (&mutex); mongoc_cond_init (&cond); COMMON_PREFIX (thread_create) (&locker_thread, locker_fn, &mutex); int r = mongoc_cond_timedwait (&cond, &mutex, 100); COMMON_PREFIX (thread_join) (locker_thread); BSON_ASSERT (r == ETIMEDOUT); BSON_ASSERT (COMMON_PREFIX (mutex_is_locked (&mutex))); /* Fails */ bson_mutex_unlock (&mutex); mongoc_cond_destroy (&cond); bson_mutex_destroy (&mutex); } void test_thread_install (TestSuite *suite) { TestSuite_AddFull (suite, "/Thread/mutex_assertion", test_mutex_assertion, NULL /* dtor */, NULL /* ctx */, NULL); }