/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "primpl.h" #include #include PRLogModuleInfo *_pr_clock_lm; PRLogModuleInfo *_pr_cmon_lm; PRLogModuleInfo *_pr_io_lm; PRLogModuleInfo *_pr_cvar_lm; PRLogModuleInfo *_pr_mon_lm; PRLogModuleInfo *_pr_linker_lm; PRLogModuleInfo *_pr_sched_lm; PRLogModuleInfo *_pr_thread_lm; PRLogModuleInfo *_pr_gc_lm; PRLogModuleInfo *_pr_shm_lm; PRLogModuleInfo *_pr_shma_lm; PRFileDesc *_pr_stdin; PRFileDesc *_pr_stdout; PRFileDesc *_pr_stderr; #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) PRCList _pr_active_local_threadQ = PR_INIT_STATIC_CLIST(&_pr_active_local_threadQ); PRCList _pr_active_global_threadQ = PR_INIT_STATIC_CLIST(&_pr_active_global_threadQ); _MDLock _pr_cpuLock; /* lock for the CPU Q */ PRCList _pr_cpuQ = PR_INIT_STATIC_CLIST(&_pr_cpuQ); PRUint32 _pr_utid; PRInt32 _pr_userActive; PRInt32 _pr_systemActive; PRUintn _pr_maxPTDs; #ifdef _PR_LOCAL_THREADS_ONLY struct _PRCPU *_pr_currentCPU; PRThread *_pr_currentThread; PRThread *_pr_lastThread; PRInt32 _pr_intsOff; #endif /* _PR_LOCAL_THREADS_ONLY */ /* Lock protecting all "termination" condition variables of all threads */ PRLock *_pr_terminationCVLock; #endif /* !defined(_PR_PTHREADS) */ PRLock *_pr_sleeplock; /* used in PR_Sleep(), classic and pthreads */ static void _PR_InitCallOnce(void); PRBool _pr_initialized = PR_FALSE; PR_IMPLEMENT(PRBool) PR_VersionCheck(const char *importedVersion) { /* ** This is the secret handshake algorithm. ** ** This release has a simple version compatibility ** check algorithm. This release is not backward ** compatible with previous major releases. It is ** not compatible with future major, minor, or ** patch releases. */ int vmajor = 0, vminor = 0, vpatch = 0; const char *ptr = importedVersion; while (isdigit(*ptr)) { vmajor = 10 * vmajor + *ptr - '0'; ptr++; } if (*ptr == '.') { ptr++; while (isdigit(*ptr)) { vminor = 10 * vminor + *ptr - '0'; ptr++; } if (*ptr == '.') { ptr++; while (isdigit(*ptr)) { vpatch = 10 * vpatch + *ptr - '0'; ptr++; } } } if (vmajor != PR_VMAJOR) { return PR_FALSE; } if (vmajor == PR_VMAJOR && vminor > PR_VMINOR) { return PR_FALSE; } if (vmajor == PR_VMAJOR && vminor == PR_VMINOR && vpatch > PR_VPATCH) { return PR_FALSE; } return PR_TRUE; } /* PR_VersionCheck */ PR_IMPLEMENT(const char*) PR_GetVersion(void) { return PR_VERSION; } PR_IMPLEMENT(PRBool) PR_Initialized(void) { return _pr_initialized; } PRInt32 _native_threads_only = 0; #ifdef WINNT static void _pr_SetNativeThreadsOnlyMode(void) { HMODULE mainExe; PRBool *globalp; char *envp; mainExe = GetModuleHandle(NULL); PR_ASSERT(NULL != mainExe); globalp = (PRBool *) GetProcAddress(mainExe, "nspr_native_threads_only"); if (globalp) { _native_threads_only = (*globalp != PR_FALSE); } else if (envp = getenv("NSPR_NATIVE_THREADS_ONLY")) { _native_threads_only = (atoi(envp) == 1); } } #endif static void _PR_InitStuff(void) { if (_pr_initialized) { return; } _pr_initialized = PR_TRUE; #ifdef _PR_ZONE_ALLOCATOR _PR_InitZones(); #endif #ifdef WINNT _pr_SetNativeThreadsOnlyMode(); #endif (void) PR_GetPageSize(); _pr_clock_lm = PR_NewLogModule("clock"); _pr_cmon_lm = PR_NewLogModule("cmon"); _pr_io_lm = PR_NewLogModule("io"); _pr_mon_lm = PR_NewLogModule("mon"); _pr_linker_lm = PR_NewLogModule("linker"); _pr_cvar_lm = PR_NewLogModule("cvar"); _pr_sched_lm = PR_NewLogModule("sched"); _pr_thread_lm = PR_NewLogModule("thread"); _pr_gc_lm = PR_NewLogModule("gc"); _pr_shm_lm = PR_NewLogModule("shm"); _pr_shma_lm = PR_NewLogModule("shma"); /* NOTE: These init's cannot depend on _PR_MD_CURRENT_THREAD() */ _PR_MD_EARLY_INIT(); _PR_InitLocks(); _PR_InitAtomic(); _PR_InitSegs(); _PR_InitStacks(); _PR_InitTPD(); _PR_InitEnv(); _PR_InitLayerCache(); _PR_InitClock(); _pr_sleeplock = PR_NewLock(); PR_ASSERT(NULL != _pr_sleeplock); _PR_InitThreads(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); #ifdef WIN16 { PRInt32 top; /* artificial top of stack, win16 */ _pr_top_of_task_stack = (char *) ⊤ } #endif #ifndef _PR_GLOBAL_THREADS_ONLY _PR_InitCPUs(); #endif /* * XXX: call _PR_InitMem only on those platforms for which nspr implements * malloc, for now. */ #ifdef _PR_OVERRIDE_MALLOC _PR_InitMem(); #endif _PR_InitCMon(); _PR_InitIO(); _PR_InitNet(); _PR_InitTime(); _PR_InitLog(); _PR_InitLinker(); _PR_InitCallOnce(); _PR_InitDtoa(); _PR_InitMW(); _PR_InitRWLocks(); nspr_InitializePRErrorTable(); _PR_MD_FINAL_INIT(); } void _PR_ImplicitInitialization(void) { _PR_InitStuff(); /* Enable interrupts */ #if !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY) _PR_MD_START_INTERRUPTS(); #endif } PR_IMPLEMENT(void) PR_DisableClockInterrupts(void) { #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) if (!_pr_initialized) { _PR_InitStuff(); } else { _PR_MD_DISABLE_CLOCK_INTERRUPTS(); } #endif } PR_IMPLEMENT(void) PR_EnableClockInterrupts(void) { #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) if (!_pr_initialized) { _PR_InitStuff(); } _PR_MD_ENABLE_CLOCK_INTERRUPTS(); #endif } PR_IMPLEMENT(void) PR_BlockClockInterrupts(void) { #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) _PR_MD_BLOCK_CLOCK_INTERRUPTS(); #endif } PR_IMPLEMENT(void) PR_UnblockClockInterrupts(void) { #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) _PR_MD_UNBLOCK_CLOCK_INTERRUPTS(); #endif } PR_IMPLEMENT(void) PR_Init( PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) { _PR_ImplicitInitialization(); } PR_IMPLEMENT(PRIntn) PR_Initialize( PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs) { PRIntn rv; _PR_ImplicitInitialization(); rv = prmain(argc, argv); PR_Cleanup(); return rv; } /* PR_Initialize */ /* *----------------------------------------------------------------------- * * _PR_CleanupBeforeExit -- * * Perform the cleanup work before exiting the process. * We first do the cleanup generic to all platforms. Then * we call _PR_MD_CLEANUP_BEFORE_EXIT(), where platform-dependent * cleanup is done. This function is used by PR_Cleanup(). * * See also: PR_Cleanup(). * *----------------------------------------------------------------------- */ #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) /* see ptthread.c */ #else static void _PR_CleanupBeforeExit(void) { /* Do not make any calls here other than to destroy resources. For example, do not make any calls that eventually may end up in PR_Lock. Because the thread is destroyed, can not access current thread any more. */ _PR_CleanupTPD(); if (_pr_terminationCVLock) /* * In light of the comment above, this looks real suspicious. * I'd go so far as to say it's just a problem waiting to happen. */ { PR_DestroyLock(_pr_terminationCVLock); } _PR_MD_CLEANUP_BEFORE_EXIT(); } #endif /* defined(_PR_PTHREADS) */ /* *---------------------------------------------------------------------- * * PR_Cleanup -- * * Perform a graceful shutdown of the NSPR runtime. PR_Cleanup() may * only be called from the primordial thread, typically at the * end of the main() function. It returns when it has completed * its platform-dependent duty and the process must not make any other * NSPR library calls prior to exiting from main(). * * PR_Cleanup() first blocks the primordial thread until all the * other user (non-system) threads, if any, have terminated. * Then it performs cleanup in preparation for exiting the process. * PR_Cleanup() does not exit the primordial thread (which would * in turn exit the process). * * PR_Cleanup() only responds when it is called by the primordial * thread. Calls by any other thread are silently ignored. * * See also: PR_ExitProcess() * *---------------------------------------------------------------------- */ #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) /* see ptthread.c */ #else PR_IMPLEMENT(PRStatus) PR_Cleanup() { PRThread *me = PR_GetCurrentThread(); PR_ASSERT((NULL != me) && (me->flags & _PR_PRIMORDIAL)); if ((NULL != me) && (me->flags & _PR_PRIMORDIAL)) { PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); /* * No more recycling of threads */ _pr_recycleThreads = 0; /* * Wait for all other user (non-system/daemon) threads * to terminate. */ PR_Lock(_pr_activeLock); while (_pr_userActive > _pr_primordialExitCount) { PR_WaitCondVar(_pr_primordialExitCVar, PR_INTERVAL_NO_TIMEOUT); } if (me->flags & _PR_SYSTEM) { _pr_systemActive--; } else { _pr_userActive--; } PR_Unlock(_pr_activeLock); _PR_MD_EARLY_CLEANUP(); _PR_CleanupMW(); _PR_CleanupTime(); _PR_CleanupDtoa(); _PR_CleanupCallOnce(); _PR_ShutdownLinker(); _PR_CleanupNet(); _PR_CleanupIO(); /* Release the primordial thread's private data, etc. */ _PR_CleanupThread(me); _PR_MD_STOP_INTERRUPTS(); PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: clean up before destroying thread")); _PR_LogCleanup(); /* * This part should look like the end of _PR_NativeRunThread * and _PR_UserRunThread. */ if (_PR_IS_NATIVE_THREAD(me)) { _PR_MD_EXIT_THREAD(me); _PR_NativeDestroyThread(me); } else { _PR_UserDestroyThread(me); PR_DELETE(me->stack); PR_DELETE(me); } /* * XXX: We are freeing the heap memory here so that Purify won't * complain, but we should also free other kinds of resources * that are allocated by the _PR_InitXXX() functions. * Ideally, for each _PR_InitXXX(), there should be a corresponding * _PR_XXXCleanup() that we can call here. */ #ifdef WINNT _PR_CleanupCPUs(); #endif _PR_CleanupThreads(); _PR_CleanupCMon(); PR_DestroyLock(_pr_sleeplock); _pr_sleeplock = NULL; _PR_CleanupLayerCache(); _PR_CleanupEnv(); _PR_CleanupStacks(); _PR_CleanupBeforeExit(); _pr_initialized = PR_FALSE; return PR_SUCCESS; } return PR_FAILURE; } #endif /* defined(_PR_PTHREADS) */ /* *------------------------------------------------------------------------ * PR_ProcessExit -- * * Cause an immediate, nongraceful, forced termination of the process. * It takes a PRIntn argument, which is the exit status code of the * process. * * See also: PR_Cleanup() * *------------------------------------------------------------------------ */ #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) /* see ptthread.c */ #else PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) { _PR_MD_EXIT(status); } #endif /* defined(_PR_PTHREADS) */ PR_IMPLEMENT(PRProcessAttr *) PR_NewProcessAttr(void) { PRProcessAttr *attr; attr = PR_NEWZAP(PRProcessAttr); if (!attr) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); } return attr; } PR_IMPLEMENT(void) PR_ResetProcessAttr(PRProcessAttr *attr) { PR_FREEIF(attr->currentDirectory); PR_FREEIF(attr->fdInheritBuffer); memset(attr, 0, sizeof(*attr)); } PR_IMPLEMENT(void) PR_DestroyProcessAttr(PRProcessAttr *attr) { PR_FREEIF(attr->currentDirectory); PR_FREEIF(attr->fdInheritBuffer); PR_DELETE(attr); } PR_IMPLEMENT(void) PR_ProcessAttrSetStdioRedirect( PRProcessAttr *attr, PRSpecialFD stdioFd, PRFileDesc *redirectFd) { switch (stdioFd) { case PR_StandardInput: attr->stdinFd = redirectFd; break; case PR_StandardOutput: attr->stdoutFd = redirectFd; break; case PR_StandardError: attr->stderrFd = redirectFd; break; default: PR_ASSERT(0); } } /* * OBSOLETE */ PR_IMPLEMENT(void) PR_SetStdioRedirect( PRProcessAttr *attr, PRSpecialFD stdioFd, PRFileDesc *redirectFd) { #if defined(DEBUG) static PRBool warn = PR_TRUE; if (warn) { warn = _PR_Obsolete("PR_SetStdioRedirect()", "PR_ProcessAttrSetStdioRedirect()"); } #endif PR_ProcessAttrSetStdioRedirect(attr, stdioFd, redirectFd); } PR_IMPLEMENT(PRStatus) PR_ProcessAttrSetCurrentDirectory( PRProcessAttr *attr, const char *dir) { PR_FREEIF(attr->currentDirectory); attr->currentDirectory = (char *) PR_MALLOC(strlen(dir) + 1); if (!attr->currentDirectory) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return PR_FAILURE; } strcpy(attr->currentDirectory, dir); return PR_SUCCESS; } PR_IMPLEMENT(PRStatus) PR_ProcessAttrSetInheritableFD( PRProcessAttr *attr, PRFileDesc *fd, const char *name) { /* We malloc the fd inherit buffer in multiples of this number. */ #define FD_INHERIT_BUFFER_INCR 128 /* The length of "NSPR_INHERIT_FDS=" */ #define NSPR_INHERIT_FDS_STRLEN 17 /* The length of osfd (PROsfd) printed in hexadecimal with 0x prefix */ #ifdef _WIN64 #define OSFD_STRLEN 18 #else #define OSFD_STRLEN 10 #endif /* The length of fd type (PRDescType) printed in decimal */ #define FD_TYPE_STRLEN 1 PRSize newSize; int remainder; char *newBuffer; int nwritten; char *cur; int freeSize; if (fd->identity != PR_NSPR_IO_LAYER) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return PR_FAILURE; } if (fd->secret->inheritable == _PR_TRI_UNKNOWN) { _PR_MD_QUERY_FD_INHERITABLE(fd); } if (fd->secret->inheritable != _PR_TRI_TRUE) { PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0); return PR_FAILURE; } /* * We also need to account for the : separators and the * terminating null byte. */ if (NULL == attr->fdInheritBuffer) { /* The first time, we print "NSPR_INHERIT_FDS=::" */ newSize = NSPR_INHERIT_FDS_STRLEN + strlen(name) + FD_TYPE_STRLEN + OSFD_STRLEN + 2 + 1; } else { /* At other times, we print ":::" */ newSize = attr->fdInheritBufferUsed + strlen(name) + FD_TYPE_STRLEN + OSFD_STRLEN + 3 + 1; } if (newSize > attr->fdInheritBufferSize) { /* Make newSize a multiple of FD_INHERIT_BUFFER_INCR */ remainder = newSize % FD_INHERIT_BUFFER_INCR; if (remainder != 0) { newSize += (FD_INHERIT_BUFFER_INCR - remainder); } if (NULL == attr->fdInheritBuffer) { newBuffer = (char *) PR_MALLOC(newSize); } else { newBuffer = (char *) PR_REALLOC(attr->fdInheritBuffer, newSize); } if (NULL == newBuffer) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return PR_FAILURE; } attr->fdInheritBuffer = newBuffer; attr->fdInheritBufferSize = newSize; } cur = attr->fdInheritBuffer + attr->fdInheritBufferUsed; freeSize = attr->fdInheritBufferSize - attr->fdInheritBufferUsed; if (0 == attr->fdInheritBufferUsed) { nwritten = PR_snprintf(cur, freeSize, "NSPR_INHERIT_FDS=%s:%d:0x%" PR_PRIxOSFD, name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd); } else { nwritten = PR_snprintf(cur, freeSize, ":%s:%d:0x%" PR_PRIxOSFD, name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd); } attr->fdInheritBufferUsed += nwritten; return PR_SUCCESS; } PR_IMPLEMENT(PRFileDesc *) PR_GetInheritedFD( const char *name) { PRFileDesc *fd; const char *envVar; const char *ptr; int len = strlen(name); PROsfd osfd; int nColons; PRIntn fileType; envVar = PR_GetEnv("NSPR_INHERIT_FDS"); if (NULL == envVar || '\0' == envVar[0]) { PR_SetError(PR_UNKNOWN_ERROR, 0); return NULL; } ptr = envVar; while (1) { if ((ptr[len] == ':') && (strncmp(ptr, name, len) == 0)) { ptr += len + 1; if (PR_sscanf(ptr, "%d:0x%" PR_SCNxOSFD, &fileType, &osfd) != 2) { PR_SetError(PR_UNKNOWN_ERROR, 0); return NULL; } switch ((PRDescType)fileType) { case PR_DESC_FILE: fd = PR_ImportFile(osfd); break; case PR_DESC_PIPE: fd = PR_ImportPipe(osfd); break; case PR_DESC_SOCKET_TCP: fd = PR_ImportTCPSocket(osfd); break; case PR_DESC_SOCKET_UDP: fd = PR_ImportUDPSocket(osfd); break; default: PR_ASSERT(0); PR_SetError(PR_UNKNOWN_ERROR, 0); fd = NULL; break; } if (fd) { /* * An inherited FD is inheritable by default. * The child process needs to call PR_SetFDInheritable * to make it non-inheritable if so desired. */ fd->secret->inheritable = _PR_TRI_TRUE; } return fd; } /* Skip three colons */ nColons = 0; while (*ptr) { if (*ptr == ':') { if (++nColons == 3) { break; } } ptr++; } if (*ptr == '\0') { PR_SetError(PR_UNKNOWN_ERROR, 0); return NULL; } ptr++; } } PR_IMPLEMENT(PRProcess*) PR_CreateProcess( const char *path, char *const *argv, char *const *envp, const PRProcessAttr *attr) { return _PR_MD_CREATE_PROCESS(path, argv, envp, attr); } /* PR_CreateProcess */ PR_IMPLEMENT(PRStatus) PR_CreateProcessDetached( const char *path, char *const *argv, char *const *envp, const PRProcessAttr *attr) { PRProcess *process; PRStatus rv; process = PR_CreateProcess(path, argv, envp, attr); if (NULL == process) { return PR_FAILURE; } rv = PR_DetachProcess(process); PR_ASSERT(PR_SUCCESS == rv); if (rv == PR_FAILURE) { PR_DELETE(process); return PR_FAILURE; } return PR_SUCCESS; } PR_IMPLEMENT(PRStatus) PR_DetachProcess(PRProcess *process) { return _PR_MD_DETACH_PROCESS(process); } PR_IMPLEMENT(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode) { return _PR_MD_WAIT_PROCESS(process, exitCode); } /* PR_WaitProcess */ PR_IMPLEMENT(PRStatus) PR_KillProcess(PRProcess *process) { return _PR_MD_KILL_PROCESS(process); } /* ******************************************************************** * * Module initialization * ******************************************************************** */ static struct { PRLock *ml; PRCondVar *cv; } mod_init; static void _PR_InitCallOnce(void) { mod_init.ml = PR_NewLock(); PR_ASSERT(NULL != mod_init.ml); mod_init.cv = PR_NewCondVar(mod_init.ml); PR_ASSERT(NULL != mod_init.cv); } void _PR_CleanupCallOnce() { PR_DestroyLock(mod_init.ml); mod_init.ml = NULL; PR_DestroyCondVar(mod_init.cv); mod_init.cv = NULL; } PR_IMPLEMENT(PRStatus) PR_CallOnce( PRCallOnceType *once, PRCallOnceFN func) { if (!_pr_initialized) { _PR_ImplicitInitialization(); } PR_Lock(mod_init.ml); PRIntn initialized = once->initialized; PRStatus status = once->status; PR_Unlock(mod_init.ml); if (!initialized) { if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) { status = (*func)(); PR_Lock(mod_init.ml); once->status = status; once->initialized = 1; PR_NotifyAllCondVar(mod_init.cv); PR_Unlock(mod_init.ml); } else { PR_Lock(mod_init.ml); while (!once->initialized) { PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); } status = once->status; PR_Unlock(mod_init.ml); if (PR_SUCCESS != status) { PR_SetError(PR_CALL_ONCE_ERROR, 0); } } return status; } if (PR_SUCCESS != status) { PR_SetError(PR_CALL_ONCE_ERROR, 0); } return status; } PR_IMPLEMENT(PRStatus) PR_CallOnceWithArg( PRCallOnceType *once, PRCallOnceWithArgFN func, void *arg) { if (!_pr_initialized) { _PR_ImplicitInitialization(); } PR_Lock(mod_init.ml); PRIntn initialized = once->initialized; PRStatus status = once->status; PR_Unlock(mod_init.ml); if (!initialized) { if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) { status = (*func)(arg); PR_Lock(mod_init.ml); once->status = status; once->initialized = 1; PR_NotifyAllCondVar(mod_init.cv); PR_Unlock(mod_init.ml); } else { PR_Lock(mod_init.ml); while (!once->initialized) { PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); } status = once->status; PR_Unlock(mod_init.ml); if (PR_SUCCESS != status) { PR_SetError(PR_CALL_ONCE_ERROR, 0); } } return status; } if (PR_SUCCESS != status) { PR_SetError(PR_CALL_ONCE_ERROR, 0); } return status; } PRBool _PR_Obsolete(const char *obsolete, const char *preferred) { #if defined(DEBUG) PR_fprintf( PR_STDERR, "'%s' is obsolete. Use '%s' instead.\n", obsolete, (NULL == preferred) ? "something else" : preferred); #endif return PR_FALSE; } /* _PR_Obsolete */ /* prinit.c */