Add to git.
[pthrlib.git] / src / pthr_mutex.c
1 /* Mutex locks.
2  * by Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: pthr_mutex.c,v 1.4 2003/02/02 18:05:31 rich Exp $
19  */
20
21 #include "config.h"
22
23 #ifdef HAVE_ASSERT_H
24 #include <assert.h>
25 #endif
26
27 #ifndef __sun__
28
29 #include <pool.h>
30
31 #include "pthr_pseudothread.h"
32 #include "pthr_wait_queue.h"
33
34 #else
35 /* SunOS annoyingly defines a 'struct mutex' type, and even worse defines
36  * it as soon as you include <stdlib.h>.
37  */
38 #define POOL_H
39 struct pool;
40 typedef struct pool *pool;
41 extern pool new_subpool (pool);
42 extern void delete_pool (pool);
43 extern void *pmalloc (pool, unsigned n);
44 extern void pool_register_cleanup_fn (pool, void (*fn) (void *), void *);
45
46 #define PTHR_PSEUDOTHREAD_H
47 struct pseudothread;
48 typedef struct pseudothread *pseudothread;
49 extern pseudothread current_pth;
50 extern pool pth_get_pool (pseudothread pth);
51
52 #define PTHR_WAIT_QUEUE_H
53 struct wait_queue;
54 typedef struct wait_queue *wait_queue;
55 extern wait_queue new_wait_queue (pool);
56 extern void wq_wake_up (wait_queue);
57 extern void wq_wake_up_one (wait_queue);
58 extern void wq_sleep_on (wait_queue);
59 extern int wq_nr_sleepers (wait_queue);
60 #endif
61
62 #include "pthr_mutex.h"
63
64 static void _do_release (void *);
65 static void _delete_mutex (void *);
66
67 struct mutex
68 {
69   pseudothread pth;     /* Pseudothread which is holding the lock, or null. */
70   wait_queue wq;        /* Queue of threads waiting to enter. */
71   pool pool;            /* Subpool of pth pool which holds the lock. If the
72                          * thread exits without releasing the lock, then
73                          * the subpool is deleted, which causes our callback
74                          * to run, releasing the lock.
75                          */
76 };
77
78 mutex
79 new_mutex (pool p)
80 {
81   mutex m = pmalloc (p, sizeof *m);
82
83   m->pth = 0;
84   m->pool = 0;
85   m->wq = new_wait_queue (p);
86
87   /* The purpose of this cleanup is just to check that the mutex
88    * isn't released with threads in the critical section.
89    */
90   pool_register_cleanup_fn (p, _delete_mutex, m);
91
92   return m;
93 }
94
95 static void
96 _delete_mutex (void *vm)
97 {
98   mutex m = (mutex) vm;
99   assert (m->pth == 0);
100 }
101
102 /* This function is identical to MUTEX_ENTER, except that it does not
103  * block if the lock is held by another thread. The function
104  * returns TRUE if the lock was successfully acquired, or FALSE
105  * if another thread is holding it.
106  */
107 inline int
108 mutex_try_enter (mutex m)
109 {
110   if (m->pth == 0)
111     {
112       /* Create a subpool. If the thread exits early, then this subpool
113        * with be deleted implicitly. If, on the other hand, we release
114        * the lock in RWLOCK_LEAVE, then we will delete this pool
115        * explicitly. Either way, _DO_RELEASE will be called.
116        */
117       pool pool = new_subpool (pth_get_pool (current_pth));
118
119       /* Register _DO_RELEASE to run when the subpool is deleted. */
120       pool_register_cleanup_fn (pool, _do_release, m);
121
122       m->pth = current_pth;
123       m->pool = pool;
124       return 1;
125     }
126   else
127     return 0;
128 }
129
130 /* Enter a critical section. This function blocks until the lock is
131  * acquired.
132  */
133 void
134 mutex_enter (mutex m)
135 {
136   while (mutex_try_enter (m) == 0)
137     wq_sleep_on (m->wq);
138 }
139
140 /* Leave a critical section.
141  */
142 void
143 mutex_leave (mutex m)
144 {
145   assert (m->pth == current_pth);
146
147   /* Force _DO_RELEASE to run. */
148   delete_pool (m->pool);
149 }
150
151 static void
152 _do_release (void *vm)
153 {
154   mutex m = (mutex) vm;
155
156   m->pth = 0;
157   m->pool = 0;
158
159   /* Anyone waiting to enter? */
160   if (wq_nr_sleepers (m->wq) > 0) wq_wake_up_one (m->wq);
161 }
162
163 int
164 mutex_nr_sleepers (mutex m)
165 {
166   return wq_nr_sleepers (m->wq);
167 }