1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: mut_fcntl.c,v 12.28 2008/01/08 20:58:43 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/mutex_int.h"
13
14/*
15 * __db_fcntl_mutex_init --
16 *	Initialize a fcntl mutex.
17 *
18 * PUBLIC: int __db_fcntl_mutex_init __P((ENV *, db_mutex_t, u_int32_t));
19 */
20int
21__db_fcntl_mutex_init(env, mutex, flags)
22	ENV *env;
23	db_mutex_t mutex;
24	u_int32_t flags;
25{
26	COMPQUIET(env, NULL);
27	COMPQUIET(mutex, MUTEX_INVALID);
28	COMPQUIET(flags, 0);
29
30	return (0);
31}
32
33/*
34 * __db_fcntl_mutex_lock
35 *	Lock on a mutex, blocking if necessary.
36 *
37 * PUBLIC: int __db_fcntl_mutex_lock __P((ENV *, db_mutex_t));
38 */
39int
40__db_fcntl_mutex_lock(env, mutex)
41	ENV *env;
42	db_mutex_t mutex;
43{
44	DB_ENV *dbenv;
45	DB_MUTEX *mutexp;
46	DB_MUTEXMGR *mtxmgr;
47	DB_MUTEXREGION *mtxregion;
48	struct flock k_lock;
49	int locked, ms, ret;
50
51	dbenv = env->dbenv;
52
53	if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
54		return (0);
55
56	mtxmgr = env->mutex_handle;
57	mtxregion = mtxmgr->reginfo.primary;
58	mutexp = MUTEXP_SET(mutex);
59
60	CHECK_MTX_THREAD(env, mutexp);
61
62#ifdef HAVE_STATISTICS
63	if (F_ISSET(mutexp, DB_MUTEX_LOCKED))
64		++mutexp->mutex_set_wait;
65	else
66		++mutexp->mutex_set_nowait;
67#endif
68
69	/* Initialize the lock. */
70	k_lock.l_whence = SEEK_SET;
71	k_lock.l_start = mutex;
72	k_lock.l_len = 1;
73
74	for (locked = 0;;) {
75		/*
76		 * Wait for the lock to become available; wait 1ms initially,
77		 * up to 1 second.
78		 */
79		for (ms = 1; F_ISSET(mutexp, DB_MUTEX_LOCKED);) {
80			__os_yield(NULL, 0, ms * US_PER_MS);
81			if ((ms <<= 1) > MS_PER_SEC)
82				ms = MS_PER_SEC;
83		}
84
85		/* Acquire an exclusive kernel lock. */
86		k_lock.l_type = F_WRLCK;
87		if (fcntl(env->lockfhp->fd, F_SETLKW, &k_lock))
88			goto err;
89
90		/* If the resource is still available, it's ours. */
91		if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
92			locked = 1;
93
94			F_SET(mutexp, DB_MUTEX_LOCKED);
95			dbenv->thread_id(dbenv, &mutexp->pid, &mutexp->tid);
96		}
97
98		/* Release the kernel lock. */
99		k_lock.l_type = F_UNLCK;
100		if (fcntl(env->lockfhp->fd, F_SETLK, &k_lock))
101			goto err;
102
103		/*
104		 * If we got the resource lock we're done.
105		 *
106		 * !!!
107		 * We can't check to see if the lock is ours, because we may
108		 * be trying to block ourselves in the lock manager, and so
109		 * the holder of the lock that's preventing us from getting
110		 * the lock may be us!  (Seriously.)
111		 */
112		if (locked)
113			break;
114	}
115
116#ifdef DIAGNOSTIC
117	/*
118	 * We want to switch threads as often as possible.  Yield every time
119	 * we get a mutex to ensure contention.
120	 */
121	if (F_ISSET(dbenv, DB_ENV_YIELDCPU))
122		__os_yield(env, 0, 0);
123#endif
124	return (0);
125
126err:	ret = __os_get_syserr();
127	__db_syserr(env, ret, "fcntl lock failed");
128	return (__env_panic(env, __os_posix_err(ret)));
129}
130
131/*
132 * __db_fcntl_mutex_unlock --
133 *	Release a mutex.
134 *
135 * PUBLIC: int __db_fcntl_mutex_unlock __P((ENV *, db_mutex_t));
136 */
137int
138__db_fcntl_mutex_unlock(env, mutex)
139	ENV *env;
140	db_mutex_t mutex;
141{
142	DB_ENV *dbenv;
143	DB_MUTEX *mutexp;
144	DB_MUTEXMGR *mtxmgr;
145	DB_MUTEXREGION *mtxregion;
146
147	dbenv = env->dbenv;
148
149	if (!MUTEX_ON(env) || F_ISSET(dbenv, DB_ENV_NOLOCKING))
150		return (0);
151
152	mtxmgr = env->mutex_handle;
153	mtxregion = mtxmgr->reginfo.primary;
154	mutexp = MUTEXP_SET(mutex);
155
156#ifdef DIAGNOSTIC
157	if (!F_ISSET(mutexp, DB_MUTEX_LOCKED)) {
158		__db_errx(env, "fcntl unlock failed: lock already unlocked");
159		return (__env_panic(env, EACCES));
160	}
161#endif
162
163	/*
164	 * Release the resource.  We don't have to acquire any locks because
165	 * processes trying to acquire the lock are waiting for the flag to
166	 * go to 0.  Once that happens the waiters will serialize acquiring
167	 * an exclusive kernel lock before locking the mutex.
168	 */
169	F_CLR(mutexp, DB_MUTEX_LOCKED);
170
171	return (0);
172}
173
174/*
175 * __db_fcntl_mutex_destroy --
176 *	Destroy a mutex.
177 *
178 * PUBLIC: int __db_fcntl_mutex_destroy __P((ENV *, db_mutex_t));
179 */
180int
181__db_fcntl_mutex_destroy(env, mutex)
182	ENV *env;
183	db_mutex_t mutex;
184{
185	COMPQUIET(env, NULL);
186	COMPQUIET(mutex, MUTEX_INVALID);
187
188	return (0);
189}
190