0.9.18.62:
[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     if (ret == EDEADLK)
143         return ret;
144     lutex_assert(ret == 0);
145
146     return ret;
147 }
148
149 int
150 lutex_trylock (tagged_lutex_t tagged_lutex)
151 {
152     int ret = 0;
153     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
154
155     ret = pthread_mutex_trylock(lutex->mutex);
156     /* The mutex is locked */
157     if (ret == EDEADLK || ret == EBUSY)
158         return ret;
159     lutex_assert(ret == 0);
160
161     return ret;
162 }
163
164 int
165 lutex_unlock (tagged_lutex_t tagged_lutex)
166 {
167     int ret = 0;
168     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
169
170     ret = thread_mutex_unlock(lutex->mutex);
171     /* Unlocking unlocked mutex would occur as:
172      * (with-mutex (mutex) (cond-wait cond mutex)) */
173     if (ret == EPERM)
174         return ret;
175     lutex_assert(ret == 0);
176
177     return ret;
178 }
179
180 int
181 lutex_destroy (tagged_lutex_t tagged_lutex)
182 {
183     struct lutex *lutex = (struct lutex*) native_pointer(tagged_lutex);
184
185     if (lutex->condition_variable) {
186         pthread_cond_destroy(lutex->condition_variable);
187         free(lutex->condition_variable);
188         lutex->condition_variable = NULL;
189     }
190
191     if (lutex->mutex) {
192         pthread_mutex_destroy(lutex->mutex);
193         free(lutex->mutex);
194         lutex->mutex = NULL;
195     }
196
197     if (lutex->mutexattr) {
198         pthread_mutexattr_destroy(lutex->mutexattr);
199         free(lutex->mutexattr);
200         lutex->mutexattr = NULL;
201     }
202
203     return 0;
204 }
205 #endif