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