1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998, 1999
5  *	Sleepycat Software.  All rights reserved.
6  */
7 
8 #include "db_config.h"
9 
10 #ifdef HAVE_MUTEX_FCNTL
11 
12 #ifndef lint
13 static const char sccsid[] = "@(#)mut_fcntl.c	11.1 (Sleepycat) 7/25/99";
14 #endif /* not lint */
15 
16 #ifndef NO_SYSTEM_INCLUDES
17 #include <sys/types.h>
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #endif
25 
26 #include "db_int.h"
27 
28 /*
29  * CDB___db_fcntl_mutex_init --
30  *	Initialize a DB mutex structure.
31  *
32  * PUBLIC: int CDB___db_fcntl_mutex_init __P((DB_ENV *, MUTEX *, u_int32_t));
33  */
34 int
CDB___db_fcntl_mutex_init(dbenv,mutexp,offset)35 CDB___db_fcntl_mutex_init(dbenv, mutexp, offset)
36 	DB_ENV *dbenv;
37 	MUTEX *mutexp;
38 	u_int32_t offset;
39 {
40 	memset(mutexp, 0, sizeof(*mutexp));
41 
42 	/*
43 	 * This is where we decide to ignore locks we don't need to set -- if
44 	 * the application is private, we don't need any locks.
45 	 */
46 	if (F_ISSET(dbenv, DB_ENV_PRIVATE)) {
47 		F_SET(mutexp, MUTEX_IGNORE);
48 		return (0);
49 	}
50 
51 	mutexp->off = offset;
52 
53 	return (0);
54 }
55 
56 /*
57  * CDB___db_fcntl_mutex_lock
58  *	Lock on a mutex, blocking if necessary.
59  *
60  * PUBLIC: int CDB___db_fcntl_mutex_lock __P((MUTEX *, DB_FH *));
61  */
62 int
CDB___db_fcntl_mutex_lock(mutexp,fhp)63 CDB___db_fcntl_mutex_lock(mutexp, fhp)
64 	MUTEX *mutexp;
65 	DB_FH *fhp;
66 {
67 	struct flock k_lock;
68 	int locked, ms, waited;
69 
70 	if (!DB_GLOBAL(db_mutexlocks))
71 		return (0);
72 
73 	/* Initialize the lock. */
74 	k_lock.l_whence = SEEK_SET;
75 	k_lock.l_start = mutexp->off;
76 	k_lock.l_len = 1;
77 
78 	for (locked = waited = 0;;) {
79 		/*
80 		 * Wait for the lock to become available; wait 1ms initially,
81 		 * up to 1 second.
82 		 */
83 		for (ms = 1; mutexp->pid != 0;) {
84 			waited = 1;
85 			CDB___os_yield(ms * USEC_PER_MS);
86 			if ((ms <<= 1) > MS_PER_SEC)
87 				ms = MS_PER_SEC;
88 		}
89 
90 		/* Acquire an exclusive kernel lock. */
91 		k_lock.l_type = F_WRLCK;
92 		if (fcntl(fhp->fd, F_SETLKW, &k_lock))
93 			return (CDB___os_get_errno());
94 
95 		/* If the resource is still available, it's ours. */
96 		if (mutexp->pid == 0) {
97 			locked = 1;
98 			mutexp->pid = (u_int32_t)getpid();
99 		}
100 
101 		/* Release the kernel lock. */
102 		k_lock.l_type = F_UNLCK;
103 		if (fcntl(fhp->fd, F_SETLK, &k_lock))
104 			return (CDB___os_get_errno());
105 
106 		/*
107 		 * If we got the resource lock we're done.
108 		 *
109 		 * !!!
110 		 * We can't check to see if the lock is ours, because we may
111 		 * be trying to block ourselves in the lock manager, and so
112 		 * the holder of the lock that's preventing us from getting
113 		 * the lock may be us!  (Seriously.)
114 		 */
115 		if (locked)
116 			break;
117 	}
118 
119 	if (waited)
120 		++mutexp->mutex_set_wait;
121 	else
122 		++mutexp->mutex_set_nowait;
123 	return (0);
124 }
125 
126 /*
127  * CDB___db_fcntl_mutex_unlock --
128  *	Release a lock.
129  *
130  * PUBLIC: int CDB___db_fcntl_mutex_unlock __P((MUTEX *));
131  */
132 int
CDB___db_fcntl_mutex_unlock(mutexp)133 CDB___db_fcntl_mutex_unlock(mutexp)
134 	MUTEX *mutexp;
135 {
136 	if (!DB_GLOBAL(db_mutexlocks))
137 		return (0);
138 
139 #ifdef DIAGNOSTIC
140 #define	MSG		"mutex_unlock: ERROR: released lock that was unlocked\n"
141 #ifndef	STDERR_FILENO
142 #define	STDERR_FILENO	2
143 #endif
144 	if (mutexp->pid == 0)
145 		write(STDERR_FILENO, MSG, sizeof(MSG) - 1);
146 #endif
147 
148 	/*
149 	 * Release the resource.  We don't have to acquire any locks because
150 	 * processes trying to acquire the lock are checking for a pid set to
151 	 * 0/non-0, not to any specific value.
152 	 */
153 	mutexp->pid = 0;
154 
155 	return (0);
156 }
157 
158 #endif /* HAVE_MUTEX_FCNTL */
159