andre@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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: /* andre@0: * ntmisc.c andre@0: * andre@0: */ andre@0: andre@0: #include "primpl.h" andre@0: #include /* for fabs() */ andre@0: #include andre@0: andre@0: char *_PR_MD_GET_ENV(const char *name) andre@0: { andre@0: return getenv(name); andre@0: } andre@0: andre@0: /* andre@0: ** _PR_MD_PUT_ENV() -- add or change environment variable andre@0: ** andre@0: ** andre@0: */ andre@0: PRIntn _PR_MD_PUT_ENV(const char *name) andre@0: { andre@0: return(putenv(name)); andre@0: } andre@0: andre@0: andre@0: /* andre@0: ************************************************************************** andre@0: ************************************************************************** andre@0: ** andre@0: ** Date and time routines andre@0: ** andre@0: ************************************************************************** andre@0: ************************************************************************** andre@0: */ andre@0: andre@0: /* andre@0: * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME. andre@0: * We store the value in a PRTime variable for convenience. andre@0: */ andre@0: #ifdef __GNUC__ andre@0: const PRTime _pr_filetime_offset = 116444736000000000LL; andre@0: const PRTime _pr_filetime_divisor = 10LL; andre@0: #else andre@0: const PRTime _pr_filetime_offset = 116444736000000000i64; andre@0: const PRTime _pr_filetime_divisor = 10i64; andre@0: #endif andre@0: andre@0: #ifdef WINCE andre@0: andre@0: #define FILETIME_TO_INT64(ft) \ andre@0: (((PRInt64)ft.dwHighDateTime) << 32 | (PRInt64)ft.dwLowDateTime) andre@0: andre@0: static void andre@0: LowResTime(LPFILETIME lpft) andre@0: { andre@0: GetCurrentFT(lpft); andre@0: } andre@0: andre@0: typedef struct CalibrationData { andre@0: long double freq; /* The performance counter frequency */ andre@0: long double offset; /* The low res 'epoch' */ andre@0: long double timer_offset; /* The high res 'epoch' */ andre@0: andre@0: /* The last high res time that we returned since recalibrating */ andre@0: PRInt64 last; andre@0: andre@0: PRBool calibrated; andre@0: andre@0: CRITICAL_SECTION data_lock; andre@0: CRITICAL_SECTION calibration_lock; andre@0: PRInt64 granularity; andre@0: } CalibrationData; andre@0: andre@0: static CalibrationData calibration; andre@0: andre@0: typedef void (*GetSystemTimeAsFileTimeFcn)(LPFILETIME); andre@0: static GetSystemTimeAsFileTimeFcn ce6_GetSystemTimeAsFileTime = NULL; andre@0: andre@0: static void andre@0: NowCalibrate(void) andre@0: { andre@0: FILETIME ft, ftStart; andre@0: LARGE_INTEGER liFreq, now; andre@0: andre@0: if (calibration.freq == 0.0) { andre@0: if(!QueryPerformanceFrequency(&liFreq)) { andre@0: /* High-performance timer is unavailable */ andre@0: calibration.freq = -1.0; andre@0: } else { andre@0: calibration.freq = (long double) liFreq.QuadPart; andre@0: } andre@0: } andre@0: if (calibration.freq > 0.0) { andre@0: PRInt64 calibrationDelta = 0; andre@0: /* andre@0: * By wrapping a timeBegin/EndPeriod pair of calls around this loop, andre@0: * the loop seems to take much less time (1 ms vs 15ms) on Vista. andre@0: */ andre@0: timeBeginPeriod(1); andre@0: LowResTime(&ftStart); andre@0: do { andre@0: LowResTime(&ft); andre@0: } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0); andre@0: timeEndPeriod(1); andre@0: andre@0: calibration.granularity = andre@0: (FILETIME_TO_INT64(ft) - FILETIME_TO_INT64(ftStart))/10; andre@0: andre@0: QueryPerformanceCounter(&now); andre@0: andre@0: calibration.offset = (long double) FILETIME_TO_INT64(ft); andre@0: calibration.timer_offset = (long double) now.QuadPart; andre@0: /* andre@0: * The windows epoch is around 1600. The unix epoch is around 1970. andre@0: * _pr_filetime_offset is the difference (in windows time units which andre@0: * are 10 times more highres than the JS time unit) andre@0: */ andre@0: calibration.offset -= _pr_filetime_offset; andre@0: calibration.offset *= 0.1; andre@0: calibration.last = 0; andre@0: andre@0: calibration.calibrated = PR_TRUE; andre@0: } andre@0: } andre@0: andre@0: #define CALIBRATIONLOCK_SPINCOUNT 0 andre@0: #define DATALOCK_SPINCOUNT 4096 andre@0: #define LASTLOCK_SPINCOUNT 4096 andre@0: andre@0: void andre@0: _MD_InitTime(void) andre@0: { andre@0: /* try for CE6 GetSystemTimeAsFileTime first */ andre@0: HANDLE h = GetModuleHandleW(L"coredll.dll"); andre@0: ce6_GetSystemTimeAsFileTime = (GetSystemTimeAsFileTimeFcn) andre@0: GetProcAddressA(h, "GetSystemTimeAsFileTime"); andre@0: andre@0: /* otherwise go the slow route */ andre@0: if (ce6_GetSystemTimeAsFileTime == NULL) { andre@0: memset(&calibration, 0, sizeof(calibration)); andre@0: NowCalibrate(); andre@0: InitializeCriticalSection(&calibration.calibration_lock); andre@0: InitializeCriticalSection(&calibration.data_lock); andre@0: } andre@0: } andre@0: andre@0: void andre@0: _MD_CleanupTime(void) andre@0: { andre@0: if (ce6_GetSystemTimeAsFileTime == NULL) { andre@0: DeleteCriticalSection(&calibration.calibration_lock); andre@0: DeleteCriticalSection(&calibration.data_lock); andre@0: } andre@0: } andre@0: andre@0: #define MUTEX_SETSPINCOUNT(m, c) andre@0: andre@0: /* andre@0: *----------------------------------------------------------------------- andre@0: * andre@0: * PR_Now -- andre@0: * andre@0: * Returns the current time in microseconds since the epoch. andre@0: * The epoch is midnight January 1, 1970 GMT. andre@0: * The implementation is machine dependent. This is the andre@0: * implementation for Windows. andre@0: * Cf. time_t time(time_t *tp) andre@0: * andre@0: *----------------------------------------------------------------------- andre@0: */ andre@0: andre@0: PR_IMPLEMENT(PRTime) andre@0: PR_Now(void) andre@0: { andre@0: long double lowresTime, highresTimerValue; andre@0: FILETIME ft; andre@0: LARGE_INTEGER now; andre@0: PRBool calibrated = PR_FALSE; andre@0: PRBool needsCalibration = PR_FALSE; andre@0: PRInt64 returnedTime; andre@0: long double cachedOffset = 0.0; andre@0: andre@0: if (ce6_GetSystemTimeAsFileTime) { andre@0: union { andre@0: FILETIME ft; andre@0: PRTime prt; andre@0: } currentTime; andre@0: andre@0: PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); andre@0: andre@0: ce6_GetSystemTimeAsFileTime(¤tTime.ft); andre@0: andre@0: /* written this way on purpose, since the second term becomes andre@0: * a constant, and the entire expression is faster to execute. andre@0: */ andre@0: return currentTime.prt/_pr_filetime_divisor - andre@0: _pr_filetime_offset/_pr_filetime_divisor; andre@0: } andre@0: andre@0: do { andre@0: if (!calibration.calibrated || needsCalibration) { andre@0: EnterCriticalSection(&calibration.calibration_lock); andre@0: EnterCriticalSection(&calibration.data_lock); andre@0: andre@0: /* Recalibrate only if no one else did before us */ andre@0: if (calibration.offset == cachedOffset) { andre@0: /* andre@0: * Since calibration can take a while, make any other andre@0: * threads immediately wait andre@0: */ andre@0: MUTEX_SETSPINCOUNT(&calibration.data_lock, 0); andre@0: andre@0: NowCalibrate(); andre@0: andre@0: calibrated = PR_TRUE; andre@0: andre@0: /* Restore spin count */ andre@0: MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT); andre@0: } andre@0: LeaveCriticalSection(&calibration.data_lock); andre@0: LeaveCriticalSection(&calibration.calibration_lock); andre@0: } andre@0: andre@0: /* Calculate a low resolution time */ andre@0: LowResTime(&ft); andre@0: lowresTime = andre@0: ((long double)(FILETIME_TO_INT64(ft) - _pr_filetime_offset)) * 0.1; andre@0: andre@0: if (calibration.freq > 0.0) { andre@0: long double highresTime, diff; andre@0: DWORD timeAdjustment, timeIncrement; andre@0: BOOL timeAdjustmentDisabled; andre@0: andre@0: /* Default to 15.625 ms if the syscall fails */ andre@0: long double skewThreshold = 15625.25; andre@0: andre@0: /* Grab high resolution time */ andre@0: QueryPerformanceCounter(&now); andre@0: highresTimerValue = (long double)now.QuadPart; andre@0: andre@0: EnterCriticalSection(&calibration.data_lock); andre@0: highresTime = calibration.offset + 1000000L * andre@0: (highresTimerValue-calibration.timer_offset)/calibration.freq; andre@0: cachedOffset = calibration.offset; andre@0: andre@0: /* andre@0: * On some dual processor/core systems, we might get an earlier andre@0: * time so we cache the last time that we returned. andre@0: */ andre@0: calibration.last = PR_MAX(calibration.last,(PRInt64)highresTime); andre@0: returnedTime = calibration.last; andre@0: LeaveCriticalSection(&calibration.data_lock); andre@0: andre@0: /* Get an estimate of clock ticks per second from our own test */ andre@0: skewThreshold = calibration.granularity; andre@0: /* Check for clock skew */ andre@0: diff = lowresTime - highresTime; andre@0: andre@0: /* andre@0: * For some reason that I have not determined, the skew can be andre@0: * up to twice a kernel tick. This does not seem to happen by andre@0: * itself, but I have only seen it triggered by another program andre@0: * doing some kind of file I/O. The symptoms are a negative diff andre@0: * followed by an equally large positive diff. andre@0: */ andre@0: if (fabs(diff) > 2*skewThreshold) { andre@0: if (calibrated) { andre@0: /* andre@0: * If we already calibrated once this instance, and the andre@0: * clock is still skewed, then either the processor(s) are andre@0: * wildly changing clockspeed or the system is so busy that andre@0: * we get switched out for long periods of time. In either andre@0: * case, it would be infeasible to make use of high andre@0: * resolution results for anything, so let's resort to old andre@0: * behavior for this call. It's possible that in the andre@0: * future, the user will want the high resolution timer, so andre@0: * we don't disable it entirely. andre@0: */ andre@0: returnedTime = (PRInt64)lowresTime; andre@0: needsCalibration = PR_FALSE; andre@0: } else { andre@0: /* andre@0: * It is possible that when we recalibrate, we will return andre@0: * a value less than what we have returned before; this is andre@0: * unavoidable. We cannot tell the different between a andre@0: * faulty QueryPerformanceCounter implementation and user andre@0: * changes to the operating system time. Since we must andre@0: * respect user changes to the operating system time, we andre@0: * cannot maintain the invariant that Date.now() never andre@0: * decreases; the old implementation has this behavior as andre@0: * well. andre@0: */ andre@0: needsCalibration = PR_TRUE; andre@0: } andre@0: } else { andre@0: /* No detectable clock skew */ andre@0: returnedTime = (PRInt64)highresTime; andre@0: needsCalibration = PR_FALSE; andre@0: } andre@0: } else { andre@0: /* No high resolution timer is available, so fall back */ andre@0: returnedTime = (PRInt64)lowresTime; andre@0: } andre@0: } while (needsCalibration); andre@0: andre@0: return returnedTime; andre@0: } andre@0: andre@0: #else andre@0: andre@0: PR_IMPLEMENT(PRTime) andre@0: PR_Now(void) andre@0: { andre@0: PRTime prt; andre@0: FILETIME ft; andre@0: SYSTEMTIME st; andre@0: andre@0: GetSystemTime(&st); andre@0: SystemTimeToFileTime(&st, &ft); andre@0: _PR_FileTimeToPRTime(&ft, &prt); andre@0: return prt; andre@0: } andre@0: andre@0: #endif andre@0: andre@0: /* andre@0: *********************************************************************** andre@0: *********************************************************************** andre@0: * andre@0: * Process creation routines andre@0: * andre@0: *********************************************************************** andre@0: *********************************************************************** andre@0: */ andre@0: andre@0: /* andre@0: * Assemble the command line by concatenating the argv array. andre@0: * On success, this function returns 0 and the resulting command andre@0: * line is returned in *cmdLine. On failure, it returns -1. andre@0: */ andre@0: static int assembleCmdLine(char *const *argv, char **cmdLine) andre@0: { andre@0: char *const *arg; andre@0: char *p, *q; andre@0: size_t cmdLineSize; andre@0: int numBackslashes; andre@0: int i; andre@0: int argNeedQuotes; andre@0: andre@0: /* andre@0: * Find out how large the command line buffer should be. andre@0: */ andre@0: cmdLineSize = 0; andre@0: for (arg = argv; *arg; arg++) { andre@0: /* andre@0: * \ and " need to be escaped by a \. In the worst case, andre@0: * every character is a \ or ", so the string of length andre@0: * may double. If we quote an argument, that needs two ". andre@0: * Finally, we need a space between arguments, and andre@0: * a null byte at the end of command line. andre@0: */ andre@0: cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */ andre@0: + 2 /* we quote every argument */ andre@0: + 1; /* space in between, or final null */ andre@0: } andre@0: p = *cmdLine = PR_MALLOC((PRUint32) cmdLineSize); andre@0: if (p == NULL) { andre@0: return -1; andre@0: } andre@0: andre@0: for (arg = argv; *arg; arg++) { andre@0: /* Add a space to separates the arguments */ andre@0: if (arg != argv) { andre@0: *p++ = ' '; andre@0: } andre@0: q = *arg; andre@0: numBackslashes = 0; andre@0: argNeedQuotes = 0; andre@0: andre@0: /* andre@0: * If the argument is empty or contains white space, it needs to andre@0: * be quoted. andre@0: */ andre@0: if (**arg == '\0' || strpbrk(*arg, " \f\n\r\t\v")) { andre@0: argNeedQuotes = 1; andre@0: } andre@0: andre@0: if (argNeedQuotes) { andre@0: *p++ = '"'; andre@0: } andre@0: while (*q) { andre@0: if (*q == '\\') { andre@0: numBackslashes++; andre@0: q++; andre@0: } else if (*q == '"') { andre@0: if (numBackslashes) { andre@0: /* andre@0: * Double the backslashes since they are followed andre@0: * by a quote andre@0: */ andre@0: for (i = 0; i < 2 * numBackslashes; i++) { andre@0: *p++ = '\\'; andre@0: } andre@0: numBackslashes = 0; andre@0: } andre@0: /* To escape the quote */ andre@0: *p++ = '\\'; andre@0: *p++ = *q++; andre@0: } else { andre@0: if (numBackslashes) { andre@0: /* andre@0: * Backslashes are not followed by a quote, so andre@0: * don't need to double the backslashes. andre@0: */ andre@0: for (i = 0; i < numBackslashes; i++) { andre@0: *p++ = '\\'; andre@0: } andre@0: numBackslashes = 0; andre@0: } andre@0: *p++ = *q++; andre@0: } andre@0: } andre@0: andre@0: /* Now we are at the end of this argument */ andre@0: if (numBackslashes) { andre@0: /* andre@0: * Double the backslashes if we have a quote string andre@0: * delimiter at the end. andre@0: */ andre@0: if (argNeedQuotes) { andre@0: numBackslashes *= 2; andre@0: } andre@0: for (i = 0; i < numBackslashes; i++) { andre@0: *p++ = '\\'; andre@0: } andre@0: } andre@0: if (argNeedQuotes) { andre@0: *p++ = '"'; andre@0: } andre@0: } andre@0: andre@0: *p = '\0'; andre@0: return 0; andre@0: } andre@0: andre@0: /* andre@0: * Assemble the environment block by concatenating the envp array andre@0: * (preserving the terminating null byte in each array element) andre@0: * and adding a null byte at the end. andre@0: * andre@0: * Returns 0 on success. The resulting environment block is returned andre@0: * in *envBlock. Note that if envp is NULL, a NULL pointer is returned andre@0: * in *envBlock. Returns -1 on failure. andre@0: */ andre@0: static int assembleEnvBlock(char **envp, char **envBlock) andre@0: { andre@0: char *p; andre@0: char *q; andre@0: char **env; andre@0: char *curEnv; andre@0: char *cwdStart, *cwdEnd; andre@0: size_t envBlockSize; andre@0: andre@0: if (envp == NULL) { andre@0: *envBlock = NULL; andre@0: return 0; andre@0: } andre@0: andre@0: #ifdef WINCE andre@0: { andre@0: PRUnichar *wideCurEnv = mozce_GetEnvString(); andre@0: int len = WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, andre@0: NULL, 0, NULL, NULL); andre@0: curEnv = (char *) PR_MALLOC(len * sizeof(char)); andre@0: WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, andre@0: curEnv, len, NULL, NULL); andre@0: free(wideCurEnv); andre@0: } andre@0: #else andre@0: curEnv = GetEnvironmentStrings(); andre@0: #endif andre@0: andre@0: cwdStart = curEnv; andre@0: while (*cwdStart) { andre@0: if (cwdStart[0] == '=' && cwdStart[1] != '\0' andre@0: && cwdStart[2] == ':' && cwdStart[3] == '=') { andre@0: break; andre@0: } andre@0: cwdStart += strlen(cwdStart) + 1; andre@0: } andre@0: cwdEnd = cwdStart; andre@0: if (*cwdEnd) { andre@0: cwdEnd += strlen(cwdEnd) + 1; andre@0: while (*cwdEnd) { andre@0: if (cwdEnd[0] != '=' || cwdEnd[1] == '\0' andre@0: || cwdEnd[2] != ':' || cwdEnd[3] != '=') { andre@0: break; andre@0: } andre@0: cwdEnd += strlen(cwdEnd) + 1; andre@0: } andre@0: } andre@0: envBlockSize = cwdEnd - cwdStart; andre@0: andre@0: for (env = envp; *env; env++) { andre@0: envBlockSize += strlen(*env) + 1; andre@0: } andre@0: envBlockSize++; andre@0: andre@0: p = *envBlock = PR_MALLOC((PRUint32) envBlockSize); andre@0: if (p == NULL) { andre@0: #ifdef WINCE andre@0: PR_Free(curEnv); andre@0: #else andre@0: FreeEnvironmentStrings(curEnv); andre@0: #endif andre@0: return -1; andre@0: } andre@0: andre@0: q = cwdStart; andre@0: while (q < cwdEnd) { andre@0: *p++ = *q++; andre@0: } andre@0: #ifdef WINCE andre@0: PR_Free(curEnv); andre@0: #else andre@0: FreeEnvironmentStrings(curEnv); andre@0: #endif andre@0: andre@0: for (env = envp; *env; env++) { andre@0: q = *env; andre@0: while (*q) { andre@0: *p++ = *q++; andre@0: } andre@0: *p++ = '\0'; andre@0: } andre@0: *p = '\0'; andre@0: return 0; andre@0: } andre@0: andre@0: /* andre@0: * For qsort. We sort (case-insensitive) the environment strings andre@0: * before generating the environment block. andre@0: */ andre@0: static int compare(const void *arg1, const void *arg2) andre@0: { andre@0: return _stricmp(* (char**)arg1, * (char**)arg2); andre@0: } andre@0: andre@0: PRProcess * _PR_CreateWindowsProcess( andre@0: const char *path, andre@0: char *const *argv, andre@0: char *const *envp, andre@0: const PRProcessAttr *attr) andre@0: { andre@0: #ifdef WINCE andre@0: STARTUPINFOW startupInfo; andre@0: PRUnichar *wideCmdLine; andre@0: PRUnichar *wideCwd; andre@0: int len = 0; andre@0: #else andre@0: STARTUPINFO startupInfo; andre@0: #endif andre@0: DWORD creationFlags = 0; andre@0: PROCESS_INFORMATION procInfo; andre@0: BOOL retVal; andre@0: char *cmdLine = NULL; andre@0: char *envBlock = NULL; andre@0: char **newEnvp = NULL; andre@0: const char *cwd = NULL; /* current working directory */ andre@0: PRProcess *proc = NULL; andre@0: PRBool hasFdInheritBuffer; andre@0: andre@0: proc = PR_NEW(PRProcess); andre@0: if (!proc) { andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); andre@0: goto errorExit; andre@0: } andre@0: andre@0: if (assembleCmdLine(argv, &cmdLine) == -1) { andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); andre@0: goto errorExit; andre@0: } andre@0: andre@0: #ifndef WINCE andre@0: /* andre@0: * If attr->fdInheritBuffer is not NULL, we need to insert andre@0: * it into the envp array, so envp cannot be NULL. andre@0: */ andre@0: hasFdInheritBuffer = (attr && attr->fdInheritBuffer); andre@0: if ((envp == NULL) && hasFdInheritBuffer) { andre@0: envp = environ; andre@0: } andre@0: andre@0: if (envp != NULL) { andre@0: int idx; andre@0: int numEnv; andre@0: PRBool found = PR_FALSE; andre@0: andre@0: numEnv = 0; andre@0: while (envp[numEnv]) { andre@0: numEnv++; andre@0: } andre@0: newEnvp = (char **) PR_MALLOC((numEnv + 2) * sizeof(char *)); andre@0: for (idx = 0; idx < numEnv; idx++) { andre@0: newEnvp[idx] = envp[idx]; andre@0: if (hasFdInheritBuffer && !found andre@0: && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) { andre@0: newEnvp[idx] = attr->fdInheritBuffer; andre@0: found = PR_TRUE; andre@0: } andre@0: } andre@0: if (hasFdInheritBuffer && !found) { andre@0: newEnvp[idx++] = attr->fdInheritBuffer; andre@0: } andre@0: newEnvp[idx] = NULL; andre@0: qsort((void *) newEnvp, (size_t) idx, sizeof(char *), compare); andre@0: } andre@0: if (assembleEnvBlock(newEnvp, &envBlock) == -1) { andre@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); andre@0: goto errorExit; andre@0: } andre@0: andre@0: ZeroMemory(&startupInfo, sizeof(startupInfo)); andre@0: startupInfo.cb = sizeof(startupInfo); andre@0: andre@0: if (attr) { andre@0: PRBool redirected = PR_FALSE; andre@0: andre@0: /* andre@0: * XXX the default value for stdin, stdout, and stderr andre@0: * should probably be the console input and output, not andre@0: * those of the parent process. andre@0: */ andre@0: startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); andre@0: startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); andre@0: startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); andre@0: if (attr->stdinFd) { andre@0: startupInfo.hStdInput = (HANDLE) attr->stdinFd->secret->md.osfd; andre@0: redirected = PR_TRUE; andre@0: } andre@0: if (attr->stdoutFd) { andre@0: startupInfo.hStdOutput = (HANDLE) attr->stdoutFd->secret->md.osfd; andre@0: redirected = PR_TRUE; andre@0: /* andre@0: * If stdout is redirected, we can assume that the process will andre@0: * not write anything useful to the console windows, and therefore andre@0: * automatically set the CREATE_NO_WINDOW flag. andre@0: */ andre@0: creationFlags |= CREATE_NO_WINDOW; andre@0: } andre@0: if (attr->stderrFd) { andre@0: startupInfo.hStdError = (HANDLE) attr->stderrFd->secret->md.osfd; andre@0: redirected = PR_TRUE; andre@0: } andre@0: if (redirected) { andre@0: startupInfo.dwFlags |= STARTF_USESTDHANDLES; andre@0: } andre@0: cwd = attr->currentDirectory; andre@0: } andre@0: #endif andre@0: andre@0: #ifdef WINCE andre@0: len = MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, NULL, 0); andre@0: wideCmdLine = (PRUnichar *)PR_MALLOC(len * sizeof(PRUnichar)); andre@0: MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, wideCmdLine, len); andre@0: len = MultiByteToWideChar(CP_ACP, 0, cwd, -1, NULL, 0); andre@0: wideCwd = PR_MALLOC(len * sizeof(PRUnichar)); andre@0: MultiByteToWideChar(CP_ACP, 0, cwd, -1, wideCwd, len); andre@0: retVal = CreateProcessW(NULL, andre@0: wideCmdLine, andre@0: NULL, /* security attributes for the new andre@0: * process */ andre@0: NULL, /* security attributes for the primary andre@0: * thread in the new process */ andre@0: TRUE, /* inherit handles */ andre@0: creationFlags, andre@0: envBlock, /* an environment block, consisting andre@0: * of a null-terminated block of andre@0: * null-terminated strings. Each andre@0: * string is in the form: andre@0: * name=value andre@0: * XXX: usually NULL */ andre@0: wideCwd, /* current drive and directory */ andre@0: &startupInfo, andre@0: &procInfo andre@0: ); andre@0: PR_Free(wideCmdLine); andre@0: PR_Free(wideCwd); andre@0: #else andre@0: retVal = CreateProcess(NULL, andre@0: cmdLine, andre@0: NULL, /* security attributes for the new andre@0: * process */ andre@0: NULL, /* security attributes for the primary andre@0: * thread in the new process */ andre@0: TRUE, /* inherit handles */ andre@0: creationFlags, andre@0: envBlock, /* an environment block, consisting andre@0: * of a null-terminated block of andre@0: * null-terminated strings. Each andre@0: * string is in the form: andre@0: * name=value andre@0: * XXX: usually NULL */ andre@0: cwd, /* current drive and directory */ andre@0: &startupInfo, andre@0: &procInfo andre@0: ); andre@0: #endif andre@0: andre@0: if (retVal == FALSE) { andre@0: /* XXX what error code? */ andre@0: PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); andre@0: goto errorExit; andre@0: } andre@0: andre@0: CloseHandle(procInfo.hThread); andre@0: proc->md.handle = procInfo.hProcess; andre@0: proc->md.id = procInfo.dwProcessId; andre@0: andre@0: PR_DELETE(cmdLine); andre@0: if (newEnvp) { andre@0: PR_DELETE(newEnvp); andre@0: } andre@0: if (envBlock) { andre@0: PR_DELETE(envBlock); andre@0: } andre@0: return proc; andre@0: andre@0: errorExit: andre@0: if (cmdLine) { andre@0: PR_DELETE(cmdLine); andre@0: } andre@0: if (newEnvp) { andre@0: PR_DELETE(newEnvp); andre@0: } andre@0: if (envBlock) { andre@0: PR_DELETE(envBlock); andre@0: } andre@0: if (proc) { andre@0: PR_DELETE(proc); andre@0: } andre@0: return NULL; andre@0: } /* _PR_CreateWindowsProcess */ andre@0: andre@0: PRStatus _PR_DetachWindowsProcess(PRProcess *process) andre@0: { andre@0: CloseHandle(process->md.handle); andre@0: PR_DELETE(process); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: * XXX: This implementation is a temporary quick solution. andre@0: * It can be called by native threads only (not by fibers). andre@0: */ andre@0: PRStatus _PR_WaitWindowsProcess(PRProcess *process, andre@0: PRInt32 *exitCode) andre@0: { andre@0: DWORD dwRetVal; andre@0: andre@0: dwRetVal = WaitForSingleObject(process->md.handle, INFINITE); andre@0: if (dwRetVal == WAIT_FAILED) { andre@0: PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: PR_ASSERT(dwRetVal == WAIT_OBJECT_0); andre@0: if (exitCode != NULL && andre@0: GetExitCodeProcess(process->md.handle, exitCode) == FALSE) { andre@0: PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: CloseHandle(process->md.handle); andre@0: PR_DELETE(process); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: PRStatus _PR_KillWindowsProcess(PRProcess *process) andre@0: { andre@0: /* andre@0: * On Unix, if a process terminates normally, its exit code is andre@0: * between 0 and 255. So here on Windows, we use the exit code andre@0: * 256 to indicate that the process is killed. andre@0: */ andre@0: if (TerminateProcess(process->md.handle, 256)) { andre@0: return PR_SUCCESS; andre@0: } andre@0: PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen) andre@0: { andre@0: PRIntn rv; andre@0: PRInt32 syserror; andre@0: andre@0: rv = gethostname(name, (PRInt32) namelen); andre@0: if (0 == rv) { andre@0: return PR_SUCCESS; andre@0: } andre@0: syserror = WSAGetLastError(); andre@0: PR_ASSERT(WSANOTINITIALISED != syserror); andre@0: _PR_MD_MAP_GETHOSTNAME_ERROR(syserror); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char *name, PRUint32 namelen) andre@0: { andre@0: OSVERSIONINFO osvi; andre@0: andre@0: PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE)); andre@0: andre@0: ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); andre@0: osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); andre@0: andre@0: if (! GetVersionEx (&osvi) ) { andre@0: _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: switch (osvi.dwPlatformId) { andre@0: case VER_PLATFORM_WIN32_NT: andre@0: if (PR_SI_SYSNAME == cmd) andre@0: (void)PR_snprintf(name, namelen, "Windows_NT"); andre@0: else if (PR_SI_RELEASE == cmd) andre@0: (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, andre@0: osvi.dwMinorVersion); andre@0: break; andre@0: case VER_PLATFORM_WIN32_WINDOWS: andre@0: if (PR_SI_SYSNAME == cmd) { andre@0: if ((osvi.dwMajorVersion > 4) || andre@0: ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion > 0))) andre@0: (void)PR_snprintf(name, namelen, "Windows_98"); andre@0: else andre@0: (void)PR_snprintf(name, namelen, "Windows_95"); andre@0: } else if (PR_SI_RELEASE == cmd) { andre@0: (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, andre@0: osvi.dwMinorVersion); andre@0: } andre@0: break; andre@0: #ifdef VER_PLATFORM_WIN32_CE andre@0: case VER_PLATFORM_WIN32_CE: andre@0: if (PR_SI_SYSNAME == cmd) andre@0: (void)PR_snprintf(name, namelen, "Windows_CE"); andre@0: else if (PR_SI_RELEASE == cmd) andre@0: (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, andre@0: osvi.dwMinorVersion); andre@0: break; andre@0: #endif andre@0: default: andre@0: if (PR_SI_SYSNAME == cmd) andre@0: (void)PR_snprintf(name, namelen, "Windows_Unknown"); andre@0: else if (PR_SI_RELEASE == cmd) andre@0: (void)PR_snprintf(name, namelen, "%d.%d",0,0); andre@0: break; andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: PRStatus _MD_WindowsGetReleaseName(char *name, PRUint32 namelen) andre@0: { andre@0: OSVERSIONINFO osvi; andre@0: andre@0: ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); andre@0: osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); andre@0: andre@0: if (! GetVersionEx (&osvi) ) { andre@0: _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: switch (osvi.dwPlatformId) { andre@0: case VER_PLATFORM_WIN32_NT: andre@0: case VER_PLATFORM_WIN32_WINDOWS: andre@0: (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, andre@0: osvi.dwMinorVersion); andre@0: break; andre@0: default: andre@0: (void)PR_snprintf(name, namelen, "%d.%d",0,0); andre@0: break; andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: ********************************************************************** andre@0: * andre@0: * Memory-mapped files andre@0: * andre@0: ********************************************************************** andre@0: */ andre@0: andre@0: PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) andre@0: { andre@0: DWORD dwHi, dwLo; andre@0: DWORD flProtect; andre@0: PROsfd osfd; andre@0: andre@0: osfd = ( fmap->fd == (PRFileDesc*)-1 )? -1 : fmap->fd->secret->md.osfd; andre@0: andre@0: dwLo = (DWORD) (size & 0xffffffff); andre@0: dwHi = (DWORD) (((PRUint64) size >> 32) & 0xffffffff); andre@0: andre@0: if (fmap->prot == PR_PROT_READONLY) { andre@0: flProtect = PAGE_READONLY; andre@0: fmap->md.dwAccess = FILE_MAP_READ; andre@0: } else if (fmap->prot == PR_PROT_READWRITE) { andre@0: flProtect = PAGE_READWRITE; andre@0: fmap->md.dwAccess = FILE_MAP_WRITE; andre@0: } else { andre@0: PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY); andre@0: #ifdef WINCE andre@0: /* WINCE does not have FILE_MAP_COPY. */ andre@0: PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); andre@0: return PR_FAILURE; andre@0: #else andre@0: flProtect = PAGE_WRITECOPY; andre@0: fmap->md.dwAccess = FILE_MAP_COPY; andre@0: #endif andre@0: } andre@0: andre@0: fmap->md.hFileMap = CreateFileMapping( andre@0: (HANDLE) osfd, andre@0: NULL, andre@0: flProtect, andre@0: dwHi, andre@0: dwLo, andre@0: NULL); andre@0: andre@0: if (fmap->md.hFileMap == NULL) { andre@0: PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: PRInt32 _MD_GetMemMapAlignment(void) andre@0: { andre@0: SYSTEM_INFO info; andre@0: GetSystemInfo(&info); andre@0: return info.dwAllocationGranularity; andre@0: } andre@0: andre@0: extern PRLogModuleInfo *_pr_shma_lm; andre@0: andre@0: void * _MD_MemMap( andre@0: PRFileMap *fmap, andre@0: PROffset64 offset, andre@0: PRUint32 len) andre@0: { andre@0: DWORD dwHi, dwLo; andre@0: void *addr; andre@0: andre@0: dwLo = (DWORD) (offset & 0xffffffff); andre@0: dwHi = (DWORD) (((PRUint64) offset >> 32) & 0xffffffff); andre@0: if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess, andre@0: dwHi, dwLo, len)) == NULL) { andre@0: { andre@0: LPVOID lpMsgBuf; andre@0: andre@0: FormatMessage( andre@0: FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, andre@0: NULL, andre@0: GetLastError(), andre@0: MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language andre@0: (LPTSTR) &lpMsgBuf, andre@0: 0, andre@0: NULL andre@0: ); andre@0: PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, ("md_memmap(): %s", lpMsgBuf )); andre@0: } andre@0: PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); andre@0: } andre@0: return addr; andre@0: } andre@0: andre@0: PRStatus _MD_MemUnmap(void *addr, PRUint32 len) andre@0: { andre@0: if (UnmapViewOfFile(addr)) { andre@0: return PR_SUCCESS; andre@0: } andre@0: _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: PRStatus _MD_CloseFileMap(PRFileMap *fmap) andre@0: { andre@0: CloseHandle(fmap->md.hFileMap); andre@0: PR_DELETE(fmap); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: PRStatus _MD_SyncMemMap( andre@0: PRFileDesc *fd, andre@0: void *addr, andre@0: PRUint32 len) andre@0: { andre@0: PROsfd osfd = fd->secret->md.osfd; andre@0: andre@0: /* The FlushViewOfFile page on MSDN says: andre@0: * To flush all the dirty pages plus the metadata for the file and andre@0: * ensure that they are physically written to disk, call andre@0: * FlushViewOfFile and then call the FlushFileBuffers function. andre@0: */ andre@0: if (FlushViewOfFile(addr, len) && FlushFileBuffers((HANDLE) osfd)) { andre@0: return PR_SUCCESS; andre@0: } andre@0: _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: /* andre@0: *********************************************************************** andre@0: * andre@0: * Atomic increment and decrement operations for x86 processors andre@0: * andre@0: * We don't use InterlockedIncrement and InterlockedDecrement andre@0: * because on NT 3.51 and Win95, they return a number with andre@0: * the same sign as the incremented/decremented result, rather andre@0: * than the result itself. On NT 4.0 these functions do return andre@0: * the incremented/decremented result. andre@0: * andre@0: * The result is returned in the eax register by the inline andre@0: * assembly code. We disable the harmless "no return value" andre@0: * warning (4035) for these two functions. andre@0: * andre@0: *********************************************************************** andre@0: */ andre@0: andre@0: #if defined(_M_IX86) || defined(_X86_) andre@0: andre@0: #pragma warning(disable: 4035) andre@0: PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *val) andre@0: { andre@0: #if defined(__GNUC__) andre@0: PRInt32 result; andre@0: asm volatile ("lock ; xadd %0, %1" andre@0: : "=r"(result), "=m"(*val) andre@0: : "0"(1), "m"(*val)); andre@0: return result + 1; andre@0: #else andre@0: __asm andre@0: { andre@0: mov ecx, val andre@0: mov eax, 1 andre@0: lock xadd dword ptr [ecx], eax andre@0: inc eax andre@0: } andre@0: #endif /* __GNUC__ */ andre@0: } andre@0: #pragma warning(default: 4035) andre@0: andre@0: #pragma warning(disable: 4035) andre@0: PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *val) andre@0: { andre@0: #if defined(__GNUC__) andre@0: PRInt32 result; andre@0: asm volatile ("lock ; xadd %0, %1" andre@0: : "=r"(result), "=m"(*val) andre@0: : "0"(-1), "m"(*val)); andre@0: //asm volatile("lock ; xadd %0, %1" : "=m" (val), "=a" (result) : "-1" (1)); andre@0: return result - 1; andre@0: #else andre@0: __asm andre@0: { andre@0: mov ecx, val andre@0: mov eax, 0ffffffffh andre@0: lock xadd dword ptr [ecx], eax andre@0: dec eax andre@0: } andre@0: #endif /* __GNUC__ */ andre@0: } andre@0: #pragma warning(default: 4035) andre@0: andre@0: #pragma warning(disable: 4035) andre@0: PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *intp, PRInt32 val) andre@0: { andre@0: #if defined(__GNUC__) andre@0: PRInt32 result; andre@0: //asm volatile("lock ; xadd %1, %0" : "=m" (intp), "=a" (result) : "1" (val)); andre@0: asm volatile ("lock ; xadd %0, %1" andre@0: : "=r"(result), "=m"(*intp) andre@0: : "0"(val), "m"(*intp)); andre@0: return result + val; andre@0: #else andre@0: __asm andre@0: { andre@0: mov ecx, intp andre@0: mov eax, val andre@0: mov edx, eax andre@0: lock xadd dword ptr [ecx], eax andre@0: add eax, edx andre@0: } andre@0: #endif /* __GNUC__ */ andre@0: } andre@0: #pragma warning(default: 4035) andre@0: andre@0: #ifdef _PR_HAVE_ATOMIC_CAS andre@0: andre@0: #pragma warning(disable: 4035) andre@0: void andre@0: PR_StackPush(PRStack *stack, PRStackElem *stack_elem) andre@0: { andre@0: #if defined(__GNUC__) andre@0: void **tos = (void **) stack; andre@0: void *tmp; andre@0: andre@0: retry: andre@0: if (*tos == (void *) -1) andre@0: goto retry; andre@0: andre@0: __asm__("xchg %0,%1" andre@0: : "=r" (tmp), "=m"(*tos) andre@0: : "0" (-1), "m"(*tos)); andre@0: andre@0: if (tmp == (void *) -1) andre@0: goto retry; andre@0: andre@0: *(void **)stack_elem = tmp; andre@0: __asm__("" : : : "memory"); andre@0: *tos = stack_elem; andre@0: #else andre@0: __asm andre@0: { andre@0: mov ebx, stack andre@0: mov ecx, stack_elem andre@0: retry: mov eax,[ebx] andre@0: cmp eax,-1 andre@0: je retry andre@0: mov eax,-1 andre@0: xchg dword ptr [ebx], eax andre@0: cmp eax,-1 andre@0: je retry andre@0: mov [ecx],eax andre@0: mov [ebx],ecx andre@0: } andre@0: #endif /* __GNUC__ */ andre@0: } andre@0: #pragma warning(default: 4035) andre@0: andre@0: #pragma warning(disable: 4035) andre@0: PRStackElem * andre@0: PR_StackPop(PRStack *stack) andre@0: { andre@0: #if defined(__GNUC__) andre@0: void **tos = (void **) stack; andre@0: void *tmp; andre@0: andre@0: retry: andre@0: if (*tos == (void *) -1) andre@0: goto retry; andre@0: andre@0: __asm__("xchg %0,%1" andre@0: : "=r" (tmp), "=m"(*tos) andre@0: : "0" (-1), "m"(*tos)); andre@0: andre@0: if (tmp == (void *) -1) andre@0: goto retry; andre@0: andre@0: if (tmp != (void *) 0) andre@0: { andre@0: void *next = *(void **)tmp; andre@0: *tos = next; andre@0: *(void **)tmp = 0; andre@0: } andre@0: else andre@0: *tos = tmp; andre@0: andre@0: return tmp; andre@0: #else andre@0: __asm andre@0: { andre@0: mov ebx, stack andre@0: retry: mov eax,[ebx] andre@0: cmp eax,-1 andre@0: je retry andre@0: mov eax,-1 andre@0: xchg dword ptr [ebx], eax andre@0: cmp eax,-1 andre@0: je retry andre@0: cmp eax,0 andre@0: je empty andre@0: mov ecx,[eax] andre@0: mov [ebx],ecx andre@0: mov [eax],0 andre@0: jmp done andre@0: empty: andre@0: mov [ebx],eax andre@0: done: andre@0: } andre@0: #endif /* __GNUC__ */ andre@0: } andre@0: #pragma warning(default: 4035) andre@0: andre@0: #endif /* _PR_HAVE_ATOMIC_CAS */ andre@0: andre@0: #endif /* x86 processors */