xref: /plan9/sys/src/cmd/gs/src/gp_psync.c (revision 3ff48bf5ed603850fcd251ddf13025d23d693782)
1 /* Copyright (C) 1999, 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gp_psync.c,v 1.2 2000/09/19 19:00:24 lpd 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
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
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
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
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
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
136 gp_monitor_sizeof(void)
137 {
138     return sizeof(pthread_mutex_t);
139 }
140 
141 int
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
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
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
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 *
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
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