1 /* Copyright (C) 1999, 2000 artofcode LLC. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify it
4 under the terms of the GNU General Public License as published by the
5 Free Software Foundation; either version 2 of the License, or (at your
6 option) any later version.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16
17 */
18
19 /*$Id: gp_psync.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */
20 /* POSIX pthreads threads / semaphore / monitor implementation */
21 #include "std.h"
22 #include "malloc_.h"
23 #include <pthread.h>
24 #include "gserror.h"
25 #include "gserrors.h"
26 #include "gpsync.h"
27
28 /*
29 * Thanks to Larry Jones <larry.jones@sdrc.com> for this revision of
30 * Aladdin's original code into a form that depends only on POSIX APIs.
31 */
32
33 /*
34 * Some old versions of the pthreads library define
35 * pthread_attr_setdetachstate as taking a Boolean rather than an enum.
36 * Compensate for this here.
37 */
38 #ifndef PTHREAD_CREATE_DETACHED
39 # define PTHREAD_CREATE_DETACHED 1
40 #endif
41
42 /* ------- Synchronization primitives -------- */
43
44 /* Semaphore supports wait/signal semantics */
45
46 typedef struct pt_semaphore_t {
47 int count;
48 pthread_mutex_t mutex;
49 pthread_cond_t cond;
50 } pt_semaphore_t;
51
52 uint
gp_semaphore_sizeof(void)53 gp_semaphore_sizeof(void)
54 {
55 return sizeof(pt_semaphore_t);
56 }
57
58 /*
59 * This procedure should really check errno and return something
60 * more informative....
61 */
62 #define SEM_ERROR_CODE(scode)\
63 (scode != 0 ? gs_note_error(gs_error_ioerror) : 0)
64
65 int
gp_semaphore_open(gp_semaphore * sema)66 gp_semaphore_open(gp_semaphore * sema)
67 {
68 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
69 int scode;
70
71 if (!sema)
72 return -1; /* semaphores are not movable */
73 sem->count = 0;
74 scode = pthread_mutex_init(&sem->mutex, NULL);
75 if (scode == 0)
76 scode = pthread_cond_init(&sem->cond, NULL);
77 return SEM_ERROR_CODE(scode);
78 }
79
80 int
gp_semaphore_close(gp_semaphore * sema)81 gp_semaphore_close(gp_semaphore * sema)
82 {
83 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
84 int scode, scode2;
85
86 scode = pthread_cond_destroy(&sem->cond);
87 scode2 = pthread_mutex_destroy(&sem->mutex);
88 if (scode == 0)
89 scode = scode2;
90 return SEM_ERROR_CODE(scode);
91 }
92
93 int
gp_semaphore_wait(gp_semaphore * sema)94 gp_semaphore_wait(gp_semaphore * sema)
95 {
96 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
97 int scode, scode2;
98
99 scode = pthread_mutex_lock(&sem->mutex);
100 if (scode != 0)
101 return SEM_ERROR_CODE(scode);
102 while (sem->count == 0) {
103 scode = pthread_cond_wait(&sem->cond, &sem->mutex);
104 if (scode != 0)
105 break;
106 }
107 if (scode == 0)
108 --sem->count;
109 scode2 = pthread_mutex_unlock(&sem->mutex);
110 if (scode == 0)
111 scode = scode2;
112 return SEM_ERROR_CODE(scode);
113 }
114
115 int
gp_semaphore_signal(gp_semaphore * sema)116 gp_semaphore_signal(gp_semaphore * sema)
117 {
118 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
119 int scode, scode2;
120
121 scode = pthread_mutex_lock(&sem->mutex);
122 if (scode != 0)
123 return SEM_ERROR_CODE(scode);
124 if (sem->count++ == 0)
125 scode = pthread_cond_signal(&sem->cond);
126 scode2 = pthread_mutex_unlock(&sem->mutex);
127 if (scode == 0)
128 scode = scode2;
129 return SEM_ERROR_CODE(scode);
130 }
131
132
133 /* Monitor supports enter/leave semantics */
134
135 uint
gp_monitor_sizeof(void)136 gp_monitor_sizeof(void)
137 {
138 return sizeof(pthread_mutex_t);
139 }
140
141 int
gp_monitor_open(gp_monitor * mona)142 gp_monitor_open(gp_monitor * mona)
143 {
144 pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
145 int scode;
146
147 if (!mona)
148 return -1; /* monitors are not movable */
149 scode = pthread_mutex_init(mon, NULL);
150 return SEM_ERROR_CODE(scode);
151 }
152
153 int
gp_monitor_close(gp_monitor * mona)154 gp_monitor_close(gp_monitor * mona)
155 {
156 pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
157 int scode;
158
159 scode = pthread_mutex_destroy(mon);
160 return SEM_ERROR_CODE(scode);
161 }
162
163 int
gp_monitor_enter(gp_monitor * mona)164 gp_monitor_enter(gp_monitor * mona)
165 {
166 pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
167 int scode;
168
169 scode = pthread_mutex_lock(mon);
170 return SEM_ERROR_CODE(scode);
171 }
172
173 int
gp_monitor_leave(gp_monitor * mona)174 gp_monitor_leave(gp_monitor * mona)
175 {
176 pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
177 int scode;
178
179 scode = pthread_mutex_unlock(mon);
180 return SEM_ERROR_CODE(scode);
181 }
182
183
184 /* --------- Thread primitives ---------- */
185
186 /*
187 * In order to deal with the type mismatch between our thread API, where
188 * the starting procedure returns void, and the API defined by pthreads,
189 * where the procedure returns void *, we need to create a wrapper
190 * closure.
191 */
192 typedef struct gp_thread_creation_closure_s {
193 gp_thread_creation_callback_t proc; /* actual start procedure */
194 void *proc_data; /* closure data for proc */
195 } gp_thread_creation_closure_t;
196
197 /* Wrapper procedure called to start the new thread. */
198 private void *
gp_thread_begin_wrapper(void * thread_data)199 gp_thread_begin_wrapper(void *thread_data /* gp_thread_creation_closure_t * */)
200 {
201 gp_thread_creation_closure_t closure;
202
203 closure = *(gp_thread_creation_closure_t *)thread_data;
204 free(thread_data);
205 DISCARD(closure.proc(closure.proc_data));
206 return NULL; /* return value is ignored */
207 }
208
209 int
gp_create_thread(gp_thread_creation_callback_t proc,void * proc_data)210 gp_create_thread(gp_thread_creation_callback_t proc, void *proc_data)
211 {
212 gp_thread_creation_closure_t *closure =
213 (gp_thread_creation_closure_t *)malloc(sizeof(*closure));
214 pthread_t ignore_thread;
215 pthread_attr_t attr;
216 int code;
217
218 if (!closure)
219 return_error(gs_error_VMerror);
220 closure->proc = proc;
221 closure->proc_data = proc_data;
222 pthread_attr_init(&attr);
223 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
224 code = pthread_create(&ignore_thread, &attr, gp_thread_begin_wrapper,
225 closure);
226 if (code) {
227 free(closure);
228 return_error(gs_error_ioerror);
229 }
230 return 0;
231 }
232