1.0.5.14: make PURIFY a no-op on gencgc
[sbcl.git] / src / runtime / pthread-lutex.c
1 /* An approximation of Linux futexes implemented using pthread mutexes
2  * and pthread condition variables.
3  */
4
5 /*
6  * This software is part of the SBCL system. See the README file for
7  * more information.
8  *
9  * The software is in the public domain and is provided with
10  * absolutely no warranty. See the COPYING and CREDITS files for more
11  * information.
12  */
13
14 #include "sbcl.h"
15
16 #if defined(LISP_FEATURE_SB_THREAD) && defined(LISP_FEATURE_SB_LUTEX)
17
18 #include <errno.h>
19 #include <stdlib.h>
20
21 #include "runtime.h"
22 #include "arch.h"
23 #include "target-arch-os.h"
24 #include "os.h"
25
26 #include "genesis/lutex.h"
27
28 typedef unsigned long tagged_lutex_t;
29
30 #if 1
31 # define lutex_assert(ex)                                              \
32 do {                                                                   \
33     if (!(ex)) lutex_abort();                                          \
34 } while (0)
35 # define lutex_assert_verbose(ex, fmt, ...)                            \
36 do {                                                                   \
37     if (!(ex)) {                                                       \
38         fprintf(stderr, fmt, ## __VA_ARGS__);                          \
39         lutex_abort();                                                 \
40     }                                                                  \
41 } while (0)
42 #else
43 # define lutex_assert(ex)
44 # define lutex_assert_verbose(ex, fmt, ...)
45 #endif
46
47 #define lutex_abort()                                                  \
48   lose("Lutex assertion failure, file \"%s\", line %d\n", __FILE__, __LINE__)
49
50
51 pthread_mutex_t lutex_register_lock = PTHREAD_MUTEX_INITIALIZER;
52
53 int
54 lutex_init (tagged_lutex_t tagged_lutex)
55 {
56     int ret;
57     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
58
59     lutex->mutexattr = malloc(sizeof(pthread_mutexattr_t));
60     lutex_assert(lutex->mutexattr != 0);
61
62     ret = pthread_mutexattr_init(lutex->mutexattr);
63     lutex_assert(ret == 0);
64
65     /* The default type of mutex is implementation dependent.
66      * We use PTHREAD_MUTEX_ERRORCHECK so that locking on mutexes
67      * locked by the same thread does not cause deadlocks. */
68     /* FIXME: pthread_mutexattr_settype is available on SUSv2 level
69      * implementations.  Can be used without checking? */
70     ret = pthread_mutexattr_settype(lutex->mutexattr,
71                                     PTHREAD_MUTEX_ERRORCHECK);
72     lutex_assert(ret == 0);
73
74     lutex->mutex = malloc(sizeof(pthread_mutex_t));
75     lutex_assert(lutex->mutex != 0);
76
77     ret = pthread_mutex_init(lutex->mutex, lutex->mutexattr);
78     lutex_assert(ret == 0);
79
80     lutex->condition_variable = malloc(sizeof(pthread_cond_t));
81     lutex_assert(lutex->condition_variable != 0);
82
83     ret = pthread_cond_init(lutex->condition_variable, NULL);
84     lutex_assert(ret == 0);
85
86     ret = thread_mutex_lock(&lutex_register_lock); lutex_assert(ret == 0);
87
88     gencgc_register_lutex(lutex);
89
90     ret = thread_mutex_unlock(&lutex_register_lock); lutex_assert(ret == 0);
91
92     return ret;
93 }
94
95 int
96 lutex_wait (tagged_lutex_t tagged_queue_lutex, tagged_lutex_t tagged_mutex_lutex)
97 {
98     int ret;
99     struct lutex *queue_lutex = (struct lutex*) native_pointer(tagged_queue_lutex);
100     struct lutex *mutex_lutex = (struct lutex*) native_pointer(tagged_mutex_lutex);
101
102     ret = pthread_cond_wait(queue_lutex->condition_variable, mutex_lutex->mutex);
103     lutex_assert(ret == 0);
104
105     return ret;
106 }
107
108 int
109 lutex_wake (tagged_lutex_t tagged_lutex, int n)
110 {
111     int ret = 0;
112     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
113
114     /* The lisp-side code passes N=2**29-1 for a broadcast. */
115     if (n >= ((1 << 29) - 1)) {
116         /* CONDITION-BROADCAST */
117         ret = pthread_cond_broadcast(lutex->condition_variable);
118         lutex_assert(ret == 0);
119     } else{
120         /* We're holding the condition variable mutex, so a thread
121          * we're waking can't re-enter the wait between to calls to
122          * pthread_cond_signal. Thus we'll wake N different threads,
123          * instead of the same thread N times.
124          */
125         while (n--) {
126             ret = pthread_cond_signal(lutex->condition_variable);
127             lutex_assert(ret == 0);
128         }
129     }
130
131     return ret;
132 }
133
134 int
135 lutex_lock (tagged_lutex_t tagged_lutex)
136 {
137     int ret = 0;
138     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
139
140     ret = thread_mutex_lock(lutex->mutex);
141     /* The mutex is locked by the same thread.
142      *
143      * FIXME: Usually when POSIX says that "an error value is returned"
144      * it actually refers to errno...
145      */
146     if (ret == EDEADLK)
147         return ret;
148     lutex_assert(ret == 0);
149
150     return ret;
151 }
152
153 int
154 lutex_trylock (tagged_lutex_t tagged_lutex)
155 {
156     int ret = 0;
157     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
158
159     ret = pthread_mutex_trylock(lutex->mutex);
160     /* The mutex is locked */
161     if (ret == EDEADLK || ret == EBUSY)
162         return ret;
163     lutex_assert(ret == 0);
164
165     return ret;
166 }
167
168 int
169 lutex_unlock (tagged_lutex_t tagged_lutex)
170 {
171     int ret = 0;
172     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
173
174     ret = thread_mutex_unlock(lutex->mutex);
175     /* Unlocking unlocked mutex would occur as:
176      * (with-mutex (mutex) (cond-wait cond mutex)) */
177     if (ret == EPERM)
178         return ret;
179     lutex_assert(ret == 0);
180
181     return ret;
182 }
183
184 int
185 lutex_destroy (tagged_lutex_t tagged_lutex)
186 {
187     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
188
189     if (lutex->condition_variable) {
190         pthread_cond_destroy(lutex->condition_variable);
191         free(lutex->condition_variable);
192         lutex->condition_variable = NULL;
193     }
194
195     if (lutex->mutex) {
196         pthread_mutex_destroy(lutex->mutex);
197         free(lutex->mutex);
198         lutex->mutex = NULL;
199     }
200
201     if (lutex->mutexattr) {
202         pthread_mutexattr_destroy(lutex->mutexattr);
203         free(lutex->mutexattr);
204         lutex->mutexattr = NULL;
205     }
206
207     return 0;
208 }
209 #endif