Bring in GSoC code: SysV IPC in userspace
[dragonfly.git] / lib / libc / sysvipc / lock.c
1 /*
2  * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3  * Copyright (c) 1998 Alex Nash
4  * Copyright (c) 2006 David Xu <yfxu@corp.netease.com>.
5  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by John Birrell.
19  * 4. Neither the name of the author nor the names of any co-contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $DragonFly: src/lib/libthread_xu/thread/thr_mutex.c,v 1.15 2008/05/09 16:03:27 dillon Exp $
36  * $FreeBSD: src/lib/libpthread/thread/thr_rwlock.c,v 1.14 2004/01/08 15:37:09 deischen Exp $
37  * $DragonFly: src/lib/libthread_xu/thread/thr_rwlock.c,v 1.7 2006/04/06 13:03:09 davidxu Exp $
38  */
39
40 #include <machine/atomic.h>
41 #include <machine/tls.h>
42 #include <errno.h>
43
44 #include "sysvipc_utils.h"
45 #include "sysvipc_lock.h"
46 #include "sysvipc_lock_generic.h"
47
48 #include <limits.h>
49 #include <stdio.h>
50 #include <unistd.h>
51
52 #define MAX_READ_LOCKS          (INT_MAX - 1)
53
54 static int rdlock_count;
55
56 int
57 sysv_mutex_init(struct sysv_mutex *mutex) {
58         if(mutex == NULL)
59                 return (EINVAL);
60         mutex->_mutex_static_lock = 0;
61         mutex->pid_owner = -1;
62         mutex->tid_owner = -1;
63         return (0);
64 }
65
66 int
67 sysv_mutex_lock(struct sysv_mutex *mutex)
68 {
69         if (mutex->pid_owner == getpid() &&
70                         mutex->tid_owner == lwp_gettid()) {
71                 sysv_print_err("deadlock: mutex aleady acquired by the thread\n");
72                 return (EDEADLK);
73         }
74         _sysv_umtx_lock(&mutex->_mutex_static_lock);
75         mutex->pid_owner = getpid();
76         mutex->tid_owner = lwp_gettid();
77         return (0);
78 }
79
80 int
81 sysv_mutex_unlock(struct sysv_mutex *mutex)
82 {
83         if (mutex->pid_owner != getpid() ||
84                         mutex->tid_owner != lwp_gettid()) {
85                 sysv_print_err("eperm try unlock a mutex that is not acquired\n");
86                 return (EPERM);
87         }
88
89         mutex->tid_owner = -1;
90         mutex->pid_owner = -1;
91         _sysv_umtx_unlock(&mutex->_mutex_static_lock);
92         return (0);
93 }
94
95 static int
96 sysv_cond_wait(int *val, struct sysv_mutex *mutex) {
97         sysv_mutex_unlock(mutex);
98
99         /* I use SYSV_TIMEOUT to avoid lossing a wakeup
100          * sent before going to sleep and remain blocked.
101          */
102         umtx_sleep(val, *val, SYSV_TIMEOUT);
103         return (sysv_mutex_lock(mutex));
104 }
105
106 static int
107 sysv_cond_signal(int *val) {
108         return (umtx_wakeup(val, 0));
109 }
110
111 int
112 sysv_rwlock_init(struct sysv_rwlock *rwlock)
113 {
114         int ret;
115
116         if (rwlock == NULL)
117                 return (EINVAL);
118
119         /* Initialize the lock. */
120         sysv_mutex_init(&rwlock->lock);
121         rwlock->state = 0;
122         rwlock->blocked_writers = 0;
123
124         return (ret);
125 }
126
127 int
128 sysv_rwlock_unlock (struct sysv_rwlock *rwlock)
129 {
130         int ret;
131
132         if (rwlock == NULL)
133                 return (EINVAL);
134
135         /* Grab the monitor lock. */
136         if ((ret = sysv_mutex_lock(&rwlock->lock)) != 0)
137                 return (ret);
138
139         if (rwlock->state > 0) {
140                 rdlock_count--;
141                 rwlock->state--;
142                 if (rwlock->state == 0 && rwlock->blocked_writers) {
143                         ret = sysv_cond_signal(&rwlock->write_signal);
144                 }
145         } else if (rwlock->state < 0) {
146                 rwlock->state = 0;
147
148                 if (rwlock->blocked_writers) {
149                         ret = sysv_cond_signal(&rwlock->write_signal);
150                 }
151                 else {
152                         ret = sysv_cond_signal(&rwlock->read_signal);
153                 }
154         } else
155                 ret = EINVAL;
156
157         sysv_mutex_unlock(&rwlock->lock);
158
159         return (ret);
160 }
161
162 int
163 sysv_rwlock_wrlock (struct sysv_rwlock *rwlock)
164 {
165         int ret;
166
167         if (rwlock == NULL)
168                 return (EINVAL);
169
170         /* Grab the monitor lock. */
171         if ((ret = sysv_mutex_lock(&rwlock->lock)) != 0)
172                 return (ret);
173
174         while (rwlock->state != 0) {
175                 rwlock->blocked_writers++;
176
177                 ret = sysv_cond_wait(&rwlock->write_signal, &rwlock->lock);
178                 if (ret != 0) {
179                         rwlock->blocked_writers--;
180                         /* No unlock is required because only the lock
181                          * operation can return error.
182                          */
183                         //sysv_mutex_unlock(&rwlock->lock);
184                         return (ret);
185                 }
186
187                 rwlock->blocked_writers--;
188         }
189
190         /* Indicate that we are locked for writing. */
191         rwlock->state = -1;
192
193         sysv_mutex_unlock(&rwlock->lock);
194
195         return (ret);
196 }
197
198 int
199 sysv_rwlock_rdlock(struct sysv_rwlock *rwlock)
200 {
201         int ret;
202
203 //      sysv_print("try get rd lock\n");
204         if (rwlock == NULL)
205                 return (EINVAL);
206
207         /* Grab the monitor lock. */
208         if ((ret = sysv_mutex_lock(&rwlock->lock)) != 0)
209                 return (ret);
210
211         /* Check the lock count. */
212         if (rwlock->state == MAX_READ_LOCKS) {
213                 sysv_mutex_unlock(&rwlock->lock);
214                 return (EAGAIN);
215         }
216
217         if ((rdlock_count > 0) && (rwlock->state > 0)) {
218                 /*
219                  * Taken from the pthread implementation with only
220                  * one change; rdlock_count is per process not per
221                  * thread;
222                  * Original comment:
223                  * To avoid having to track all the rdlocks held by
224                  * a thread or all of the threads that hold a rdlock,
225                  * we keep a simple count of all the rdlocks held by
226                  * a thread.  If a thread holds any rdlocks it is
227                  * possible that it is attempting to take a recursive
228                  * rdlock.  If there are blocked writers and precedence
229                  * is given to them, then that would result in the thread
230                  * deadlocking.  So allowing a thread to take the rdlock
231                  * when it already has one or more rdlocks avoids the
232                  * deadlock.  I hope the reader can follow that logic ;-)
233                  */
234                 ;       /* nothing needed */
235         } else {
236                 /* Give writers priority over readers. */
237                 while (rwlock->blocked_writers || rwlock->state < 0) {
238                         ret = sysv_cond_wait(&rwlock->read_signal,
239                            &rwlock->lock);
240                         if (ret != 0) {
241                                 /* No unlock necessary because only lock
242                                  * operation can return error.
243                                  */
244                                 //sysv_mutex_unlock(&rwlock->lock);
245                                 return (ret);
246                         }
247                 }
248         }
249
250         rdlock_count++;
251         rwlock->state++; /* Indicate we are locked for reading. */
252
253         /*
254          * Something is really wrong if this call fails.  Returning
255          * error won't do because we've already obtained the read
256          * lock.  Decrementing 'state' is no good because we probably
257          * don't have the monitor lock.
258          */
259         sysv_mutex_unlock(&rwlock->lock);
260
261         return (ret);
262 }