/* -*- 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 /* for fabs() */ #include 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 */