1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: mut_tas.c,v 12.36 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 architecture/compiler specific mutex code.
15 */
16#define	LOAD_ACTUAL_MUTEX_CODE
17#include "dbinc/mutex_int.h"
18
19/*
20 * __db_tas_mutex_init --
21 *	Initialize a test-and-set mutex.
22 *
23 * PUBLIC: int __db_tas_mutex_init __P((ENV *, db_mutex_t, u_int32_t));
24 */
25int
26__db_tas_mutex_init(env, mutex, flags)
27	ENV *env;
28	db_mutex_t mutex;
29	u_int32_t flags;
30{
31	DB_ENV *dbenv;
32	DB_MUTEX *mutexp;
33	DB_MUTEXMGR *mtxmgr;
34	DB_MUTEXREGION *mtxregion;
35	int ret;
36
37	COMPQUIET(flags, 0);
38
39	dbenv = env->dbenv;
40	mtxmgr = env->mutex_handle;
41	mtxregion = mtxmgr->reginfo.primary;
42	mutexp = MUTEXP_SET(mutex);
43
44	/* Check alignment. */
45	if (((uintptr_t)mutexp & (dbenv->mutex_align - 1)) != 0) {
46		__db_errx(env, "TAS: mutex not appropriately aligned");
47		return (EINVAL);
48	}
49
50	if (MUTEX_INIT(&mutexp->tas)) {
51		ret = __os_get_syserr();
52		__db_syserr(env, ret, "TAS: mutex initialize");
53		return (__os_posix_err(ret));
54	}
55#ifdef HAVE_MUTEX_HYBRID
56	if ((ret = __db_pthread_mutex_init(env,
57	     mutex, flags | DB_MUTEX_SELF_BLOCK)) != 0)
58		return (ret);
59#endif
60	return (0);
61}
62
63/*
64 * __db_tas_mutex_lock
65 *	Lock on a mutex, blocking if necessary.
66 *
67 * PUBLIC: int __db_tas_mutex_lock __P((ENV *, db_mutex_t));
68 */
69int
70__db_tas_mutex_lock(env, mutex)
71	ENV *env;
72	db_mutex_t mutex;
73{
74	DB_ENV *dbenv;
75	DB_MUTEX *mutexp;
76	DB_MUTEXMGR *mtxmgr;
77	DB_MUTEXREGION *mtxregion;
78	u_int32_t nspins;
79#ifdef HAVE_MUTEX_HYBRID
80	int ret;
81#else
82	u_long ms, max_ms;
83#endif
84	dbenv = env->dbenv;
85
86	if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
87		return (0);
88
89	mtxmgr = env->mutex_handle;
90	mtxregion = mtxmgr->reginfo.primary;
91	mutexp = MUTEXP_SET(mutex);
92
93	CHECK_MTX_THREAD(env, mutexp);
94
95#ifdef HAVE_STATISTICS
96	if (F_ISSET(mutexp, DB_MUTEX_LOCKED))
97		++mutexp->mutex_set_wait;
98	else
99		++mutexp->mutex_set_nowait;
100#endif
101
102#ifndef HAVE_MUTEX_HYBRID
103	/*
104	 * Wait 1ms initially, up to 10ms for mutexes backing logical database
105	 * locks, and up to 25 ms for mutual exclusion data structure mutexes.
106	 * SR: #7675
107	 */
108	ms = 1;
109	max_ms = F_ISSET(mutexp, DB_MUTEX_LOGICAL_LOCK) ? 10 : 25;
110#endif
111
112loop:	/* Attempt to acquire the resource for N spins. */
113	for (nspins =
114	    mtxregion->stat.st_mutex_tas_spins; nspins > 0; --nspins) {
115#ifdef HAVE_MUTEX_HPPA_MSEM_INIT
116relock:
117#endif
118#ifdef HAVE_MUTEX_S390_CC_ASSEMBLY
119		tsl_t zero = 0;
120#endif
121		/*
122		 * Avoid interlocked instructions until they're likely to
123		 * succeed.
124		 */
125		if (F_ISSET(mutexp, DB_MUTEX_LOCKED) ||
126		    !MUTEX_SET(&mutexp->tas)) {
127			/*
128			 * Some systems (notably those with newer Intel CPUs)
129			 * need a small pause here. [#6975]
130			 */
131#ifdef MUTEX_PAUSE
132			MUTEX_PAUSE
133#endif
134			continue;
135		}
136
137#ifdef HAVE_MUTEX_HPPA_MSEM_INIT
138		/*
139		 * HP semaphores are unlocked automatically when a holding
140		 * process exits.  If the mutex appears to be locked
141		 * (F_ISSET(DB_MUTEX_LOCKED)) but we got here, assume this
142		 * has happened.  Set the pid and tid into the mutex and
143		 * lock again.  (The default state of the mutexes used to
144		 * block in __lock_get_internal is locked, so exiting with
145		 * a locked mutex is reasonable behavior for a process that
146		 * happened to initialize or use one of them.)
147		 */
148		if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
149			F_SET(mutexp, DB_MUTEX_LOCKED);
150			dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid);
151			goto relock;
152		}
153		/*
154		 * If we make it here, the mutex isn't locked, the diagnostic
155		 * won't fire, and we were really unlocked by someone calling
156		 * the DB mutex unlock function.
157		 */
158#endif
159#ifdef DIAGNOSTIC
160		if (F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
161			char buf[DB_THREADID_STRLEN];
162			__db_errx(env,
163			      "TAS lock failed: lock currently in use: ID: %s",
164			      dbenv->thread_id_string(dbenv,
165			      mutexp->pid, mutexp->tid, buf));
166			return (__env_panic(env, EACCES));
167		}
168#endif
169		F_SET(mutexp, DB_MUTEX_LOCKED);
170		dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid);
171
172#ifdef DIAGNOSTIC
173		/*
174		 * We want to switch threads as often as possible.  Yield
175		 * every time we get a mutex to ensure contention.
176		 */
177		if (F_ISSET(dbenv, DB_ENV_YIELDCPU))
178			__os_yield(env, 0, 0);
179#endif
180		return (0);
181	}
182
183	/* Wait for the lock to become available. */
184#ifdef HAVE_MUTEX_HYBRID
185	/*
186	 * By yielding here we can get the other thread to give up the
187	 * mutex before calling the more expensive library mutex call.
188	 * Tests have shown this to be a big win when there is contention.
189	 */
190	__os_yield(env, 0, 0);
191	if (!F_ISSET(mutexp, DB_MUTEX_LOCKED))
192		goto loop;
193	if ((ret = __db_pthread_mutex_lock(env, mutex)) != 0)
194		return (ret);
195#else
196	__os_yield(env, 0, ms * US_PER_MS);
197	if ((ms <<= 1) > max_ms)
198		ms = max_ms;
199#endif
200
201	/*
202	 * We're spinning.  The environment might be hung, and somebody else
203	 * has already recovered it.  The first thing recovery does is panic
204	 * the environment.  Check to see if we're never going to get this
205	 * mutex.
206	 */
207	PANIC_CHECK(env);
208
209	goto loop;
210}
211
212/*
213 * __db_tas_mutex_unlock --
214 *	Release a mutex.
215 *
216 * PUBLIC: int __db_tas_mutex_unlock __P((ENV *, db_mutex_t));
217 */
218int
219__db_tas_mutex_unlock(env, mutex)
220	ENV *env;
221	db_mutex_t mutex;
222{
223	DB_ENV *dbenv;
224	DB_MUTEX *mutexp;
225	DB_MUTEXMGR *mtxmgr;
226	DB_MUTEXREGION *mtxregion;
227#ifdef HAVE_MUTEX_HYBRID
228	int ret;
229#endif
230	dbenv = env->dbenv;
231
232	if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
233		return (0);
234
235	mtxmgr = env->mutex_handle;
236	mtxregion = mtxmgr->reginfo.primary;
237	mutexp = MUTEXP_SET(mutex);
238
239#ifdef DIAGNOSTIC
240	if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
241		__db_errx(env, "TAS unlock failed: lock already unlocked");
242		return (__env_panic(env, EACCES));
243	}
244#endif
245
246	F_CLR(mutexp, DB_MUTEX_LOCKED);
247#ifdef HAVE_MUTEX_HYBRID
248	MUTEX_MEMBAR(mutexp->flags);
249
250	if (mutexp->wait &&
251	    (ret = __db_pthread_mutex_unlock(env, mutex)) != 0)
252		return (ret);
253#endif
254	MUTEX_UNSET(&mutexp->tas);
255
256	return (0);
257}
258
259/*
260 * __db_tas_mutex_destroy --
261 *	Destroy a mutex.
262 *
263 * PUBLIC: int __db_tas_mutex_destroy __P((ENV *, db_mutex_t));
264 */
265int
266__db_tas_mutex_destroy(env, mutex)
267	ENV *env;
268	db_mutex_t mutex;
269{
270	DB_MUTEX *mutexp;
271	DB_MUTEXMGR *mtxmgr;
272	DB_MUTEXREGION *mtxregion;
273#ifdef HAVE_MUTEX_HYBRID
274	int ret;
275#endif
276
277	if (!MUTEX_ON(env))
278		return (0);
279
280	mtxmgr = env->mutex_handle;
281	mtxregion = mtxmgr->reginfo.primary;
282	mutexp = MUTEXP_SET(mutex);
283
284	MUTEX_DESTROY(&mutexp->tas);
285
286#ifdef HAVE_MUTEX_HYBRID
287	if ((ret = __db_pthread_mutex_destroy(env, mutex)) != 0)
288		return (ret);
289#endif
290
291	COMPQUIET(mutexp, NULL);	/* MUTEX_DESTROY may not be defined. */
292	return (0);
293}
294