Back to index

lightning-sunbird  0.9+nobinonly
ntmisc.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * ntmisc.c
00040  *
00041  */
00042 
00043 #include "primpl.h"
00044 
00045 char *_PR_MD_GET_ENV(const char *name)
00046 {
00047     return getenv(name);
00048 }
00049 
00050 /*
00051 ** _PR_MD_PUT_ENV() -- add or change environment variable
00052 **
00053 **
00054 */
00055 PRIntn _PR_MD_PUT_ENV(const char *name)
00056 {
00057     return(putenv(name));
00058 }
00059 
00060 
00061 /*
00062  **************************************************************************
00063  **************************************************************************
00064  **
00065  **     Date and time routines
00066  **
00067  **************************************************************************
00068  **************************************************************************
00069  */
00070 
00071 /*
00072  *-----------------------------------------------------------------------
00073  *
00074  * PR_Now --
00075  *
00076  *     Returns the current time in microseconds since the epoch.
00077  *     The epoch is midnight January 1, 1970 GMT.
00078  *     The implementation is machine dependent.  This is the
00079  *     implementation for Windows.
00080  *     Cf. time_t time(time_t *tp)
00081  *
00082  *-----------------------------------------------------------------------
00083  */
00084 
00085 PR_IMPLEMENT(PRTime)
00086 PR_Now(void)
00087 {
00088     PRTime prt;
00089     FILETIME ft;
00090 
00091     GetSystemTimeAsFileTime(&ft);
00092     _PR_FileTimeToPRTime(&ft, &prt);
00093     return prt;       
00094 }
00095 
00096 /*
00097  ***********************************************************************
00098  ***********************************************************************
00099  *
00100  * Process creation routines
00101  *
00102  ***********************************************************************
00103  ***********************************************************************
00104  */
00105 
00106 /*
00107  * Assemble the command line by concatenating the argv array.
00108  * On success, this function returns 0 and the resulting command
00109  * line is returned in *cmdLine.  On failure, it returns -1.
00110  */
00111 static int assembleCmdLine(char *const *argv, char **cmdLine)
00112 {
00113     char *const *arg;
00114     char *p, *q;
00115     int cmdLineSize;
00116     int numBackslashes;
00117     int i;
00118     int argNeedQuotes;
00119 
00120     /*
00121      * Find out how large the command line buffer should be.
00122      */
00123     cmdLineSize = 0;
00124     for (arg = argv; *arg; arg++) {
00125         /*
00126          * \ and " need to be escaped by a \.  In the worst case,
00127          * every character is a \ or ", so the string of length
00128          * may double.  If we quote an argument, that needs two ".
00129          * Finally, we need a space between arguments, and
00130          * a null byte at the end of command line.
00131          */
00132         cmdLineSize += 2 * strlen(*arg)  /* \ and " need to be escaped */
00133                 + 2                      /* we quote every argument */
00134                 + 1;                     /* space in between, or final null */
00135     }
00136     p = *cmdLine = PR_MALLOC(cmdLineSize);
00137     if (p == NULL) {
00138         return -1;
00139     }
00140 
00141     for (arg = argv; *arg; arg++) {
00142         /* Add a space to separates the arguments */
00143         if (arg != argv) {
00144             *p++ = ' '; 
00145         }
00146         q = *arg;
00147         numBackslashes = 0;
00148         argNeedQuotes = 0;
00149 
00150         /* If the argument contains white space, it needs to be quoted. */
00151         if (strpbrk(*arg, " \f\n\r\t\v")) {
00152             argNeedQuotes = 1;
00153         }
00154 
00155         if (argNeedQuotes) {
00156             *p++ = '"';
00157         }
00158         while (*q) {
00159             if (*q == '\\') {
00160                 numBackslashes++;
00161                 q++;
00162             } else if (*q == '"') {
00163                 if (numBackslashes) {
00164                     /*
00165                      * Double the backslashes since they are followed
00166                      * by a quote
00167                      */
00168                     for (i = 0; i < 2 * numBackslashes; i++) {
00169                         *p++ = '\\';
00170                     }
00171                     numBackslashes = 0;
00172                 }
00173                 /* To escape the quote */
00174                 *p++ = '\\';
00175                 *p++ = *q++;
00176             } else {
00177                 if (numBackslashes) {
00178                     /*
00179                      * Backslashes are not followed by a quote, so
00180                      * don't need to double the backslashes.
00181                      */
00182                     for (i = 0; i < numBackslashes; i++) {
00183                         *p++ = '\\';
00184                     }
00185                     numBackslashes = 0;
00186                 }
00187                 *p++ = *q++;
00188             }
00189         }
00190 
00191         /* Now we are at the end of this argument */
00192         if (numBackslashes) {
00193             /*
00194              * Double the backslashes if we have a quote string
00195              * delimiter at the end.
00196              */
00197             if (argNeedQuotes) {
00198                 numBackslashes *= 2;
00199             }
00200             for (i = 0; i < numBackslashes; i++) {
00201                 *p++ = '\\';
00202             }
00203         }
00204         if (argNeedQuotes) {
00205             *p++ = '"';
00206         }
00207     } 
00208 
00209     *p = '\0';
00210     return 0;
00211 }
00212 
00213 /*
00214  * Assemble the environment block by concatenating the envp array
00215  * (preserving the terminating null byte in each array element)
00216  * and adding a null byte at the end.
00217  *
00218  * Returns 0 on success.  The resulting environment block is returned
00219  * in *envBlock.  Note that if envp is NULL, a NULL pointer is returned
00220  * in *envBlock.  Returns -1 on failure.
00221  */
00222 static int assembleEnvBlock(char **envp, char **envBlock)
00223 {
00224     char *p;
00225     char *q;
00226     char **env;
00227     char *curEnv;
00228     char *cwdStart, *cwdEnd;
00229     int envBlockSize;
00230 
00231     if (envp == NULL) {
00232         *envBlock = NULL;
00233         return 0;
00234     }
00235 
00236     curEnv = GetEnvironmentStrings();
00237 
00238     cwdStart = curEnv;
00239     while (*cwdStart) {
00240         if (cwdStart[0] == '=' && cwdStart[1] != '\0'
00241                 && cwdStart[2] == ':' && cwdStart[3] == '=') {
00242             break;
00243         }
00244         cwdStart += strlen(cwdStart) + 1;
00245     }
00246     cwdEnd = cwdStart;
00247     if (*cwdEnd) {
00248         cwdEnd += strlen(cwdEnd) + 1;
00249         while (*cwdEnd) {
00250             if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
00251                     || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
00252                 break;
00253             }
00254             cwdEnd += strlen(cwdEnd) + 1;
00255         }
00256     }
00257     envBlockSize = cwdEnd - cwdStart;
00258 
00259     for (env = envp; *env; env++) {
00260         envBlockSize += strlen(*env) + 1;
00261     }
00262     envBlockSize++;
00263 
00264     p = *envBlock = PR_MALLOC(envBlockSize);
00265     if (p == NULL) {
00266         FreeEnvironmentStrings(curEnv);
00267         return -1;
00268     }
00269 
00270     q = cwdStart;
00271     while (q < cwdEnd) {
00272         *p++ = *q++;
00273     }
00274     FreeEnvironmentStrings(curEnv);
00275 
00276     for (env = envp; *env; env++) {
00277         q = *env;
00278         while (*q) {
00279             *p++ = *q++;
00280         }
00281         *p++ = '\0';
00282     }
00283     *p = '\0';
00284     return 0;
00285 }
00286 
00287 /*
00288  * For qsort.  We sort (case-insensitive) the environment strings
00289  * before generating the environment block.
00290  */
00291 static int compare(const void *arg1, const void *arg2)
00292 {
00293     return _stricmp(* (char**)arg1, * (char**)arg2);
00294 }
00295 
00296 PRProcess * _PR_CreateWindowsProcess(
00297     const char *path,
00298     char *const *argv,
00299     char *const *envp,
00300     const PRProcessAttr *attr)
00301 {
00302     STARTUPINFO startupInfo;
00303     PROCESS_INFORMATION procInfo;
00304     BOOL retVal;
00305     char *cmdLine = NULL;
00306     char *envBlock = NULL;
00307     char **newEnvp = NULL;
00308     const char *cwd = NULL; /* current working directory */
00309     PRProcess *proc = NULL;
00310 
00311     proc = PR_NEW(PRProcess);
00312     if (!proc) {
00313         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00314         goto errorExit;
00315     }
00316 
00317     if (assembleCmdLine(argv, &cmdLine) == -1) {
00318         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00319         goto errorExit;
00320     }
00321 
00322     /*
00323      * If attr->fdInheritBuffer is not NULL, we need to insert
00324      * it into the envp array, so envp cannot be NULL.
00325      */
00326     if ((envp == NULL) && attr && attr->fdInheritBuffer) {
00327         envp = environ;
00328     }
00329 
00330     if (envp != NULL) {
00331         int idx;
00332         int numEnv;
00333         int newEnvpSize;
00334 
00335         numEnv = 0;
00336         while (envp[numEnv]) {
00337             numEnv++;
00338         }
00339         newEnvpSize = numEnv + 1;  /* terminating null pointer */
00340         if (attr && attr->fdInheritBuffer) {
00341             newEnvpSize++;
00342         }
00343         newEnvp = (char **) PR_MALLOC(newEnvpSize * sizeof(char *));
00344         for (idx = 0; idx < numEnv; idx++) {
00345             newEnvp[idx] = envp[idx];
00346         }
00347         if (attr && attr->fdInheritBuffer) {
00348             newEnvp[idx++] = attr->fdInheritBuffer;
00349         }
00350         newEnvp[idx] = NULL;
00351         qsort((void *) newEnvp, (size_t) (newEnvpSize - 1),
00352                 sizeof(char *), compare);
00353     }
00354     if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
00355         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00356         goto errorExit;
00357     }
00358 
00359     ZeroMemory(&startupInfo, sizeof(startupInfo));
00360     startupInfo.cb = sizeof(startupInfo);
00361 
00362     if (attr) {
00363         PRBool redirected = PR_FALSE;
00364 
00365         /*
00366          * XXX the default value for stdin, stdout, and stderr
00367          * should probably be the console input and output, not
00368          * those of the parent process.
00369          */
00370         startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
00371         startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
00372         startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
00373         if (attr->stdinFd) {
00374             startupInfo.hStdInput = (HANDLE) attr->stdinFd->secret->md.osfd;
00375             redirected = PR_TRUE;
00376         }
00377         if (attr->stdoutFd) {
00378             startupInfo.hStdOutput = (HANDLE) attr->stdoutFd->secret->md.osfd;
00379             redirected = PR_TRUE;
00380         }
00381         if (attr->stderrFd) {
00382             startupInfo.hStdError = (HANDLE) attr->stderrFd->secret->md.osfd;
00383             redirected = PR_TRUE;
00384         }
00385         if (redirected) {
00386             startupInfo.dwFlags |= STARTF_USESTDHANDLES;
00387         }
00388         cwd = attr->currentDirectory;
00389     }
00390 
00391     retVal = CreateProcess(NULL,
00392                            cmdLine,
00393                            NULL,  /* security attributes for the new
00394                                    * process */
00395                            NULL,  /* security attributes for the primary
00396                                    * thread in the new process */
00397                            TRUE,  /* inherit handles */
00398                            0,     /* creation flags */
00399                            envBlock,  /* an environment block, consisting
00400                                        * of a null-terminated block of
00401                                        * null-terminated strings.  Each
00402                                        * string is in the form:
00403                                        *     name=value
00404                                        * XXX: usually NULL */
00405                            cwd,  /* current drive and directory */
00406                            &startupInfo,
00407                            &procInfo
00408                           );
00409     if (retVal == FALSE) {
00410         /* XXX what error code? */
00411         PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00412         goto errorExit;
00413     }
00414 
00415     CloseHandle(procInfo.hThread);
00416     proc->md.handle = procInfo.hProcess;
00417     proc->md.id = procInfo.dwProcessId;
00418 
00419     PR_DELETE(cmdLine);
00420     if (newEnvp) {
00421         PR_DELETE(newEnvp);
00422     }
00423     if (envBlock) {
00424         PR_DELETE(envBlock);
00425     }
00426     return proc;
00427 
00428 errorExit:
00429     if (cmdLine) {
00430         PR_DELETE(cmdLine);
00431     }
00432     if (newEnvp) {
00433         PR_DELETE(newEnvp);
00434     }
00435     if (envBlock) {
00436         PR_DELETE(envBlock);
00437     }
00438     if (proc) {
00439         PR_DELETE(proc);
00440     }
00441     return NULL;
00442 }  /* _PR_CreateWindowsProcess */
00443 
00444 PRStatus _PR_DetachWindowsProcess(PRProcess *process)
00445 {
00446     CloseHandle(process->md.handle);
00447     PR_DELETE(process);
00448     return PR_SUCCESS;
00449 }
00450 
00451 /*
00452  * XXX: This implementation is a temporary quick solution.
00453  * It can be called by native threads only (not by fibers).
00454  */
00455 PRStatus _PR_WaitWindowsProcess(PRProcess *process,
00456     PRInt32 *exitCode)
00457 {
00458     DWORD dwRetVal;
00459 
00460     dwRetVal = WaitForSingleObject(process->md.handle, INFINITE);
00461     if (dwRetVal == WAIT_FAILED) {
00462         PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00463         return PR_FAILURE;
00464     }
00465     PR_ASSERT(dwRetVal == WAIT_OBJECT_0);
00466     if (exitCode != NULL &&
00467             GetExitCodeProcess(process->md.handle, exitCode) == FALSE) {
00468         PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00469         return PR_FAILURE;
00470     }
00471     CloseHandle(process->md.handle);
00472     PR_DELETE(process);
00473     return PR_SUCCESS;
00474 }
00475 
00476 PRStatus _PR_KillWindowsProcess(PRProcess *process)
00477 {
00478     /*
00479      * On Unix, if a process terminates normally, its exit code is
00480      * between 0 and 255.  So here on Windows, we use the exit code
00481      * 256 to indicate that the process is killed.
00482      */
00483     if (TerminateProcess(process->md.handle, 256)) {
00484        return PR_SUCCESS;
00485     }
00486     PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00487     return PR_FAILURE;
00488 }
00489 
00490 PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen)
00491 {
00492     PRIntn rv;
00493     PRInt32 syserror;
00494 
00495     rv = gethostname(name, (PRInt32) namelen);
00496     if (0 == rv) {
00497         return PR_SUCCESS;
00498     }
00499     syserror = WSAGetLastError();
00500     PR_ASSERT(WSANOTINITIALISED != syserror);
00501        _PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
00502     return PR_FAILURE;
00503 }
00504 
00505 PRStatus _MD_WindowsGetSysInfo(PRSysInfo cmd, char *name, PRUint32 namelen)
00506 {
00507        OSVERSIONINFO osvi;
00508 
00509        PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE));
00510 
00511        ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
00512        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00513 
00514        if (! GetVersionEx (&osvi) ) {
00515               _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
00516        return PR_FAILURE;
00517        }
00518 
00519        switch (osvi.dwPlatformId) {
00520               case VER_PLATFORM_WIN32_NT:
00521                      if (PR_SI_SYSNAME == cmd)
00522                             (void)PR_snprintf(name, namelen, "Windows_NT");
00523                      else if (PR_SI_RELEASE == cmd)
00524                             (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, 
00525                                                         osvi.dwMinorVersion);
00526                      break;
00527               case VER_PLATFORM_WIN32_WINDOWS:
00528                      if (PR_SI_SYSNAME == cmd) {
00529                             if ((osvi.dwMajorVersion > 4) || 
00530                                    ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion > 0)))
00531                                    (void)PR_snprintf(name, namelen, "Windows_98");
00532                             else
00533                                    (void)PR_snprintf(name, namelen, "Windows_95");
00534                      } else if (PR_SI_RELEASE == cmd) {
00535                             (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, 
00536                                                         osvi.dwMinorVersion);
00537                      }
00538                      break;
00539               default:
00540                      if (PR_SI_SYSNAME == cmd)
00541                             (void)PR_snprintf(name, namelen, "Windows_Unknown");
00542                      else if (PR_SI_RELEASE == cmd)
00543                             (void)PR_snprintf(name, namelen, "%d.%d",0,0);
00544                      break;
00545        }
00546        return PR_SUCCESS;
00547 }
00548 
00549 PRStatus _MD_WindowsGetReleaseName(char *name, PRUint32 namelen)
00550 {
00551        OSVERSIONINFO osvi;
00552 
00553        ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
00554        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00555 
00556        if (! GetVersionEx (&osvi) ) {
00557               _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
00558        return PR_FAILURE;
00559        }
00560 
00561        switch (osvi.dwPlatformId) {
00562               case VER_PLATFORM_WIN32_NT:
00563               case VER_PLATFORM_WIN32_WINDOWS:
00564                      (void)PR_snprintf(name, namelen, "%d.%d",osvi.dwMajorVersion, 
00565                                                         osvi.dwMinorVersion);
00566                      break;
00567               default:
00568                      (void)PR_snprintf(name, namelen, "%d.%d",0,0);
00569                      break;
00570        }
00571        return PR_SUCCESS;
00572 }
00573 
00574 /*
00575  **********************************************************************
00576  *
00577  * Memory-mapped files
00578  *
00579  **********************************************************************
00580  */
00581 
00582 PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
00583 {
00584     DWORD dwHi, dwLo;
00585     DWORD flProtect;
00586     PRUint32    osfd;
00587 
00588     osfd = ( fmap->fd == (PRFileDesc*)-1 )?  -1 : fmap->fd->secret->md.osfd;
00589 
00590     dwLo = (DWORD) (size & 0xffffffff);
00591     dwHi = (DWORD) (((PRUint64) size >> 32) & 0xffffffff);
00592 
00593     if (fmap->prot == PR_PROT_READONLY) {
00594         flProtect = PAGE_READONLY;
00595         fmap->md.dwAccess = FILE_MAP_READ;
00596     } else if (fmap->prot == PR_PROT_READWRITE) {
00597         flProtect = PAGE_READWRITE;
00598         fmap->md.dwAccess = FILE_MAP_WRITE;
00599     } else {
00600         PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
00601         flProtect = PAGE_WRITECOPY;
00602         fmap->md.dwAccess = FILE_MAP_COPY;
00603     }
00604 
00605     fmap->md.hFileMap = CreateFileMapping(
00606         (HANDLE) osfd,
00607         NULL,
00608         flProtect,
00609         dwHi,
00610         dwLo,
00611         NULL);
00612 
00613     if (fmap->md.hFileMap == NULL) {
00614         PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00615         return PR_FAILURE;
00616     }
00617     return PR_SUCCESS;
00618 }
00619 
00620 PRInt32 _MD_GetMemMapAlignment(void)
00621 {
00622     SYSTEM_INFO info;
00623     GetSystemInfo(&info);
00624     return info.dwAllocationGranularity;
00625 }
00626 
00627 extern PRLogModuleInfo *_pr_shma_lm;
00628 
00629 void * _MD_MemMap(
00630     PRFileMap *fmap,
00631     PROffset64 offset,
00632     PRUint32 len)
00633 {
00634     DWORD dwHi, dwLo;
00635     void *addr;
00636 
00637     dwLo = (DWORD) (offset & 0xffffffff);
00638     dwHi = (DWORD) (((PRUint64) offset >> 32) & 0xffffffff);
00639     if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess,
00640             dwHi, dwLo, len)) == NULL) {
00641         {
00642             LPVOID lpMsgBuf; 
00643             
00644             FormatMessage( 
00645                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00646                 NULL,
00647                 GetLastError(),
00648                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
00649                 (LPTSTR) &lpMsgBuf,
00650                 0,
00651                 NULL 
00652             );
00653             PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, ("md_memmap(): %s", lpMsgBuf ));
00654         }
00655         PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00656     }
00657     return addr;
00658 }
00659 
00660 PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
00661 {
00662     if (UnmapViewOfFile(addr)) {
00663         return PR_SUCCESS;
00664     } else {
00665         PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
00666         return PR_FAILURE;
00667     }
00668 }
00669 
00670 PRStatus _MD_CloseFileMap(PRFileMap *fmap)
00671 {
00672     CloseHandle(fmap->md.hFileMap);
00673     PR_DELETE(fmap);
00674     return PR_SUCCESS;
00675 }
00676 
00677 /*
00678  ***********************************************************************
00679  *
00680  * Atomic increment and decrement operations for x86 processors
00681  *
00682  * We don't use InterlockedIncrement and InterlockedDecrement
00683  * because on NT 3.51 and Win95, they return a number with
00684  * the same sign as the incremented/decremented result, rather
00685  * than the result itself.  On NT 4.0 these functions do return
00686  * the incremented/decremented result.
00687  *
00688  * The result is returned in the eax register by the inline
00689  * assembly code.  We disable the harmless "no return value"
00690  * warning (4035) for these two functions.
00691  *
00692  ***********************************************************************
00693  */
00694 
00695 #if defined(_M_IX86) || defined(_X86_)
00696 
00697 #pragma warning(disable: 4035)
00698 PRInt32 _PR_MD_ATOMIC_INCREMENT(PRInt32 *val)
00699 {    
00700 #if defined(__GNUC__)
00701   PRInt32 result;
00702   asm volatile ("lock ; xadd %0, %1" 
00703                 : "=r"(result), "=m"(*val)
00704                 : "0"(1), "m"(*val));
00705   return result + 1;
00706 #else
00707     __asm
00708     {
00709         mov ecx, val
00710         mov eax, 1
00711         lock xadd dword ptr [ecx], eax
00712         inc eax
00713     }
00714 #endif /* __GNUC__ */
00715 }
00716 #pragma warning(default: 4035)
00717 
00718 #pragma warning(disable: 4035)
00719 PRInt32 _PR_MD_ATOMIC_DECREMENT(PRInt32 *val)
00720 {
00721 #if defined(__GNUC__)
00722   PRInt32 result;
00723   asm volatile ("lock ; xadd %0, %1" 
00724                 : "=r"(result), "=m"(*val)
00725                 : "0"(-1), "m"(*val));
00726   //asm volatile("lock ; xadd %0, %1" : "=m" (val), "=a" (result) : "-1" (1));
00727   return result - 1;
00728 #else
00729     __asm
00730     {
00731         mov ecx, val
00732         mov eax, 0ffffffffh
00733         lock xadd dword ptr [ecx], eax
00734         dec eax
00735     }
00736 #endif /* __GNUC__ */
00737 }
00738 #pragma warning(default: 4035)
00739 
00740 #pragma warning(disable: 4035)
00741 PRInt32 _PR_MD_ATOMIC_ADD(PRInt32 *intp, PRInt32 val)
00742 {
00743 #if defined(__GNUC__)
00744   PRInt32 result;
00745   //asm volatile("lock ; xadd %1, %0" : "=m" (intp), "=a" (result) : "1" (val));
00746   asm volatile ("lock ; xadd %0, %1" 
00747                 : "=r"(result), "=m"(*intp)
00748                 : "0"(val), "m"(*intp));
00749   return result + val;
00750 #else
00751     __asm
00752     {
00753         mov ecx, intp
00754         mov eax, val
00755         mov edx, eax
00756         lock xadd dword ptr [ecx], eax
00757         add eax, edx
00758     }
00759 #endif /* __GNUC__ */
00760 }
00761 #pragma warning(default: 4035)
00762 
00763 #ifdef _PR_HAVE_ATOMIC_CAS
00764 
00765 #pragma warning(disable: 4035)
00766 void 
00767 PR_StackPush(PRStack *stack, PRStackElem *stack_elem)
00768 {
00769 #if defined(__GNUC__)
00770   void **tos = (void **) stack;
00771   void *tmp;
00772   
00773  retry:
00774   if (*tos == (void *) -1)
00775     goto retry;
00776   
00777   __asm__("lock xchg %0,%1"
00778           : "=r" (tmp), "=m"(*tos)
00779           : "0" (-1), "m"(*tos));
00780   
00781   if (tmp == (void *) -1)
00782     goto retry;
00783   
00784   *(void **)stack_elem = tmp;
00785   __asm__("" : : : "memory");
00786   *tos = stack_elem;
00787 #else
00788     __asm
00789     {
00790        mov ebx, stack
00791        mov ecx, stack_elem
00792 retry: mov eax,[ebx]
00793        cmp eax,-1
00794        je retry
00795        mov eax,-1
00796        xchg dword ptr [ebx], eax
00797        cmp eax,-1
00798        je  retry
00799        mov [ecx],eax
00800        mov [ebx],ecx
00801     }
00802 #endif /* __GNUC__ */
00803 }
00804 #pragma warning(default: 4035)
00805 
00806 #pragma warning(disable: 4035)
00807 PRStackElem * 
00808 PR_StackPop(PRStack *stack)
00809 {
00810 #if defined(__GNUC__)
00811   void **tos = (void **) stack;
00812   void *tmp;
00813   
00814  retry:
00815   if (*tos == (void *) -1)
00816     goto retry;
00817   
00818   __asm__("lock xchg %0,%1"
00819           : "=r" (tmp), "=m"(*tos)
00820           : "0" (-1), "m"(*tos));
00821 
00822   if (tmp == (void *) -1)
00823     goto retry;
00824   
00825   if (tmp != (void *) 0)
00826     {
00827       void *next = *(void **)tmp;
00828       *tos = next;
00829       *(void **)tmp = 0;
00830     }
00831   else
00832     *tos = tmp;
00833   
00834   return tmp;
00835 #else
00836     __asm
00837     {
00838        mov ebx, stack
00839 retry: mov eax,[ebx]
00840        cmp eax,-1
00841        je retry
00842        mov eax,-1
00843        xchg dword ptr [ebx], eax
00844        cmp eax,-1
00845        je  retry
00846        cmp eax,0
00847        je  empty
00848        mov ecx,[eax]
00849        mov [ebx],ecx
00850        mov [eax],0
00851        jmp done
00852 empty:
00853        mov [ebx],eax
00854 done:  
00855        }
00856 #endif /* __GNUC__ */
00857 }
00858 #pragma warning(default: 4035)
00859 
00860 #endif /* _PR_HAVE_ATOMIC_CAS */
00861 
00862 #endif /* x86 processors */