|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "primpl.h" |
|
7 #include <process.h> /* for _beginthread() */ |
|
8 #include <signal.h> |
|
9 #include <float.h> |
|
10 |
|
11 /* --- globals ------------------------------------------------ */ |
|
12 _NSPR_TLS* pThreadLocalStorage = 0; |
|
13 _PRInterruptTable _pr_interruptTable[] = { { 0 } }; |
|
14 APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD); |
|
15 |
|
16 void |
|
17 _PR_MD_ENSURE_TLS(void) |
|
18 { |
|
19 if(!pThreadLocalStorage) |
|
20 { |
|
21 /* Allocate thread local storage (TLS). Note, that only 32 bytes can |
|
22 * be allocated at a time. |
|
23 */ |
|
24 int rc = DosAllocThreadLocalMemory(sizeof(_NSPR_TLS) / 4, (PULONG*)&pThreadLocalStorage); |
|
25 PR_ASSERT(rc == NO_ERROR); |
|
26 memset(pThreadLocalStorage, 0, sizeof(_NSPR_TLS)); |
|
27 } |
|
28 } |
|
29 |
|
30 void |
|
31 _PR_MD_EARLY_INIT() |
|
32 { |
|
33 HMODULE hmod; |
|
34 |
|
35 if (DosLoadModule(NULL, 0, "DOSCALL1", &hmod) == 0) |
|
36 DosQueryProcAddr(hmod, 877, "DOSQUERYTHREADCONTEXT", |
|
37 (PFN *)&QueryThreadContext); |
|
38 } |
|
39 |
|
40 static void |
|
41 _pr_SetThreadMDHandle(PRThread *thread) |
|
42 { |
|
43 PTIB ptib; |
|
44 PPIB ppib; |
|
45 PRUword rc; |
|
46 |
|
47 rc = DosGetInfoBlocks(&ptib, &ppib); |
|
48 |
|
49 thread->md.handle = ptib->tib_ptib2->tib2_ultid; |
|
50 } |
|
51 |
|
52 /* On OS/2, some system function calls seem to change the FPU control word, |
|
53 * such that we crash with a floating underflow exception. The FIX_FPU() call |
|
54 * in jsnum.c does not always work, as sometimes FIX_FPU() is called BEFORE the |
|
55 * OS/2 system call that horks the FPU control word. So, we set an exception |
|
56 * handler that covers any floating point exceptions and resets the FPU CW to |
|
57 * the required value. |
|
58 */ |
|
59 static ULONG |
|
60 _System OS2_FloatExcpHandler(PEXCEPTIONREPORTRECORD p1, |
|
61 PEXCEPTIONREGISTRATIONRECORD p2, |
|
62 PCONTEXTRECORD p3, |
|
63 PVOID pv) |
|
64 { |
|
65 #ifdef DEBUG_pedemonte |
|
66 printf("Entering exception handler; ExceptionNum = %x\n", p1->ExceptionNum); |
|
67 switch(p1->ExceptionNum) { |
|
68 case XCPT_FLOAT_DENORMAL_OPERAND: |
|
69 printf("got XCPT_FLOAT_DENORMAL_OPERAND\n"); |
|
70 break; |
|
71 case XCPT_FLOAT_DIVIDE_BY_ZERO: |
|
72 printf("got XCPT_FLOAT_DIVIDE_BY_ZERO\n"); |
|
73 break; |
|
74 case XCPT_FLOAT_INEXACT_RESULT: |
|
75 printf("got XCPT_FLOAT_INEXACT_RESULT\n"); |
|
76 break; |
|
77 case XCPT_FLOAT_INVALID_OPERATION: |
|
78 printf("got XCPT_FLOAT_INVALID_OPERATION\n"); |
|
79 break; |
|
80 case XCPT_FLOAT_OVERFLOW: |
|
81 printf("got XCPT_FLOAT_OVERFLOW\n"); |
|
82 break; |
|
83 case XCPT_FLOAT_STACK_CHECK: |
|
84 printf("got XCPT_FLOAT_STACK_CHECK\n"); |
|
85 break; |
|
86 case XCPT_FLOAT_UNDERFLOW: |
|
87 printf("got XCPT_FLOAT_UNDERFLOW\n"); |
|
88 break; |
|
89 } |
|
90 #endif |
|
91 |
|
92 switch(p1->ExceptionNum) { |
|
93 case XCPT_FLOAT_DENORMAL_OPERAND: |
|
94 case XCPT_FLOAT_DIVIDE_BY_ZERO: |
|
95 case XCPT_FLOAT_INEXACT_RESULT: |
|
96 case XCPT_FLOAT_INVALID_OPERATION: |
|
97 case XCPT_FLOAT_OVERFLOW: |
|
98 case XCPT_FLOAT_STACK_CHECK: |
|
99 case XCPT_FLOAT_UNDERFLOW: |
|
100 { |
|
101 unsigned cw = p3->ctx_env[0]; |
|
102 if ((cw & MCW_EM) != MCW_EM) { |
|
103 /* Mask out all floating point exceptions */ |
|
104 p3->ctx_env[0] |= MCW_EM; |
|
105 /* Following two lines set precision to 53 bit mantissa. See jsnum.c */ |
|
106 p3->ctx_env[0] &= ~MCW_PC; |
|
107 p3->ctx_env[0] |= PC_53; |
|
108 return XCPT_CONTINUE_EXECUTION; |
|
109 } |
|
110 } |
|
111 } |
|
112 return XCPT_CONTINUE_SEARCH; |
|
113 } |
|
114 |
|
115 PR_IMPLEMENT(void) |
|
116 PR_OS2_SetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg) |
|
117 { |
|
118 /* setup the exception handler for the thread */ |
|
119 APIRET rv; |
|
120 excpreg->ExceptionHandler = OS2_FloatExcpHandler; |
|
121 excpreg->prev_structure = NULL; |
|
122 rv = DosSetExceptionHandler(excpreg); |
|
123 PR_ASSERT(rv == NO_ERROR); |
|
124 } |
|
125 |
|
126 PR_IMPLEMENT(void) |
|
127 PR_OS2_UnsetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg) |
|
128 { |
|
129 /* unset exception handler */ |
|
130 APIRET rv = DosUnsetExceptionHandler(excpreg); |
|
131 PR_ASSERT(rv == NO_ERROR); |
|
132 } |
|
133 |
|
134 PRStatus |
|
135 _PR_MD_INIT_THREAD(PRThread *thread) |
|
136 { |
|
137 APIRET rv; |
|
138 |
|
139 if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { |
|
140 _pr_SetThreadMDHandle(thread); |
|
141 } |
|
142 |
|
143 /* Create the blocking IO semaphore */ |
|
144 rv = DosCreateEventSem(NULL, &(thread->md.blocked_sema), 0, 0); |
|
145 return (rv == NO_ERROR) ? PR_SUCCESS : PR_FAILURE; |
|
146 } |
|
147 |
|
148 typedef struct param_store |
|
149 { |
|
150 void (*start)(void *); |
|
151 PRThread* thread; |
|
152 } PARAMSTORE; |
|
153 |
|
154 /* This is a small intermediate function that sets/unsets the exception |
|
155 handler before calling the initial thread function */ |
|
156 static void |
|
157 ExcpStartFunc(void* arg) |
|
158 { |
|
159 EXCEPTIONREGISTRATIONRECORD excpreg; |
|
160 PARAMSTORE params, *pParams = arg; |
|
161 |
|
162 PR_OS2_SetFloatExcpHandler(&excpreg); |
|
163 |
|
164 params = *pParams; |
|
165 PR_Free(pParams); |
|
166 params.start(params.thread); |
|
167 |
|
168 PR_OS2_UnsetFloatExcpHandler(&excpreg); |
|
169 } |
|
170 |
|
171 PRStatus |
|
172 _PR_MD_CREATE_THREAD(PRThread *thread, |
|
173 void (*start)(void *), |
|
174 PRThreadPriority priority, |
|
175 PRThreadScope scope, |
|
176 PRThreadState state, |
|
177 PRUint32 stackSize) |
|
178 { |
|
179 PARAMSTORE* params = PR_Malloc(sizeof(PARAMSTORE)); |
|
180 params->start = start; |
|
181 params->thread = thread; |
|
182 thread->md.handle = thread->id = (TID) _beginthread(ExcpStartFunc, |
|
183 NULL, |
|
184 thread->stack->stackSize, |
|
185 params); |
|
186 if(thread->md.handle == -1) { |
|
187 return PR_FAILURE; |
|
188 } |
|
189 |
|
190 /* |
|
191 * On OS/2, a thread is created with a thread priority of |
|
192 * THREAD_PRIORITY_NORMAL |
|
193 */ |
|
194 |
|
195 if (priority != PR_PRIORITY_NORMAL) { |
|
196 _PR_MD_SET_PRIORITY(&(thread->md), priority); |
|
197 } |
|
198 |
|
199 return PR_SUCCESS; |
|
200 } |
|
201 |
|
202 void |
|
203 _PR_MD_YIELD(void) |
|
204 { |
|
205 /* Isn't there some problem with DosSleep(0) on OS/2? */ |
|
206 DosSleep(0); |
|
207 } |
|
208 |
|
209 void |
|
210 _PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) |
|
211 { |
|
212 int nativePri = PRTYC_NOCHANGE; |
|
213 BOOL rv; |
|
214 |
|
215 if (newPri < PR_PRIORITY_FIRST) { |
|
216 newPri = PR_PRIORITY_FIRST; |
|
217 } else if (newPri > PR_PRIORITY_LAST) { |
|
218 newPri = PR_PRIORITY_LAST; |
|
219 } |
|
220 switch (newPri) { |
|
221 case PR_PRIORITY_LOW: |
|
222 case PR_PRIORITY_NORMAL: |
|
223 nativePri = PRTYC_REGULAR; |
|
224 break; |
|
225 case PR_PRIORITY_HIGH: |
|
226 nativePri = PRTYC_FOREGROUNDSERVER; |
|
227 break; |
|
228 case PR_PRIORITY_URGENT: |
|
229 nativePri = PRTYC_TIMECRITICAL; |
|
230 } |
|
231 rv = DosSetPriority(PRTYS_THREAD, nativePri, 0, thread->handle); |
|
232 PR_ASSERT(rv == NO_ERROR); |
|
233 if (rv != NO_ERROR) { |
|
234 PR_LOG(_pr_thread_lm, PR_LOG_MIN, |
|
235 ("PR_SetThreadPriority: can't set thread priority\n")); |
|
236 } |
|
237 return; |
|
238 } |
|
239 |
|
240 void |
|
241 _PR_MD_CLEAN_THREAD(PRThread *thread) |
|
242 { |
|
243 APIRET rv; |
|
244 |
|
245 if (thread->md.blocked_sema) { |
|
246 rv = DosCloseEventSem(thread->md.blocked_sema); |
|
247 PR_ASSERT(rv == NO_ERROR); |
|
248 thread->md.blocked_sema = 0; |
|
249 } |
|
250 |
|
251 if (thread->md.handle) { |
|
252 thread->md.handle = 0; |
|
253 } |
|
254 } |
|
255 |
|
256 void |
|
257 _PR_MD_EXIT_THREAD(PRThread *thread) |
|
258 { |
|
259 _PR_MD_CLEAN_THREAD(thread); |
|
260 _PR_MD_SET_CURRENT_THREAD(NULL); |
|
261 } |
|
262 |
|
263 |
|
264 void |
|
265 _PR_MD_EXIT(PRIntn status) |
|
266 { |
|
267 _exit(status); |
|
268 } |
|
269 |
|
270 #ifdef HAVE_THREAD_AFFINITY |
|
271 PR_EXTERN(PRInt32) |
|
272 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ) |
|
273 { |
|
274 /* Can we do this on OS/2? Only on SMP versions? */ |
|
275 PR_ASSERT(!"Not implemented"); |
|
276 return 0; |
|
277 |
|
278 /* This is what windows does: |
|
279 int rv; |
|
280 |
|
281 rv = SetThreadAffinityMask(thread->md.handle, mask); |
|
282 |
|
283 return rv?0:-1; |
|
284 */ |
|
285 } |
|
286 |
|
287 PR_EXTERN(PRInt32) |
|
288 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask) |
|
289 { |
|
290 /* Can we do this on OS/2? Only on SMP versions? */ |
|
291 PR_ASSERT(!"Not implemented"); |
|
292 return 0; |
|
293 |
|
294 /* This is what windows does: |
|
295 PRInt32 rv, system_mask; |
|
296 |
|
297 rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask); |
|
298 |
|
299 return rv?0:-1; |
|
300 */ |
|
301 } |
|
302 #endif /* HAVE_THREAD_AFFINITY */ |
|
303 |
|
304 void |
|
305 _PR_MD_SUSPEND_CPU(_PRCPU *cpu) |
|
306 { |
|
307 _PR_MD_SUSPEND_THREAD(cpu->thread); |
|
308 } |
|
309 |
|
310 void |
|
311 _PR_MD_RESUME_CPU(_PRCPU *cpu) |
|
312 { |
|
313 _PR_MD_RESUME_THREAD(cpu->thread); |
|
314 } |
|
315 |
|
316 void |
|
317 _PR_MD_SUSPEND_THREAD(PRThread *thread) |
|
318 { |
|
319 if (_PR_IS_NATIVE_THREAD(thread)) { |
|
320 APIRET rc; |
|
321 |
|
322 /* XXXMB - DosSuspendThread() is not a blocking call; how do we |
|
323 * know when the thread is *REALLY* suspended? |
|
324 */ |
|
325 rc = DosSuspendThread(thread->md.handle); |
|
326 PR_ASSERT(rc == NO_ERROR); |
|
327 } |
|
328 } |
|
329 |
|
330 void |
|
331 _PR_MD_RESUME_THREAD(PRThread *thread) |
|
332 { |
|
333 if (_PR_IS_NATIVE_THREAD(thread)) { |
|
334 DosResumeThread(thread->md.handle); |
|
335 } |
|
336 } |
|
337 |
|
338 |
|
339 PRThread* |
|
340 _MD_CURRENT_THREAD(void) |
|
341 { |
|
342 PRThread *thread; |
|
343 |
|
344 thread = _MD_GET_ATTACHED_THREAD(); |
|
345 |
|
346 if (NULL == thread) { |
|
347 thread = _PRI_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0); |
|
348 } |
|
349 |
|
350 PR_ASSERT(thread != NULL); |
|
351 return thread; |
|
352 } |
|
353 |