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