|
37 | 37 | *
|
38 | 38 | *
|
39 | 39 | * IDENTIFICATION
|
40 |
| - * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.542 2007/09/26 22:36:30 tgl Exp $ |
| 40 | + * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.543 2007/10/26 21:50:10 mha Exp $ |
41 | 41 | *
|
42 | 42 | * NOTES
|
43 | 43 | *
|
@@ -331,14 +331,17 @@ static void StartAutovacuumWorker(void);
|
331 | 331 | #ifdef EXEC_BACKEND
|
332 | 332 |
|
333 | 333 | #ifdef WIN32
|
334 |
| -static void win32_AddChild(pid_t pid, HANDLE handle); |
335 |
| -static void win32_RemoveChild(pid_t pid); |
336 | 334 | static pid_t win32_waitpid(int *exitstatus);
|
337 |
| -static DWORD WINAPI win32_sigchld_waiter(LPVOID param); |
| 335 | +static void WINAPI pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired); |
338 | 336 |
|
339 |
| -static pid_t *win32_childPIDArray; |
340 |
| -static HANDLE *win32_childHNDArray; |
341 |
| -static unsigned long win32_numChildren = 0; |
| 337 | +static HANDLE win32ChildQueue; |
| 338 | + |
| 339 | +typedef struct |
| 340 | +{ |
| 341 | + HANDLE waitHandle; |
| 342 | + HANDLE procHandle; |
| 343 | + DWORD procId; |
| 344 | +} win32_deadchild_waitinfo; |
342 | 345 |
|
343 | 346 | HANDLE PostmasterHandle;
|
344 | 347 | #endif
|
@@ -899,16 +902,12 @@ PostmasterMain(int argc, char *argv[])
|
899 | 902 | #ifdef WIN32
|
900 | 903 |
|
901 | 904 | /*
|
902 |
| - * Initialize the child pid/HANDLE arrays for signal handling. |
| 905 | + * Initialize I/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 | #define LOOPHEADER() (exitstatus = status.w_status)
|
2073 | 2072 | #else /* WIN32 */
|
2074 | 2073 | #define LOOPTEST() ((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 |
| -#define LOOPHEADER() (win32_RemoveChild(pid)) |
| 2074 | +#define LOOPHEADER() |
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 | int i;
|
3333 | 3327 | int j;
|
3334 | 3328 | char cmdLine[MAXPGPATH * 2];
|
3335 |
| - HANDLE childHandleCopy; |
3336 |
| - HANDLE waiterThread; |
3337 | 3329 | HANDLE paramHandle;
|
3338 | 3330 | BackendParameters *param;
|
3339 | 3331 | SECURITY_ATTRIBUTES sa;
|
3340 | 3332 | char paramHandleStr[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 | return pi.dwProcessId;
|
@@ -4500,137 +4484,61 @@ ShmemBackendArrayRemove(pid_t pid)
|
4500 | 4484 |
|
4501 | 4485 | #ifdef WIN32
|
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 |
| -static void |
4515 |
| -win32_AddChild(pid_t pid, HANDLE handle) |
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 |
| -static void |
4531 |
| -win32_RemoveChild(pid_t pid) |
4532 |
| -{ |
4533 |
| - int i; |
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 | static pid_t
|
4557 | 4488 | win32_waitpid(int *exitstatus)
|
4558 | 4489 | {
|
| 4490 | + DWORD dwd; |
| 4491 | + ULONG_PTR key; |
| 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 |
| - int index; |
4564 |
| - DWORD exitCode; |
4565 |
| - DWORD ret; |
4566 |
| - unsigned long offset; |
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 long num = Min(MAXIMUM_WAIT_OBJECTS, win32_numChildren - offset); |
4574 |
| - |
4575 |
| - ret = WaitForMultipleObjects(num, &win32_childHNDArray[offset], FALSE, 0); |
4576 |
| - switch (ret) |
4577 |
| - { |
4578 |
| - case WAIT_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 |
| - case WAIT_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 |
| - return win32_childPIDArray[index]; |
4609 |
| - } |
| 4499 | + *exitstatus = (int)key; |
| 4500 | + return dwd; |
4610 | 4501 | }
|
4611 | 4502 |
|
4612 |
| - /* No children have finished */ |
4613 | 4503 | return -1;
|
4614 | 4504 | }
|
4615 | 4505 |
|
4616 | 4506 | /*
|
4617 |
| - * Note! Code below executes on separate threads, one for |
4618 |
| - * each child process created |
| 4507 | + * Note! Code below executes on a thread pool! All operations must |
| 4508 | + * be thread safe! Note that elog() and friends must *not* be used. |
4619 | 4509 | */
|
4620 |
| -static DWORD WINAPI |
4621 |
| -win32_sigchld_waiter(LPVOID param) |
| 4510 | +static void WINAPI |
| 4511 | +pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) |
4622 | 4512 | {
|
4623 |
| - HANDLE procHandle = (HANDLE) param; |
| 4513 | + win32_deadchild_waitinfo *childinfo = (win32_deadchild_waitinfo *)lpParameter; |
| 4514 | + DWORD exitcode; |
4624 | 4515 |
|
4625 |
| - DWORD r = 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 |
| - return 0; |
| 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 */
|
0 commit comments