1 /* Copyright (C) 2001-2012 Artifex Software, Inc.
2 All Rights Reserved.
3
4 This software is provided AS-IS with no warranty, either express or
5 implied.
6
7 This software is distributed under license and may not be copied,
8 modified or distributed except as expressly authorized under the terms
9 of the license contained in the file LICENSE in this distribution.
10
11 Refer to licensing information at http://www.artifex.com or contact
12 Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
13 CA 94903, U.S.A., +1(415)492-9861, for further information.
14 */
15
16
17 /* MS Windows (Win32) thread / semaphore / monitor implementation */
18 /* original multi-threading code by John Desrosiers */
19 #include "malloc_.h"
20 #include "gserrors.h"
21 #include "gpsync.h"
22 #include "windows_.h"
23 #include <process.h>
24
25 /* It seems that both Borland and Watcom *should* be able to cope with the
26 * new style threading using _beginthreadex/_endthreadex. I am unable to test
27 * this properly however, and the tests I have done lead me to believe it
28 * may be problematic. Given that these platforms are not a support priority,
29 * we leave the code falling back to just using the old style code (which
30 * presumably works).
31 *
32 * The upshot of this should be that we continue to work exactly as before.
33 *
34 * To try using the new functions, simply omit the definition of
35 * FALLBACK_TO_OLD_THREADING_FUNCTIONS below. If you find this works for
36 * either Borland or Watcom, then please let us know and we can make the
37 * change permanently.
38 */
39 #if defined(__BORLANDC__)
40 #define FALLBACK_TO_OLD_THREADING_FUNCTIONS
41 #elif defined(__WATCOMC__)
42 #define FALLBACK_TO_OLD_THREADING_FUNCTIONS
43 #endif
44
45 /* ------- Synchronization primitives -------- */
46
47 /* Semaphore supports wait/signal semantics */
48
49 typedef struct win32_semaphore_s {
50 HANDLE handle; /* returned from CreateSemaphore */
51 } win32_semaphore;
52
53 uint
gp_semaphore_sizeof(void)54 gp_semaphore_sizeof(void)
55 {
56 return sizeof(win32_semaphore);
57 }
58
59 int /* if sema <> 0 rets -ve error, 0 ok; if sema == 0, 0 movable, 1 fixed */
gp_semaphore_open(gp_semaphore * sema)60 gp_semaphore_open(
61 gp_semaphore * sema /* create semaphore here */
62 )
63 {
64 win32_semaphore *const winSema = (win32_semaphore *)sema;
65
66 if (winSema) {
67 winSema->handle = CreateSemaphore(NULL, 0, max_int, NULL);
68 return
69 (winSema->handle != NULL ? 0 :
70 gs_note_error(gs_error_unknownerror));
71 } else
72 return 0; /* Win32 semaphores handles may be moved */
73 }
74
75 int
gp_semaphore_close(gp_semaphore * sema)76 gp_semaphore_close(
77 gp_semaphore * sema /* semaphore to affect */
78 )
79 {
80 win32_semaphore *const winSema = (win32_semaphore *)sema;
81
82 if (winSema->handle != NULL)
83 CloseHandle(winSema->handle);
84 winSema->handle = NULL;
85 return 0;
86 }
87
88 int /* rets 0 ok, -ve error */
gp_semaphore_wait(gp_semaphore * sema)89 gp_semaphore_wait(
90 gp_semaphore * sema /* semaphore to affect */
91 )
92 {
93 win32_semaphore *const winSema = (win32_semaphore *)sema;
94
95 return
96 (WaitForSingleObject(winSema->handle, INFINITE) == WAIT_OBJECT_0
97 ? 0 : gs_error_unknownerror);
98 }
99
100 int /* rets 0 ok, -ve error */
gp_semaphore_signal(gp_semaphore * sema)101 gp_semaphore_signal(
102 gp_semaphore * sema /* semaphore to affect */
103 )
104 {
105 win32_semaphore *const winSema = (win32_semaphore *)sema;
106
107 return
108 (ReleaseSemaphore(winSema->handle, 1, NULL) ? 0 :
109 gs_error_unknownerror);
110 }
111
112 /* Monitor supports enter/leave semantics */
113
114 typedef struct win32_monitor_s {
115 CRITICAL_SECTION lock; /* critical section lock */
116 } win32_monitor;
117
118 uint
gp_monitor_sizeof(void)119 gp_monitor_sizeof(void)
120 {
121 return sizeof(win32_monitor);
122 }
123
124 int /* if sema <> 0 rets -ve error, 0 ok; if sema == 0, 0 movable, 1 fixed */
gp_monitor_open(gp_monitor * mon)125 gp_monitor_open(
126 gp_monitor * mon /* create monitor here */
127 )
128 {
129 win32_monitor *const winMon = (win32_monitor *)mon;
130
131 if (mon) {
132 InitializeCriticalSection(&winMon->lock); /* returns no status */
133 return 0;
134 } else
135 return 1; /* Win32 critical sections mutsn't be moved */
136 }
137
138 int
gp_monitor_close(gp_monitor * mon)139 gp_monitor_close(
140 gp_monitor * mon /* monitor to affect */
141 )
142 {
143 win32_monitor *const winMon = (win32_monitor *)mon;
144
145 DeleteCriticalSection(&winMon->lock); /* rets no status */
146 return 0;
147 }
148
149 int /* rets 0 ok, -ve error */
gp_monitor_enter(gp_monitor * mon)150 gp_monitor_enter(
151 gp_monitor * mon /* monitor to affect */
152 )
153 {
154 win32_monitor *const winMon = (win32_monitor *)mon;
155
156 EnterCriticalSection(&winMon->lock); /* rets no status */
157 return 0;
158 }
159
160 int /* rets 0 ok, -ve error */
gp_monitor_leave(gp_monitor * mon)161 gp_monitor_leave(
162 gp_monitor * mon /* monitor to affect */
163 )
164 {
165 win32_monitor *const winMon = (win32_monitor *)mon;
166
167 LeaveCriticalSection(&winMon->lock); /* rets no status */
168 return 0;
169 }
170
171 /* --------- Thread primitives ---------- */
172
173 typedef struct gp_thread_creation_closure_s {
174 gp_thread_creation_callback_t function; /* function to start */
175 void *data; /* magic data to pass to thread */
176 } gp_thread_creation_closure;
177
178 /* Origin of new threads started by gp_create_thread */
179 static void
gp_thread_begin_wrapper(void * thread_data)180 gp_thread_begin_wrapper(
181 void *thread_data /* gp_thread_creation_closure passed as magic data */
182 )
183 {
184 gp_thread_creation_closure closure;
185
186 closure = *(gp_thread_creation_closure *)thread_data;
187 free(thread_data);
188 (*closure.function)(closure.data);
189 _endthread();
190 }
191
192 /* Call a function on a brand new thread */
193 int /* 0 ok, -ve error */
gp_create_thread(gp_thread_creation_callback_t function,void * data)194 gp_create_thread(
195 gp_thread_creation_callback_t function, /* function to start */
196 void *data /* magic data to pass to thread fn */
197 )
198 {
199 /* Create the magic closure that thread_wrapper gets passed */
200 gp_thread_creation_closure *closure =
201 (gp_thread_creation_closure *)malloc(sizeof(*closure));
202
203 if (!closure)
204 return gs_error_VMerror;
205 closure->function = function;
206 closure->data = data;
207
208 /*
209 * Start thread_wrapper. The Watcom _beginthread returns (int)(-1) if
210 * the call fails. The Microsoft _beginthread returns -1 (according to
211 * the doc, even though the return type is "unsigned long" !!!) if the
212 * call fails; we aren't sure what the Borland _beginthread returns.
213 * The hack with ~ avoids a source code commitment as to whether the
214 * return type is [u]int or [u]long.
215 *
216 * BEGIN_THREAD is a macro (defined in windows_.h) because _beginthread
217 * takes different arguments in Watcom C.
218 */
219 if (~BEGIN_THREAD(gp_thread_begin_wrapper, 128*1024, closure) != 0)
220 {
221 free(closure);
222 return 0;
223 }
224 return_error(gs_error_unknownerror);
225 }
226
227 /* gp_thread_creation_closure passed as magic data */
228 static unsigned __stdcall
gp_thread_start_wrapper(void * thread_data)229 gp_thread_start_wrapper(void *thread_data)
230 {
231 gp_thread_creation_closure closure;
232
233 closure = *(gp_thread_creation_closure *)thread_data;
234 free(thread_data);
235 (*closure.function)(closure.data);
236 _endthreadex(0);
237 return 0;
238 }
239
gp_thread_start(gp_thread_creation_callback_t function,void * data,gp_thread_id * thread)240 int gp_thread_start(gp_thread_creation_callback_t function,
241 void *data, gp_thread_id *thread)
242 {
243 #ifdef FALLBACK_TO_OLD_THREADING_FUNCTIONS
244 *thread = (gp_thread_id)1;
245 return gp_create_thread(function, data);
246 #else
247 /* Create the magic closure that thread_wrapper gets passed */
248 HANDLE hThread;
249 unsigned threadID;
250 gp_thread_creation_closure *closure =
251 (gp_thread_creation_closure *)malloc(sizeof(*closure));
252
253 if (!closure)
254 return gs_error_VMerror;
255 closure->function = function;
256 closure->data = data;
257 hThread = (HANDLE)_beginthreadex(NULL, 0, &gp_thread_start_wrapper,
258 closure, 0, &threadID);
259 if (hThread == (HANDLE)0)
260 {
261 free(closure);
262 *thread = NULL;
263 return_error(gs_error_unknownerror);
264 }
265 *thread = (gp_thread_id)hThread;
266 return 0;
267 #endif
268 }
269
gp_thread_finish(gp_thread_id thread)270 void gp_thread_finish(gp_thread_id thread)
271 {
272 #ifndef FALLBACK_TO_OLD_THREADING_FUNCTIONS
273 if (thread == NULL)
274 return;
275 WaitForSingleObject((HANDLE)thread, INFINITE);
276 CloseHandle((HANDLE)thread);
277 #endif
278 }
279