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(&currentTime.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