michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "primpl.h" michael@0: #include /* for _beginthread() */ michael@0: #include michael@0: #include michael@0: michael@0: /* --- globals ------------------------------------------------ */ michael@0: _NSPR_TLS* pThreadLocalStorage = 0; michael@0: _PRInterruptTable _pr_interruptTable[] = { { 0 } }; michael@0: APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD); michael@0: michael@0: void michael@0: _PR_MD_ENSURE_TLS(void) michael@0: { michael@0: if(!pThreadLocalStorage) michael@0: { michael@0: /* Allocate thread local storage (TLS). Note, that only 32 bytes can michael@0: * be allocated at a time. michael@0: */ michael@0: int rc = DosAllocThreadLocalMemory(sizeof(_NSPR_TLS) / 4, (PULONG*)&pThreadLocalStorage); michael@0: PR_ASSERT(rc == NO_ERROR); michael@0: memset(pThreadLocalStorage, 0, sizeof(_NSPR_TLS)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: _PR_MD_EARLY_INIT() michael@0: { michael@0: HMODULE hmod; michael@0: michael@0: if (DosLoadModule(NULL, 0, "DOSCALL1", &hmod) == 0) michael@0: DosQueryProcAddr(hmod, 877, "DOSQUERYTHREADCONTEXT", michael@0: (PFN *)&QueryThreadContext); michael@0: } michael@0: michael@0: static void michael@0: _pr_SetThreadMDHandle(PRThread *thread) michael@0: { michael@0: PTIB ptib; michael@0: PPIB ppib; michael@0: PRUword rc; michael@0: michael@0: rc = DosGetInfoBlocks(&ptib, &ppib); michael@0: michael@0: thread->md.handle = ptib->tib_ptib2->tib2_ultid; michael@0: } michael@0: michael@0: /* On OS/2, some system function calls seem to change the FPU control word, michael@0: * such that we crash with a floating underflow exception. The FIX_FPU() call michael@0: * in jsnum.c does not always work, as sometimes FIX_FPU() is called BEFORE the michael@0: * OS/2 system call that horks the FPU control word. So, we set an exception michael@0: * handler that covers any floating point exceptions and resets the FPU CW to michael@0: * the required value. michael@0: */ michael@0: static ULONG michael@0: _System OS2_FloatExcpHandler(PEXCEPTIONREPORTRECORD p1, michael@0: PEXCEPTIONREGISTRATIONRECORD p2, michael@0: PCONTEXTRECORD p3, michael@0: PVOID pv) michael@0: { michael@0: #ifdef DEBUG_pedemonte michael@0: printf("Entering exception handler; ExceptionNum = %x\n", p1->ExceptionNum); michael@0: switch(p1->ExceptionNum) { michael@0: case XCPT_FLOAT_DENORMAL_OPERAND: michael@0: printf("got XCPT_FLOAT_DENORMAL_OPERAND\n"); michael@0: break; michael@0: case XCPT_FLOAT_DIVIDE_BY_ZERO: michael@0: printf("got XCPT_FLOAT_DIVIDE_BY_ZERO\n"); michael@0: break; michael@0: case XCPT_FLOAT_INEXACT_RESULT: michael@0: printf("got XCPT_FLOAT_INEXACT_RESULT\n"); michael@0: break; michael@0: case XCPT_FLOAT_INVALID_OPERATION: michael@0: printf("got XCPT_FLOAT_INVALID_OPERATION\n"); michael@0: break; michael@0: case XCPT_FLOAT_OVERFLOW: michael@0: printf("got XCPT_FLOAT_OVERFLOW\n"); michael@0: break; michael@0: case XCPT_FLOAT_STACK_CHECK: michael@0: printf("got XCPT_FLOAT_STACK_CHECK\n"); michael@0: break; michael@0: case XCPT_FLOAT_UNDERFLOW: michael@0: printf("got XCPT_FLOAT_UNDERFLOW\n"); michael@0: break; michael@0: } michael@0: #endif michael@0: michael@0: switch(p1->ExceptionNum) { michael@0: case XCPT_FLOAT_DENORMAL_OPERAND: michael@0: case XCPT_FLOAT_DIVIDE_BY_ZERO: michael@0: case XCPT_FLOAT_INEXACT_RESULT: michael@0: case XCPT_FLOAT_INVALID_OPERATION: michael@0: case XCPT_FLOAT_OVERFLOW: michael@0: case XCPT_FLOAT_STACK_CHECK: michael@0: case XCPT_FLOAT_UNDERFLOW: michael@0: { michael@0: unsigned cw = p3->ctx_env[0]; michael@0: if ((cw & MCW_EM) != MCW_EM) { michael@0: /* Mask out all floating point exceptions */ michael@0: p3->ctx_env[0] |= MCW_EM; michael@0: /* Following two lines set precision to 53 bit mantissa. See jsnum.c */ michael@0: p3->ctx_env[0] &= ~MCW_PC; michael@0: p3->ctx_env[0] |= PC_53; michael@0: return XCPT_CONTINUE_EXECUTION; michael@0: } michael@0: } michael@0: } michael@0: return XCPT_CONTINUE_SEARCH; michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) michael@0: PR_OS2_SetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg) michael@0: { michael@0: /* setup the exception handler for the thread */ michael@0: APIRET rv; michael@0: excpreg->ExceptionHandler = OS2_FloatExcpHandler; michael@0: excpreg->prev_structure = NULL; michael@0: rv = DosSetExceptionHandler(excpreg); michael@0: PR_ASSERT(rv == NO_ERROR); michael@0: } michael@0: michael@0: PR_IMPLEMENT(void) michael@0: PR_OS2_UnsetFloatExcpHandler(EXCEPTIONREGISTRATIONRECORD* excpreg) michael@0: { michael@0: /* unset exception handler */ michael@0: APIRET rv = DosUnsetExceptionHandler(excpreg); michael@0: PR_ASSERT(rv == NO_ERROR); michael@0: } michael@0: michael@0: PRStatus michael@0: _PR_MD_INIT_THREAD(PRThread *thread) michael@0: { michael@0: APIRET rv; michael@0: michael@0: if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { michael@0: _pr_SetThreadMDHandle(thread); michael@0: } michael@0: michael@0: /* Create the blocking IO semaphore */ michael@0: rv = DosCreateEventSem(NULL, &(thread->md.blocked_sema), 0, 0); michael@0: return (rv == NO_ERROR) ? PR_SUCCESS : PR_FAILURE; michael@0: } michael@0: michael@0: typedef struct param_store michael@0: { michael@0: void (*start)(void *); michael@0: PRThread* thread; michael@0: } PARAMSTORE; michael@0: michael@0: /* This is a small intermediate function that sets/unsets the exception michael@0: handler before calling the initial thread function */ michael@0: static void michael@0: ExcpStartFunc(void* arg) michael@0: { michael@0: EXCEPTIONREGISTRATIONRECORD excpreg; michael@0: PARAMSTORE params, *pParams = arg; michael@0: michael@0: PR_OS2_SetFloatExcpHandler(&excpreg); michael@0: michael@0: params = *pParams; michael@0: PR_Free(pParams); michael@0: params.start(params.thread); michael@0: michael@0: PR_OS2_UnsetFloatExcpHandler(&excpreg); michael@0: } michael@0: michael@0: PRStatus michael@0: _PR_MD_CREATE_THREAD(PRThread *thread, michael@0: void (*start)(void *), michael@0: PRThreadPriority priority, michael@0: PRThreadScope scope, michael@0: PRThreadState state, michael@0: PRUint32 stackSize) michael@0: { michael@0: PARAMSTORE* params = PR_Malloc(sizeof(PARAMSTORE)); michael@0: params->start = start; michael@0: params->thread = thread; michael@0: thread->md.handle = thread->id = (TID) _beginthread(ExcpStartFunc, michael@0: NULL, michael@0: thread->stack->stackSize, michael@0: params); michael@0: if(thread->md.handle == -1) { michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: /* michael@0: * On OS/2, a thread is created with a thread priority of michael@0: * THREAD_PRIORITY_NORMAL michael@0: */ michael@0: michael@0: if (priority != PR_PRIORITY_NORMAL) { michael@0: _PR_MD_SET_PRIORITY(&(thread->md), priority); michael@0: } michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: void michael@0: _PR_MD_YIELD(void) michael@0: { michael@0: /* Isn't there some problem with DosSleep(0) on OS/2? */ michael@0: DosSleep(0); michael@0: } michael@0: michael@0: void michael@0: _PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) michael@0: { michael@0: int nativePri = PRTYC_NOCHANGE; michael@0: BOOL rv; michael@0: michael@0: if (newPri < PR_PRIORITY_FIRST) { michael@0: newPri = PR_PRIORITY_FIRST; michael@0: } else if (newPri > PR_PRIORITY_LAST) { michael@0: newPri = PR_PRIORITY_LAST; michael@0: } michael@0: switch (newPri) { michael@0: case PR_PRIORITY_LOW: michael@0: case PR_PRIORITY_NORMAL: michael@0: nativePri = PRTYC_REGULAR; michael@0: break; michael@0: case PR_PRIORITY_HIGH: michael@0: nativePri = PRTYC_FOREGROUNDSERVER; michael@0: break; michael@0: case PR_PRIORITY_URGENT: michael@0: nativePri = PRTYC_TIMECRITICAL; michael@0: } michael@0: rv = DosSetPriority(PRTYS_THREAD, nativePri, 0, thread->handle); michael@0: PR_ASSERT(rv == NO_ERROR); michael@0: if (rv != NO_ERROR) { michael@0: PR_LOG(_pr_thread_lm, PR_LOG_MIN, michael@0: ("PR_SetThreadPriority: can't set thread priority\n")); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: void michael@0: _PR_MD_CLEAN_THREAD(PRThread *thread) michael@0: { michael@0: APIRET rv; michael@0: michael@0: if (thread->md.blocked_sema) { michael@0: rv = DosCloseEventSem(thread->md.blocked_sema); michael@0: PR_ASSERT(rv == NO_ERROR); michael@0: thread->md.blocked_sema = 0; michael@0: } michael@0: michael@0: if (thread->md.handle) { michael@0: thread->md.handle = 0; michael@0: } michael@0: } michael@0: michael@0: void michael@0: _PR_MD_EXIT_THREAD(PRThread *thread) michael@0: { michael@0: _PR_MD_CLEAN_THREAD(thread); michael@0: _PR_MD_SET_CURRENT_THREAD(NULL); michael@0: } michael@0: michael@0: michael@0: void michael@0: _PR_MD_EXIT(PRIntn status) michael@0: { michael@0: _exit(status); michael@0: } michael@0: michael@0: #ifdef HAVE_THREAD_AFFINITY michael@0: PR_EXTERN(PRInt32) michael@0: _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ) michael@0: { michael@0: /* Can we do this on OS/2? Only on SMP versions? */ michael@0: PR_ASSERT(!"Not implemented"); michael@0: return 0; michael@0: michael@0: /* This is what windows does: michael@0: int rv; michael@0: michael@0: rv = SetThreadAffinityMask(thread->md.handle, mask); michael@0: michael@0: return rv?0:-1; michael@0: */ michael@0: } michael@0: michael@0: PR_EXTERN(PRInt32) michael@0: _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask) michael@0: { michael@0: /* Can we do this on OS/2? Only on SMP versions? */ michael@0: PR_ASSERT(!"Not implemented"); michael@0: return 0; michael@0: michael@0: /* This is what windows does: michael@0: PRInt32 rv, system_mask; michael@0: michael@0: rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask); michael@0: michael@0: return rv?0:-1; michael@0: */ michael@0: } michael@0: #endif /* HAVE_THREAD_AFFINITY */ michael@0: michael@0: void michael@0: _PR_MD_SUSPEND_CPU(_PRCPU *cpu) michael@0: { michael@0: _PR_MD_SUSPEND_THREAD(cpu->thread); michael@0: } michael@0: michael@0: void michael@0: _PR_MD_RESUME_CPU(_PRCPU *cpu) michael@0: { michael@0: _PR_MD_RESUME_THREAD(cpu->thread); michael@0: } michael@0: michael@0: void michael@0: _PR_MD_SUSPEND_THREAD(PRThread *thread) michael@0: { michael@0: if (_PR_IS_NATIVE_THREAD(thread)) { michael@0: APIRET rc; michael@0: michael@0: /* XXXMB - DosSuspendThread() is not a blocking call; how do we michael@0: * know when the thread is *REALLY* suspended? michael@0: */ michael@0: rc = DosSuspendThread(thread->md.handle); michael@0: PR_ASSERT(rc == NO_ERROR); michael@0: } michael@0: } michael@0: michael@0: void michael@0: _PR_MD_RESUME_THREAD(PRThread *thread) michael@0: { michael@0: if (_PR_IS_NATIVE_THREAD(thread)) { michael@0: DosResumeThread(thread->md.handle); michael@0: } michael@0: } michael@0: michael@0: michael@0: PRThread* michael@0: _MD_CURRENT_THREAD(void) michael@0: { michael@0: PRThread *thread; michael@0: michael@0: thread = _MD_GET_ATTACHED_THREAD(); michael@0: michael@0: if (NULL == thread) { michael@0: thread = _PRI_AttachThread(PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0); michael@0: } michael@0: michael@0: PR_ASSERT(thread != NULL); michael@0: return thread; michael@0: } michael@0: