Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
/*
* 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 */