]>
Commit | Line | Data |
---|---|---|
878885b4 TT |
1 | // win32-threads.cc - interface between libjava and Win32 threads. |
2 | ||
85ea93b3 | 3 | /* Copyright (C) 1998, 1999 Free Software Foundation, Inc. |
878885b4 TT |
4 | |
5 | This file is part of libgcj. | |
6 | ||
7 | This software is copyrighted work licensed under the terms of the | |
8 | Libgcj License. Please consult the file "LIBGCJ_LICENSE" for | |
9 | details. */ | |
10 | ||
11 | #include <config.h> | |
12 | ||
13 | // If we're using the Boehm GC, then we need to override some of the | |
14 | // thread primitives. This is fairly gross. | |
15 | #ifdef HAVE_BOEHM_GC | |
16 | extern "C" | |
17 | { | |
878885b4 | 18 | #include <gc.h> |
1d3efb36 AM |
19 | // <windows.h> #define's STRICT, which conflicts with Modifier.h |
20 | #undef STRICT | |
878885b4 TT |
21 | }; |
22 | #endif /* HAVE_BOEHM_GC */ | |
23 | ||
24 | #include <gcj/cni.h> | |
25 | #include <jvm.h> | |
26 | #include <java/lang/Thread.h> | |
27 | #include <java/lang/System.h> | |
28 | ||
29 | #include <errno.h> | |
30 | ||
31 | #ifndef ETIMEDOUT | |
32 | #define ETIMEDOUT 116 | |
33 | #endif | |
34 | ||
35 | // This is used to implement thread startup. | |
36 | struct starter | |
37 | { | |
38 | _Jv_ThreadStartFunc *method; | |
878885b4 TT |
39 | _Jv_Thread_t *data; |
40 | }; | |
41 | ||
42 | // Controls access to the variable below | |
43 | static HANDLE daemon_mutex; | |
44 | static HANDLE daemon_cond; | |
45 | // Number of non-daemon threads - _Jv_ThreadWait returns when this is 0 | |
46 | static int non_daemon_count; | |
47 | ||
48 | // TLS key get Java object representing the thread | |
49 | DWORD _Jv_ThreadKey; | |
50 | // TLS key to get _Jv_Thread_t* representing the thread | |
51 | DWORD _Jv_ThreadDataKey; | |
52 | ||
53 | // | |
54 | // These are the flags that can appear in _Jv_Thread_t. | |
55 | // | |
56 | ||
57 | // Thread started. | |
58 | #define FLAG_START 0x01 | |
59 | // Thread is daemon. | |
60 | #define FLAG_DAEMON 0x02 | |
61 | ||
62 | // | |
63 | // Condition variables. | |
64 | // | |
65 | ||
a70cb11b AM |
66 | // we do lazy creation of Events since CreateEvent() is insanely |
67 | // expensive, and because the rest of libgcj will call _Jv_CondInit | |
68 | // when only a mutex is needed. | |
69 | ||
70 | inline void | |
71 | ensure_condvar_initialized(_Jv_ConditionVariable_t *cv) | |
72 | { | |
73 | if (cv->ev[0] == 0) { | |
74 | cv->ev[0] = CreateEvent (NULL, 0, 0, NULL); | |
75 | if (cv->ev[0] == 0) JvFail("CreateEvent() failed"); | |
76 | cv->ev[1] = CreateEvent (NULL, 1, 0, NULL); | |
77 | if (cv->ev[1] == 0) JvFail("CreateEvent() failed"); | |
78 | } | |
79 | } | |
80 | ||
81 | // Reimplementation of the general algorithm described at | |
82 | // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html (isomorphic to | |
83 | // 3.2, not a cut-and-paste). | |
84 | ||
878885b4 | 85 | int |
a70cb11b | 86 | _Jv_CondWait(_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint nanos) |
878885b4 | 87 | { |
878885b4 | 88 | |
a70cb11b AM |
89 | EnterCriticalSection(&cv->count_mutex); |
90 | ensure_condvar_initialized(cv); | |
91 | cv->blocked_count++; | |
92 | LeaveCriticalSection(&cv->count_mutex); | |
93 | ||
94 | DWORD time; | |
95 | if ((millis == 0) && (nanos > 0)) time = 1; | |
96 | else if (millis == 0) time = INFINITE; | |
97 | else time = millis; | |
b834f1fa | 98 | |
878885b4 TT |
99 | _Jv_MutexUnlock (mu); |
100 | ||
a70cb11b AM |
101 | DWORD rval = WaitForMultipleObjects (2, &(cv->ev[0]), 0, time); |
102 | ||
103 | EnterCriticalSection(&cv->count_mutex); | |
104 | cv->blocked_count--; | |
105 | // If we were unblocked by the second event (the broadcast one) and nobody is | |
106 | // left, then reset the signal. | |
107 | int last_waiter = rval == WAIT_OBJECT_0 + 1 && cv->blocked_count == 0; | |
108 | LeaveCriticalSection(&cv->count_mutex); | |
109 | ||
110 | if (last_waiter) ResetEvent(&cv->ev[1]); | |
878885b4 | 111 | |
878885b4 TT |
112 | _Jv_MutexLock (mu); |
113 | ||
a70cb11b AM |
114 | if (rval == WAIT_FAILED) return GetLastError(); |
115 | else if (rval == WAIT_TIMEOUT) return ETIMEDOUT; | |
116 | else return 0; | |
878885b4 TT |
117 | } |
118 | ||
a70cb11b AM |
119 | void |
120 | _Jv_CondInit (_Jv_ConditionVariable_t *cv) | |
121 | { | |
122 | // we do lazy creation of Events since CreateEvent() is insanely expensive | |
123 | cv->ev[0] = 0; | |
124 | InitializeCriticalSection(&cv->count_mutex); | |
125 | cv->blocked_count = 0; | |
126 | } | |
127 | ||
128 | void | |
129 | _Jv_CondDestroy (_Jv_ConditionVariable_t *cv) | |
130 | { | |
131 | if (cv->ev[0] != 0) CloseHandle(cv->ev[0]); | |
132 | cv = NULL; | |
133 | } | |
878885b4 TT |
134 | |
135 | int | |
a70cb11b | 136 | _Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *) |
878885b4 | 137 | { |
a70cb11b AM |
138 | EnterCriticalSection(&cv->count_mutex); |
139 | ensure_condvar_initialized(cv); | |
140 | int somebody_is_blocked = cv->blocked_count > 0; | |
141 | LeaveCriticalSection(&cv->count_mutex); | |
878885b4 | 142 | |
a70cb11b AM |
143 | if (somebody_is_blocked) return SetEvent (cv->ev[0]) ? 0 : GetLastError(); |
144 | else return 0; | |
145 | } | |
878885b4 | 146 | |
a70cb11b AM |
147 | int |
148 | _Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *) | |
149 | { | |
150 | EnterCriticalSection(&cv->count_mutex); | |
151 | ensure_condvar_initialized(cv); | |
152 | int somebody_is_blocked = cv->blocked_count > 0; | |
153 | LeaveCriticalSection(&cv->count_mutex); | |
154 | ||
155 | if (somebody_is_blocked) return SetEvent (cv->ev[1]) ? 0 : GetLastError(); | |
156 | else return 0; | |
878885b4 TT |
157 | } |
158 | ||
159 | // | |
160 | // Threads. | |
161 | // | |
162 | ||
163 | void | |
164 | _Jv_InitThreads (void) | |
165 | { | |
166 | _Jv_ThreadKey = TlsAlloc(); | |
167 | _Jv_ThreadDataKey = TlsAlloc(); | |
168 | daemon_mutex = CreateMutex(NULL, 0, NULL); | |
169 | daemon_cond = CreateEvent(NULL, 0, 0, NULL); | |
170 | non_daemon_count = 0; | |
171 | } | |
172 | ||
e301621d | 173 | _Jv_Thread_t * |
c93d7fae | 174 | _Jv_ThreadInitData (java::lang::Thread* obj) |
878885b4 | 175 | { |
e301621d BM |
176 | _Jv_Thread_t *data = new _Jv_Thread_t; |
177 | data->flags = 0; | |
c93d7fae | 178 | data->thread_obj = obj; |
878885b4 | 179 | |
e301621d BM |
180 | return data; |
181 | } | |
878885b4 | 182 | |
e301621d BM |
183 | void |
184 | _Jv_ThreadDestroyData (_Jv_Thread_t *data) | |
185 | { | |
186 | delete data; | |
878885b4 TT |
187 | } |
188 | ||
189 | void | |
190 | _Jv_ThreadSetPriority (_Jv_Thread_t *data, jint prio) | |
191 | { | |
192 | int actual = THREAD_PRIORITY_NORMAL; | |
193 | ||
194 | if (data->flags & FLAG_START) | |
195 | { | |
196 | switch (prio) | |
197 | { | |
198 | case 10: | |
199 | actual = THREAD_PRIORITY_TIME_CRITICAL; | |
200 | break; | |
201 | case 9: | |
202 | actual = THREAD_PRIORITY_HIGHEST; | |
203 | break; | |
204 | case 8: | |
205 | case 7: | |
206 | actual = THREAD_PRIORITY_ABOVE_NORMAL; | |
207 | break; | |
208 | case 6: | |
209 | case 5: | |
210 | actual = THREAD_PRIORITY_NORMAL; | |
211 | break; | |
212 | case 4: | |
213 | case 3: | |
214 | actual = THREAD_PRIORITY_BELOW_NORMAL; | |
215 | break; | |
216 | case 2: | |
217 | actual = THREAD_PRIORITY_LOWEST; | |
218 | break; | |
219 | case 1: | |
220 | actual = THREAD_PRIORITY_IDLE; | |
221 | break; | |
222 | } | |
223 | SetThreadPriority(data->handle, actual); | |
224 | } | |
225 | } | |
226 | ||
c93d7fae PB |
227 | void |
228 | _Jv_ThreadRegister (_Jv_Thread_t *data) | |
229 | { | |
230 | TlsSetValue (_Jv_ThreadKey, data->thread_obj); | |
231 | TlsSetValue (_Jv_ThreadDataKey, data); | |
232 | } | |
233 | ||
234 | void | |
235 | _Jv_ThreadUnRegister () | |
236 | { | |
237 | TlsSetValue (_Jv_ThreadKey, NULL); | |
238 | TlsSetValue (_Jv_ThreadDataKey, NULL); | |
239 | } | |
240 | ||
878885b4 TT |
241 | // This function is called when a thread is started. We don't arrange |
242 | // to call the `run' method directly, because this function must | |
243 | // return a value. | |
a70cb11b | 244 | static DWORD WINAPI |
878885b4 TT |
245 | really_start (void* x) |
246 | { | |
247 | struct starter *info = (struct starter *) x; | |
248 | ||
c93d7fae PB |
249 | _Jv_ThreadRegister (info->data); |
250 | ||
251 | info->method (info->data->thread_obj); | |
878885b4 TT |
252 | |
253 | if (! (info->data->flags & FLAG_DAEMON)) | |
254 | { | |
255 | WaitForSingleObject (daemon_mutex, INFINITE); | |
256 | non_daemon_count--; | |
257 | if (! non_daemon_count) | |
258 | PulseEvent (daemon_cond); | |
259 | ReleaseMutex (daemon_mutex); | |
260 | } | |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
265 | void | |
266 | _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, _Jv_ThreadStartFunc *meth) | |
267 | { | |
268 | DWORD id; | |
269 | struct starter *info; | |
270 | ||
271 | // Do nothing if thread has already started | |
272 | if (data->flags & FLAG_START) | |
273 | return; | |
274 | data->flags |= FLAG_START; | |
275 | ||
276 | // FIXME: handle marking the info object for GC. | |
277 | info = (struct starter *) _Jv_AllocBytes (sizeof (struct starter)); | |
278 | info->method = meth; | |
878885b4 TT |
279 | info->data = data; |
280 | ||
281 | if (! thread->isDaemon ()) | |
282 | { | |
283 | WaitForSingleObject (daemon_mutex, INFINITE); | |
284 | non_daemon_count++; | |
285 | ReleaseMutex (daemon_mutex); | |
286 | } | |
287 | else | |
288 | data->flags |= FLAG_DAEMON; | |
289 | ||
a70cb11b | 290 | HANDLE h = GC_CreateThread(NULL, 0, really_start, info, 0, &id); |
878885b4 TT |
291 | _Jv_ThreadSetPriority(data, thread->getPriority()); |
292 | ||
293 | //if (!h) | |
294 | //JvThrow (); | |
295 | } | |
296 | ||
297 | void | |
298 | _Jv_ThreadWait (void) | |
299 | { | |
300 | WaitForSingleObject(daemon_mutex, INFINITE); | |
301 | if(non_daemon_count) | |
302 | SignalObjectAndWait(daemon_mutex, daemon_cond, INFINITE, 0); | |
303 | ReleaseMutex(daemon_mutex); | |
304 | } | |
305 | ||
306 | void | |
307 | _Jv_ThreadInterrupt (_Jv_Thread_t *data) | |
308 | { | |
309 | MessageBox(NULL, "Unimplemented", "win32-threads.cc:_Jv_ThreadInterrupt", MB_OK); | |
310 | // FIXME: | |
311 | } |