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