[CDRIVER-4067] Debug assertion mutex_is_locked is incorrect if lock occurs during a condition variable wait Created: 07/Jul/21  Updated: 15/Apr/22

Status: Backlog
Project: C Driver
Component/s: tests
Affects Version/s: None
Fix Version/s: None

Type: Task Priority: Major - P3
Reporter: Kevin Albertson Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: neweng
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Epic Link: CDRIVER-4107

 Description   

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);
}


Generated at Wed Feb 07 21:19:50 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.