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