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