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