1 /* Copyright (C) 1999, 2000 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gp_psync.c,v 1.2.6.1.2.1 2003/01/17 00:49:02 giles 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
gp_semaphore_sizeof(void)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
gp_semaphore_open(gp_semaphore * sema)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
gp_semaphore_close(gp_semaphore * sema)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
gp_semaphore_wait(gp_semaphore * sema)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
gp_semaphore_signal(gp_semaphore * sema)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
gp_monitor_sizeof(void)136 gp_monitor_sizeof(void)
137 {
138     return sizeof(pthread_mutex_t);
139 }
140 
141 int
gp_monitor_open(gp_monitor * mona)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
gp_monitor_close(gp_monitor * mona)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
gp_monitor_enter(gp_monitor * mona)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
gp_monitor_leave(gp_monitor * mona)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 *
gp_thread_begin_wrapper(void * thread_data)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
gp_create_thread(gp_thread_creation_callback_t proc,void * proc_data)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