Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_mutex_checkMutexEnter.c183 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3 4#include <pthread.h>5#include <string.h>6#include <stdlib.h>7#include <stdio.h>8 9/* External symbols from the mutex module under test */10extern void test_checkMutexEnter(sqlite3_mutex *p);11extern void sqlite3MutexWarnOnContention(sqlite3_mutex *p);12 13/* Simple log capture structure */14typedef struct LogCapture {15  int count;16  int lastErr;17  char lastMsg[256];18} LogCapture;19 20static LogCapture gLog;21 22/* Log callback */23static void testLogCallback(void *pArg, int iErrCode, const char *zMsg){24  LogCapture *p = (LogCapture*)pArg;25  if( p ){26    p->count++;27    p->lastErr = iErrCode;28    if( zMsg ){29      /* Use SQLite's safe snprintf if available, otherwise standard */30      sqlite3_snprintf(sizeof(p->lastMsg), p->lastMsg, "%s", zMsg);31    }else{32      p->lastMsg[0] = '\0';33    }34  }35}36 37/* Thread context for creating contention */38typedef struct LockerCtx {39  sqlite3_mutex *p;40  volatile int holding;         /* Set to 1 when thread holds the mutex */41  volatile int mainAttempting;  /* Main sets to 1 before calling enter() */42} LockerCtx;43 44static void* lockerThread(void *arg){45  LockerCtx *ctx = (LockerCtx*)arg;46  /* Acquire and hold the mutex until main attempts to enter */47  sqlite3_mutex_enter(ctx->p);48  ctx->holding = 1;49 50  /* Wait until the main thread signals it is attempting to enter */51  while( ctx->mainAttempting==0 ){52    sqlite3_sleep(1);53  }54 55  /* Allow the main thread to certainly hit the try/enter path and block */56  sqlite3_sleep(50);57 58  /* Release so the main thread can proceed */59  sqlite3_mutex_leave(ctx->p);60  return NULL;61}62 63static void resetLog(void){64  memset(&gLog, 0, sizeof(gLog));65}66 67/* Allocate a recursive mutex, optionally mark warn-on-contention */68static sqlite3_mutex* alloc_recursive_mutex(int warnOnContention){69  sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE);70  TEST_ASSERT_NOT_NULL(p);71  if( warnOnContention ){72    sqlite3MutexWarnOnContention(p);73  }74  return p;75}76 77void setUp(void) {78  /* Ensure clean state and install log callback before initialization */79  sqlite3_shutdown();80  resetLog();81  TEST_ASSERT_EQUAL_INT(SQLITE_OK,82    sqlite3_config(SQLITE_CONFIG_LOG, testLogCallback, &gLog)83  );84  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_initialize());85}86 87void tearDown(void) {88  sqlite3_shutdown();89}90 91/* Test: No contention, warn-on-contention enabled -> no log, lock acquired */92void test_checkMutexEnter_no_contention_warn(void){93  sqlite3_mutex *p = alloc_recursive_mutex(1);94 95  resetLog();96  test_checkMutexEnter(p);  /* Should acquire via Try without logging */97  TEST_ASSERT_EQUAL_INT(0, gLog.count);98 99  /* Release and free */100  sqlite3_mutex_leave(p);101  sqlite3_mutex_free(p);102}103 104/* Test: Contention exists, warn-on-contention enabled -> logs once and then enters */105void test_checkMutexEnter_contention_warn_logs_once(void){106  sqlite3_mutex *p = alloc_recursive_mutex(1);107 108  LockerCtx ctx;109  memset(&ctx, 0, sizeof(ctx));110  ctx.p = p;111 112  pthread_t th;113  int rc = pthread_create(&th, NULL, lockerThread, &ctx);114  TEST_ASSERT_EQUAL_INT(0, rc);115 116  /* Wait until the worker holds the mutex */117  int waited = 0;118  while( ctx.holding==0 && waited<5000 ){119    sqlite3_sleep(1);120    waited++;121  }122  TEST_ASSERT_TRUE_MESSAGE(ctx.holding==1, "Worker failed to acquire mutex");123 124  resetLog();125  ctx.mainAttempting = 1;126 127  /* This should detect contention, log SQLITE_MISUSE, then block until release */128  test_checkMutexEnter(p);129 130  /* After return, we own the mutex */131  TEST_ASSERT_TRUE(gLog.count >= 1);132  TEST_ASSERT_EQUAL_INT(SQLITE_MISUSE, gLog.lastErr);133  /* Verify message content */134  TEST_ASSERT_NOT_NULL(gLog.lastMsg);135  TEST_ASSERT_TRUE(strstr(gLog.lastMsg, "illegal multi-threaded access to database connection") != NULL);136 137  /* Clean up: release our lock, join thread, free mutex */138  sqlite3_mutex_leave(p);139  pthread_join(th, NULL);140  sqlite3_mutex_free(p);141}142 143/* Test: Contention exists, no warn-on-contention -> no log, then enters */144void test_checkMutexEnter_contention_no_warn_no_log(void){145  sqlite3_mutex *p = alloc_recursive_mutex(0);146 147  LockerCtx ctx;148  memset(&ctx, 0, sizeof(ctx));149  ctx.p = p;150 151  pthread_t th;152  int rc = pthread_create(&th, NULL, lockerThread, &ctx);153  TEST_ASSERT_EQUAL_INT(0, rc);154 155  /* Wait until the worker holds the mutex */156  int waited = 0;157  while( ctx.holding==0 && waited<5000 ){158    sqlite3_sleep(1);159    waited++;160  }161  TEST_ASSERT_TRUE_MESSAGE(ctx.holding==1, "Worker failed to acquire mutex");162 163  resetLog();164  ctx.mainAttempting = 1;165 166  /* With no warn-on-contention, should block and acquire without logging */167  test_checkMutexEnter(p);168 169  TEST_ASSERT_EQUAL_INT(0, gLog.count);170 171  /* Clean up */172  sqlite3_mutex_leave(p);173  pthread_join(th, NULL);174  sqlite3_mutex_free(p);175}176 177int main(void) {178  UNITY_BEGIN();179  RUN_TEST(test_checkMutexEnter_no_contention_warn);180  RUN_TEST(test_checkMutexEnter_contention_warn_logs_once);181  RUN_TEST(test_checkMutexEnter_contention_no_warn_no_log);182  return UNITY_END();183}