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