ntmisc.c (32394B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 * ntmisc.c 8 * 9 */ 10 11 #include "primpl.h" 12 #include <math.h> /* for fabs() */ 13 #include <windows.h> 14 15 char* _PR_MD_GET_ENV(const char* name) { return getenv(name); } 16 17 /* 18 ** _PR_MD_PUT_ENV() -- add or change environment variable 19 ** 20 ** 21 */ 22 PRIntn _PR_MD_PUT_ENV(const char* name) { return (putenv(name)); } 23 24 /* 25 ************************************************************************** 26 ************************************************************************** 27 ** 28 ** Date and time routines 29 ** 30 ************************************************************************** 31 ************************************************************************** 32 */ 33 34 /* 35 * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME. 36 * We store the value in a PRTime variable for convenience. 37 */ 38 #ifdef __GNUC__ 39 const PRTime _pr_filetime_offset = 116444736000000000LL; 40 const PRTime _pr_filetime_divisor = 10LL; 41 #else 42 const PRTime _pr_filetime_offset = 116444736000000000i64; 43 const PRTime _pr_filetime_divisor = 10i64; 44 #endif 45 46 #ifdef WINCE 47 48 # define FILETIME_TO_INT64(ft) \ 49 (((PRInt64)ft.dwHighDateTime) << 32 | (PRInt64)ft.dwLowDateTime) 50 51 static void LowResTime(LPFILETIME lpft) { GetCurrentFT(lpft); } 52 53 typedef struct CalibrationData { 54 long double freq; /* The performance counter frequency */ 55 long double offset; /* The low res 'epoch' */ 56 long double timer_offset; /* The high res 'epoch' */ 57 58 /* The last high res time that we returned since recalibrating */ 59 PRInt64 last; 60 61 PRBool calibrated; 62 63 CRITICAL_SECTION data_lock; 64 CRITICAL_SECTION calibration_lock; 65 PRInt64 granularity; 66 } CalibrationData; 67 68 static CalibrationData calibration; 69 70 typedef void (*GetSystemTimeAsFileTimeFcn)(LPFILETIME); 71 static GetSystemTimeAsFileTimeFcn ce6_GetSystemTimeAsFileTime = NULL; 72 73 static void NowCalibrate(void) { 74 FILETIME ft, ftStart; 75 LARGE_INTEGER liFreq, now; 76 77 if (calibration.freq == 0.0) { 78 if (!QueryPerformanceFrequency(&liFreq)) { 79 /* High-performance timer is unavailable */ 80 calibration.freq = -1.0; 81 } else { 82 calibration.freq = (long double)liFreq.QuadPart; 83 } 84 } 85 if (calibration.freq > 0.0) { 86 PRInt64 calibrationDelta = 0; 87 /* 88 * By wrapping a timeBegin/EndPeriod pair of calls around this loop, 89 * the loop seems to take much less time (1 ms vs 15ms) on Vista. 90 */ 91 timeBeginPeriod(1); 92 LowResTime(&ftStart); 93 do { 94 LowResTime(&ft); 95 } while (memcmp(&ftStart, &ft, sizeof(ft)) == 0); 96 timeEndPeriod(1); 97 98 calibration.granularity = 99 (FILETIME_TO_INT64(ft) - FILETIME_TO_INT64(ftStart)) / 10; 100 101 QueryPerformanceCounter(&now); 102 103 calibration.offset = (long double)FILETIME_TO_INT64(ft); 104 calibration.timer_offset = (long double)now.QuadPart; 105 /* 106 * The windows epoch is around 1600. The unix epoch is around 1970. 107 * _pr_filetime_offset is the difference (in windows time units which 108 * are 10 times more highres than the JS time unit) 109 */ 110 calibration.offset -= _pr_filetime_offset; 111 calibration.offset *= 0.1; 112 calibration.last = 0; 113 114 calibration.calibrated = PR_TRUE; 115 } 116 } 117 118 # define CALIBRATIONLOCK_SPINCOUNT 0 119 # define DATALOCK_SPINCOUNT 4096 120 # define LASTLOCK_SPINCOUNT 4096 121 122 void _MD_InitTime(void) { 123 /* try for CE6 GetSystemTimeAsFileTime first */ 124 HANDLE h = GetModuleHandleW(L"coredll.dll"); 125 ce6_GetSystemTimeAsFileTime = 126 (GetSystemTimeAsFileTimeFcn)GetProcAddressA(h, "GetSystemTimeAsFileTime"); 127 128 /* otherwise go the slow route */ 129 if (ce6_GetSystemTimeAsFileTime == NULL) { 130 memset(&calibration, 0, sizeof(calibration)); 131 NowCalibrate(); 132 InitializeCriticalSection(&calibration.calibration_lock); 133 InitializeCriticalSection(&calibration.data_lock); 134 } 135 } 136 137 void _MD_CleanupTime(void) { 138 if (ce6_GetSystemTimeAsFileTime == NULL) { 139 DeleteCriticalSection(&calibration.calibration_lock); 140 DeleteCriticalSection(&calibration.data_lock); 141 } 142 } 143 144 # define MUTEX_SETSPINCOUNT(m, c) 145 146 /* 147 *----------------------------------------------------------------------- 148 * 149 * PR_Now -- 150 * 151 * Returns the current time in microseconds since the epoch. 152 * The epoch is midnight January 1, 1970 GMT. 153 * The implementation is machine dependent. This is the 154 * implementation for Windows. 155 * Cf. time_t time(time_t *tp) 156 * 157 *----------------------------------------------------------------------- 158 */ 159 160 PR_IMPLEMENT(PRTime) 161 PR_Now(void) { 162 long double lowresTime, highresTimerValue; 163 FILETIME ft; 164 LARGE_INTEGER now; 165 PRBool calibrated = PR_FALSE; 166 PRBool needsCalibration = PR_FALSE; 167 PRInt64 returnedTime; 168 long double cachedOffset = 0.0; 169 170 if (ce6_GetSystemTimeAsFileTime) { 171 union { 172 FILETIME ft; 173 PRTime prt; 174 } currentTime; 175 176 PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime)); 177 178 ce6_GetSystemTimeAsFileTime(¤tTime.ft); 179 180 /* written this way on purpose, since the second term becomes 181 * a constant, and the entire expression is faster to execute. 182 */ 183 return currentTime.prt / _pr_filetime_divisor - 184 _pr_filetime_offset / _pr_filetime_divisor; 185 } 186 187 do { 188 if (!calibration.calibrated || needsCalibration) { 189 EnterCriticalSection(&calibration.calibration_lock); 190 EnterCriticalSection(&calibration.data_lock); 191 192 /* Recalibrate only if no one else did before us */ 193 if (calibration.offset == cachedOffset) { 194 /* 195 * Since calibration can take a while, make any other 196 * threads immediately wait 197 */ 198 MUTEX_SETSPINCOUNT(&calibration.data_lock, 0); 199 200 NowCalibrate(); 201 202 calibrated = PR_TRUE; 203 204 /* Restore spin count */ 205 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT); 206 } 207 LeaveCriticalSection(&calibration.data_lock); 208 LeaveCriticalSection(&calibration.calibration_lock); 209 } 210 211 /* Calculate a low resolution time */ 212 LowResTime(&ft); 213 lowresTime = 214 ((long double)(FILETIME_TO_INT64(ft) - _pr_filetime_offset)) * 0.1; 215 216 if (calibration.freq > 0.0) { 217 long double highresTime, diff; 218 DWORD timeAdjustment, timeIncrement; 219 BOOL timeAdjustmentDisabled; 220 221 /* Default to 15.625 ms if the syscall fails */ 222 long double skewThreshold = 15625.25; 223 224 /* Grab high resolution time */ 225 QueryPerformanceCounter(&now); 226 highresTimerValue = (long double)now.QuadPart; 227 228 EnterCriticalSection(&calibration.data_lock); 229 highresTime = calibration.offset + 230 1000000L * (highresTimerValue - calibration.timer_offset) / 231 calibration.freq; 232 cachedOffset = calibration.offset; 233 234 /* 235 * On some dual processor/core systems, we might get an earlier 236 * time so we cache the last time that we returned. 237 */ 238 calibration.last = PR_MAX(calibration.last, (PRInt64)highresTime); 239 returnedTime = calibration.last; 240 LeaveCriticalSection(&calibration.data_lock); 241 242 /* Get an estimate of clock ticks per second from our own test */ 243 skewThreshold = calibration.granularity; 244 /* Check for clock skew */ 245 diff = lowresTime - highresTime; 246 247 /* 248 * For some reason that I have not determined, the skew can be 249 * up to twice a kernel tick. This does not seem to happen by 250 * itself, but I have only seen it triggered by another program 251 * doing some kind of file I/O. The symptoms are a negative diff 252 * followed by an equally large positive diff. 253 */ 254 if (fabs(diff) > 2 * skewThreshold) { 255 if (calibrated) { 256 /* 257 * If we already calibrated once this instance, and the 258 * clock is still skewed, then either the processor(s) are 259 * wildly changing clockspeed or the system is so busy that 260 * we get switched out for long periods of time. In either 261 * case, it would be infeasible to make use of high 262 * resolution results for anything, so let's resort to old 263 * behavior for this call. It's possible that in the 264 * future, the user will want the high resolution timer, so 265 * we don't disable it entirely. 266 */ 267 returnedTime = (PRInt64)lowresTime; 268 needsCalibration = PR_FALSE; 269 } else { 270 /* 271 * It is possible that when we recalibrate, we will return 272 * a value less than what we have returned before; this is 273 * unavoidable. We cannot tell the different between a 274 * faulty QueryPerformanceCounter implementation and user 275 * changes to the operating system time. Since we must 276 * respect user changes to the operating system time, we 277 * cannot maintain the invariant that Date.now() never 278 * decreases; the old implementation has this behavior as 279 * well. 280 */ 281 needsCalibration = PR_TRUE; 282 } 283 } else { 284 /* No detectable clock skew */ 285 returnedTime = (PRInt64)highresTime; 286 needsCalibration = PR_FALSE; 287 } 288 } else { 289 /* No high resolution timer is available, so fall back */ 290 returnedTime = (PRInt64)lowresTime; 291 } 292 } while (needsCalibration); 293 294 return returnedTime; 295 } 296 297 #else 298 299 PR_IMPLEMENT(PRTime) 300 PR_Now(void) { 301 PRTime prt; 302 FILETIME ft; 303 SYSTEMTIME st; 304 305 GetSystemTime(&st); 306 SystemTimeToFileTime(&st, &ft); 307 _PR_FileTimeToPRTime(&ft, &prt); 308 return prt; 309 } 310 311 #endif 312 313 /* 314 *********************************************************************** 315 *********************************************************************** 316 * 317 * Process creation routines 318 * 319 *********************************************************************** 320 *********************************************************************** 321 */ 322 323 /* 324 * Assemble the command line by concatenating the argv array. 325 * On success, this function returns 0 and the resulting command 326 * line is returned in *cmdLine. On failure, it returns -1. 327 */ 328 static int assembleCmdLine(char* const* argv, char** cmdLine) { 329 char* const* arg; 330 char *p, *q; 331 size_t cmdLineSize; 332 int numBackslashes; 333 int i; 334 int argNeedQuotes; 335 336 /* 337 * Find out how large the command line buffer should be. 338 */ 339 cmdLineSize = 0; 340 for (arg = argv; *arg; arg++) { 341 /* 342 * \ and " need to be escaped by a \. In the worst case, 343 * every character is a \ or ", so the string of length 344 * may double. If we quote an argument, that needs two ". 345 * Finally, we need a space between arguments, and 346 * a null byte at the end of command line. 347 */ 348 cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */ 349 + 2 /* we quote every argument */ 350 + 1; /* space in between, or final null */ 351 } 352 p = *cmdLine = PR_MALLOC((PRUint32)cmdLineSize); 353 if (p == NULL) { 354 return -1; 355 } 356 357 for (arg = argv; *arg; arg++) { 358 /* Add a space to separates the arguments */ 359 if (arg != argv) { 360 *p++ = ' '; 361 } 362 q = *arg; 363 numBackslashes = 0; 364 argNeedQuotes = 0; 365 366 /* 367 * If the argument is empty or contains white space, it needs to 368 * be quoted. 369 */ 370 if (**arg == '\0' || strpbrk(*arg, " \f\n\r\t\v")) { 371 argNeedQuotes = 1; 372 } 373 374 if (argNeedQuotes) { 375 *p++ = '"'; 376 } 377 while (*q) { 378 if (*q == '\\') { 379 numBackslashes++; 380 q++; 381 } else if (*q == '"') { 382 if (numBackslashes) { 383 /* 384 * Double the backslashes since they are followed 385 * by a quote 386 */ 387 for (i = 0; i < 2 * numBackslashes; i++) { 388 *p++ = '\\'; 389 } 390 numBackslashes = 0; 391 } 392 /* To escape the quote */ 393 *p++ = '\\'; 394 *p++ = *q++; 395 } else { 396 if (numBackslashes) { 397 /* 398 * Backslashes are not followed by a quote, so 399 * don't need to double the backslashes. 400 */ 401 for (i = 0; i < numBackslashes; i++) { 402 *p++ = '\\'; 403 } 404 numBackslashes = 0; 405 } 406 *p++ = *q++; 407 } 408 } 409 410 /* Now we are at the end of this argument */ 411 if (numBackslashes) { 412 /* 413 * Double the backslashes if we have a quote string 414 * delimiter at the end. 415 */ 416 if (argNeedQuotes) { 417 numBackslashes *= 2; 418 } 419 for (i = 0; i < numBackslashes; i++) { 420 *p++ = '\\'; 421 } 422 } 423 if (argNeedQuotes) { 424 *p++ = '"'; 425 } 426 } 427 428 *p = '\0'; 429 return 0; 430 } 431 432 /* 433 * Assemble the environment block by concatenating the envp array 434 * (preserving the terminating null byte in each array element) 435 * and adding a null byte at the end. 436 * 437 * Returns 0 on success. The resulting environment block is returned 438 * in *envBlock. Note that if envp is NULL, a NULL pointer is returned 439 * in *envBlock. Returns -1 on failure. 440 */ 441 static int assembleEnvBlock(char** envp, char** envBlock) { 442 char* p; 443 char* q; 444 char** env; 445 char* curEnv; 446 char *cwdStart, *cwdEnd; 447 size_t envBlockSize; 448 449 if (envp == NULL) { 450 *envBlock = NULL; 451 return 0; 452 } 453 454 #ifdef WINCE 455 { 456 PRUnichar* wideCurEnv = mozce_GetEnvString(); 457 int len = 458 WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, NULL, 0, NULL, NULL); 459 curEnv = (char*)PR_MALLOC(len * sizeof(char)); 460 WideCharToMultiByte(CP_ACP, 0, wideCurEnv, -1, curEnv, len, NULL, NULL); 461 free(wideCurEnv); 462 } 463 #else 464 curEnv = GetEnvironmentStrings(); 465 #endif 466 467 cwdStart = curEnv; 468 while (*cwdStart) { 469 if (cwdStart[0] == '=' && cwdStart[1] != '\0' && cwdStart[2] == ':' && 470 cwdStart[3] == '=') { 471 break; 472 } 473 cwdStart += strlen(cwdStart) + 1; 474 } 475 cwdEnd = cwdStart; 476 if (*cwdEnd) { 477 cwdEnd += strlen(cwdEnd) + 1; 478 while (*cwdEnd) { 479 if (cwdEnd[0] != '=' || cwdEnd[1] == '\0' || cwdEnd[2] != ':' || 480 cwdEnd[3] != '=') { 481 break; 482 } 483 cwdEnd += strlen(cwdEnd) + 1; 484 } 485 } 486 envBlockSize = cwdEnd - cwdStart; 487 488 for (env = envp; *env; env++) { 489 envBlockSize += strlen(*env) + 1; 490 } 491 envBlockSize++; 492 493 p = *envBlock = PR_MALLOC((PRUint32)envBlockSize); 494 if (p == NULL) { 495 #ifdef WINCE 496 PR_Free(curEnv); 497 #else 498 FreeEnvironmentStrings(curEnv); 499 #endif 500 return -1; 501 } 502 503 q = cwdStart; 504 while (q < cwdEnd) { 505 *p++ = *q++; 506 } 507 #ifdef WINCE 508 PR_Free(curEnv); 509 #else 510 FreeEnvironmentStrings(curEnv); 511 #endif 512 513 for (env = envp; *env; env++) { 514 q = *env; 515 while (*q) { 516 *p++ = *q++; 517 } 518 *p++ = '\0'; 519 } 520 *p = '\0'; 521 return 0; 522 } 523 524 /* 525 * For qsort. We sort (case-insensitive) the environment strings 526 * before generating the environment block. 527 */ 528 static int compare(const void* arg1, const void* arg2) { 529 return _stricmp(*(char**)arg1, *(char**)arg2); 530 } 531 532 PRProcess* _PR_CreateWindowsProcess(const char* path, char* const* argv, 533 char* const* envp, 534 const PRProcessAttr* attr) { 535 #ifdef WINCE 536 STARTUPINFOW startupInfo; 537 PRUnichar* wideCmdLine; 538 PRUnichar* wideCwd; 539 int len = 0; 540 #else 541 STARTUPINFO startupInfo; 542 #endif 543 DWORD creationFlags = 0; 544 PROCESS_INFORMATION procInfo; 545 BOOL retVal; 546 char* cmdLine = NULL; 547 char* envBlock = NULL; 548 char** newEnvp = NULL; 549 const char* cwd = NULL; /* current working directory */ 550 PRProcess* proc = NULL; 551 PRBool hasFdInheritBuffer; 552 553 proc = PR_NEW(PRProcess); 554 if (!proc) { 555 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 556 goto errorExit; 557 } 558 559 if (assembleCmdLine(argv, &cmdLine) == -1) { 560 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 561 goto errorExit; 562 } 563 564 #ifndef WINCE 565 /* 566 * If attr->fdInheritBuffer is not NULL, we need to insert 567 * it into the envp array, so envp cannot be NULL. 568 */ 569 hasFdInheritBuffer = (attr && attr->fdInheritBuffer); 570 if ((envp == NULL) && hasFdInheritBuffer) { 571 envp = environ; 572 } 573 574 if (envp != NULL) { 575 int idx; 576 int numEnv; 577 PRBool found = PR_FALSE; 578 579 numEnv = 0; 580 while (envp[numEnv]) { 581 numEnv++; 582 } 583 newEnvp = (char**)PR_MALLOC((numEnv + 2) * sizeof(char*)); 584 for (idx = 0; idx < numEnv; idx++) { 585 newEnvp[idx] = envp[idx]; 586 if (hasFdInheritBuffer && !found && 587 !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) { 588 newEnvp[idx] = attr->fdInheritBuffer; 589 found = PR_TRUE; 590 } 591 } 592 if (hasFdInheritBuffer && !found) { 593 newEnvp[idx++] = attr->fdInheritBuffer; 594 } 595 newEnvp[idx] = NULL; 596 qsort((void*)newEnvp, (size_t)idx, sizeof(char*), compare); 597 } 598 if (assembleEnvBlock(newEnvp, &envBlock) == -1) { 599 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 600 goto errorExit; 601 } 602 603 ZeroMemory(&startupInfo, sizeof(startupInfo)); 604 startupInfo.cb = sizeof(startupInfo); 605 606 if (attr) { 607 PRBool redirected = PR_FALSE; 608 609 /* 610 * XXX the default value for stdin, stdout, and stderr 611 * should probably be the console input and output, not 612 * those of the parent process. 613 */ 614 startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 615 startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 616 startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); 617 if (attr->stdinFd) { 618 startupInfo.hStdInput = (HANDLE)attr->stdinFd->secret->md.osfd; 619 redirected = PR_TRUE; 620 } 621 if (attr->stdoutFd) { 622 startupInfo.hStdOutput = (HANDLE)attr->stdoutFd->secret->md.osfd; 623 redirected = PR_TRUE; 624 /* 625 * If stdout is redirected, we can assume that the process will 626 * not write anything useful to the console windows, and therefore 627 * automatically set the CREATE_NO_WINDOW flag. 628 */ 629 creationFlags |= CREATE_NO_WINDOW; 630 } 631 if (attr->stderrFd) { 632 startupInfo.hStdError = (HANDLE)attr->stderrFd->secret->md.osfd; 633 redirected = PR_TRUE; 634 } 635 if (redirected) { 636 startupInfo.dwFlags |= STARTF_USESTDHANDLES; 637 } 638 cwd = attr->currentDirectory; 639 } 640 #endif 641 642 #ifdef WINCE 643 len = MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, NULL, 0); 644 wideCmdLine = (PRUnichar*)PR_MALLOC(len * sizeof(PRUnichar)); 645 MultiByteToWideChar(CP_ACP, 0, cmdLine, -1, wideCmdLine, len); 646 len = MultiByteToWideChar(CP_ACP, 0, cwd, -1, NULL, 0); 647 wideCwd = PR_MALLOC(len * sizeof(PRUnichar)); 648 MultiByteToWideChar(CP_ACP, 0, cwd, -1, wideCwd, len); 649 retVal = 650 CreateProcessW(NULL, wideCmdLine, NULL, /* security attributes for the new 651 * process */ 652 NULL, /* security attributes for the primary 653 * thread in the new process */ 654 TRUE, /* inherit handles */ 655 creationFlags, envBlock, /* an environment block, 656 * consisting of a null-terminated 657 * block of null-terminated 658 * strings. Each string is in the 659 * form: name=value 660 * XXX: usually NULL */ 661 wideCwd, /* current drive and directory */ 662 &startupInfo, &procInfo); 663 PR_Free(wideCmdLine); 664 PR_Free(wideCwd); 665 #else 666 retVal = 667 CreateProcess(NULL, cmdLine, NULL, /* security attributes for the new 668 * process */ 669 NULL, /* security attributes for the primary 670 * thread in the new process */ 671 TRUE, /* inherit handles */ 672 creationFlags, envBlock, /* an environment block, consisting 673 * of a null-terminated block of 674 * null-terminated strings. Each 675 * string is in the form: 676 * name=value 677 * XXX: usually NULL */ 678 cwd, /* current drive and directory */ 679 &startupInfo, &procInfo); 680 #endif 681 682 if (retVal == FALSE) { 683 /* XXX what error code? */ 684 PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); 685 goto errorExit; 686 } 687 688 CloseHandle(procInfo.hThread); 689 proc->md.handle = procInfo.hProcess; 690 proc->md.id = procInfo.dwProcessId; 691 692 PR_DELETE(cmdLine); 693 if (newEnvp) { 694 PR_DELETE(newEnvp); 695 } 696 if (envBlock) { 697 PR_DELETE(envBlock); 698 } 699 return proc; 700 701 errorExit: 702 if (cmdLine) { 703 PR_DELETE(cmdLine); 704 } 705 if (newEnvp) { 706 PR_DELETE(newEnvp); 707 } 708 if (envBlock) { 709 PR_DELETE(envBlock); 710 } 711 if (proc) { 712 PR_DELETE(proc); 713 } 714 return NULL; 715 } /* _PR_CreateWindowsProcess */ 716 717 PRStatus _PR_DetachWindowsProcess(PRProcess* process) { 718 CloseHandle(process->md.handle); 719 PR_DELETE(process); 720 return PR_SUCCESS; 721 } 722 723 /* 724 * XXX: This implementation is a temporary quick solution. 725 * It can be called by native threads only (not by fibers). 726 */ 727 PRStatus _PR_WaitWindowsProcess(PRProcess* process, PRInt32* exitCode) { 728 DWORD dwRetVal; 729 730 dwRetVal = WaitForSingleObject(process->md.handle, INFINITE); 731 if (dwRetVal == WAIT_FAILED) { 732 PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); 733 return PR_FAILURE; 734 } 735 PR_ASSERT(dwRetVal == WAIT_OBJECT_0); 736 if (exitCode != NULL && 737 GetExitCodeProcess(process->md.handle, exitCode) == FALSE) { 738 PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); 739 return PR_FAILURE; 740 } 741 CloseHandle(process->md.handle); 742 PR_DELETE(process); 743 return PR_SUCCESS; 744 } 745 746 PRStatus _PR_KillWindowsProcess(PRProcess* process) { 747 /* 748 * On Unix, if a process terminates normally, its exit code is 749 * between 0 and 255. So here on Windows, we use the exit code 750 * 256 to indicate that the process is killed. 751 */ 752 if (TerminateProcess(process->md.handle, 256)) { 753 return PR_SUCCESS; 754 } 755 PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); 756 return PR_FAILURE; 757 } 758 759 PRStatus _MD_WindowsGetHostName(char* name, PRUint32 namelen) { 760 PRIntn rv; 761 PRInt32 syserror; 762 763 rv = gethostname(name, (PRInt32)namelen); 764 if (0 == rv) { 765 return PR_SUCCESS; 766 } 767 syserror = WSAGetLastError(); 768 PR_ASSERT(WSANOTINITIALISED != syserror); 769 _PR_MD_MAP_GETHOSTNAME_ERROR(syserror); 770 return PR_FAILURE; 771 } 772 773 PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char* name, PRUint32 namelen) { 774 OSVERSIONINFO osvi; 775 776 PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) || 777 (cmd == PR_SI_RELEASE_BUILD)); 778 779 ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); 780 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 781 782 if (!GetVersionEx(&osvi)) { 783 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); 784 return PR_FAILURE; 785 } 786 787 switch (osvi.dwPlatformId) { 788 case VER_PLATFORM_WIN32_NT: 789 if (PR_SI_SYSNAME == cmd) { 790 (void)PR_snprintf(name, namelen, "Windows_NT"); 791 } else if (PR_SI_RELEASE == cmd) { 792 (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion, 793 osvi.dwMinorVersion); 794 } else if (PR_SI_RELEASE_BUILD == cmd) { 795 (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber); 796 } 797 break; 798 case VER_PLATFORM_WIN32_WINDOWS: 799 if (PR_SI_SYSNAME == cmd) { 800 if ((osvi.dwMajorVersion > 4) || 801 ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion > 0))) { 802 (void)PR_snprintf(name, namelen, "Windows_98"); 803 } else { 804 (void)PR_snprintf(name, namelen, "Windows_95"); 805 } 806 } else if (PR_SI_RELEASE == cmd) { 807 (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion, 808 osvi.dwMinorVersion); 809 } else if (PR_SI_RELEASE_BUILD == cmd) { 810 (void)PR_snprintf(name, namelen, "%d", osvi.dwBuildNumber); 811 } 812 break; 813 #ifdef VER_PLATFORM_WIN32_CE 814 case VER_PLATFORM_WIN32_CE: 815 if (PR_SI_SYSNAME == cmd) { 816 (void)PR_snprintf(name, namelen, "Windows_CE"); 817 } else if (PR_SI_RELEASE == cmd) { 818 (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion, 819 osvi.dwMinorVersion); 820 } else if (PR_SI_RELEASE_BUILD == cmd) { 821 if (namelen) { 822 *name = 0; 823 } 824 } 825 break; 826 #endif 827 default: 828 if (PR_SI_SYSNAME == cmd) { 829 (void)PR_snprintf(name, namelen, "Windows_Unknown"); 830 } else if (PR_SI_RELEASE == cmd) { 831 (void)PR_snprintf(name, namelen, "%d.%d", 0, 0); 832 } else if (PR_SI_RELEASE_BUILD == cmd) { 833 if (namelen) { 834 *name = 0; 835 } 836 } 837 break; 838 } 839 return PR_SUCCESS; 840 } 841 842 PRStatus _MD_WindowsGetReleaseName(char* name, PRUint32 namelen) { 843 OSVERSIONINFO osvi; 844 845 ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); 846 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 847 848 if (!GetVersionEx(&osvi)) { 849 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); 850 return PR_FAILURE; 851 } 852 853 switch (osvi.dwPlatformId) { 854 case VER_PLATFORM_WIN32_NT: 855 case VER_PLATFORM_WIN32_WINDOWS: 856 (void)PR_snprintf(name, namelen, "%d.%d", osvi.dwMajorVersion, 857 osvi.dwMinorVersion); 858 break; 859 default: 860 (void)PR_snprintf(name, namelen, "%d.%d", 0, 0); 861 break; 862 } 863 return PR_SUCCESS; 864 } 865 866 /* 867 ********************************************************************** 868 * 869 * Memory-mapped files 870 * 871 ********************************************************************** 872 */ 873 874 PRStatus _MD_CreateFileMap(PRFileMap* fmap, PRInt64 size) { 875 DWORD dwHi, dwLo; 876 DWORD flProtect; 877 PROsfd osfd; 878 879 osfd = (fmap->fd == (PRFileDesc*)-1) ? -1 : fmap->fd->secret->md.osfd; 880 881 dwLo = (DWORD)(size & 0xffffffff); 882 dwHi = (DWORD)(((PRUint64)size >> 32) & 0xffffffff); 883 884 if (fmap->prot == PR_PROT_READONLY) { 885 flProtect = PAGE_READONLY; 886 fmap->md.dwAccess = FILE_MAP_READ; 887 } else if (fmap->prot == PR_PROT_READWRITE) { 888 flProtect = PAGE_READWRITE; 889 fmap->md.dwAccess = FILE_MAP_WRITE; 890 } else { 891 PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY); 892 #ifdef WINCE 893 /* WINCE does not have FILE_MAP_COPY. */ 894 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 895 return PR_FAILURE; 896 #else 897 flProtect = PAGE_WRITECOPY; 898 fmap->md.dwAccess = FILE_MAP_COPY; 899 #endif 900 } 901 902 fmap->md.hFileMap = 903 CreateFileMapping((HANDLE)osfd, NULL, flProtect, dwHi, dwLo, NULL); 904 905 if (fmap->md.hFileMap == NULL) { 906 PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); 907 return PR_FAILURE; 908 } 909 return PR_SUCCESS; 910 } 911 912 PRInt32 _MD_GetMemMapAlignment(void) { 913 SYSTEM_INFO info; 914 GetSystemInfo(&info); 915 return info.dwAllocationGranularity; 916 } 917 918 extern PRLogModuleInfo* _pr_shma_lm; 919 920 void* _MD_MemMap(PRFileMap* fmap, PROffset64 offset, PRUint32 len) { 921 DWORD dwHi, dwLo; 922 void* addr; 923 924 dwLo = (DWORD)(offset & 0xffffffff); 925 dwHi = (DWORD)(((PRUint64)offset >> 32) & 0xffffffff); 926 if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess, dwHi, dwLo, 927 len)) == NULL) { 928 { 929 LPVOID lpMsgBuf; 930 931 FormatMessage( 932 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, 933 GetLastError(), 934 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language 935 (LPTSTR)&lpMsgBuf, 0, NULL); 936 PR_LOG(_pr_shma_lm, PR_LOG_DEBUG, ("md_memmap(): %s", lpMsgBuf)); 937 } 938 PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); 939 } 940 return addr; 941 } 942 943 PRStatus _MD_MemUnmap(void* addr, PRUint32 len) { 944 if (UnmapViewOfFile(addr)) { 945 return PR_SUCCESS; 946 } 947 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); 948 return PR_FAILURE; 949 } 950 951 PRStatus _MD_CloseFileMap(PRFileMap* fmap) { 952 CloseHandle(fmap->md.hFileMap); 953 PR_DELETE(fmap); 954 return PR_SUCCESS; 955 } 956 957 PRStatus _MD_SyncMemMap(PRFileDesc* fd, void* addr, PRUint32 len) { 958 PROsfd osfd = fd->secret->md.osfd; 959 960 /* The FlushViewOfFile page on MSDN says: 961 * To flush all the dirty pages plus the metadata for the file and 962 * ensure that they are physically written to disk, call 963 * FlushViewOfFile and then call the FlushFileBuffers function. 964 */ 965 if (FlushViewOfFile(addr, len) && FlushFileBuffers((HANDLE)osfd)) { 966 return PR_SUCCESS; 967 } 968 _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); 969 return PR_FAILURE; 970 } 971 972 /* 973 *********************************************************************** 974 * 975 * Atomic increment and decrement operations for x86 processors 976 * 977 * We don't use InterlockedIncrement and InterlockedDecrement 978 * because on NT 3.51 and Win95, they return a number with 979 * the same sign as the incremented/decremented result, rather 980 * than the result itself. On NT 4.0 these functions do return 981 * the incremented/decremented result. 982 * 983 * The result is returned in the eax register by the inline 984 * assembly code. We disable the harmless "no return value" 985 * warning (4035) for these two functions. 986 * 987 *********************************************************************** 988 */ 989 990 #if defined(_M_IX86) || defined(_X86_) 991 992 # pragma warning(disable : 4035) 993 PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32* val) { 994 # if defined(__GNUC__) 995 PRInt32 result; 996 asm volatile("lock ; xadd %0, %1" 997 : "=r"(result), "=m"(*val) 998 : "0"(1), "m"(*val)); 999 return result + 1; 1000 # else 1001 __asm 1002 { 1003 mov ecx, val 1004 mov eax, 1 1005 lock xadd dword ptr [ecx], eax 1006 inc eax 1007 } 1008 # endif /* __GNUC__ */ 1009 } 1010 # pragma warning(default : 4035) 1011 1012 # pragma warning(disable : 4035) 1013 PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32* val) { 1014 # if defined(__GNUC__) 1015 PRInt32 result; 1016 asm volatile("lock ; xadd %0, %1" 1017 : "=r"(result), "=m"(*val) 1018 : "0"(-1), "m"(*val)); 1019 // asm volatile("lock ; xadd %0, %1" : "=m" (val), "=a" (result) : "-1" (1)); 1020 return result - 1; 1021 # else 1022 __asm 1023 { 1024 mov ecx, val 1025 mov eax, 0ffffffffh 1026 lock xadd dword ptr [ecx], eax 1027 dec eax 1028 } 1029 # endif /* __GNUC__ */ 1030 } 1031 # pragma warning(default : 4035) 1032 1033 # pragma warning(disable : 4035) 1034 PRInt32 _PR_MD_ATOMIC_ADD(PRInt32* intp, PRInt32 val) { 1035 # if defined(__GNUC__) 1036 PRInt32 result; 1037 // asm volatile("lock ; xadd %1, %0" : "=m" (intp), "=a" (result) : "1" 1038 // (val)); 1039 asm volatile("lock ; xadd %0, %1" 1040 : "=r"(result), "=m"(*intp) 1041 : "0"(val), "m"(*intp)); 1042 return result + val; 1043 # else 1044 __asm 1045 { 1046 mov ecx, intp 1047 mov eax, val 1048 mov edx, eax 1049 lock xadd dword ptr [ecx], eax 1050 add eax, edx 1051 } 1052 # endif /* __GNUC__ */ 1053 } 1054 # pragma warning(default : 4035) 1055 1056 # ifdef _PR_HAVE_ATOMIC_CAS 1057 1058 # pragma warning(disable : 4035) 1059 void PR_StackPush(PRStack* stack, PRStackElem* stack_elem) { 1060 # if defined(__GNUC__) 1061 void** tos = (void**)stack; 1062 void* tmp; 1063 1064 retry: 1065 if (*tos == (void*)-1) { 1066 goto retry; 1067 } 1068 1069 __asm__("xchg %0,%1" : "=r"(tmp), "=m"(*tos) : "0"(-1), "m"(*tos)); 1070 1071 if (tmp == (void*)-1) { 1072 goto retry; 1073 } 1074 1075 *(void**)stack_elem = tmp; 1076 __asm__("" : : : "memory"); 1077 *tos = stack_elem; 1078 # else 1079 __asm 1080 { 1081 mov ebx, stack 1082 mov ecx, stack_elem 1083 retry: mov eax,[ebx] 1084 cmp eax,-1 1085 je retry 1086 mov eax,-1 1087 xchg dword ptr [ebx], eax 1088 cmp eax,-1 1089 je retry 1090 mov [ecx],eax 1091 mov [ebx],ecx 1092 } 1093 # endif /* __GNUC__ */ 1094 } 1095 # pragma warning(default : 4035) 1096 1097 # pragma warning(disable : 4035) 1098 PRStackElem* PR_StackPop(PRStack* stack) { 1099 # if defined(__GNUC__) 1100 void** tos = (void**)stack; 1101 void* tmp; 1102 1103 retry: 1104 if (*tos == (void*)-1) { 1105 goto retry; 1106 } 1107 1108 __asm__("xchg %0,%1" : "=r"(tmp), "=m"(*tos) : "0"(-1), "m"(*tos)); 1109 1110 if (tmp == (void*)-1) { 1111 goto retry; 1112 } 1113 1114 if (tmp != (void*)0) { 1115 void* next = *(void**)tmp; 1116 *tos = next; 1117 *(void**)tmp = 0; 1118 } else { 1119 *tos = tmp; 1120 } 1121 1122 return tmp; 1123 # else 1124 __asm 1125 { 1126 mov ebx, stack 1127 retry: mov eax,[ebx] 1128 cmp eax,-1 1129 je retry 1130 mov eax,-1 1131 xchg dword ptr [ebx], eax 1132 cmp eax,-1 1133 je retry 1134 cmp eax,0 1135 je empty 1136 mov ecx,[eax] 1137 mov [ebx],ecx 1138 mov [eax],0 1139 jmp done 1140 empty: 1141 mov [ebx],eax 1142 done: 1143 } 1144 # endif /* __GNUC__ */ 1145 } 1146 # pragma warning(default : 4035) 1147 1148 # endif /* _PR_HAVE_ATOMIC_CAS */ 1149 1150 #endif /* x86 processors */