Remove definition of builtin function
[platform/upstream/db4.git] / mutex / mut_win32.c
1 /*
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002-2009 Oracle.  All rights reserved.
5  *
6  * $Id$
7  */
8
9 #include "db_config.h"
10
11 #define LOAD_ACTUAL_MUTEX_CODE
12 #include "db_int.h"
13
14 #include "dbinc/atomic.h"
15 /*
16  * This is where we load in the actual test-and-set mutex code.
17  */
18 #include "dbinc/mutex_int.h"
19
20 /* We don't want to run this code even in "ordinary" diagnostic mode. */
21 #undef MUTEX_DIAG
22
23 /*
24  * Common code to get an event handle.  This is executed whenever a mutex
25  * blocks, or when unlocking a mutex that a thread is waiting on.  We can't
26  * keep these handles around, since the mutex structure is in shared memory,
27  * and each process gets its own handle value.
28  *
29  * We pass security attributes so that the created event is accessible by all
30  * users, in case a Windows service is sharing an environment with a local
31  * process run as a different user.
32  */
33 static _TCHAR hex_digits[] = _T("0123456789abcdef");
34 static SECURITY_DESCRIPTOR null_sd;
35 static SECURITY_ATTRIBUTES all_sa;
36 static int security_initialized = 0;
37
38 static __inline int get_handle(env, mutexp, eventp)
39         ENV *env;
40         DB_MUTEX *mutexp;
41         HANDLE *eventp;
42 {
43         _TCHAR idbuf[] = _T("db.m00000000");
44         _TCHAR *p = idbuf + 12;
45         int ret = 0;
46         u_int32_t id;
47
48         for (id = (mutexp)->id; id != 0; id >>= 4)
49                 *--p = hex_digits[id & 0xf];
50
51 #ifndef DB_WINCE
52         if (!security_initialized) {
53                 InitializeSecurityDescriptor(&null_sd,
54                     SECURITY_DESCRIPTOR_REVISION);
55                 SetSecurityDescriptorDacl(&null_sd, TRUE, 0, FALSE);
56                 all_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
57                 all_sa.bInheritHandle = FALSE;
58                 all_sa.lpSecurityDescriptor = &null_sd;
59                 security_initialized = 1;
60         }
61 #endif
62
63         if ((*eventp = CreateEvent(&all_sa, FALSE, FALSE, idbuf)) == NULL) {
64                 ret = __os_get_syserr();
65                 __db_syserr(env, ret, "Win32 create event failed");
66         }
67
68         return (ret);
69 }
70
71 /*
72  * __db_win32_mutex_lock_int
73  *      Internal function to lock a win32 mutex
74  *
75  *      If the wait paramter is 0, this function will return DB_LOCK_NOTGRANTED
76  *      rather than wait.
77  *
78  */
79 static __inline int
80 __db_win32_mutex_lock_int(env, mutex, wait)
81         ENV *env;
82         db_mutex_t mutex;
83         int wait;
84 {
85         DB_ENV *dbenv;
86         DB_MUTEX *mutexp;
87         DB_MUTEXMGR *mtxmgr;
88         DB_MUTEXREGION *mtxregion;
89         DB_THREAD_INFO *ip;
90         HANDLE event;
91         u_int32_t nspins;
92         int ms, ret;
93 #ifdef MUTEX_DIAG
94         LARGE_INTEGER now;
95 #endif
96         dbenv = env->dbenv;
97
98         if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
99                 return (0);
100
101         mtxmgr = env->mutex_handle;
102         mtxregion = mtxmgr->reginfo.primary;
103         mutexp = MUTEXP_SET(mtxmgr, mutex);
104
105         CHECK_MTX_THREAD(env, mutexp);
106
107         /*
108          * See WINCE_ATOMIC_MAGIC definition for details.
109          * Use sharecount, because the value just needs to be a db_atomic_t
110          * memory mapped onto the same page as those being Interlocked*.
111          */
112         WINCE_ATOMIC_MAGIC(&mutexp->sharecount);
113
114         event = NULL;
115         ms = 50;
116         ret = 0;
117
118         /*
119          * Only check the thread state once, by initializing the thread
120          * control block pointer to null.  If it is not the failchk
121          * thread, then ip will have a valid value subsequent times
122          * in the loop.
123          */
124         ip = NULL;
125
126 loop:   /* Attempt to acquire the mutex mutex_tas_spins times, if waiting. */
127         for (nspins =
128             mtxregion->stat.st_mutex_tas_spins; nspins > 0; --nspins) {
129                 /*
130                  * We can avoid the (expensive) interlocked instructions if
131                  * the mutex is already busy.
132                  */
133                 if (MUTEXP_IS_BUSY(mutexp) || !MUTEXP_ACQUIRE(mutexp)) {
134                         if (F_ISSET(dbenv, DB_ENV_FAILCHK) &&
135                             ip == NULL && dbenv->is_alive(dbenv,
136                             mutexp->pid, mutexp->tid, 0) == 0) {
137                                 ret = __env_set_state(env, &ip, THREAD_VERIFY);
138                                 if (ret != 0 ||
139                                     ip->dbth_state == THREAD_FAILCHK)
140                                         return (DB_RUNRECOVERY);
141                         }
142                         if (!wait)
143                                 return (DB_LOCK_NOTGRANTED);
144                         /*
145                          * Some systems (notably those with newer Intel CPUs)
146                          * need a small pause before retrying. [#6975]
147                          */
148                         MUTEX_PAUSE
149                         continue;
150                 }
151
152 #ifdef DIAGNOSTIC
153                 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
154                         char buf[DB_THREADID_STRLEN];
155                         __db_errx(env,
156                             "Win32 lock failed: mutex already locked by %s",
157                              dbenv->thread_id_string(dbenv,
158                              mutexp->pid, mutexp->tid, buf));
159                         return (__env_panic(env, EACCES));
160                 }
161 #endif
162                 F_SET(mutexp, DB_MUTEX_LOCKED);
163                 dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid);
164
165 #ifdef HAVE_STATISTICS
166                 if (event == NULL)
167                         ++mutexp->mutex_set_nowait;
168                 else
169                         ++mutexp->mutex_set_wait;
170 #endif
171                 if (event != NULL) {
172                         CloseHandle(event);
173                         InterlockedDecrement(&mutexp->nwaiters);
174 #ifdef MUTEX_DIAG
175                         if (ret != WAIT_OBJECT_0) {
176                                 QueryPerformanceCounter(&now);
177                                 printf("[%I64d]: Lost signal on mutex %p, "
178                                     "id %d, ms %d\n",
179                                     now.QuadPart, mutexp, mutexp->id, ms);
180                         }
181 #endif
182                 }
183
184 #ifdef DIAGNOSTIC
185                 /*
186                  * We want to switch threads as often as possible.  Yield
187                  * every time we get a mutex to ensure contention.
188                  */
189                 if (F_ISSET(dbenv, DB_ENV_YIELDCPU))
190                         __os_yield(env, 0, 0);
191 #endif
192
193                 return (0);
194         }
195
196         /*
197          * Yield the processor; wait 50 ms initially, up to 1 second.  This
198          * loop is needed to work around a race where the signal from the
199          * unlocking thread gets lost.  We start at 50 ms because it's unlikely
200          * to happen often and we want to avoid wasting CPU.
201          */
202         if (event == NULL) {
203 #ifdef MUTEX_DIAG
204                 QueryPerformanceCounter(&now);
205                 printf("[%I64d]: Waiting on mutex %p, id %d\n",
206                     now.QuadPart, mutexp, mutexp->id);
207 #endif
208                 InterlockedIncrement(&mutexp->nwaiters);
209                 if ((ret = get_handle(env, mutexp, &event)) != 0)
210                         goto err;
211         }
212         if ((ret = WaitForSingleObject(event, ms)) == WAIT_FAILED) {
213                 ret = __os_get_syserr();
214                 goto err;
215         }
216         if ((ms <<= 1) > MS_PER_SEC)
217                 ms = MS_PER_SEC;
218
219         PANIC_CHECK(env);
220         goto loop;
221
222 err:    __db_syserr(env, ret, "Win32 lock failed");
223         return (__env_panic(env, __os_posix_err(ret)));
224 }
225
226 /*
227  * __db_win32_mutex_init --
228  *      Initialize a Win32 mutex.
229  *
230  * PUBLIC: int __db_win32_mutex_init __P((ENV *, db_mutex_t, u_int32_t));
231  */
232 int
233 __db_win32_mutex_init(env, mutex, flags)
234         ENV *env;
235         db_mutex_t mutex;
236         u_int32_t flags;
237 {
238         DB_MUTEX *mutexp;
239
240         mutexp = MUTEXP_SET(env->mutex_handle, mutex);
241         mutexp->id = ((getpid() & 0xffff) << 16) ^ P_TO_UINT32(mutexp);
242         F_SET(mutexp, flags);
243
244         return (0);
245 }
246
247 /*
248  * __db_win32_mutex_lock
249  *      Lock on a mutex, blocking if necessary.
250  *
251  * PUBLIC: int __db_win32_mutex_lock __P((ENV *, db_mutex_t));
252  */
253 int
254 __db_win32_mutex_lock(env, mutex)
255         ENV *env;
256         db_mutex_t mutex;
257 {
258         return (__db_win32_mutex_lock_int(env, mutex, 1));
259 }
260
261 /*
262  * __db_win32_mutex_trylock
263  *      Try to lock a mutex, returning without waiting if it is busy
264  *
265  * PUBLIC: int __db_win32_mutex_trylock __P((ENV *, db_mutex_t));
266  */
267 int
268 __db_win32_mutex_trylock(env, mutex)
269         ENV *env;
270         db_mutex_t mutex;
271 {
272         return (__db_win32_mutex_lock_int(env, mutex, 0));
273 }
274
275 #if defined(HAVE_SHARED_LATCHES)
276 /*
277  * __db_win32_mutex_readlock_int
278  *      Try to lock a mutex, possibly waiting if requested and necessary.
279  */
280 int
281 __db_win32_mutex_readlock_int(env, mutex, nowait)
282         ENV *env;
283         db_mutex_t mutex;
284         int nowait;
285 {
286         DB_ENV *dbenv;
287         DB_MUTEX *mutexp;
288         DB_MUTEXMGR *mtxmgr;
289         DB_MUTEXREGION *mtxregion;
290         HANDLE event;
291         u_int32_t nspins;
292         int ms, ret;
293         long exch_ret, mtx_val;
294 #ifdef MUTEX_DIAG
295         LARGE_INTEGER now;
296 #endif
297         dbenv = env->dbenv;
298
299         if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
300                 return (0);
301
302         mtxmgr = env->mutex_handle;
303         mtxregion = mtxmgr->reginfo.primary;
304         mutexp = MUTEXP_SET(mtxmgr, mutex);
305
306         CHECK_MTX_THREAD(env, mutexp);
307
308         /*
309          * See WINCE_ATOMIC_MAGIC definition for details.
310          * Use sharecount, because the value just needs to be a db_atomic_t
311          * memory mapped onto the same page as those being Interlocked*.
312          */
313         WINCE_ATOMIC_MAGIC(&mutexp->sharecount);
314
315         event = NULL;
316         ms = 50;
317         ret = 0;
318         /*
319          * This needs to be initialized, since if mutexp->tas
320          * is write locked on the first pass, it needs a value.
321          */
322         exch_ret = 0;
323
324 loop:   /* Attempt to acquire the resource for N spins. */
325         for (nspins =
326             mtxregion->stat.st_mutex_tas_spins; nspins > 0; --nspins) {
327                 /*
328                  * We can avoid the (expensive) interlocked instructions if
329                  * the mutex is already "set".
330                  */
331 retry:          mtx_val = atomic_read(&mutexp->sharecount);
332                 if (mtx_val == MUTEX_SHARE_ISEXCLUSIVE) {
333                         if (nowait)
334                                 return (DB_LOCK_NOTGRANTED);
335
336                         continue;
337                 } else if (!atomic_compare_exchange(env, &mutexp->sharecount,
338                     mtx_val, mtx_val + 1)) {
339                         /*
340                          * Some systems (notably those with newer Intel CPUs)
341                          * need a small pause here. [#6975]
342                          */
343                         MUTEX_PAUSE
344                         goto retry;
345                 }
346
347 #ifdef HAVE_STATISTICS
348                 if (event == NULL)
349                         ++mutexp->mutex_set_rd_nowait;
350                 else
351                         ++mutexp->mutex_set_rd_wait;
352 #endif
353                 if (event != NULL) {
354                         CloseHandle(event);
355                         InterlockedDecrement(&mutexp->nwaiters);
356 #ifdef MUTEX_DIAG
357                         if (ret != WAIT_OBJECT_0) {
358                                 QueryPerformanceCounter(&now);
359                                 printf("[%I64d]: Lost signal on mutex %p, "
360                                     "id %d, ms %d\n",
361                                     now.QuadPart, mutexp, mutexp->id, ms);
362                         }
363 #endif
364                 }
365
366 #ifdef DIAGNOSTIC
367                 /*
368                  * We want to switch threads as often as possible.  Yield
369                  * every time we get a mutex to ensure contention.
370                  */
371                 if (F_ISSET(dbenv, DB_ENV_YIELDCPU))
372                         __os_yield(env, 0, 0);
373 #endif
374
375                 return (0);
376         }
377
378         /*
379          * Yield the processor; wait 50 ms initially, up to 1 second.  This
380          * loop is needed to work around a race where the signal from the
381          * unlocking thread gets lost.  We start at 50 ms because it's unlikely
382          * to happen often and we want to avoid wasting CPU.
383          */
384         if (event == NULL) {
385 #ifdef MUTEX_DIAG
386                 QueryPerformanceCounter(&now);
387                 printf("[%I64d]: Waiting on mutex %p, id %d\n",
388                     now.QuadPart, mutexp, mutexp->id);
389 #endif
390                 InterlockedIncrement(&mutexp->nwaiters);
391                 if ((ret = get_handle(env, mutexp, &event)) != 0)
392                         goto err;
393         }
394         if ((ret = WaitForSingleObject(event, ms)) == WAIT_FAILED) {
395                 ret = __os_get_syserr();
396                 goto err;
397         }
398         if ((ms <<= 1) > MS_PER_SEC)
399                 ms = MS_PER_SEC;
400
401         PANIC_CHECK(env);
402         goto loop;
403
404 err:    __db_syserr(env, ret, "Win32 read lock failed");
405         return (__env_panic(env, __os_posix_err(ret)));
406 }
407
408 /*
409  * __db_win32_mutex_readlock
410  *      Get a shared lock on a latch
411  *
412  * PUBLIC: #if defined(HAVE_SHARED_LATCHES)
413  * PUBLIC: int __db_win32_mutex_readlock __P((ENV *, db_mutex_t));
414  * PUBLIC: #endif
415  */
416 int
417 __db_win32_mutex_readlock(env, mutex)
418         ENV *env;
419         db_mutex_t mutex;
420 {
421         return (__db_win32_mutex_readlock_int(env, mutex, 0));
422 }
423
424 /*
425  * __db_win32_mutex_tryreadlock
426  *      Try to a shared lock on a latch
427  *
428  * PUBLIC: #if defined(HAVE_SHARED_LATCHES)
429  * PUBLIC: int __db_win32_mutex_tryreadlock __P((ENV *, db_mutex_t));
430  * PUBLIC: #endif
431  */
432 int
433 __db_win32_mutex_tryreadlock(env, mutex)
434         ENV *env;
435         db_mutex_t mutex;
436 {
437         return (__db_win32_mutex_readlock_int(env, mutex, 1));
438 }
439 #endif
440
441 /*
442  * __db_win32_mutex_unlock --
443  *      Release a mutex.
444  *
445  * PUBLIC: int __db_win32_mutex_unlock __P((ENV *, db_mutex_t));
446  */
447 int
448 __db_win32_mutex_unlock(env, mutex)
449         ENV *env;
450         db_mutex_t mutex;
451 {
452         DB_ENV *dbenv;
453         DB_MUTEX *mutexp;
454         DB_MUTEXMGR *mtxmgr;
455         HANDLE event;
456         int ret;
457 #ifdef MUTEX_DIAG
458         LARGE_INTEGER now;
459 #endif
460         dbenv = env->dbenv;
461
462         if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
463                 return (0);
464
465         mtxmgr = env->mutex_handle;
466         mutexp = MUTEXP_SET(mtxmgr, mutex);
467
468 #ifdef DIAGNOSTIC
469         if (!MUTEXP_IS_BUSY(mutexp) || !(F_ISSET(mutexp, DB_MUTEX_SHARED) ||
470             F_ISSET(mutexp, DB_MUTEX_LOCKED))) {
471                 __db_errx(env,
472                  "Win32 unlock failed: lock already unlocked: mutex %d busy %d",
473                     mutex, MUTEXP_BUSY_FIELD(mutexp));
474                 return (__env_panic(env, EACCES));
475         }
476 #endif
477         /*
478          * If we have a shared latch, and a read lock (DB_MUTEX_LOCKED is only
479          * set for write locks), then decrement the latch. If the readlock is
480          * still held by other threads, just return. Otherwise go ahead and
481          * notify any waiting threads.
482          */
483 #ifdef HAVE_SHARED_LATCHES
484         if (F_ISSET(mutexp, DB_MUTEX_SHARED)) {
485                 if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
486                         F_CLR(mutexp, DB_MUTEX_LOCKED);
487                         if ((ret = InterlockedExchange(
488                             (interlocked_val)(&atomic_read(
489                             &mutexp->sharecount)), 0)) !=
490                             MUTEX_SHARE_ISEXCLUSIVE) {
491                                 ret = DB_RUNRECOVERY;
492                                 goto err;
493                         }
494                 } else if (InterlockedDecrement(
495                     (interlocked_val)(&atomic_read(&mutexp->sharecount))) > 0)
496                         return (0);
497         } else
498 #endif
499         {
500                 F_CLR(mutexp, DB_MUTEX_LOCKED);
501                 MUTEX_UNSET(&mutexp->tas);
502         }
503
504         if (mutexp->nwaiters > 0) {
505                 if ((ret = get_handle(env, mutexp, &event)) != 0)
506                         goto err;
507
508 #ifdef MUTEX_DIAG
509                 QueryPerformanceCounter(&now);
510                 printf("[%I64d]: Signalling mutex %p, id %d\n",
511                     now.QuadPart, mutexp, mutexp->id);
512 #endif
513                 if (!PulseEvent(event)) {
514                         ret = __os_get_syserr();
515                         CloseHandle(event);
516                         goto err;
517                 }
518
519                 CloseHandle(event);
520         }
521
522         return (0);
523
524 err:    __db_syserr(env, ret, "Win32 unlock failed");
525         return (__env_panic(env, __os_posix_err(ret)));
526 }
527
528 /*
529  * __db_win32_mutex_destroy --
530  *      Destroy a mutex.
531  *
532  * PUBLIC: int __db_win32_mutex_destroy __P((ENV *, db_mutex_t));
533  */
534 int
535 __db_win32_mutex_destroy(env, mutex)
536         ENV *env;
537         db_mutex_t mutex;
538 {
539         return (0);
540 }