1/* 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: mut_win32.c,v 12.32 2008/03/13 15:23:09 mbrey Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12 13/* 14 * This is where we load in the actual test-and-set mutex code. 15 */ 16#define LOAD_ACTUAL_MUTEX_CODE 17#include "dbinc/mutex_int.h" 18 19/* We don't want to run this code even in "ordinary" diagnostic mode. */ 20#undef MUTEX_DIAG 21 22/* 23 * Common code to get an event handle. This is executed whenever a mutex 24 * blocks, or when unlocking a mutex that a thread is waiting on. We can't 25 * keep these handles around, since the mutex structure is in shared memory, 26 * and each process gets its own handle value. 27 * 28 * We pass security attributes so that the created event is accessible by all 29 * users, in case a Windows service is sharing an environment with a local 30 * process run as a different user. 31 */ 32static _TCHAR hex_digits[] = _T("0123456789abcdef"); 33static SECURITY_DESCRIPTOR null_sd; 34static SECURITY_ATTRIBUTES all_sa; 35static int security_initialized = 0; 36 37static __inline int get_handle(env, mutexp, eventp) 38 ENV *env; 39 DB_MUTEX *mutexp; 40 HANDLE *eventp; 41{ 42 _TCHAR idbuf[] = _T("db.m00000000"); 43 _TCHAR *p = idbuf + 12; 44 int ret = 0; 45 u_int32_t id; 46 47 for (id = (mutexp)->id; id != 0; id >>= 4) 48 *--p = hex_digits[id & 0xf]; 49 50#ifndef DB_WINCE 51 if (!security_initialized) { 52 InitializeSecurityDescriptor(&null_sd, 53 SECURITY_DESCRIPTOR_REVISION); 54 SetSecurityDescriptorDacl(&null_sd, TRUE, 0, FALSE); 55 all_sa.nLength = sizeof(SECURITY_ATTRIBUTES); 56 all_sa.bInheritHandle = FALSE; 57 all_sa.lpSecurityDescriptor = &null_sd; 58 security_initialized = 1; 59 } 60#endif 61 62 if ((*eventp = CreateEvent(&all_sa, FALSE, FALSE, idbuf)) == NULL) { 63 ret = __os_get_syserr(); 64 __db_syserr(env, ret, "Win32 create event failed"); 65 } 66 67 return (ret); 68} 69 70/* 71 * __db_win32_mutex_init -- 72 * Initialize a Win32 mutex. 73 * 74 * PUBLIC: int __db_win32_mutex_init __P((ENV *, db_mutex_t, u_int32_t)); 75 */ 76int 77__db_win32_mutex_init(env, mutex, flags) 78 ENV *env; 79 db_mutex_t mutex; 80 u_int32_t flags; 81{ 82 DB_MUTEX *mutexp; 83 DB_MUTEXMGR *mtxmgr; 84 DB_MUTEXREGION *mtxregion; 85 86 mtxmgr = env->mutex_handle; 87 mtxregion = mtxmgr->reginfo.primary; 88 mutexp = MUTEXP_SET(mutex); 89 90 mutexp->id = ((getpid() & 0xffff) << 16) ^ P_TO_UINT32(mutexp); 91 92 return (0); 93} 94 95/* 96 * __db_win32_mutex_lock 97 * Lock on a mutex, blocking if necessary. 98 * 99 * PUBLIC: int __db_win32_mutex_lock __P((ENV *, db_mutex_t)); 100 */ 101int 102__db_win32_mutex_lock(env, mutex) 103 ENV *env; 104 db_mutex_t mutex; 105{ 106 DB_ENV *dbenv; 107 DB_MUTEX *mutexp; 108 DB_MUTEXMGR *mtxmgr; 109 DB_MUTEXREGION *mtxregion; 110 HANDLE event; 111 u_int32_t nspins; 112 int ms, ret; 113#ifdef MUTEX_DIAG 114 LARGE_INTEGER now; 115#endif 116#ifdef DB_WINCE 117 volatile db_threadid_t tmp_tid; 118#endif 119 dbenv = env->dbenv; 120 121 if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) 122 return (0); 123 124 mtxmgr = env->mutex_handle; 125 mtxregion = mtxmgr->reginfo.primary; 126 mutexp = MUTEXP_SET(mutex); 127 128 CHECK_MTX_THREAD(env, mutexp); 129 130 event = NULL; 131 ms = 50; 132 ret = 0; 133 134loop: /* Attempt to acquire the resource for N spins. */ 135 for (nspins = 136 mtxregion->stat.st_mutex_tas_spins; nspins > 0; --nspins) { 137 /* 138 * We can avoid the (expensive) interlocked instructions if 139 * the mutex is already "set". 140 */ 141#ifdef DB_WINCE 142 /* 143 * Memory mapped regions on Windows CE cause problems with 144 * InterlockedExchange calls. Each page in a mapped region 145 * needs to have been written to prior to an 146 * InterlockedExchange call, or the InterlockedExchange call 147 * hangs. This does not seem to be documented anywhere. For 148 * now, read/write a non-critical piece of memory from the 149 * shared region prior to attempting an InterlockedExchange 150 * operation. 151 */ 152 tmp_tid = mutexp->tid; 153 mutexp->tid = tmp_tid; 154#endif 155 if (mutexp->tas || !MUTEX_SET(&mutexp->tas)) { 156 /* 157 * Some systems (notably those with newer Intel CPUs) 158 * need a small pause here. [#6975] 159 */ 160#ifdef MUTEX_PAUSE 161 MUTEX_PAUSE 162#endif 163 continue; 164 } 165 166#ifdef DIAGNOSTIC 167 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 168 char buf[DB_THREADID_STRLEN]; 169 __db_errx(env, 170 "Win32 lock failed: mutex already locked by %s", 171 dbenv->thread_id_string(dbenv, 172 mutexp->pid, mutexp->tid, buf)); 173 return (__env_panic(env, EACCES)); 174 } 175#endif 176 F_SET(mutexp, DB_MUTEX_LOCKED); 177 dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid); 178 179#ifdef HAVE_STATISTICS 180 if (event == NULL) 181 ++mutexp->mutex_set_nowait; 182 else 183 ++mutexp->mutex_set_wait; 184#endif 185 if (event != NULL) { 186 CloseHandle(event); 187 InterlockedDecrement(&mutexp->nwaiters); 188#ifdef MUTEX_DIAG 189 if (ret != WAIT_OBJECT_0) { 190 QueryPerformanceCounter(&now); 191 printf("[%I64d]: Lost signal on mutex %p, " 192 "id %d, ms %d\n", 193 now.QuadPart, mutexp, mutexp->id, ms); 194 } 195#endif 196 } 197 198#ifdef DIAGNOSTIC 199 /* 200 * We want to switch threads as often as possible. Yield 201 * every time we get a mutex to ensure contention. 202 */ 203 if (F_ISSET(dbenv, DB_ENV_YIELDCPU)) 204 __os_yield(env, 0, 0); 205#endif 206 207 return (0); 208 } 209 210 /* 211 * Yield the processor; wait 50 ms initially, up to 1 second. This 212 * loop is needed to work around a race where the signal from the 213 * unlocking thread gets lost. We start at 50 ms because it's unlikely 214 * to happen often and we want to avoid wasting CPU. 215 */ 216 if (event == NULL) { 217#ifdef MUTEX_DIAG 218 QueryPerformanceCounter(&now); 219 printf("[%I64d]: Waiting on mutex %p, id %d\n", 220 now.QuadPart, mutexp, mutexp->id); 221#endif 222 InterlockedIncrement(&mutexp->nwaiters); 223 if ((ret = get_handle(env, mutexp, &event)) != 0) 224 goto err; 225 } 226 if ((ret = WaitForSingleObject(event, ms)) == WAIT_FAILED) { 227 ret = __os_get_syserr(); 228 goto err; 229 } 230 if ((ms <<= 1) > MS_PER_SEC) 231 ms = MS_PER_SEC; 232 233 PANIC_CHECK(env); 234 goto loop; 235 236err: __db_syserr(env, ret, "Win32 lock failed"); 237 return (__env_panic(env, __os_posix_err(ret))); 238} 239 240/* 241 * __db_win32_mutex_unlock -- 242 * Release a mutex. 243 * 244 * PUBLIC: int __db_win32_mutex_unlock __P((ENV *, db_mutex_t)); 245 */ 246int 247__db_win32_mutex_unlock(env, mutex) 248 ENV *env; 249 db_mutex_t mutex; 250{ 251 DB_ENV *dbenv; 252 DB_MUTEX *mutexp; 253 DB_MUTEXMGR *mtxmgr; 254 DB_MUTEXREGION *mtxregion; 255 HANDLE event; 256 int ret; 257#ifdef MUTEX_DIAG 258 LARGE_INTEGER now; 259#endif 260 dbenv = env->dbenv; 261 262 if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING)) 263 return (0); 264 265 mtxmgr = env->mutex_handle; 266 mtxregion = mtxmgr->reginfo.primary; 267 mutexp = MUTEXP_SET(mutex); 268 269#ifdef DIAGNOSTIC 270 if (!mutexp->tas || !F_ISSET(mutexp, DB_MUTEX_LOCKED)) { 271 __db_errx(env, "Win32 unlock failed: lock already unlocked"); 272 return (__env_panic(env, EACCES)); 273 } 274#endif 275 F_CLR(mutexp, DB_MUTEX_LOCKED); 276 MUTEX_UNSET(&mutexp->tas); 277 278 if (mutexp->nwaiters > 0) { 279 if ((ret = get_handle(env, mutexp, &event)) != 0) 280 goto err; 281 282#ifdef MUTEX_DIAG 283 QueryPerformanceCounter(&now); 284 printf("[%I64d]: Signalling mutex %p, id %d\n", 285 now.QuadPart, mutexp, mutexp->id); 286#endif 287 if (!PulseEvent(event)) { 288 ret = __os_get_syserr(); 289 CloseHandle(event); 290 goto err; 291 } 292 293 CloseHandle(event); 294 } 295 296 return (0); 297 298err: __db_syserr(env, ret, "Win32 unlock failed"); 299 return (__env_panic(env, __os_posix_err(ret))); 300} 301 302/* 303 * __db_win32_mutex_destroy -- 304 * Destroy a mutex. 305 * 306 * PUBLIC: int __db_win32_mutex_destroy __P((ENV *, db_mutex_t)); 307 */ 308int 309__db_win32_mutex_destroy(env, mutex) 310 ENV *env; 311 db_mutex_t mutex; 312{ 313 return (0); 314} 315