| Index: nspr-4.8.6/mozilla/nsprpub/pr/src/md/unix/uxproces.c |
| =================================================================== |
| --- nspr-4.8.6.orig/mozilla/nsprpub/pr/src/md/unix/uxproces.c |
| +++ nspr-4.8.6/mozilla/nsprpub/pr/src/md/unix/uxproces.c |
| @@ -60,6 +60,10 @@ PR_IMPORT_DATA(char **) environ; |
| #define SA_RESTART 0 |
| #endif |
| |
| +#ifndef ARRAY_SIZE |
| +#define ARRAY_SIZE(ra) (sizeof(ra)/sizeof((ra)[0])) |
| +#endif |
| + |
| /* |
| ********************************************************************** |
| * |
| @@ -457,6 +461,9 @@ _MD_CreateUnixProcess( |
| #define NBUCKETS_LOG2 6 |
| #define NBUCKETS (1 << NBUCKETS_LOG2) |
| #define PID_HASH_MASK ((pid_t) (NBUCKETS - 1)) |
| +#define BITS_PER_CHAR 8 |
| +#define MAX_PIDS 65535 |
| +#define MAX_ENTRIES_PER_BUCKET (MAX_PIDS / NBUCKETS) |
| |
| static pr_PidRecord * |
| CreatePidRecord(void) |
| @@ -631,14 +638,90 @@ static void WaitPidDaemonThread(void *un |
| |
| #else /* _PR_NATIVE_THREADS */ |
| |
| +// only accessed from WaitPidDaemonThread() |
| +static pid_t pr_wp_pids[MAX_ENTRIES_PER_BUCKET]; |
| + |
| +static void WaitForPidsInArray(unsigned count) |
| +{ |
| + unsigned n; |
| + pid_t pid; |
| + int status; |
| + |
| + for (n = 0; n < count; ++n) { |
| + do { |
| + pid = waitpid(pr_wp_pids[n], &status, WNOHANG); |
| + } while ((pid_t) -1 == pid && EINTR == errno); |
| + |
| + if (pid == 0) /* still running */ |
| + continue; |
| + |
| + PR_ASSERT((pid_t) -1 != pid); |
| + |
| + PR_Lock(pr_wp.ml); |
| + ProcessReapedChildInternal(pid, status); |
| + PR_Unlock(pr_wp.ml); |
| + } |
| +} |
| + |
| +static void WaitForPidsInBucket(unsigned bucketIndex) |
| +{ |
| + pr_PidRecord *pRec = NULL; |
| + unsigned count; |
| + unsigned maxIteratorInvalidation = 2; |
| + |
| + while (1) |
| + { |
| + count = 0; |
| + |
| + PR_Lock(pr_wp.ml); |
| + |
| + if (NULL == pRec) { |
| + pRec = pr_wp.pidTable[bucketIndex]; |
| + } else { |
| + // while unlocked, pRec could have been deleted. |
| + pr_PidRecord *pCur = pr_wp.pidTable[bucketIndex]; |
| + while ((NULL != pCur) && (pCur != pRec)) { |
| + pCur = pCur->next; |
| + } |
| + |
| + if (NULL == pCur) { |
| + if (maxIteratorInvalidation > 0) { |
| + --maxIteratorInvalidation; |
| + pRec = pr_wp.pidTable[bucketIndex]; |
| + } else { |
| + pRec = NULL; |
| + } |
| + } |
| + // else pRec was found and is still valid |
| + } |
| + |
| + while ((NULL != pRec) && (count < ARRAY_SIZE(pr_wp_pids))) { |
| + if (pRec->pid > 0 && (_PR_PID_REAPED != pRec->state)) { |
| + pr_wp_pids[count] = pRec->pid; |
| + ++count; |
| + } |
| + pRec = pRec->next; |
| + } |
| + |
| + PR_Unlock(pr_wp.ml); |
| + |
| + if (count == 0) |
| + break; |
| + |
| + WaitForPidsInArray(count); |
| + |
| + if (NULL == pRec) |
| + break; |
| + } |
| +} |
| + |
| static void WaitPidDaemonThread(void *unused) |
| { |
| PRPollDesc pd; |
| PRFileDesc *fd; |
| int rv; |
| char ctmp; |
| - pid_t pid; |
| - int status; |
| + unsigned bucket; |
| #ifdef _PR_SHARE_CLONES |
| struct pr_CreateProcOp *op; |
| #endif |
| @@ -677,6 +760,18 @@ static void WaitPidDaemonThread(void *un |
| op->oserror = PR_GetOSError(); |
| } |
| PR_Lock(pr_wp.ml); |
| + if (op->process) { |
| + pr_PidRecord *pRec = FindPidTable(op->process->md.pid); |
| + // If someone forgets to detach or wait for their process, |
| + // the stale entry hangs around until the OS re-uses the PID |
| + PR_ASSERT(NULL == pRec); |
| + pRec = CreatePidRecord(); |
| + PR_ASSERT(pRec); |
| + pRec->pid = op->process->md.pid; |
| + pRec->state = _PR_PID_WAITING; |
| + pRec->exitStatus = -1; |
| + InsertPidTable(pRec); |
| + } |
| pr_wp.opHead = op->next; |
| if (NULL == pr_wp.opHead) { |
| pr_wp.opTail = NULL; |
| @@ -687,20 +782,8 @@ static void WaitPidDaemonThread(void *un |
| } |
| #endif |
| |
| - while (1) { |
| - do { |
| - pid = waitpid((pid_t) -1, &status, WNOHANG); |
| - } while ((pid_t) -1 == pid && EINTR == errno); |
| - if (0 == pid) break; |
| - if ((pid_t) -1 == pid) { |
| - /* must be because we have no child processes */ |
| - PR_ASSERT(ECHILD == errno); |
| - break; |
| - } |
| - |
| - PR_Lock(pr_wp.ml); |
| - ProcessReapedChildInternal(pid, status); |
| - PR_Unlock(pr_wp.ml); |
| + for (bucket = 0; bucket < NBUCKETS; ++bucket) { |
| + WaitForPidsInBucket(bucket); |
| } |
| } |
| } |
| @@ -825,13 +908,14 @@ PRStatus _MD_DetachUnixProcess(PRProcess |
| pRec->state = _PR_PID_DETACHED; |
| InsertPidTable(pRec); |
| } else { |
| - PR_ASSERT(_PR_PID_REAPED == pRec->state); |
| - if (_PR_PID_REAPED != pRec->state) { |
| + if (_PR_PID_WAITING == pRec->state) { |
| + pRec->state = _PR_PID_DETACHED; |
| + } else if (_PR_PID_REAPED == pRec->state) { |
| + DeletePidTable(pRec); |
| + DeletePidRecord(pRec); |
| + } else { |
| PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| retVal = PR_FAILURE; |
| - } else { |
| - DeletePidTable(pRec); |
| - DeletePidRecord(pRec); |
| } |
| } |
| PR_DELETE(process); |
| @@ -879,11 +963,32 @@ PRStatus _MD_WaitUnixProcess( |
| DeletePidTable(pRec); |
| DeletePidRecord(pRec); |
| } else { |
| - PR_ASSERT(_PR_PID_REAPED == pRec->state); |
| + if (_PR_PID_WAITING == pRec->state) |
| + { |
| + while (!interrupted && _PR_PID_REAPED != pRec->state) { |
| + if (PR_WaitCondVar(pRec->reapedCV, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE |
| + && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { |
| + interrupted = PR_TRUE; |
| + } |
| + } |
| + } else if (_PR_PID_DETACHED == pRec->state) { |
| + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
| + retVal = PR_FAILURE; |
| + goto done; |
| + } else { |
| + PR_ASSERT(_PR_PID_REAPED == pRec->state); |
| + } |
| + |
| + if (_PR_PID_REAPED == pRec->state) { |
| + if (exitCode) { |
| + *exitCode = pRec->exitStatus; |
| + } |
| + } else if (retVal == PR_SUCCESS) { |
| + PR_ASSERT(interrupted); |
| + retVal = PR_FAILURE; |
| + } |
| + |
| DeletePidTable(pRec); |
| - if (exitCode) { |
| - *exitCode = pRec->exitStatus; |
| - } |
| DeletePidRecord(pRec); |
| } |
| PR_DELETE(process); |