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