1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2 All Rights Reserved.
3
4 This software is provided AS-IS with no warranty, either express or
5 implied.
6
7 This software is distributed under license and may not be copied, modified
8 or distributed except as expressly authorized under the terms of that
9 license. Refer to licensing information at http://www.artifex.com/
10 or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11 San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
12 */
13
14 /* $Id: gp_psync.c 10559 2009-12-26 22:21:45Z alexcher $ */
15 /* POSIX pthreads threads / semaphore / monitor implementation */
16 #include "std.h"
17 #include "malloc_.h"
18 #include <pthread.h>
19 #include "gserror.h"
20 #include "gserrors.h"
21 #include "gpsync.h"
22
23 /*
24 * Thanks to Larry Jones <larry.jones@sdrc.com> for this revision of
25 * Aladdin's original code into a form that depends only on POSIX APIs.
26 */
27
28 /*
29 * Some old versions of the pthreads library define
30 * pthread_attr_setdetachstate as taking a Boolean rather than an enum.
31 * Compensate for this here.
32 */
33 #ifndef PTHREAD_CREATE_DETACHED
34 # define PTHREAD_CREATE_DETACHED 1
35 #endif
36
37 /* ------- Synchronization primitives -------- */
38
39 /* Semaphore supports wait/signal semantics */
40
41 typedef struct pt_semaphore_t {
42 int count;
43 pthread_mutex_t mutex;
44 pthread_cond_t cond;
45 } pt_semaphore_t;
46
47 uint
gp_semaphore_sizeof(void)48 gp_semaphore_sizeof(void)
49 {
50 return sizeof(pt_semaphore_t);
51 }
52
53 /*
54 * This procedure should really check errno and return something
55 * more informative....
56 */
57 #define SEM_ERROR_CODE(scode)\
58 (scode != 0 ? gs_note_error(gs_error_ioerror) : 0)
59
60 int
gp_semaphore_open(gp_semaphore * sema)61 gp_semaphore_open(gp_semaphore * sema)
62 {
63 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
64 int scode;
65
66 if (!sema)
67 return -1; /* semaphores are not movable */
68 sem->count = 0;
69 scode = pthread_mutex_init(&sem->mutex, NULL);
70 if (scode == 0)
71 scode = pthread_cond_init(&sem->cond, NULL);
72 return SEM_ERROR_CODE(scode);
73 }
74
75 int
gp_semaphore_close(gp_semaphore * sema)76 gp_semaphore_close(gp_semaphore * sema)
77 {
78 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
79 int scode, scode2;
80
81 scode = pthread_cond_destroy(&sem->cond);
82 scode2 = pthread_mutex_destroy(&sem->mutex);
83 if (scode == 0)
84 scode = scode2;
85 return SEM_ERROR_CODE(scode);
86 }
87
88 int
gp_semaphore_wait(gp_semaphore * sema)89 gp_semaphore_wait(gp_semaphore * sema)
90 {
91 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
92 int scode, scode2;
93
94 scode = pthread_mutex_lock(&sem->mutex);
95 if (scode != 0)
96 return SEM_ERROR_CODE(scode);
97 while (sem->count == 0) {
98 scode = pthread_cond_wait(&sem->cond, &sem->mutex);
99 if (scode != 0)
100 break;
101 }
102 if (scode == 0)
103 --sem->count;
104 scode2 = pthread_mutex_unlock(&sem->mutex);
105 if (scode == 0)
106 scode = scode2;
107 return SEM_ERROR_CODE(scode);
108 }
109
110 int
gp_semaphore_signal(gp_semaphore * sema)111 gp_semaphore_signal(gp_semaphore * sema)
112 {
113 pt_semaphore_t * const sem = (pt_semaphore_t *)sema;
114 int scode, scode2;
115
116 scode = pthread_mutex_lock(&sem->mutex);
117 if (scode != 0)
118 return SEM_ERROR_CODE(scode);
119 if (sem->count++ == 0)
120 scode = pthread_cond_signal(&sem->cond);
121 scode2 = pthread_mutex_unlock(&sem->mutex);
122 if (scode == 0)
123 scode = scode2;
124 return SEM_ERROR_CODE(scode);
125 }
126
127
128 /* Monitor supports enter/leave semantics */
129
130 /*
131 * We need PTHREAD_MUTEX_RECURSIVE behavior, but this isn't totally portable
132 * so we implement it in a more portable fashion, keeping track of the
133 * owner thread using 'pthread_self()'
134 */
135 typedef struct gp_pthread_recursive_s {
136 pthread_mutex_t mutex; /* actual mutex */
137 pthread_t self_id; /* owner */
138 } gp_pthread_recursive_t;
139
140 uint
gp_monitor_sizeof(void)141 gp_monitor_sizeof(void)
142 {
143 return sizeof(gp_pthread_recursive_t);
144 }
145
146 int
gp_monitor_open(gp_monitor * mona)147 gp_monitor_open(gp_monitor * mona)
148 {
149 pthread_mutex_t *mon;
150 int scode;
151
152 if (!mona)
153 return -1; /* monitors are not movable */
154 mon = &((gp_pthread_recursive_t *)mona)->mutex;
155 ((gp_pthread_recursive_t *)mona)->self_id = 0; /* Not valid unless mutex is locked */
156 scode = pthread_mutex_init(mon, NULL);
157 return SEM_ERROR_CODE(scode);
158 }
159
160 int
gp_monitor_close(gp_monitor * mona)161 gp_monitor_close(gp_monitor * mona)
162 {
163 pthread_mutex_t * const mon = &((gp_pthread_recursive_t *)mona)->mutex;
164 int scode;
165
166 scode = pthread_mutex_destroy(mon);
167 return SEM_ERROR_CODE(scode);
168 }
169
170 int
gp_monitor_enter(gp_monitor * mona)171 gp_monitor_enter(gp_monitor * mona)
172 {
173 pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
174 int scode;
175
176 if ((scode = pthread_mutex_trylock(mon)) == 0) {
177 ((gp_pthread_recursive_t *)mona)->self_id = pthread_self();
178 return SEM_ERROR_CODE(scode);
179 } else {
180 if (pthread_equal(pthread_self(),((gp_pthread_recursive_t *)mona)->self_id))
181 return 0;
182 else {
183 /* we were not the owner, wait */
184 scode = pthread_mutex_lock(mon);
185 ((gp_pthread_recursive_t *)mona)->self_id = pthread_self();
186 return SEM_ERROR_CODE(scode);
187 }
188 }
189 }
190
191 int
gp_monitor_leave(gp_monitor * mona)192 gp_monitor_leave(gp_monitor * mona)
193 {
194 pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
195 int scode;
196
197 scode = pthread_mutex_unlock(mon);
198 ((gp_pthread_recursive_t *)mona)->self_id = 0; /* Not valid unless mutex is locked */
199 return SEM_ERROR_CODE(scode);
200 }
201
202
203 /* --------- Thread primitives ---------- */
204
205 /*
206 * In order to deal with the type mismatch between our thread API, where
207 * the starting procedure returns void, and the API defined by pthreads,
208 * where the procedure returns void *, we need to create a wrapper
209 * closure.
210 */
211 typedef struct gp_thread_creation_closure_s {
212 gp_thread_creation_callback_t proc; /* actual start procedure */
213 void *proc_data; /* closure data for proc */
214 } gp_thread_creation_closure_t;
215
216 /* Wrapper procedure called to start the new thread. */
217 static void *
gp_thread_begin_wrapper(void * thread_data)218 gp_thread_begin_wrapper(void *thread_data /* gp_thread_creation_closure_t * */)
219 {
220 gp_thread_creation_closure_t closure;
221
222 closure = *(gp_thread_creation_closure_t *)thread_data;
223 free(thread_data);
224 DISCARD(closure.proc(closure.proc_data));
225 return NULL; /* return value is ignored */
226 }
227
228 int
gp_create_thread(gp_thread_creation_callback_t proc,void * proc_data)229 gp_create_thread(gp_thread_creation_callback_t proc, void *proc_data)
230 {
231 gp_thread_creation_closure_t *closure =
232 (gp_thread_creation_closure_t *)malloc(sizeof(*closure));
233 pthread_t ignore_thread;
234 pthread_attr_t attr;
235 int code;
236
237 if (!closure)
238 return_error(gs_error_VMerror);
239 closure->proc = proc;
240 closure->proc_data = proc_data;
241 pthread_attr_init(&attr);
242 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
243 code = pthread_create(&ignore_thread, &attr, gp_thread_begin_wrapper,
244 closure);
245 if (code) {
246 free(closure);
247 return_error(gs_error_ioerror);
248 }
249 return 0;
250 }
251