|
37 | 37 | *
|
38 | 38 | *
|
39 | 39 | * IDENTIFICATION
|
40 |
| - * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.542 2007/09/2622:36:30 tgl Exp $ |
| 40 | + * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.543 2007/10/2621:50:10 mha Exp $ |
41 | 41 | *
|
42 | 42 | * NOTES
|
43 | 43 | *
|
@@ -331,14 +331,17 @@ static void StartAutovacuumWorker(void);
|
331 | 331 | #ifdefEXEC_BACKEND
|
332 | 332 |
|
333 | 333 | #ifdefWIN32
|
334 |
| -staticvoidwin32_AddChild(pid_tpid,HANDLEhandle); |
335 |
| -staticvoidwin32_RemoveChild(pid_tpid); |
336 | 334 | staticpid_twin32_waitpid(int*exitstatus);
|
337 |
| -staticDWORDWINAPIwin32_sigchld_waiter(LPVOIDparam); |
| 335 | +staticvoidWINAPIpgwin32_deadchild_callback(PVOIDlpParameter,BOOLEANTimerOrWaitFired); |
338 | 336 |
|
339 |
| -staticpid_t*win32_childPIDArray; |
340 |
| -staticHANDLE*win32_childHNDArray; |
341 |
| -staticunsigned longwin32_numChildren=0; |
| 337 | +staticHANDLEwin32ChildQueue; |
| 338 | + |
| 339 | +typedefstruct |
| 340 | +{ |
| 341 | +HANDLEwaitHandle; |
| 342 | +HANDLEprocHandle; |
| 343 | +DWORDprocId; |
| 344 | +}win32_deadchild_waitinfo; |
342 | 345 |
|
343 | 346 | HANDLEPostmasterHandle;
|
344 | 347 | #endif
|
@@ -899,16 +902,12 @@ PostmasterMain(int argc, char *argv[])
|
899 | 902 | #ifdefWIN32
|
900 | 903 |
|
901 | 904 | /*
|
902 |
| - * Initializethe child pid/HANDLE arrays for signal handling. |
| 905 | + * InitializeI/O completion port used to deliver list of dead children. |
903 | 906 | */
|
904 |
| -win32_childPIDArray= (pid_t*) |
905 |
| -malloc(mul_size(NUM_BACKENDARRAY_ELEMS,sizeof(pid_t))); |
906 |
| -win32_childHNDArray= (HANDLE*) |
907 |
| -malloc(mul_size(NUM_BACKENDARRAY_ELEMS,sizeof(HANDLE))); |
908 |
| -if (!win32_childPIDArray|| !win32_childHNDArray) |
| 907 | +win32ChildQueue=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,1); |
| 908 | +if (win32ChildQueue==NULL) |
909 | 909 | ereport(FATAL,
|
910 |
| -(errcode(ERRCODE_OUT_OF_MEMORY), |
911 |
| -errmsg("out of memory"))); |
| 910 | +(errmsg("could not create I/O completion port for child queue"))); |
912 | 911 |
|
913 | 912 | /*
|
914 | 913 | * Set up a handle that child processes can use to check whether the
|
@@ -2072,12 +2071,7 @@ reaper(SIGNAL_ARGS)
|
2072 | 2071 | #defineLOOPHEADER()(exitstatus = status.w_status)
|
2073 | 2072 | #else/* WIN32 */
|
2074 | 2073 | #defineLOOPTEST()((pid = win32_waitpid(&exitstatus)) > 0)
|
2075 |
| -/* |
2076 |
| - * We need to do this here, and not in CleanupBackend, since this is |
2077 |
| - * to be called on all children when we are done with them. Could move |
2078 |
| - * to LogChildExit, but that seems like asking for future trouble... |
2079 |
| - */ |
2080 |
| -#defineLOOPHEADER()(win32_RemoveChild(pid)) |
| 2074 | +#defineLOOPHEADER() |
2081 | 2075 | #endif/* WIN32 */
|
2082 | 2076 | #endif/* HAVE_WAITPID */
|
2083 | 2077 |
|
@@ -3332,28 +3326,18 @@ internal_forkexec(int argc, char *argv[], Port *port)
|
3332 | 3326 | inti;
|
3333 | 3327 | intj;
|
3334 | 3328 | charcmdLine[MAXPGPATH*2];
|
3335 |
| -HANDLEchildHandleCopy; |
3336 |
| -HANDLEwaiterThread; |
3337 | 3329 | HANDLEparamHandle;
|
3338 | 3330 | BackendParameters*param;
|
3339 | 3331 | SECURITY_ATTRIBUTESsa;
|
3340 | 3332 | charparamHandleStr[32];
|
| 3333 | +win32_deadchild_waitinfo*childinfo; |
3341 | 3334 |
|
3342 | 3335 | /* Make sure caller set up argv properly */
|
3343 | 3336 | Assert(argc >=3);
|
3344 | 3337 | Assert(argv[argc]==NULL);
|
3345 | 3338 | Assert(strncmp(argv[1],"--fork",6)==0);
|
3346 | 3339 | Assert(argv[2]==NULL);
|
3347 | 3340 |
|
3348 |
| -/* Verify that there is room in the child list */ |
3349 |
| -if (win32_numChildren >=NUM_BACKENDARRAY_ELEMS) |
3350 |
| -{ |
3351 |
| -elog(LOG,"no room for child entry in backend list"); |
3352 |
| -/* Report same error as for a fork failure on Unix */ |
3353 |
| -errno=EAGAIN; |
3354 |
| -return-1; |
3355 |
| -} |
3356 |
| - |
3357 | 3341 | /* Set up shared memory for parameter passing */
|
3358 | 3342 | ZeroMemory(&sa,sizeof(sa));
|
3359 | 3343 | sa.nLength=sizeof(sa);
|
@@ -3463,34 +3447,34 @@ internal_forkexec(int argc, char *argv[], Port *port)
|
3463 | 3447 | return-1;
|
3464 | 3448 | }
|
3465 | 3449 |
|
3466 |
| -if (!IsUnderPostmaster) |
3467 |
| -{ |
3468 |
| -/* We are the Postmaster creating a child... */ |
3469 |
| -win32_AddChild(pi.dwProcessId,pi.hProcess); |
3470 |
| -} |
3471 |
| - |
3472 |
| -/* Set up the thread to handle the SIGCHLD for this process */ |
3473 |
| -if (DuplicateHandle(GetCurrentProcess(), |
3474 |
| -pi.hProcess, |
3475 |
| -GetCurrentProcess(), |
3476 |
| -&childHandleCopy, |
3477 |
| -0, |
3478 |
| -FALSE, |
3479 |
| -DUPLICATE_SAME_ACCESS)==0) |
| 3450 | +/* |
| 3451 | + * Queue a waiter for to signal when this child dies. The wait will be handled automatically |
| 3452 | + * by an operating system thread pool. |
| 3453 | + * |
| 3454 | + * Note: use malloc instead of palloc, since it needs to be thread-safe. Struct will be |
| 3455 | + * free():d from the callback function that runs on a different thread. |
| 3456 | + */ |
| 3457 | +childinfo=malloc(sizeof(win32_deadchild_waitinfo)); |
| 3458 | +if (!childinfo) |
3480 | 3459 | ereport(FATAL,
|
3481 |
| - (errmsg_internal("could not duplicate child handle: error code %d", |
| 3460 | + (errcode(ERRCODE_OUT_OF_MEMORY), |
| 3461 | +errmsg("out of memory"))); |
| 3462 | + |
| 3463 | +childinfo->procHandle=pi.hProcess; |
| 3464 | +childinfo->procId=pi.dwProcessId; |
| 3465 | + |
| 3466 | +if (!RegisterWaitForSingleObject(&childinfo->waitHandle, |
| 3467 | +pi.hProcess, |
| 3468 | +pgwin32_deadchild_callback, |
| 3469 | +childinfo, |
| 3470 | +INFINITE, |
| 3471 | +WT_EXECUTEONLYONCE |WT_EXECUTEINWAITTHREAD)) |
| 3472 | +ereport(FATAL, |
| 3473 | + (errmsg_internal("could not register process for wait: error code %d", |
3482 | 3474 | (int)GetLastError())));
|
3483 | 3475 |
|
3484 |
| -waiterThread=CreateThread(NULL,64*1024,win32_sigchld_waiter, |
3485 |
| -(LPVOID)childHandleCopy,0,NULL); |
3486 |
| -if (!waiterThread) |
3487 |
| -ereport(FATAL, |
3488 |
| -(errmsg_internal("could not create sigchld waiter thread: error code %d", |
3489 |
| - (int)GetLastError()))); |
3490 |
| -CloseHandle(waiterThread); |
| 3476 | +/* Don't close pi.hProcess here - the wait thread needs access to it */ |
3491 | 3477 |
|
3492 |
| -if (IsUnderPostmaster) |
3493 |
| -CloseHandle(pi.hProcess); |
3494 | 3478 | CloseHandle(pi.hThread);
|
3495 | 3479 |
|
3496 | 3480 | returnpi.dwProcessId;
|
@@ -4500,137 +4484,61 @@ ShmemBackendArrayRemove(pid_t pid)
|
4500 | 4484 |
|
4501 | 4485 | #ifdefWIN32
|
4502 | 4486 |
|
4503 |
| -/* |
4504 |
| - * Note: The following three functions must not be interrupted (eg. by |
4505 |
| - * signals). As the Postgres Win32 signalling architecture (currently) |
4506 |
| - * requires polling, or APC checking functions which aren't used here, this |
4507 |
| - * is not an issue. |
4508 |
| - * |
4509 |
| - * We keep two separate arrays, instead of a single array of pid/HANDLE |
4510 |
| - * structs, to avoid having to re-create a handle array for |
4511 |
| - * WaitForMultipleObjects on each call to win32_waitpid. |
4512 |
| - */ |
4513 |
| - |
4514 |
| -staticvoid |
4515 |
| -win32_AddChild(pid_tpid,HANDLEhandle) |
4516 |
| -{ |
4517 |
| -Assert(win32_childPIDArray&&win32_childHNDArray); |
4518 |
| -if (win32_numChildren<NUM_BACKENDARRAY_ELEMS) |
4519 |
| -{ |
4520 |
| -win32_childPIDArray[win32_numChildren]=pid; |
4521 |
| -win32_childHNDArray[win32_numChildren]=handle; |
4522 |
| -++win32_numChildren; |
4523 |
| -} |
4524 |
| -else |
4525 |
| -ereport(FATAL, |
4526 |
| -(errmsg_internal("no room for child entry with pid %lu", |
4527 |
| - (unsigned long)pid))); |
4528 |
| -} |
4529 |
| - |
4530 |
| -staticvoid |
4531 |
| -win32_RemoveChild(pid_tpid) |
4532 |
| -{ |
4533 |
| -inti; |
4534 |
| - |
4535 |
| -Assert(win32_childPIDArray&&win32_childHNDArray); |
4536 |
| - |
4537 |
| -for (i=0;i<win32_numChildren;i++) |
4538 |
| -{ |
4539 |
| -if (win32_childPIDArray[i]==pid) |
4540 |
| -{ |
4541 |
| -CloseHandle(win32_childHNDArray[i]); |
4542 |
| - |
4543 |
| -/* Swap last entry into the "removed" one */ |
4544 |
| ---win32_numChildren; |
4545 |
| -win32_childPIDArray[i]=win32_childPIDArray[win32_numChildren]; |
4546 |
| -win32_childHNDArray[i]=win32_childHNDArray[win32_numChildren]; |
4547 |
| -return; |
4548 |
| -} |
4549 |
| -} |
4550 |
| - |
4551 |
| -ereport(WARNING, |
4552 |
| -(errmsg_internal("could not find child entry with pid %lu", |
4553 |
| - (unsigned long)pid))); |
4554 |
| -} |
4555 |
| - |
4556 | 4487 | staticpid_t
|
4557 | 4488 | win32_waitpid(int*exitstatus)
|
4558 | 4489 | {
|
| 4490 | +DWORDdwd; |
| 4491 | +ULONG_PTRkey; |
| 4492 | +OVERLAPPED*ovl; |
| 4493 | + |
4559 | 4494 | /*
|
4560 |
| - * Note: Do NOT use WaitForMultipleObjectsEx, as we don't want to run |
4561 |
| - * queued APCs here. |
| 4495 | + * Check if there are any dead children. If there are, return the pid of the first one that died. |
4562 | 4496 | */
|
4563 |
| -intindex; |
4564 |
| -DWORDexitCode; |
4565 |
| -DWORDret; |
4566 |
| -unsigned longoffset; |
4567 |
| - |
4568 |
| -Assert(win32_childPIDArray&&win32_childHNDArray); |
4569 |
| -elog(DEBUG3,"waiting on %lu children",win32_numChildren); |
4570 |
| - |
4571 |
| -for (offset=0;offset<win32_numChildren;offset+=MAXIMUM_WAIT_OBJECTS) |
| 4497 | +if (GetQueuedCompletionStatus(win32ChildQueue,&dwd,&key,&ovl,0)) |
4572 | 4498 | {
|
4573 |
| -unsigned longnum=Min(MAXIMUM_WAIT_OBJECTS,win32_numChildren-offset); |
4574 |
| - |
4575 |
| -ret=WaitForMultipleObjects(num,&win32_childHNDArray[offset], FALSE,0); |
4576 |
| -switch (ret) |
4577 |
| -{ |
4578 |
| -caseWAIT_FAILED: |
4579 |
| -ereport(LOG, |
4580 |
| -(errmsg_internal("failed to wait on %lu of %lu children: error code %d", |
4581 |
| -num,win32_numChildren, (int)GetLastError()))); |
4582 |
| -return-1; |
4583 |
| - |
4584 |
| -caseWAIT_TIMEOUT: |
4585 |
| -/* No children (in this chunk) have finished */ |
4586 |
| -break; |
4587 |
| - |
4588 |
| -default: |
4589 |
| - |
4590 |
| -/* |
4591 |
| - * Get the exit code, and return the PID of, the respective |
4592 |
| - * process |
4593 |
| - */ |
4594 |
| -index=offset+ret-WAIT_OBJECT_0; |
4595 |
| -Assert(index >=0&&index<win32_numChildren); |
4596 |
| -if (!GetExitCodeProcess(win32_childHNDArray[index],&exitCode)) |
4597 |
| -{ |
4598 |
| -/* |
4599 |
| - * If we get this far, this should never happen, but, then |
4600 |
| - * again... No choice other than to assume a catastrophic |
4601 |
| - * failure. |
4602 |
| - */ |
4603 |
| -ereport(FATAL, |
4604 |
| -(errmsg_internal("failed to get exit code for child %lu", |
4605 |
| - (unsigned long)win32_childPIDArray[index]))); |
4606 |
| -} |
4607 |
| -*exitstatus= (int)exitCode; |
4608 |
| -returnwin32_childPIDArray[index]; |
4609 |
| -} |
| 4499 | +*exitstatus= (int)key; |
| 4500 | +returndwd; |
4610 | 4501 | }
|
4611 | 4502 |
|
4612 |
| -/* No children have finished */ |
4613 | 4503 | return-1;
|
4614 | 4504 | }
|
4615 | 4505 |
|
4616 | 4506 | /*
|
4617 |
| - * Note! Code below executes onseparate threads, one for |
4618 |
| - *each child process created |
| 4507 | + * Note! Code below executes ona thread pool! All operations must |
| 4508 | + *be thread safe! Note that elog() and friends must *not* be used. |
4619 | 4509 | */
|
4620 |
| -staticDWORDWINAPI |
4621 |
| -win32_sigchld_waiter(LPVOIDparam) |
| 4510 | +staticvoidWINAPI |
| 4511 | +pgwin32_deadchild_callback(PVOIDlpParameter,BOOLEANTimerOrWaitFired) |
4622 | 4512 | {
|
4623 |
| -HANDLEprocHandle= (HANDLE)param; |
| 4513 | +win32_deadchild_waitinfo*childinfo= (win32_deadchild_waitinfo*)lpParameter; |
| 4514 | +DWORDexitcode; |
4624 | 4515 |
|
4625 |
| -DWORDr=WaitForSingleObject(procHandle,INFINITE); |
| 4516 | +if (TimerOrWaitFired) |
| 4517 | +return;/* timeout. Should never happen, since we use INFINITE as timeout value. */ |
4626 | 4518 |
|
4627 |
| -if (r==WAIT_OBJECT_0) |
4628 |
| -pg_queue_signal(SIGCHLD); |
4629 |
| -else |
4630 |
| -write_stderr("could not wait on child process handle: error code %d\n", |
4631 |
| - (int)GetLastError()); |
4632 |
| -CloseHandle(procHandle); |
4633 |
| -return0; |
| 4519 | +/* Remove handle from wait - required even though it's set to wait only once */ |
| 4520 | +UnregisterWaitEx(childinfo->waitHandle,NULL); |
| 4521 | + |
| 4522 | +if (!GetExitCodeProcess(childinfo->procHandle,&exitcode)) |
| 4523 | +{ |
| 4524 | +/* |
| 4525 | + * Should never happen. Inform user and set a fixed exitcode. |
| 4526 | + */ |
| 4527 | +write_stderr("could not read exitcode for process\n"); |
| 4528 | +exitcode=255; |
| 4529 | +} |
| 4530 | + |
| 4531 | +if (!PostQueuedCompletionStatus(win32ChildQueue,childinfo->procId, (ULONG_PTR)exitcode,NULL)) |
| 4532 | +write_stderr("could not post child completion status\n"); |
| 4533 | + |
| 4534 | +/* Handle is per-process, so we close it here instead of in the originating thread */ |
| 4535 | +CloseHandle(childinfo->procHandle); |
| 4536 | + |
| 4537 | +/* Free struct that was allocated before the call to RegisterWaitForSingleObject() */ |
| 4538 | +free(childinfo); |
| 4539 | + |
| 4540 | +/* Queue SIGCHLD signal */ |
| 4541 | +pg_queue_signal(SIGCHLD); |
4634 | 4542 | }
|
4635 | 4543 |
|
4636 | 4544 | #endif/* WIN32 */
|