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