PostgreSQL Source Code git master
Loading...
Searching...
No Matches
parallel.c File Reference
#include "postgres_fe.h"
#include <sys/select.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include "fe_utils/string_utils.h"
#include "parallel.h"
#include "pg_backup_utils.h"
Include dependency graph for parallel.c:

Go to the source code of this file.

Data Structures

struct  ParallelSlot
 
struct  ShutdownInformation
 
struct  DumpSignalInformation
 

Macros

#define PIPE_READ   0
 
#define PIPE_WRITE   1
 
#define NO_SLOT   (-1) /* Failure result for GetIdleWorker() */
 
#define WORKER_IS_RUNNING(workerStatus)    ((workerStatus) == WRKR_IDLE || (workerStatus) == WRKR_WORKING)
 
#define pgpipe(a)   pipe(a)
 
#define piperead(a, b, c)   read(a,b,c)
 
#define pipewrite(a, b, c)   write(a,b,c)
 
#define write_stderr(str)
 
#define messageStartsWith(msg, prefix)    (strncmp(msg, prefix, strlen(prefix)) == 0)
 

Typedefs

typedef struct ShutdownInformation ShutdownInformation
 
typedef struct DumpSignalInformation DumpSignalInformation
 

Enumerations

enum  T_WorkerStatus { WRKR_NOT_STARTED = 0 , WRKR_IDLE , WRKR_WORKING , WRKR_TERMINATED }
 

Functions

static ParallelSlotGetMyPSlot (ParallelState *pstate)
 
static void archive_close_connection (int code, void *arg)
 
static void ShutdownWorkersHard (ParallelState *pstate)
 
static void WaitForTerminatingWorkers (ParallelState *pstate)
 
static void set_cancel_handler (void)
 
static void set_cancel_pstate (ParallelState *pstate)
 
static void set_cancel_slot_archive (ParallelSlot *slot, ArchiveHandle *AH)
 
static void RunWorker (ArchiveHandle *AH, ParallelSlot *slot)
 
static int GetIdleWorker (ParallelState *pstate)
 
static bool HasEveryWorkerTerminated (ParallelState *pstate)
 
static void lockTableForWorker (ArchiveHandle *AH, TocEntry *te)
 
static void WaitForCommands (ArchiveHandle *AH, int pipefd[2])
 
static bool ListenToWorkers (ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
 
static chargetMessageFromLeader (int pipefd[2])
 
static void sendMessageToLeader (int pipefd[2], const char *str)
 
static int select_loop (int maxFd, fd_set *workerset)
 
static chargetMessageFromWorker (ParallelState *pstate, bool do_wait, int *worker)
 
static void sendMessageToWorker (ParallelState *pstate, int worker, const char *str)
 
static charreadMessageFromPipe (int fd)
 
void init_parallel_dump_utils (void)
 
void on_exit_close_archive (Archive *AHX)
 
static void sigTermHandler (SIGNAL_ARGS)
 
void set_archive_cancel_info (ArchiveHandle *AH, PGconn *conn)
 
ParallelStateParallelBackupStart (ArchiveHandle *AH)
 
void ParallelBackupEnd (ArchiveHandle *AH, ParallelState *pstate)
 
static void buildWorkerCommand (ArchiveHandle *AH, TocEntry *te, T_Action act, char *buf, int buflen)
 
static void parseWorkerCommand (ArchiveHandle *AH, TocEntry **te, T_Action *act, const char *msg)
 
static void buildWorkerResponse (ArchiveHandle *AH, TocEntry *te, T_Action act, int status, char *buf, int buflen)
 
static int parseWorkerResponse (ArchiveHandle *AH, TocEntry *te, const char *msg)
 
void DispatchJobForTocEntry (ArchiveHandle *AH, ParallelState *pstate, TocEntry *te, T_Action act, ParallelCompletionPtr callback, void *callback_data)
 
bool IsEveryWorkerIdle (ParallelState *pstate)
 
void WaitForWorkers (ArchiveHandle *AH, ParallelState *pstate, WFW_WaitOption mode)
 

Variables

static ShutdownInformation shutdown_info
 
static volatile DumpSignalInformation signal_info
 

Macro Definition Documentation

◆ messageStartsWith

#define messageStartsWith (   msg,
  prefix 
)     (strncmp(msg, prefix, strlen(prefix)) == 0)

Definition at line 228 of file parallel.c.

238{
239#ifdef WIN32
241 {
243 int err;
244
245 /* Prepare for threaded operation */
248
249 /* Initialize socket access */
250 err = WSAStartup(MAKEWORD(2, 2), &wsaData);
251 if (err != 0)
252 pg_fatal("%s() failed: error code %d", "WSAStartup", err);
253
254 parallel_init_done = true;
255 }
256#endif
257}
258
259/*
260 * Find the ParallelSlot for the current worker process or thread.
261 *
262 * Returns NULL if no matching slot is found (this implies we're the leader).
263 */
264static ParallelSlot *
266{
267 int i;
268
269 for (i = 0; i < pstate->numWorkers; i++)
270 {
271#ifdef WIN32
272 if (pstate->parallelSlot[i].threadId == GetCurrentThreadId())
273#else
274 if (pstate->parallelSlot[i].pid == getpid())
275#endif
276 return &(pstate->parallelSlot[i]);
277 }
278
279 return NULL;
280}
281
282/*
283 * A thread-local version of getLocalPQExpBuffer().
284 *
285 * Non-reentrant but reduces memory leakage: we'll consume one buffer per
286 * thread, which is much better than one per fmtId/fmtQualifiedId call.
287 */
288#ifdef WIN32
289static PQExpBuffer
291{
292 /*
293 * The Tls code goes awry if we use a static var, so we provide for both
294 * static and auto, and omit any use of the static var when using Tls. We
295 * rely on TlsGetValue() to return 0 if the value is not yet set.
296 */
299
302 else
304
305 if (id_return) /* first time through? */
306 {
307 /* same buffer, just wipe contents */
309 }
310 else
311 {
312 /* new buffer */
316 else
318 }
319
320 return id_return;
321}
322#endif /* WIN32 */
323
324/*
325 * pg_dump and pg_restore call this to register the cleanup handler
326 * as soon as they've created the ArchiveHandle.
327 */
328void
330{
331 shutdown_info.AHX = AHX;
333}
334
335/*
336 * on_exit_nicely handler for shutting down database connections and
337 * worker processes cleanly.
338 */
339static void
340archive_close_connection(int code, void *arg)
341{
343
344 if (si->pstate)
345 {
346 /* In parallel mode, must figure out who we are */
347 ParallelSlot *slot = GetMyPSlot(si->pstate);
348
349 if (!slot)
350 {
351 /*
352 * We're the leader. Forcibly shut down workers, then close our
353 * own database connection, if any.
354 */
355 ShutdownWorkersHard(si->pstate);
356
357 if (si->AHX)
359 }
360 else
361 {
362 /*
363 * We're a worker. Shut down our own DB connection if any. On
364 * Windows, we also have to close our communication sockets, to
365 * emulate what will happen on Unix when the worker process exits.
366 * (Without this, if this is a premature exit, the leader would
367 * fail to detect it because there would be no EOF condition on
368 * the other end of the pipe.)
369 */
370 if (slot->AH)
371 DisconnectDatabase(&(slot->AH->public));
372
373#ifdef WIN32
376#endif
377 }
378 }
379 else
380 {
381 /* Non-parallel operation: just kill the leader DB connection */
382 if (si->AHX)
384 }
385}
386
387/*
388 * Forcibly shut down any remaining workers, waiting for them to finish.
389 *
390 * Note that we don't expect to come here during normal exit (the workers
391 * should be long gone, and the ParallelState too). We're only here in a
392 * pg_fatal() situation, so intervening to cancel active commands is
393 * appropriate.
394 */
395static void
397{
398 int i;
399
400 /*
401 * Close our write end of the sockets so that any workers waiting for
402 * commands know they can exit. (Note: some of the pipeWrite fields might
403 * still be zero, if we failed to initialize all the workers. Hence, just
404 * ignore errors here.)
405 */
406 for (i = 0; i < pstate->numWorkers; i++)
408
409 /*
410 * Force early termination of any commands currently in progress.
411 */
412#ifndef WIN32
413 /* On non-Windows, send SIGTERM to each worker process. */
414 for (i = 0; i < pstate->numWorkers; i++)
415 {
416 pid_t pid = pstate->parallelSlot[i].pid;
417
418 if (pid != 0)
419 kill(pid, SIGTERM);
420 }
421#else
422
423 /*
424 * On Windows, send query cancels directly to the workers' backends. Use
425 * a critical section to ensure worker threads don't change state.
426 */
428 for (i = 0; i < pstate->numWorkers; i++)
429 {
430 ArchiveHandle *AH = pstate->parallelSlot[i].AH;
431 char errbuf[1];
432
433 if (AH != NULL && AH->connCancel != NULL)
434 (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
435 }
437#endif
438
439 /* Now wait for them to terminate. */
441}
442
443/*
444 * Wait for all workers to terminate.
445 */
446static void
448{
449 while (!HasEveryWorkerTerminated(pstate))
450 {
451 ParallelSlot *slot = NULL;
452 int j;
453
454#ifndef WIN32
455 /* On non-Windows, use wait() to wait for next worker to end */
456 int status;
457 pid_t pid = wait(&status);
458
459 /* Find dead worker's slot, and clear the PID field */
460 for (j = 0; j < pstate->numWorkers; j++)
461 {
462 slot = &(pstate->parallelSlot[j]);
463 if (slot->pid == pid)
464 {
465 slot->pid = 0;
466 break;
467 }
468 }
469#else /* WIN32 */
470 /* On Windows, we must use WaitForMultipleObjects() */
472 int nrun = 0;
473 DWORD ret;
475
476 for (j = 0; j < pstate->numWorkers; j++)
477 {
479 {
480 lpHandles[nrun] = (HANDLE) pstate->parallelSlot[j].hThread;
481 nrun++;
482 }
483 }
485 Assert(ret != WAIT_FAILED);
488
489 /* Find dead worker's slot, and clear the hThread field */
490 for (j = 0; j < pstate->numWorkers; j++)
491 {
492 slot = &(pstate->parallelSlot[j]);
493 if (slot->hThread == hThread)
494 {
495 /* For cleanliness, close handles for dead threads */
496 CloseHandle((HANDLE) slot->hThread);
497 slot->hThread = (uintptr_t) INVALID_HANDLE_VALUE;
498 break;
499 }
500 }
501#endif /* WIN32 */
502
503 /* On all platforms, update workerStatus and te[] as well */
504 Assert(j < pstate->numWorkers);
506 pstate->te[j] = NULL;
507 }
508}
509
510
511/*
512 * Code for responding to cancel interrupts (SIGINT, control-C, etc)
513 *
514 * This doesn't quite belong in this module, but it needs access to the
515 * ParallelState data, so there's not really a better place either.
516 *
517 * When we get a cancel interrupt, we could just die, but in pg_restore that
518 * could leave a SQL command (e.g., CREATE INDEX on a large table) running
519 * for a long time. Instead, we try to send a cancel request and then die.
520 * pg_dump probably doesn't really need this, but we might as well use it
521 * there too. Note that sending the cancel directly from the signal handler
522 * is safe because PQcancel() is written to make it so.
523 *
524 * In parallel operation on Unix, each process is responsible for canceling
525 * its own connection (this must be so because nobody else has access to it).
526 * Furthermore, the leader process should attempt to forward its signal to
527 * each child. In simple manual use of pg_dump/pg_restore, forwarding isn't
528 * needed because typing control-C at the console would deliver SIGINT to
529 * every member of the terminal process group --- but in other scenarios it
530 * might be that only the leader gets signaled.
531 *
532 * On Windows, the cancel handler runs in a separate thread, because that's
533 * how SetConsoleCtrlHandler works. Because the workers are threads in this
534 * same process, we set a flag (is_cancel_in_progress()) so they stay quiet
535 * about the query cancellations instead of cluttering the screen, then send
536 * cancels on all active connections and return FALSE, which will allow the
537 * process to die. For safety's sake, we use a critical section to protect
538 * the PGcancel structures against being changed while the signal thread runs.
539 */
540
541#ifndef WIN32
542
543/*
544 * Signal handler (Unix only)
545 */
546static void
548{
549 int i;
550 char errbuf[1];
551
552 /*
553 * Some platforms allow delivery of new signals to interrupt an active
554 * signal handler. That could muck up our attempt to send PQcancel, so
555 * disable the signals that set_cancel_handler enabled.
556 */
560
561 /*
562 * If we're in the leader, forward signal to all workers. (It seems best
563 * to do this before PQcancel; killing the leader transaction will result
564 * in invalid-snapshot errors from active workers, which maybe we can
565 * quiet by killing workers first.) Ignore any errors.
566 */
567 if (signal_info.pstate != NULL)
568 {
569 for (i = 0; i < signal_info.pstate->numWorkers; i++)
570 {
572
573 if (pid != 0)
574 kill(pid, SIGTERM);
575 }
576 }
577
578 /*
579 * Send QueryCancel if we have a connection to send to. Ignore errors,
580 * there's not much we can do about them anyway.
581 */
583 (void) PQcancel(signal_info.myAH->connCancel, errbuf, sizeof(errbuf));
584
585 /*
586 * Report we're quitting, using nothing more complicated than write(2).
587 * When in parallel operation, only the leader process should do this.
588 */
590 {
591 if (progname)
592 {
594 write_stderr(": ");
595 }
596 write_stderr("terminated by user\n");
597 }
598
599 /*
600 * And die, using _exit() not exit() because the latter will invoke atexit
601 * handlers that can fail if we interrupted related code.
602 */
603 _exit(1);
604}
605
606/*
607 * Enable cancel interrupt handler, if not already done.
608 */
609static void
611{
612 /*
613 * When forking, signal_info.handler_set will propagate into the new
614 * process, but that's fine because the signal handler state does too.
615 */
617 {
619
623 }
624}
625
626#else /* WIN32 */
627
628/*
629 * Console interrupt handler --- runs in a newly-started thread.
630 *
631 * After stopping other threads and sending cancel requests on all open
632 * connections, we return FALSE which will allow the default ExitProcess()
633 * action to be taken.
634 */
635static BOOL WINAPI
637{
638 int i;
639 char errbuf[1];
640
641 if (dwCtrlType == CTRL_C_EVENT ||
643 {
644 /*
645 * Tell worker threads to stay quiet about the query cancellations
646 * we're about to send them; otherwise they'd report them as errors
647 * and clutter the user's screen. This must be set before we send any
648 * cancel, so that a worker is guaranteed to see it by the time its
649 * query fails as a result.
650 */
652
653 /* Critical section prevents changing data we look at here */
655
656 /*
657 * If in parallel mode, send QueryCancel to each worker's connected
658 * backend. Do this before canceling the main transaction, else we
659 * might get invalid-snapshot errors reported before we can stop the
660 * workers. Ignore errors, there's not much we can do about them
661 * anyway.
662 */
663 if (signal_info.pstate != NULL)
664 {
665 for (i = 0; i < signal_info.pstate->numWorkers; i++)
666 {
668
669 if (AH != NULL && AH->connCancel != NULL)
670 (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
671 }
672 }
673
674 /*
675 * Send QueryCancel to leader connection, if enabled. Ignore errors,
676 * there's not much we can do about them anyway.
677 */
680 errbuf, sizeof(errbuf));
681
683
684 /*
685 * Report we're quitting, using nothing more complicated than
686 * write(2). We should be able to use pg_log_*() here, but for now we
687 * stay aligned with the sigTermHandler behavior.
688 */
689 if (progname)
690 {
692 write_stderr(": ");
693 }
694 write_stderr("terminated by user\n");
695 }
696
697 /* Always return FALSE to allow signal handling to continue */
698 return FALSE;
699}
700
701/*
702 * Enable cancel interrupt handler, if not already done.
703 */
704static void
706{
708 {
710
712
714 }
715}
716
717#endif /* WIN32 */
718
719
720/*
721 * set_archive_cancel_info
722 *
723 * Fill AH->connCancel with cancellation info for the specified database
724 * connection; or clear it if conn is NULL.
725 */
726void
728{
730
731 /*
732 * Activate the interrupt handler if we didn't yet in this process. On
733 * Windows, this also initializes signal_info_lock; therefore it's
734 * important that this happen at least once before we fork off any
735 * threads.
736 */
738
739 /*
740 * On Unix, we assume that storing a pointer value is atomic with respect
741 * to any possible signal interrupt. On Windows, use a critical section.
742 */
743
744#ifdef WIN32
746#endif
747
748 /* Free the old one if we have one */
750 /* be sure interrupt handler doesn't use pointer while freeing */
751 AH->connCancel = NULL;
752
753 if (oldConnCancel != NULL)
755
756 /* Set the new one if specified */
757 if (conn)
759
760 /*
761 * On Unix, there's only ever one active ArchiveHandle per process, so we
762 * can just set signal_info.myAH unconditionally. On Windows, do that
763 * only in the main thread; worker threads have to make sure their
764 * ArchiveHandle appears in the pstate data, which is dealt with in
765 * RunWorker().
766 */
767#ifndef WIN32
768 signal_info.myAH = AH;
769#else
771 signal_info.myAH = AH;
772#endif
773
774#ifdef WIN32
776#endif
777}
778
779/*
780 * set_cancel_pstate
781 *
782 * Set signal_info.pstate to point to the specified ParallelState, if any.
783 * We need this mainly to have an interlock against Windows signal thread.
784 */
785static void
787{
788#ifdef WIN32
790#endif
791
792 signal_info.pstate = pstate;
793
794#ifdef WIN32
796#endif
797}
798
799/*
800 * set_cancel_slot_archive
801 *
802 * Set ParallelSlot's AH field to point to the specified archive, if any.
803 * We need this mainly to have an interlock against Windows signal thread.
804 */
805static void
807{
808#ifdef WIN32
810#endif
811
812 slot->AH = AH;
813
814#ifdef WIN32
816#endif
817}
818
819
820/*
821 * This function is called by both Unix and Windows variants to set up
822 * and run a worker process. Caller should exit the process (or thread)
823 * upon return.
824 */
825static void
827{
828 int pipefd[2];
829
830 /* fetch child ends of pipes */
833
834 /*
835 * Clone the archive so that we have our own state to work with, and in
836 * particular our own database connection.
837 *
838 * We clone on Unix as well as Windows, even though technically we don't
839 * need to because fork() gives us a copy in our own address space
840 * already. But CloneArchive resets the state information and also clones
841 * the database connection which both seem kinda helpful.
842 */
843 AH = CloneArchive(AH);
844
845 /* Remember cloned archive where signal handler can find it */
846 set_cancel_slot_archive(slot, AH);
847
848 /*
849 * Call the setup worker function that's defined in the ArchiveHandle.
850 */
851 (AH->SetupWorkerPtr) ((Archive *) AH);
852
853 /*
854 * Execute commands until done.
855 */
857
858 /*
859 * Disconnect from database and clean up.
860 */
863 DeCloneArchive(AH);
864}
865
866/*
867 * Thread base function for Windows
868 */
869#ifdef WIN32
870static unsigned __stdcall
872{
873 ArchiveHandle *AH = wi->AH;
874 ParallelSlot *slot = wi->slot;
875
876 /* Don't need WorkerInfo anymore */
877 free(wi);
878
879 /* Run the worker ... */
880 RunWorker(AH, slot);
881
882 /* Exit the thread */
883 _endthreadex(0);
884 return 0;
885}
886#endif /* WIN32 */
887
888/*
889 * This function starts a parallel dump or restore by spawning off the worker
890 * processes. For Windows, it creates a number of threads; on Unix the
891 * workers are created with fork().
892 */
895{
896 ParallelState *pstate;
897 int i;
898
899 Assert(AH->public.numWorkers > 0);
900
902
903 pstate->numWorkers = AH->public.numWorkers;
904 pstate->te = NULL;
905 pstate->parallelSlot = NULL;
906
907 if (AH->public.numWorkers == 1)
908 return pstate;
909
910 /* Create status arrays, being sure to initialize all fields to 0 */
911 pstate->te =
913 pstate->parallelSlot =
915
916#ifdef WIN32
917 /* Make fmtId() and fmtQualifiedId() use thread-local storage */
919#endif
920
921 /*
922 * Set the pstate in shutdown_info, to tell the exit handler that it must
923 * clean up workers as well as the main database connection. But we don't
924 * set this in signal_info yet, because we don't want child processes to
925 * inherit non-NULL signal_info.pstate.
926 */
927 shutdown_info.pstate = pstate;
928
929 /*
930 * Temporarily disable query cancellation on the leader connection. This
931 * ensures that child processes won't inherit valid AH->connCancel
932 * settings and thus won't try to issue cancels against the leader's
933 * connection. No harm is done if we fail while it's disabled, because
934 * the leader connection is idle at this point anyway.
935 */
937
938 /* Ensure stdio state is quiesced before forking */
939 fflush(NULL);
940
941 /* Create desired number of workers */
942 for (i = 0; i < pstate->numWorkers; i++)
943 {
944#ifdef WIN32
945 WorkerInfo *wi;
946 uintptr_t handle;
947#else
948 pid_t pid;
949#endif
950 ParallelSlot *slot = &(pstate->parallelSlot[i]);
951 int pipeMW[2],
952 pipeWM[2];
953
954 /* Create communication pipes for this worker */
955 if (pgpipe(pipeMW) < 0 || pgpipe(pipeWM) < 0)
956 pg_fatal("could not create communication channels: %m");
957
958 /* leader's ends of the pipes */
959 slot->pipeRead = pipeWM[PIPE_READ];
960 slot->pipeWrite = pipeMW[PIPE_WRITE];
961 /* child's ends of the pipes */
964
965#ifdef WIN32
966 /* Create transient structure to pass args to worker function */
968
969 wi->AH = AH;
970 wi->slot = slot;
971
972 handle = _beginthreadex(NULL, 0, (void *) &init_spawned_worker_win32,
973 wi, 0, &(slot->threadId));
974 if (handle == 0)
975 pg_fatal("could not create worker thread: %m");
976 slot->hThread = handle;
977 slot->workerStatus = WRKR_IDLE;
978#else /* !WIN32 */
979 pid = fork();
980 if (pid == 0)
981 {
982 /* we are the worker */
983 int j;
984
985 /* this is needed for GetMyPSlot() */
986 slot->pid = getpid();
987
988 /* instruct signal handler that we're in a worker now */
989 signal_info.am_worker = true;
990
991 /* close read end of Worker -> Leader */
993 /* close write end of Leader -> Worker */
995
996 /*
997 * Close all inherited fds for communication of the leader with
998 * previously-forked workers.
999 */
1000 for (j = 0; j < i; j++)
1001 {
1004 }
1005
1006 /* Run the worker ... */
1007 RunWorker(AH, slot);
1008
1009 /* We can just exit(0) when done */
1010 exit(0);
1011 }
1012 else if (pid < 0)
1013 {
1014 /* fork failed */
1015 pg_fatal("could not create worker process: %m");
1016 }
1017
1018 /* In Leader after successful fork */
1019 slot->pid = pid;
1020 slot->workerStatus = WRKR_IDLE;
1021
1022 /* close read end of Leader -> Worker */
1024 /* close write end of Worker -> Leader */
1026#endif /* WIN32 */
1027 }
1028
1029 /*
1030 * Having forked off the workers, disable SIGPIPE so that leader isn't
1031 * killed if it tries to send a command to a dead worker. We don't want
1032 * the workers to inherit this setting, though.
1033 */
1034#ifndef WIN32
1036#endif
1037
1038 /*
1039 * Re-establish query cancellation on the leader connection.
1040 */
1042
1043 /*
1044 * Tell the cancel signal handler to forward signals to worker processes,
1045 * too. (As with query cancel, we did not need this earlier because the
1046 * workers have not yet been given anything to do; if we die before this
1047 * point, any already-started workers will see EOF and quit promptly.)
1048 */
1049 set_cancel_pstate(pstate);
1050
1051 return pstate;
1052}
1053
1054/*
1055 * Close down a parallel dump or restore.
1056 */
1057void
1059{
1060 int i;
1061
1062 /* No work if non-parallel */
1063 if (pstate->numWorkers == 1)
1064 return;
1065
1066 /* There should not be any unfinished jobs */
1067 Assert(IsEveryWorkerIdle(pstate));
1068
1069 /* Close the sockets so that the workers know they can exit */
1070 for (i = 0; i < pstate->numWorkers; i++)
1071 {
1074 }
1075
1076 /* Wait for them to exit */
1078
1079 /*
1080 * Unlink pstate from shutdown_info, so the exit handler will not try to
1081 * use it; and likewise unlink from signal_info.
1082 */
1085
1086 /* Release state (mere neatnik-ism, since we're about to terminate) */
1087 free(pstate->te);
1088 free(pstate->parallelSlot);
1089 free(pstate);
1090}
1091
1092/*
1093 * These next four functions handle construction and parsing of the command
1094 * strings and response strings for parallel workers.
1095 *
1096 * Currently, these can be the same regardless of which archive format we are
1097 * processing. In future, we might want to let format modules override these
1098 * functions to add format-specific data to a command or response.
1099 */
1100
1101/*
1102 * buildWorkerCommand: format a command string to send to a worker.
1103 *
1104 * The string is built in the caller-supplied buffer of size buflen.
1105 */
1106static void
1108 char *buf, int buflen)
1109{
1110 if (act == ACT_DUMP)
1111 snprintf(buf, buflen, "DUMP %d", te->dumpId);
1112 else if (act == ACT_RESTORE)
1113 snprintf(buf, buflen, "RESTORE %d", te->dumpId);
1114 else
1115 Assert(false);
1116}
1117
1118/*
1119 * parseWorkerCommand: interpret a command string in a worker.
1120 */
1121static void
1123 const char *msg)
1124{
1125 DumpId dumpId;
1126 int nBytes;
1127
1128 if (messageStartsWith(msg, "DUMP "))
1129 {
1130 *act = ACT_DUMP;
1131 sscanf(msg, "DUMP %d%n", &dumpId, &nBytes);
1132 Assert(nBytes == strlen(msg));
1133 *te = getTocEntryByDumpId(AH, dumpId);
1134 Assert(*te != NULL);
1135 }
1136 else if (messageStartsWith(msg, "RESTORE "))
1137 {
1138 *act = ACT_RESTORE;
1139 sscanf(msg, "RESTORE %d%n", &dumpId, &nBytes);
1140 Assert(nBytes == strlen(msg));
1141 *te = getTocEntryByDumpId(AH, dumpId);
1142 Assert(*te != NULL);
1143 }
1144 else
1145 pg_fatal("unrecognized command received from leader: \"%s\"",
1146 msg);
1147}
1148
1149/*
1150 * buildWorkerResponse: format a response string to send to the leader.
1151 *
1152 * The string is built in the caller-supplied buffer of size buflen.
1153 */
1154static void
1156 char *buf, int buflen)
1157{
1158 snprintf(buf, buflen, "OK %d %d %d",
1159 te->dumpId,
1160 status,
1161 status == WORKER_IGNORED_ERRORS ? AH->public.n_errors : 0);
1162}
1163
1164/*
1165 * parseWorkerResponse: parse the status message returned by a worker.
1166 *
1167 * Returns the integer status code, and may update fields of AH and/or te.
1168 */
1169static int
1171 const char *msg)
1172{
1173 DumpId dumpId;
1174 int nBytes,
1175 n_errors;
1176 int status = 0;
1177
1178 if (messageStartsWith(msg, "OK "))
1179 {
1180 sscanf(msg, "OK %d %d %d%n", &dumpId, &status, &n_errors, &nBytes);
1181
1182 Assert(dumpId == te->dumpId);
1183 Assert(nBytes == strlen(msg));
1184
1185 AH->public.n_errors += n_errors;
1186 }
1187 else
1188 pg_fatal("invalid message received from worker: \"%s\"",
1189 msg);
1190
1191 return status;
1192}
1193
1194/*
1195 * Dispatch a job to some free worker.
1196 *
1197 * te is the TocEntry to be processed, act is the action to be taken on it.
1198 * callback is the function to call on completion of the job.
1199 *
1200 * If no worker is currently available, this will block, and previously
1201 * registered callback functions may be called.
1202 */
1203void
1205 ParallelState *pstate,
1206 TocEntry *te,
1207 T_Action act,
1209 void *callback_data)
1210{
1211 int worker;
1212 char buf[256];
1213
1214 /* Get a worker, waiting if none are idle */
1215 while ((worker = GetIdleWorker(pstate)) == NO_SLOT)
1216 WaitForWorkers(AH, pstate, WFW_ONE_IDLE);
1217
1218 /* Construct and send command string */
1219 buildWorkerCommand(AH, te, act, buf, sizeof(buf));
1220
1221 sendMessageToWorker(pstate, worker, buf);
1222
1223 /* Remember worker is busy, and which TocEntry it's working on */
1224 pstate->parallelSlot[worker].workerStatus = WRKR_WORKING;
1225 pstate->parallelSlot[worker].callback = callback;
1226 pstate->parallelSlot[worker].callback_data = callback_data;
1227 pstate->te[worker] = te;
1228}
1229
1230/*
1231 * Find an idle worker and return its slot number.
1232 * Return NO_SLOT if none are idle.
1233 */
1234static int
1236{
1237 int i;
1238
1239 for (i = 0; i < pstate->numWorkers; i++)
1240 {
1241 if (pstate->parallelSlot[i].workerStatus == WRKR_IDLE)
1242 return i;
1243 }
1244 return NO_SLOT;
1245}
1246
1247/*
1248 * Return true iff no worker is running.
1249 */
1250static bool
1252{
1253 int i;
1254
1255 for (i = 0; i < pstate->numWorkers; i++)
1256 {
1258 return false;
1259 }
1260 return true;
1261}
1262
1263/*
1264 * Return true iff every worker is in the WRKR_IDLE state.
1265 */
1266bool
1268{
1269 int i;
1270
1271 for (i = 0; i < pstate->numWorkers; i++)
1272 {
1273 if (pstate->parallelSlot[i].workerStatus != WRKR_IDLE)
1274 return false;
1275 }
1276 return true;
1277}
1278
1279/*
1280 * Acquire lock on a table to be dumped by a worker process.
1281 *
1282 * The leader process is already holding an ACCESS SHARE lock. Ordinarily
1283 * it's no problem for a worker to get one too, but if anything else besides
1284 * pg_dump is running, there's a possible deadlock:
1285 *
1286 * 1) Leader dumps the schema and locks all tables in ACCESS SHARE mode.
1287 * 2) Another process requests an ACCESS EXCLUSIVE lock (which is not granted
1288 * because the leader holds a conflicting ACCESS SHARE lock).
1289 * 3) A worker process also requests an ACCESS SHARE lock to read the table.
1290 * The worker is enqueued behind the ACCESS EXCLUSIVE lock request.
1291 * 4) Now we have a deadlock, since the leader is effectively waiting for
1292 * the worker. The server cannot detect that, however.
1293 *
1294 * To prevent an infinite wait, prior to touching a table in a worker, request
1295 * a lock in ACCESS SHARE mode but with NOWAIT. If we don't get the lock,
1296 * then we know that somebody else has requested an ACCESS EXCLUSIVE lock and
1297 * so we have a deadlock. We must fail the backup in that case.
1298 */
1299static void
1301{
1302 const char *qualId;
1303 PQExpBuffer query;
1304 PGresult *res;
1305
1306 /* Nothing to do for BLOBS */
1307 if (strcmp(te->desc, "BLOBS") == 0)
1308 return;
1309
1310 query = createPQExpBuffer();
1311
1312 qualId = fmtQualifiedId(te->namespace, te->tag);
1313
1314 appendPQExpBuffer(query, "LOCK TABLE %s IN ACCESS SHARE MODE NOWAIT",
1315 qualId);
1316
1317 res = PQexec(AH->connection, query->data);
1318
1319 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1320 pg_fatal("could not obtain lock on relation \"%s\"\n"
1321 "This usually means that someone requested an ACCESS EXCLUSIVE lock "
1322 "on the table after the pg_dump parent process had gotten the "
1323 "initial ACCESS SHARE lock on the table.", qualId);
1324
1325 PQclear(res);
1326 destroyPQExpBuffer(query);
1327}
1328
1329/*
1330 * WaitForCommands: main routine for a worker process.
1331 *
1332 * Read and execute commands from the leader until we see EOF on the pipe.
1333 */
1334static void
1336{
1337 char *command;
1338 TocEntry *te;
1339 T_Action act;
1340 int status = 0;
1341 char buf[256];
1342
1343 for (;;)
1344 {
1345 if (!(command = getMessageFromLeader(pipefd)))
1346 {
1347 /* EOF, so done */
1348 return;
1349 }
1350
1351 /* Decode the command */
1352 parseWorkerCommand(AH, &te, &act, command);
1353
1354 if (act == ACT_DUMP)
1355 {
1356 /* Acquire lock on this table within the worker's session */
1357 lockTableForWorker(AH, te);
1358
1359 /* Perform the dump command */
1360 status = (AH->WorkerJobDumpPtr) (AH, te);
1361 }
1362 else if (act == ACT_RESTORE)
1363 {
1364 /* Perform the restore command */
1365 status = (AH->WorkerJobRestorePtr) (AH, te);
1366 }
1367 else
1368 Assert(false);
1369
1370 /* Return status to leader */
1371 buildWorkerResponse(AH, te, act, status, buf, sizeof(buf));
1372
1374
1375 /* command was pg_malloc'd and we are responsible for free()ing it. */
1376 free(command);
1377 }
1378}
1379
1380/*
1381 * Check for status messages from workers.
1382 *
1383 * If do_wait is true, wait to get a status message; otherwise, just return
1384 * immediately if there is none available.
1385 *
1386 * When we get a status message, we pass the status code to the callback
1387 * function that was specified to DispatchJobForTocEntry, then reset the
1388 * worker status to IDLE.
1389 *
1390 * Returns true if we collected a status message, else false.
1391 *
1392 * XXX is it worth checking for more than one status message per call?
1393 * It seems somewhat unlikely that multiple workers would finish at exactly
1394 * the same time.
1395 */
1396static bool
1398{
1399 int worker;
1400 char *msg;
1401
1402 /* Try to collect a status message */
1403 msg = getMessageFromWorker(pstate, do_wait, &worker);
1404
1405 if (!msg)
1406 {
1407 /* If do_wait is true, we must have detected EOF on some socket */
1408 if (do_wait)
1409 pg_fatal("a worker process died unexpectedly");
1410 return false;
1411 }
1412
1413 /* Process it and update our idea of the worker's status */
1414 if (messageStartsWith(msg, "OK "))
1415 {
1416 ParallelSlot *slot = &pstate->parallelSlot[worker];
1417 TocEntry *te = pstate->te[worker];
1418 int status;
1419
1420 status = parseWorkerResponse(AH, te, msg);
1421 slot->callback(AH, te, status, slot->callback_data);
1422 slot->workerStatus = WRKR_IDLE;
1423 pstate->te[worker] = NULL;
1424 }
1425 else
1426 pg_fatal("invalid message received from worker: \"%s\"",
1427 msg);
1428
1429 /* Free the string returned from getMessageFromWorker */
1430 free(msg);
1431
1432 return true;
1433}
1434
1435/*
1436 * Check for status results from workers, waiting if necessary.
1437 *
1438 * Available wait modes are:
1439 * WFW_NO_WAIT: reap any available status, but don't block
1440 * WFW_GOT_STATUS: wait for at least one more worker to finish
1441 * WFW_ONE_IDLE: wait for at least one worker to be idle
1442 * WFW_ALL_IDLE: wait for all workers to be idle
1443 *
1444 * Any received results are passed to the callback specified to
1445 * DispatchJobForTocEntry.
1446 *
1447 * This function is executed in the leader process.
1448 */
1449void
1451{
1452 bool do_wait = false;
1453
1454 /*
1455 * In GOT_STATUS mode, always block waiting for a message, since we can't
1456 * return till we get something. In other modes, we don't block the first
1457 * time through the loop.
1458 */
1459 if (mode == WFW_GOT_STATUS)
1460 {
1461 /* Assert that caller knows what it's doing */
1462 Assert(!IsEveryWorkerIdle(pstate));
1463 do_wait = true;
1464 }
1465
1466 for (;;)
1467 {
1468 /*
1469 * Check for status messages, even if we don't need to block. We do
1470 * not try very hard to reap all available messages, though, since
1471 * there's unlikely to be more than one.
1472 */
1473 if (ListenToWorkers(AH, pstate, do_wait))
1474 {
1475 /*
1476 * If we got a message, we are done by definition for GOT_STATUS
1477 * mode, and we can also be certain that there's at least one idle
1478 * worker. So we're done in all but ALL_IDLE mode.
1479 */
1480 if (mode != WFW_ALL_IDLE)
1481 return;
1482 }
1483
1484 /* Check whether we must wait for new status messages */
1485 switch (mode)
1486 {
1487 case WFW_NO_WAIT:
1488 return; /* never wait */
1489 case WFW_GOT_STATUS:
1490 Assert(false); /* can't get here, because we waited */
1491 break;
1492 case WFW_ONE_IDLE:
1493 if (GetIdleWorker(pstate) != NO_SLOT)
1494 return;
1495 break;
1496 case WFW_ALL_IDLE:
1497 if (IsEveryWorkerIdle(pstate))
1498 return;
1499 break;
1500 }
1501
1502 /* Loop back, and this time wait for something to happen */
1503 do_wait = true;
1504 }
1505}
1506
1507/*
1508 * Read one command message from the leader, blocking if necessary
1509 * until one is available, and return it as a malloc'd string.
1510 * On EOF, return NULL.
1511 *
1512 * This function is executed in worker processes.
1513 */
1514static char *
1516{
1518}
1519
1520/*
1521 * Send a status message to the leader.
1522 *
1523 * This function is executed in worker processes.
1524 */
1525static void
1526sendMessageToLeader(int pipefd[2], const char *str)
1527{
1528 size_t len = strlen(str) + 1;
1529
1530 if (pipewrite(pipefd[PIPE_WRITE], str, len) != len)
1531 pg_fatal("could not write to the communication channel: %m");
1532}
1533
1534/*
1535 * Wait until some descriptor in "workerset" becomes readable.
1536 * Returns -1 on error, else the number of readable descriptors.
1537 */
1538static int
1539select_loop(int maxFd, fd_set *workerset)
1540{
1541 int i;
1542 fd_set saveSet = *workerset;
1543
1544 for (;;)
1545 {
1546 *workerset = saveSet;
1547 i = select(maxFd + 1, workerset, NULL, NULL, NULL);
1548
1549#ifndef WIN32
1550 if (i < 0 && errno == EINTR)
1551 continue;
1552#else
1553 if (i == SOCKET_ERROR && WSAGetLastError() == WSAEINTR)
1554 continue;
1555#endif
1556 break;
1557 }
1558
1559 return i;
1560}
1561
1562
1563/*
1564 * Check for messages from worker processes.
1565 *
1566 * If a message is available, return it as a malloc'd string, and put the
1567 * index of the sending worker in *worker.
1568 *
1569 * If nothing is available, wait if "do_wait" is true, else return NULL.
1570 *
1571 * If we detect EOF on any socket, we'll return NULL. It's not great that
1572 * that's hard to distinguish from the no-data-available case, but for now
1573 * our one caller is okay with that.
1574 *
1575 * This function is executed in the leader process.
1576 */
1577static char *
1578getMessageFromWorker(ParallelState *pstate, bool do_wait, int *worker)
1579{
1580 int i;
1581 fd_set workerset;
1582 int maxFd = -1;
1583 struct timeval nowait = {0, 0};
1584
1585 /* construct bitmap of socket descriptors for select() */
1586 FD_ZERO(&workerset);
1587 for (i = 0; i < pstate->numWorkers; i++)
1588 {
1590 continue;
1591 FD_SET(pstate->parallelSlot[i].pipeRead, &workerset);
1592 if (pstate->parallelSlot[i].pipeRead > maxFd)
1593 maxFd = pstate->parallelSlot[i].pipeRead;
1594 }
1595
1596 if (do_wait)
1597 {
1598 i = select_loop(maxFd, &workerset);
1599 Assert(i != 0);
1600 }
1601 else
1602 {
1603 if ((i = select(maxFd + 1, &workerset, NULL, NULL, &nowait)) == 0)
1604 return NULL;
1605 }
1606
1607 if (i < 0)
1608 pg_fatal("%s() failed: %m", "select");
1609
1610 for (i = 0; i < pstate->numWorkers; i++)
1611 {
1612 char *msg;
1613
1615 continue;
1616 if (!FD_ISSET(pstate->parallelSlot[i].pipeRead, &workerset))
1617 continue;
1618
1619 /*
1620 * Read the message if any. If the socket is ready because of EOF,
1621 * we'll return NULL instead (and the socket will stay ready, so the
1622 * condition will persist).
1623 *
1624 * Note: because this is a blocking read, we'll wait if only part of
1625 * the message is available. Waiting a long time would be bad, but
1626 * since worker status messages are short and are always sent in one
1627 * operation, it shouldn't be a problem in practice.
1628 */
1630 *worker = i;
1631 return msg;
1632 }
1633 Assert(false);
1634 return NULL;
1635}
1636
1637/*
1638 * Send a command message to the specified worker process.
1639 *
1640 * This function is executed in the leader process.
1641 */
1642static void
1643sendMessageToWorker(ParallelState *pstate, int worker, const char *str)
1644{
1645 size_t len = strlen(str) + 1;
1646
1647 if (pipewrite(pstate->parallelSlot[worker].pipeWrite, str, len) != len)
1648 {
1649 pg_fatal("could not write to the communication channel: %m");
1650 }
1651}
1652
1653/*
1654 * Read one message from the specified pipe (fd), blocking if necessary
1655 * until one is available, and return it as a malloc'd string.
1656 * On EOF, return NULL.
1657 *
1658 * A "message" on the channel is just a null-terminated string.
1659 */
1660static char *
1662{
1663 char *msg;
1664 int msgsize,
1665 bufsize;
1666 int ret;
1667
1668 /*
1669 * In theory, if we let piperead() read multiple bytes, it might give us
1670 * back fragments of multiple messages. (That can't actually occur, since
1671 * neither leader nor workers send more than one message without waiting
1672 * for a reply, but we don't wish to assume that here.) For simplicity,
1673 * read a byte at a time until we get the terminating '\0'. This method
1674 * is a bit inefficient, but since this is only used for relatively short
1675 * command and status strings, it shouldn't matter.
1676 */
1677 bufsize = 64; /* could be any number */
1678 msg = (char *) pg_malloc(bufsize);
1679 msgsize = 0;
1680 for (;;)
1681 {
1683 ret = piperead(fd, msg + msgsize, 1);
1684 if (ret <= 0)
1685 break; /* error or connection closure */
1686
1687 Assert(ret == 1);
1688
1689 if (msg[msgsize] == '\0')
1690 return msg; /* collected whole message */
1691
1692 msgsize++;
1693 if (msgsize == bufsize) /* enlarge buffer if needed */
1694 {
1695 bufsize += 16; /* could be any number */
1696 msg = (char *) pg_realloc(msg, bufsize);
1697 }
1698 }
1699
1700 /* Other end has closed the connection */
1701 pg_free(msg);
1702 return NULL;
1703}
1704
1705#ifdef WIN32
1706
1707/*
1708 * This is a replacement version of pipe(2) for Windows which allows the pipe
1709 * handles to be used in select().
1710 *
1711 * Reads and writes on the pipe must go through piperead()/pipewrite().
1712 *
1713 * For consistency with Unix we declare the returned handles as "int".
1714 * This is okay even on WIN64 because system handles are not more than
1715 * 32 bits wide, but we do have to do some casting.
1716 */
1717static int
1718pgpipe(int handles[2])
1719{
1720 pgsocket s,
1721 tmp_sock;
1722 struct sockaddr_in serv_addr;
1723 int len = sizeof(serv_addr);
1724
1725 /* We have to use the Unix socket invalid file descriptor value here. */
1726 handles[0] = handles[1] = -1;
1727
1728 /*
1729 * setup listen socket
1730 */
1731 if ((s = socket(AF_INET, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
1732 {
1733 pg_log_error("pgpipe: could not create socket: error code %d",
1734 WSAGetLastError());
1735 return -1;
1736 }
1737
1738 memset(&serv_addr, 0, sizeof(serv_addr));
1739 serv_addr.sin_family = AF_INET;
1740 serv_addr.sin_port = pg_hton16(0);
1741 serv_addr.sin_addr.s_addr = pg_hton32(INADDR_LOOPBACK);
1742 if (bind(s, (SOCKADDR *) &serv_addr, len) == SOCKET_ERROR)
1743 {
1744 pg_log_error("pgpipe: could not bind: error code %d",
1745 WSAGetLastError());
1746 closesocket(s);
1747 return -1;
1748 }
1749 if (listen(s, 1) == SOCKET_ERROR)
1750 {
1751 pg_log_error("pgpipe: could not listen: error code %d",
1752 WSAGetLastError());
1753 closesocket(s);
1754 return -1;
1755 }
1756 if (getsockname(s, (SOCKADDR *) &serv_addr, &len) == SOCKET_ERROR)
1757 {
1758 pg_log_error("pgpipe: %s() failed: error code %d", "getsockname",
1759 WSAGetLastError());
1760 closesocket(s);
1761 return -1;
1762 }
1763
1764 /*
1765 * setup pipe handles
1766 */
1768 {
1769 pg_log_error("pgpipe: could not create second socket: error code %d",
1770 WSAGetLastError());
1771 closesocket(s);
1772 return -1;
1773 }
1774 handles[1] = (int) tmp_sock;
1775
1777 {
1778 pg_log_error("pgpipe: could not connect socket: error code %d",
1779 WSAGetLastError());
1780 closesocket(handles[1]);
1781 handles[1] = -1;
1782 closesocket(s);
1783 return -1;
1784 }
1785 if ((tmp_sock = accept(s, (SOCKADDR *) &serv_addr, &len)) == PGINVALID_SOCKET)
1786 {
1787 pg_log_error("pgpipe: could not accept connection: error code %d",
1788 WSAGetLastError());
1789 closesocket(handles[1]);
1790 handles[1] = -1;
1791 closesocket(s);
1792 return -1;
1793 }
1794 handles[0] = (int) tmp_sock;
1795
1796 closesocket(s);
1797 return 0;
1798}
1799
1800#endif /* WIN32 */
void ParallelBackupEnd(ArchiveHandle *AH, ParallelState *pstate)
Definition parallel.c:1059
static void sendMessageToLeader(int pipefd[2], const char *str)
Definition parallel.c:1527
static ParallelSlot * GetMyPSlot(ParallelState *pstate)
Definition parallel.c:266
static void WaitForCommands(ArchiveHandle *AH, int pipefd[2])
Definition parallel.c:1336
void WaitForWorkers(ArchiveHandle *AH, ParallelState *pstate, WFW_WaitOption mode)
Definition parallel.c:1451
@ WRKR_WORKING
Definition parallel.c:81
@ WRKR_IDLE
Definition parallel.c:80
@ WRKR_TERMINATED
Definition parallel.c:82
static bool HasEveryWorkerTerminated(ParallelState *pstate)
Definition parallel.c:1252
#define pgpipe(a)
Definition parallel.c:139
static bool ListenToWorkers(ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
Definition parallel.c:1398
static void sigTermHandler(SIGNAL_ARGS)
Definition parallel.c:548
#define PIPE_READ
Definition parallel.c:71
ParallelState * ParallelBackupStart(ArchiveHandle *AH)
Definition parallel.c:895
static char * readMessageFromPipe(int fd)
Definition parallel.c:1662
static int select_loop(int maxFd, fd_set *workerset)
Definition parallel.c:1540
static int parseWorkerResponse(ArchiveHandle *AH, TocEntry *te, const char *msg)
Definition parallel.c:1171
static int GetIdleWorker(ParallelState *pstate)
Definition parallel.c:1236
static void set_cancel_pstate(ParallelState *pstate)
Definition parallel.c:787
static void RunWorker(ArchiveHandle *AH, ParallelSlot *slot)
Definition parallel.c:827
static void set_cancel_slot_archive(ParallelSlot *slot, ArchiveHandle *AH)
Definition parallel.c:807
static void buildWorkerCommand(ArchiveHandle *AH, TocEntry *te, T_Action act, char *buf, int buflen)
Definition parallel.c:1108
static char * getMessageFromWorker(ParallelState *pstate, bool do_wait, int *worker)
Definition parallel.c:1579
static void archive_close_connection(int code, void *arg)
Definition parallel.c:341
#define NO_SLOT
Definition parallel.c:74
static void sendMessageToWorker(ParallelState *pstate, int worker, const char *str)
Definition parallel.c:1644
#define PIPE_WRITE
Definition parallel.c:72
static ShutdownInformation shutdown_info
Definition parallel.c:154
void on_exit_close_archive(Archive *AHX)
Definition parallel.c:330
void DispatchJobForTocEntry(ArchiveHandle *AH, ParallelState *pstate, TocEntry *te, T_Action act, ParallelCompletionPtr callback, void *callback_data)
Definition parallel.c:1205
#define WORKER_IS_RUNNING(workerStatus)
Definition parallel.c:85
static char * getMessageFromLeader(int pipefd[2])
Definition parallel.c:1516
static void lockTableForWorker(ArchiveHandle *AH, TocEntry *te)
Definition parallel.c:1301
#define piperead(a, b, c)
Definition parallel.c:140
#define pipewrite(a, b, c)
Definition parallel.c:141
static void set_cancel_handler(void)
Definition parallel.c:611
static void buildWorkerResponse(ArchiveHandle *AH, TocEntry *te, T_Action act, int status, char *buf, int buflen)
Definition parallel.c:1156
static volatile DumpSignalInformation signal_info
Definition parallel.c:175
bool IsEveryWorkerIdle(ParallelState *pstate)
Definition parallel.c:1268
#define write_stderr(str)
Definition parallel.c:186
static void parseWorkerCommand(ArchiveHandle *AH, TocEntry **te, T_Action *act, const char *msg)
Definition parallel.c:1123
#define messageStartsWith(msg, prefix)
Definition parallel.c:228
static void ShutdownWorkersHard(ParallelState *pstate)
Definition parallel.c:397
static void WaitForTerminatingWorkers(ParallelState *pstate)
Definition parallel.c:448
void set_archive_cancel_info(ArchiveHandle *AH, PGconn *conn)
Definition parallel.c:728
void(* ParallelCompletionPtr)(ArchiveHandle *AH, TocEntry *te, int status, void *callback_data)
Definition parallel.h:24
WFW_WaitOption
Definition parallel.h:31
@ WFW_ALL_IDLE
Definition parallel.h:35
@ WFW_GOT_STATUS
Definition parallel.h:33
@ WFW_NO_WAIT
Definition parallel.h:32
@ WFW_ONE_IDLE
Definition parallel.h:34
#define SIGNAL_ARGS
Definition c.h:1519
#define Assert(condition)
Definition c.h:1002
Datum arg
Definition elog.c:1323
void err(int eval, const char *fmt,...)
Definition err.c:43
PGcancel * PQgetCancel(PGconn *conn)
Definition fe-cancel.c:368
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
Definition fe-cancel.c:548
void PQfreeCancel(PGcancel *cancel)
Definition fe-cancel.c:502
PGresult * PQexec(PGconn *conn, const char *query)
Definition fe-exec.c:2279
void * pg_malloc(size_t size)
Definition fe_memutils.c:53
void pg_free(void *ptr)
void * pg_realloc(void *ptr, size_t size)
Definition fe_memutils.c:71
#define pg_malloc_array(type, count)
Definition fe_memutils.h:66
#define pg_malloc_object(type)
Definition fe_memutils.h:60
#define pg_malloc0_array(type, count)
Definition fe_memutils.h:67
const char * str
#define bufsize
int j
Definition isn.c:78
int i
Definition isn.c:77
#define PQclear
#define PQresultStatus
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
#define pg_log_error(...)
Definition logging.h:108
const char * progname
Definition main.c:44
int DumpId
Definition pg_backup.h:285
void DisconnectDatabase(Archive *AHX)
void DeCloneArchive(ArchiveHandle *AH)
ArchiveHandle * CloneArchive(ArchiveHandle *AH)
TocEntry * getTocEntryByDumpId(ArchiveHandle *AH, DumpId id)
#define WORKER_IGNORED_ERRORS
@ ACT_RESTORE
void on_exit_nicely(on_exit_nicely_callback function, void *arg)
#define pg_fatal(...)
#define pg_hton32(x)
Definition pg_bswap.h:121
#define pg_hton16(x)
Definition pg_bswap.h:120
static PgChecksumMode mode
const void size_t len
static bool do_wait
Definition pg_ctl.c:76
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define pqsignal
Definition port.h:548
#define PG_SIG_IGN
Definition port.h:552
int pgsocket
Definition port.h:29
#define snprintf
Definition port.h:261
#define PGINVALID_SOCKET
Definition port.h:31
#define closesocket
Definition port.h:398
PQExpBuffer createPQExpBuffer(void)
Definition pqexpbuffer.c:72
void resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void destroyPQExpBuffer(PQExpBuffer str)
PQExpBufferData * PQExpBuffer
Definition pqexpbuffer.h:51
static int fd(const char *x, int i)
static int fb(int x)
#define free(a)
PGconn * conn
Definition streamutil.c:52
const char * fmtQualifiedId(const char *schema, const char *id)
PQExpBuffer(* getLocalPQExpBuffer)(void)
int n_errors
Definition pg_backup.h:253
int numWorkers
Definition pg_backup.h:240
ArchiveHandle * myAH
Definition parallel.c:167
ParallelState * pstate
Definition parallel.c:168
ParallelCompletionPtr callback
Definition parallel.c:100
ArchiveHandle * AH
Definition parallel.c:103
void * callback_data
Definition parallel.c:101
T_WorkerStatus workerStatus
Definition parallel.c:97
int pipeRevRead
Definition parallel.c:107
int pipeRevWrite
Definition parallel.c:108
TocEntry ** te
Definition parallel.h:59
ParallelSlot * parallelSlot
Definition parallel.h:60
ParallelState * pstate
Definition parallel.c:150
WorkerJobDumpPtrType WorkerJobDumpPtr
PGcancel *volatile connCancel
WorkerJobRestorePtrType WorkerJobRestorePtr
SetupWorkerPtrType SetupWorkerPtr
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
#define bind(s, addr, addrlen)
Definition win32_port.h:513
#define EINTR
Definition win32_port.h:378
#define SIGPIPE
Definition win32_port.h:163
#define SIGQUIT
Definition win32_port.h:159
#define kill(pid, sig)
Definition win32_port.h:507
#define socket(af, type, protocol)
Definition win32_port.h:512
#define accept(s, addr, addrlen)
Definition win32_port.h:515
#define connect(s, name, namelen)
Definition win32_port.h:516
#define listen(s, backlog)
Definition win32_port.h:514
#define select(n, r, w, e, timeout)
Definition win32_port.h:517

◆ NO_SLOT

#define NO_SLOT   (-1) /* Failure result for GetIdleWorker() */

Definition at line 74 of file parallel.c.

◆ pgpipe

#define pgpipe (   a)    pipe(a)

Definition at line 139 of file parallel.c.

◆ PIPE_READ

#define PIPE_READ   0

Definition at line 71 of file parallel.c.

◆ PIPE_WRITE

#define PIPE_WRITE   1

Definition at line 72 of file parallel.c.

◆ piperead

#define piperead (   a,
  b,
  c 
)    read(a,b,c)

Definition at line 140 of file parallel.c.

◆ pipewrite

#define pipewrite (   a,
  b,
  c 
)    write(a,b,c)

Definition at line 141 of file parallel.c.

◆ WORKER_IS_RUNNING

#define WORKER_IS_RUNNING (   workerStatus)     ((workerStatus) == WRKR_IDLE || (workerStatus) == WRKR_WORKING)

Definition at line 85 of file parallel.c.

◆ write_stderr

#define write_stderr (   str)
Value:
do { \
const char *str_ = (str); \
rc_ = write(fileno(stderr), str_, strlen(str_)); \
(void) rc_; \
} while (0)
#define write(a, b, c)
Definition win32.h:14

Definition at line 186 of file parallel.c.

187 { \
188 const char *str_ = (str); \
189 ssize_t rc_; \
190 rc_ = write(fileno(stderr), str_, strlen(str_)); \
191 (void) rc_; \
192 } while (0)

Typedef Documentation

◆ DumpSignalInformation

◆ ShutdownInformation

Enumeration Type Documentation

◆ T_WorkerStatus

Enumerator
WRKR_NOT_STARTED 
WRKR_IDLE 
WRKR_WORKING 
WRKR_TERMINATED 

Definition at line 77 of file parallel.c.

78{
T_WorkerStatus
Definition parallel.c:78
@ WRKR_NOT_STARTED
Definition parallel.c:79

Function Documentation

◆ archive_close_connection()

static void archive_close_connection ( int  code,
void arg 
)
static

Definition at line 341 of file parallel.c.

342{
344
345 if (si->pstate)
346 {
347 /* In parallel mode, must figure out who we are */
348 ParallelSlot *slot = GetMyPSlot(si->pstate);
349
350 if (!slot)
351 {
352 /*
353 * We're the leader. Forcibly shut down workers, then close our
354 * own database connection, if any.
355 */
356 ShutdownWorkersHard(si->pstate);
357
358 if (si->AHX)
360 }
361 else
362 {
363 /*
364 * We're a worker. Shut down our own DB connection if any. On
365 * Windows, we also have to close our communication sockets, to
366 * emulate what will happen on Unix when the worker process exits.
367 * (Without this, if this is a premature exit, the leader would
368 * fail to detect it because there would be no EOF condition on
369 * the other end of the pipe.)
370 */
371 if (slot->AH)
372 DisconnectDatabase(&(slot->AH->public));
373
374#ifdef WIN32
377#endif
378 }
379 }
380 else
381 {
382 /* Non-parallel operation: just kill the leader DB connection */
383 if (si->AHX)
385 }
386}

References ParallelSlot::AH, arg, closesocket, DisconnectDatabase(), fb(), GetMyPSlot(), ParallelSlot::pipeRevRead, ParallelSlot::pipeRevWrite, _archiveHandle::public, and ShutdownWorkersHard().

Referenced by on_exit_close_archive().

◆ buildWorkerCommand()

static void buildWorkerCommand ( ArchiveHandle AH,
TocEntry te,
T_Action  act,
char buf,
int  buflen 
)
static

Definition at line 1108 of file parallel.c.

1110{
1111 if (act == ACT_DUMP)
1112 snprintf(buf, buflen, "DUMP %d", te->dumpId);
1113 else if (act == ACT_RESTORE)
1114 snprintf(buf, buflen, "RESTORE %d", te->dumpId);
1115 else
1116 Assert(false);
1117}

References ACT_DUMP, ACT_RESTORE, Assert, buf, _tocEntry::dumpId, fb(), and snprintf.

Referenced by DispatchJobForTocEntry().

◆ buildWorkerResponse()

static void buildWorkerResponse ( ArchiveHandle AH,
TocEntry te,
T_Action  act,
int  status,
char buf,
int  buflen 
)
static

Definition at line 1156 of file parallel.c.

1158{
1159 snprintf(buf, buflen, "OK %d %d %d",
1160 te->dumpId,
1161 status,
1162 status == WORKER_IGNORED_ERRORS ? AH->public.n_errors : 0);
1163}

References buf, _tocEntry::dumpId, Archive::n_errors, _archiveHandle::public, snprintf, and WORKER_IGNORED_ERRORS.

Referenced by WaitForCommands().

◆ DispatchJobForTocEntry()

void DispatchJobForTocEntry ( ArchiveHandle AH,
ParallelState pstate,
TocEntry te,
T_Action  act,
ParallelCompletionPtr  callback,
void callback_data 
)

Definition at line 1205 of file parallel.c.

1211{
1212 int worker;
1213 char buf[256];
1214
1215 /* Get a worker, waiting if none are idle */
1216 while ((worker = GetIdleWorker(pstate)) == NO_SLOT)
1217 WaitForWorkers(AH, pstate, WFW_ONE_IDLE);
1218
1219 /* Construct and send command string */
1220 buildWorkerCommand(AH, te, act, buf, sizeof(buf));
1221
1222 sendMessageToWorker(pstate, worker, buf);
1223
1224 /* Remember worker is busy, and which TocEntry it's working on */
1225 pstate->parallelSlot[worker].workerStatus = WRKR_WORKING;
1226 pstate->parallelSlot[worker].callback = callback;
1227 pstate->parallelSlot[worker].callback_data = callback_data;
1228 pstate->te[worker] = te;
1229}

References buf, buildWorkerCommand(), ParallelSlot::callback, callback(), ParallelSlot::callback_data, fb(), GetIdleWorker(), NO_SLOT, ParallelState::parallelSlot, sendMessageToWorker(), ParallelState::te, WaitForWorkers(), WFW_ONE_IDLE, ParallelSlot::workerStatus, and WRKR_WORKING.

Referenced by restore_toc_entries_parallel(), and WriteDataChunks().

◆ GetIdleWorker()

static int GetIdleWorker ( ParallelState pstate)
static

Definition at line 1236 of file parallel.c.

1237{
1238 int i;
1239
1240 for (i = 0; i < pstate->numWorkers; i++)
1241 {
1242 if (pstate->parallelSlot[i].workerStatus == WRKR_IDLE)
1243 return i;
1244 }
1245 return NO_SLOT;
1246}

References i, NO_SLOT, ParallelState::numWorkers, ParallelState::parallelSlot, ParallelSlot::workerStatus, and WRKR_IDLE.

Referenced by DispatchJobForTocEntry(), and WaitForWorkers().

◆ getMessageFromLeader()

static char * getMessageFromLeader ( int  pipefd[2])
static

Definition at line 1516 of file parallel.c.

1517{
1519}

References fb(), PIPE_READ, and readMessageFromPipe().

Referenced by WaitForCommands().

◆ getMessageFromWorker()

static char * getMessageFromWorker ( ParallelState pstate,
bool  do_wait,
int worker 
)
static

Definition at line 1579 of file parallel.c.

1580{
1581 int i;
1582 fd_set workerset;
1583 int maxFd = -1;
1584 struct timeval nowait = {0, 0};
1585
1586 /* construct bitmap of socket descriptors for select() */
1587 FD_ZERO(&workerset);
1588 for (i = 0; i < pstate->numWorkers; i++)
1589 {
1591 continue;
1592 FD_SET(pstate->parallelSlot[i].pipeRead, &workerset);
1593 if (pstate->parallelSlot[i].pipeRead > maxFd)
1594 maxFd = pstate->parallelSlot[i].pipeRead;
1595 }
1596
1597 if (do_wait)
1598 {
1599 i = select_loop(maxFd, &workerset);
1600 Assert(i != 0);
1601 }
1602 else
1603 {
1604 if ((i = select(maxFd + 1, &workerset, NULL, NULL, &nowait)) == 0)
1605 return NULL;
1606 }
1607
1608 if (i < 0)
1609 pg_fatal("%s() failed: %m", "select");
1610
1611 for (i = 0; i < pstate->numWorkers; i++)
1612 {
1613 char *msg;
1614
1616 continue;
1617 if (!FD_ISSET(pstate->parallelSlot[i].pipeRead, &workerset))
1618 continue;
1619
1620 /*
1621 * Read the message if any. If the socket is ready because of EOF,
1622 * we'll return NULL instead (and the socket will stay ready, so the
1623 * condition will persist).
1624 *
1625 * Note: because this is a blocking read, we'll wait if only part of
1626 * the message is available. Waiting a long time would be bad, but
1627 * since worker status messages are short and are always sent in one
1628 * operation, it shouldn't be a problem in practice.
1629 */
1631 *worker = i;
1632 return msg;
1633 }
1634 Assert(false);
1635 return NULL;
1636}

References Assert, do_wait, fb(), i, ParallelState::numWorkers, ParallelState::parallelSlot, pg_fatal, ParallelSlot::pipeRead, readMessageFromPipe(), select, select_loop(), WORKER_IS_RUNNING, and ParallelSlot::workerStatus.

Referenced by ListenToWorkers().

◆ GetMyPSlot()

static ParallelSlot * GetMyPSlot ( ParallelState pstate)
static

Definition at line 266 of file parallel.c.

267{
268 int i;
269
270 for (i = 0; i < pstate->numWorkers; i++)
271 {
272#ifdef WIN32
273 if (pstate->parallelSlot[i].threadId == GetCurrentThreadId())
274#else
275 if (pstate->parallelSlot[i].pid == getpid())
276#endif
277 return &(pstate->parallelSlot[i]);
278 }
279
280 return NULL;
281}

References fb(), i, ParallelState::numWorkers, ParallelState::parallelSlot, and ParallelSlot::pid.

Referenced by archive_close_connection().

◆ HasEveryWorkerTerminated()

static bool HasEveryWorkerTerminated ( ParallelState pstate)
static

Definition at line 1252 of file parallel.c.

1253{
1254 int i;
1255
1256 for (i = 0; i < pstate->numWorkers; i++)
1257 {
1259 return false;
1260 }
1261 return true;
1262}

References i, ParallelState::numWorkers, ParallelState::parallelSlot, WORKER_IS_RUNNING, and ParallelSlot::workerStatus.

Referenced by WaitForTerminatingWorkers().

◆ init_parallel_dump_utils()

void init_parallel_dump_utils ( void  )

Definition at line 238 of file parallel.c.

239{
240#ifdef WIN32
242 {
244 int err;
245
246 /* Prepare for threaded operation */
249
250 /* Initialize socket access */
251 err = WSAStartup(MAKEWORD(2, 2), &wsaData);
252 if (err != 0)
253 pg_fatal("%s() failed: error code %d", "WSAStartup", err);
254
255 parallel_init_done = true;
256 }
257#endif
258}

References err(), fb(), and pg_fatal.

Referenced by main().

◆ IsEveryWorkerIdle()

bool IsEveryWorkerIdle ( ParallelState pstate)

Definition at line 1268 of file parallel.c.

1269{
1270 int i;
1271
1272 for (i = 0; i < pstate->numWorkers; i++)
1273 {
1274 if (pstate->parallelSlot[i].workerStatus != WRKR_IDLE)
1275 return false;
1276 }
1277 return true;
1278}

References i, ParallelState::numWorkers, ParallelState::parallelSlot, ParallelSlot::workerStatus, and WRKR_IDLE.

Referenced by ParallelBackupEnd(), restore_toc_entries_parallel(), and WaitForWorkers().

◆ ListenToWorkers()

static bool ListenToWorkers ( ArchiveHandle AH,
ParallelState pstate,
bool  do_wait 
)
static

Definition at line 1398 of file parallel.c.

1399{
1400 int worker;
1401 char *msg;
1402
1403 /* Try to collect a status message */
1404 msg = getMessageFromWorker(pstate, do_wait, &worker);
1405
1406 if (!msg)
1407 {
1408 /* If do_wait is true, we must have detected EOF on some socket */
1409 if (do_wait)
1410 pg_fatal("a worker process died unexpectedly");
1411 return false;
1412 }
1413
1414 /* Process it and update our idea of the worker's status */
1415 if (messageStartsWith(msg, "OK "))
1416 {
1417 ParallelSlot *slot = &pstate->parallelSlot[worker];
1418 TocEntry *te = pstate->te[worker];
1419 int status;
1420
1421 status = parseWorkerResponse(AH, te, msg);
1422 slot->callback(AH, te, status, slot->callback_data);
1423 slot->workerStatus = WRKR_IDLE;
1424 pstate->te[worker] = NULL;
1425 }
1426 else
1427 pg_fatal("invalid message received from worker: \"%s\"",
1428 msg);
1429
1430 /* Free the string returned from getMessageFromWorker */
1431 free(msg);
1432
1433 return true;
1434}

References ParallelSlot::callback, ParallelSlot::callback_data, do_wait, fb(), free, getMessageFromWorker(), messageStartsWith, ParallelState::parallelSlot, parseWorkerResponse(), pg_fatal, ParallelState::te, ParallelSlot::workerStatus, and WRKR_IDLE.

Referenced by WaitForWorkers().

◆ lockTableForWorker()

static void lockTableForWorker ( ArchiveHandle AH,
TocEntry te 
)
static

Definition at line 1301 of file parallel.c.

1302{
1303 const char *qualId;
1304 PQExpBuffer query;
1305 PGresult *res;
1306
1307 /* Nothing to do for BLOBS */
1308 if (strcmp(te->desc, "BLOBS") == 0)
1309 return;
1310
1311 query = createPQExpBuffer();
1312
1313 qualId = fmtQualifiedId(te->namespace, te->tag);
1314
1315 appendPQExpBuffer(query, "LOCK TABLE %s IN ACCESS SHARE MODE NOWAIT",
1316 qualId);
1317
1318 res = PQexec(AH->connection, query->data);
1319
1320 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1321 pg_fatal("could not obtain lock on relation \"%s\"\n"
1322 "This usually means that someone requested an ACCESS EXCLUSIVE lock "
1323 "on the table after the pg_dump parent process had gotten the "
1324 "initial ACCESS SHARE lock on the table.", qualId);
1325
1326 PQclear(res);
1327 destroyPQExpBuffer(query);
1328}

References appendPQExpBuffer(), _archiveHandle::connection, createPQExpBuffer(), PQExpBufferData::data, _tocEntry::desc, destroyPQExpBuffer(), fb(), fmtQualifiedId(), pg_fatal, PGRES_COMMAND_OK, PQclear, PQexec(), PQresultStatus, and _tocEntry::tag.

Referenced by WaitForCommands().

◆ on_exit_close_archive()

void on_exit_close_archive ( Archive AHX)

◆ ParallelBackupEnd()

void ParallelBackupEnd ( ArchiveHandle AH,
ParallelState pstate 
)

Definition at line 1059 of file parallel.c.

1060{
1061 int i;
1062
1063 /* No work if non-parallel */
1064 if (pstate->numWorkers == 1)
1065 return;
1066
1067 /* There should not be any unfinished jobs */
1068 Assert(IsEveryWorkerIdle(pstate));
1069
1070 /* Close the sockets so that the workers know they can exit */
1071 for (i = 0; i < pstate->numWorkers; i++)
1072 {
1075 }
1076
1077 /* Wait for them to exit */
1079
1080 /*
1081 * Unlink pstate from shutdown_info, so the exit handler will not try to
1082 * use it; and likewise unlink from signal_info.
1083 */
1086
1087 /* Release state (mere neatnik-ism, since we're about to terminate) */
1088 free(pstate->te);
1089 free(pstate->parallelSlot);
1090 free(pstate);
1091}

References Assert, closesocket, fb(), free, i, IsEveryWorkerIdle(), ParallelState::numWorkers, ParallelState::parallelSlot, ParallelSlot::pipeRead, ParallelSlot::pipeWrite, ShutdownInformation::pstate, set_cancel_pstate(), shutdown_info, ParallelState::te, and WaitForTerminatingWorkers().

Referenced by _CloseArchive(), and RestoreArchive().

◆ ParallelBackupStart()

ParallelState * ParallelBackupStart ( ArchiveHandle AH)

Definition at line 895 of file parallel.c.

896{
897 ParallelState *pstate;
898 int i;
899
900 Assert(AH->public.numWorkers > 0);
901
903
904 pstate->numWorkers = AH->public.numWorkers;
905 pstate->te = NULL;
906 pstate->parallelSlot = NULL;
907
908 if (AH->public.numWorkers == 1)
909 return pstate;
910
911 /* Create status arrays, being sure to initialize all fields to 0 */
912 pstate->te =
914 pstate->parallelSlot =
916
917#ifdef WIN32
918 /* Make fmtId() and fmtQualifiedId() use thread-local storage */
920#endif
921
922 /*
923 * Set the pstate in shutdown_info, to tell the exit handler that it must
924 * clean up workers as well as the main database connection. But we don't
925 * set this in signal_info yet, because we don't want child processes to
926 * inherit non-NULL signal_info.pstate.
927 */
928 shutdown_info.pstate = pstate;
929
930 /*
931 * Temporarily disable query cancellation on the leader connection. This
932 * ensures that child processes won't inherit valid AH->connCancel
933 * settings and thus won't try to issue cancels against the leader's
934 * connection. No harm is done if we fail while it's disabled, because
935 * the leader connection is idle at this point anyway.
936 */
938
939 /* Ensure stdio state is quiesced before forking */
940 fflush(NULL);
941
942 /* Create desired number of workers */
943 for (i = 0; i < pstate->numWorkers; i++)
944 {
945#ifdef WIN32
946 WorkerInfo *wi;
947 uintptr_t handle;
948#else
949 pid_t pid;
950#endif
951 ParallelSlot *slot = &(pstate->parallelSlot[i]);
952 int pipeMW[2],
953 pipeWM[2];
954
955 /* Create communication pipes for this worker */
956 if (pgpipe(pipeMW) < 0 || pgpipe(pipeWM) < 0)
957 pg_fatal("could not create communication channels: %m");
958
959 /* leader's ends of the pipes */
960 slot->pipeRead = pipeWM[PIPE_READ];
961 slot->pipeWrite = pipeMW[PIPE_WRITE];
962 /* child's ends of the pipes */
965
966#ifdef WIN32
967 /* Create transient structure to pass args to worker function */
969
970 wi->AH = AH;
971 wi->slot = slot;
972
973 handle = _beginthreadex(NULL, 0, (void *) &init_spawned_worker_win32,
974 wi, 0, &(slot->threadId));
975 if (handle == 0)
976 pg_fatal("could not create worker thread: %m");
977 slot->hThread = handle;
978 slot->workerStatus = WRKR_IDLE;
979#else /* !WIN32 */
980 pid = fork();
981 if (pid == 0)
982 {
983 /* we are the worker */
984 int j;
985
986 /* this is needed for GetMyPSlot() */
987 slot->pid = getpid();
988
989 /* instruct signal handler that we're in a worker now */
990 signal_info.am_worker = true;
991
992 /* close read end of Worker -> Leader */
994 /* close write end of Leader -> Worker */
996
997 /*
998 * Close all inherited fds for communication of the leader with
999 * previously-forked workers.
1000 */
1001 for (j = 0; j < i; j++)
1002 {
1005 }
1006
1007 /* Run the worker ... */
1008 RunWorker(AH, slot);
1009
1010 /* We can just exit(0) when done */
1011 exit(0);
1012 }
1013 else if (pid < 0)
1014 {
1015 /* fork failed */
1016 pg_fatal("could not create worker process: %m");
1017 }
1018
1019 /* In Leader after successful fork */
1020 slot->pid = pid;
1021 slot->workerStatus = WRKR_IDLE;
1022
1023 /* close read end of Leader -> Worker */
1025 /* close write end of Worker -> Leader */
1027#endif /* WIN32 */
1028 }
1029
1030 /*
1031 * Having forked off the workers, disable SIGPIPE so that leader isn't
1032 * killed if it tries to send a command to a dead worker. We don't want
1033 * the workers to inherit this setting, though.
1034 */
1035#ifndef WIN32
1037#endif
1038
1039 /*
1040 * Re-establish query cancellation on the leader connection.
1041 */
1043
1044 /*
1045 * Tell the cancel signal handler to forward signals to worker processes,
1046 * too. (As with query cancel, we did not need this earlier because the
1047 * workers have not yet been given anything to do; if we die before this
1048 * point, any already-started workers will see EOF and quit promptly.)
1049 */
1050 set_cancel_pstate(pstate);
1051
1052 return pstate;
1053}

References DumpSignalInformation::am_worker, Assert, closesocket, _archiveHandle::connection, fb(), getLocalPQExpBuffer, i, j, ParallelState::numWorkers, Archive::numWorkers, ParallelState::parallelSlot, pg_fatal, pg_malloc0_array, pg_malloc_object, PG_SIG_IGN, pgpipe, ParallelSlot::pid, PIPE_READ, PIPE_WRITE, ParallelSlot::pipeRead, ParallelSlot::pipeRevRead, ParallelSlot::pipeRevWrite, ParallelSlot::pipeWrite, pqsignal, ShutdownInformation::pstate, _archiveHandle::public, RunWorker(), set_archive_cancel_info(), set_cancel_pstate(), shutdown_info, signal_info, SIGPIPE, ParallelState::te, ParallelSlot::workerStatus, and WRKR_IDLE.

Referenced by _CloseArchive(), and RestoreArchive().

◆ parseWorkerCommand()

static void parseWorkerCommand ( ArchiveHandle AH,
TocEntry **  te,
T_Action act,
const char msg 
)
static

Definition at line 1123 of file parallel.c.

1125{
1126 DumpId dumpId;
1127 int nBytes;
1128
1129 if (messageStartsWith(msg, "DUMP "))
1130 {
1131 *act = ACT_DUMP;
1132 sscanf(msg, "DUMP %d%n", &dumpId, &nBytes);
1133 Assert(nBytes == strlen(msg));
1134 *te = getTocEntryByDumpId(AH, dumpId);
1135 Assert(*te != NULL);
1136 }
1137 else if (messageStartsWith(msg, "RESTORE "))
1138 {
1139 *act = ACT_RESTORE;
1140 sscanf(msg, "RESTORE %d%n", &dumpId, &nBytes);
1141 Assert(nBytes == strlen(msg));
1142 *te = getTocEntryByDumpId(AH, dumpId);
1143 Assert(*te != NULL);
1144 }
1145 else
1146 pg_fatal("unrecognized command received from leader: \"%s\"",
1147 msg);
1148}

References ACT_DUMP, ACT_RESTORE, Assert, fb(), getTocEntryByDumpId(), messageStartsWith, and pg_fatal.

Referenced by WaitForCommands().

◆ parseWorkerResponse()

static int parseWorkerResponse ( ArchiveHandle AH,
TocEntry te,
const char msg 
)
static

Definition at line 1171 of file parallel.c.

1173{
1174 DumpId dumpId;
1175 int nBytes,
1176 n_errors;
1177 int status = 0;
1178
1179 if (messageStartsWith(msg, "OK "))
1180 {
1181 sscanf(msg, "OK %d %d %d%n", &dumpId, &status, &n_errors, &nBytes);
1182
1183 Assert(dumpId == te->dumpId);
1184 Assert(nBytes == strlen(msg));
1185
1186 AH->public.n_errors += n_errors;
1187 }
1188 else
1189 pg_fatal("invalid message received from worker: \"%s\"",
1190 msg);
1191
1192 return status;
1193}

References Assert, _tocEntry::dumpId, fb(), messageStartsWith, Archive::n_errors, pg_fatal, and _archiveHandle::public.

Referenced by ListenToWorkers().

◆ readMessageFromPipe()

static char * readMessageFromPipe ( int  fd)
static

Definition at line 1662 of file parallel.c.

1663{
1664 char *msg;
1665 int msgsize,
1666 bufsize;
1667 int ret;
1668
1669 /*
1670 * In theory, if we let piperead() read multiple bytes, it might give us
1671 * back fragments of multiple messages. (That can't actually occur, since
1672 * neither leader nor workers send more than one message without waiting
1673 * for a reply, but we don't wish to assume that here.) For simplicity,
1674 * read a byte at a time until we get the terminating '\0'. This method
1675 * is a bit inefficient, but since this is only used for relatively short
1676 * command and status strings, it shouldn't matter.
1677 */
1678 bufsize = 64; /* could be any number */
1679 msg = (char *) pg_malloc(bufsize);
1680 msgsize = 0;
1681 for (;;)
1682 {
1684 ret = piperead(fd, msg + msgsize, 1);
1685 if (ret <= 0)
1686 break; /* error or connection closure */
1687
1688 Assert(ret == 1);
1689
1690 if (msg[msgsize] == '\0')
1691 return msg; /* collected whole message */
1692
1693 msgsize++;
1694 if (msgsize == bufsize) /* enlarge buffer if needed */
1695 {
1696 bufsize += 16; /* could be any number */
1697 msg = (char *) pg_realloc(msg, bufsize);
1698 }
1699 }
1700
1701 /* Other end has closed the connection */
1702 pg_free(msg);
1703 return NULL;
1704}

References Assert, bufsize, fb(), fd(), pg_free(), pg_malloc(), pg_realloc(), and piperead.

Referenced by getMessageFromLeader(), and getMessageFromWorker().

◆ RunWorker()

static void RunWorker ( ArchiveHandle AH,
ParallelSlot slot 
)
static

Definition at line 827 of file parallel.c.

828{
829 int pipefd[2];
830
831 /* fetch child ends of pipes */
834
835 /*
836 * Clone the archive so that we have our own state to work with, and in
837 * particular our own database connection.
838 *
839 * We clone on Unix as well as Windows, even though technically we don't
840 * need to because fork() gives us a copy in our own address space
841 * already. But CloneArchive resets the state information and also clones
842 * the database connection which both seem kinda helpful.
843 */
844 AH = CloneArchive(AH);
845
846 /* Remember cloned archive where signal handler can find it */
847 set_cancel_slot_archive(slot, AH);
848
849 /*
850 * Call the setup worker function that's defined in the ArchiveHandle.
851 */
852 (AH->SetupWorkerPtr) ((Archive *) AH);
853
854 /*
855 * Execute commands until done.
856 */
858
859 /*
860 * Disconnect from database and clean up.
861 */
864 DeCloneArchive(AH);
865}

References CloneArchive(), DeCloneArchive(), DisconnectDatabase(), fb(), PIPE_READ, PIPE_WRITE, ParallelSlot::pipeRevRead, ParallelSlot::pipeRevWrite, _archiveHandle::public, set_cancel_slot_archive(), _archiveHandle::SetupWorkerPtr, and WaitForCommands().

Referenced by ParallelBackupStart().

◆ select_loop()

static int select_loop ( int  maxFd,
fd_set workerset 
)
static

Definition at line 1540 of file parallel.c.

1541{
1542 int i;
1543 fd_set saveSet = *workerset;
1544
1545 for (;;)
1546 {
1547 *workerset = saveSet;
1548 i = select(maxFd + 1, workerset, NULL, NULL, NULL);
1549
1550#ifndef WIN32
1551 if (i < 0 && errno == EINTR)
1552 continue;
1553#else
1554 if (i == SOCKET_ERROR && WSAGetLastError() == WSAEINTR)
1555 continue;
1556#endif
1557 break;
1558 }
1559
1560 return i;
1561}

References EINTR, fb(), i, and select.

Referenced by getMessageFromWorker().

◆ sendMessageToLeader()

static void sendMessageToLeader ( int  pipefd[2],
const char str 
)
static

Definition at line 1527 of file parallel.c.

1528{
1529 size_t len = strlen(str) + 1;
1530
1531 if (pipewrite(pipefd[PIPE_WRITE], str, len) != len)
1532 pg_fatal("could not write to the communication channel: %m");
1533}

References fb(), len, pg_fatal, PIPE_WRITE, pipewrite, and str.

Referenced by WaitForCommands().

◆ sendMessageToWorker()

static void sendMessageToWorker ( ParallelState pstate,
int  worker,
const char str 
)
static

Definition at line 1644 of file parallel.c.

1645{
1646 size_t len = strlen(str) + 1;
1647
1648 if (pipewrite(pstate->parallelSlot[worker].pipeWrite, str, len) != len)
1649 {
1650 pg_fatal("could not write to the communication channel: %m");
1651 }
1652}

References fb(), len, ParallelState::parallelSlot, pg_fatal, ParallelSlot::pipeWrite, pipewrite, and str.

Referenced by DispatchJobForTocEntry().

◆ set_archive_cancel_info()

void set_archive_cancel_info ( ArchiveHandle AH,
PGconn conn 
)

Definition at line 728 of file parallel.c.

729{
731
732 /*
733 * Activate the interrupt handler if we didn't yet in this process. On
734 * Windows, this also initializes signal_info_lock; therefore it's
735 * important that this happen at least once before we fork off any
736 * threads.
737 */
739
740 /*
741 * On Unix, we assume that storing a pointer value is atomic with respect
742 * to any possible signal interrupt. On Windows, use a critical section.
743 */
744
745#ifdef WIN32
747#endif
748
749 /* Free the old one if we have one */
751 /* be sure interrupt handler doesn't use pointer while freeing */
752 AH->connCancel = NULL;
753
754 if (oldConnCancel != NULL)
756
757 /* Set the new one if specified */
758 if (conn)
760
761 /*
762 * On Unix, there's only ever one active ArchiveHandle per process, so we
763 * can just set signal_info.myAH unconditionally. On Windows, do that
764 * only in the main thread; worker threads have to make sure their
765 * ArchiveHandle appears in the pstate data, which is dealt with in
766 * RunWorker().
767 */
768#ifndef WIN32
769 signal_info.myAH = AH;
770#else
772 signal_info.myAH = AH;
773#endif
774
775#ifdef WIN32
777#endif
778}

References conn, _archiveHandle::connCancel, fb(), DumpSignalInformation::myAH, PQfreeCancel(), PQgetCancel(), set_cancel_handler(), and signal_info.

Referenced by ConnectDatabaseAhx(), DisconnectDatabase(), and ParallelBackupStart().

◆ set_cancel_handler()

static void set_cancel_handler ( void  )
static

Definition at line 611 of file parallel.c.

612{
613 /*
614 * When forking, signal_info.handler_set will propagate into the new
615 * process, but that's fine because the signal handler state does too.
616 */
618 {
620
624 }
625}

References fb(), DumpSignalInformation::handler_set, pqsignal, signal_info, SIGQUIT, and sigTermHandler().

Referenced by set_archive_cancel_info().

◆ set_cancel_pstate()

static void set_cancel_pstate ( ParallelState pstate)
static

Definition at line 787 of file parallel.c.

788{
789#ifdef WIN32
791#endif
792
793 signal_info.pstate = pstate;
794
795#ifdef WIN32
797#endif
798}

References fb(), DumpSignalInformation::pstate, and signal_info.

Referenced by ParallelBackupEnd(), and ParallelBackupStart().

◆ set_cancel_slot_archive()

static void set_cancel_slot_archive ( ParallelSlot slot,
ArchiveHandle AH 
)
static

Definition at line 807 of file parallel.c.

808{
809#ifdef WIN32
811#endif
812
813 slot->AH = AH;
814
815#ifdef WIN32
817#endif
818}

References ParallelSlot::AH, and fb().

Referenced by RunWorker().

◆ ShutdownWorkersHard()

static void ShutdownWorkersHard ( ParallelState pstate)
static

Definition at line 397 of file parallel.c.

398{
399 int i;
400
401 /*
402 * Close our write end of the sockets so that any workers waiting for
403 * commands know they can exit. (Note: some of the pipeWrite fields might
404 * still be zero, if we failed to initialize all the workers. Hence, just
405 * ignore errors here.)
406 */
407 for (i = 0; i < pstate->numWorkers; i++)
409
410 /*
411 * Force early termination of any commands currently in progress.
412 */
413#ifndef WIN32
414 /* On non-Windows, send SIGTERM to each worker process. */
415 for (i = 0; i < pstate->numWorkers; i++)
416 {
417 pid_t pid = pstate->parallelSlot[i].pid;
418
419 if (pid != 0)
420 kill(pid, SIGTERM);
421 }
422#else
423
424 /*
425 * On Windows, send query cancels directly to the workers' backends. Use
426 * a critical section to ensure worker threads don't change state.
427 */
429 for (i = 0; i < pstate->numWorkers; i++)
430 {
431 ArchiveHandle *AH = pstate->parallelSlot[i].AH;
432 char errbuf[1];
433
434 if (AH != NULL && AH->connCancel != NULL)
435 (void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
436 }
438#endif
439
440 /* Now wait for them to terminate. */
442}

References ParallelSlot::AH, closesocket, _archiveHandle::connCancel, fb(), i, kill, ParallelState::numWorkers, ParallelState::parallelSlot, ParallelSlot::pid, ParallelSlot::pipeWrite, PQcancel(), and WaitForTerminatingWorkers().

Referenced by archive_close_connection().

◆ sigTermHandler()

static void sigTermHandler ( SIGNAL_ARGS  )
static

Definition at line 548 of file parallel.c.

549{
550 int i;
551 char errbuf[1];
552
553 /*
554 * Some platforms allow delivery of new signals to interrupt an active
555 * signal handler. That could muck up our attempt to send PQcancel, so
556 * disable the signals that set_cancel_handler enabled.
557 */
561
562 /*
563 * If we're in the leader, forward signal to all workers. (It seems best
564 * to do this before PQcancel; killing the leader transaction will result
565 * in invalid-snapshot errors from active workers, which maybe we can
566 * quiet by killing workers first.) Ignore any errors.
567 */
568 if (signal_info.pstate != NULL)
569 {
570 for (i = 0; i < signal_info.pstate->numWorkers; i++)
571 {
573
574 if (pid != 0)
575 kill(pid, SIGTERM);
576 }
577 }
578
579 /*
580 * Send QueryCancel if we have a connection to send to. Ignore errors,
581 * there's not much we can do about them anyway.
582 */
584 (void) PQcancel(signal_info.myAH->connCancel, errbuf, sizeof(errbuf));
585
586 /*
587 * Report we're quitting, using nothing more complicated than write(2).
588 * When in parallel operation, only the leader process should do this.
589 */
591 {
592 if (progname)
593 {
595 write_stderr(": ");
596 }
597 write_stderr("terminated by user\n");
598 }
599
600 /*
601 * And die, using _exit() not exit() because the latter will invoke atexit
602 * handlers that can fail if we interrupted related code.
603 */
604 _exit(1);
605}

References DumpSignalInformation::am_worker, _archiveHandle::connCancel, fb(), i, kill, DumpSignalInformation::myAH, ParallelState::numWorkers, ParallelState::parallelSlot, PG_SIG_IGN, ParallelSlot::pid, PQcancel(), pqsignal, progname, DumpSignalInformation::pstate, signal_info, SIGQUIT, and write_stderr.

Referenced by set_cancel_handler().

◆ WaitForCommands()

static void WaitForCommands ( ArchiveHandle AH,
int  pipefd[2] 
)
static

Definition at line 1336 of file parallel.c.

1337{
1338 char *command;
1339 TocEntry *te;
1340 T_Action act;
1341 int status = 0;
1342 char buf[256];
1343
1344 for (;;)
1345 {
1346 if (!(command = getMessageFromLeader(pipefd)))
1347 {
1348 /* EOF, so done */
1349 return;
1350 }
1351
1352 /* Decode the command */
1353 parseWorkerCommand(AH, &te, &act, command);
1354
1355 if (act == ACT_DUMP)
1356 {
1357 /* Acquire lock on this table within the worker's session */
1358 lockTableForWorker(AH, te);
1359
1360 /* Perform the dump command */
1361 status = (AH->WorkerJobDumpPtr) (AH, te);
1362 }
1363 else if (act == ACT_RESTORE)
1364 {
1365 /* Perform the restore command */
1366 status = (AH->WorkerJobRestorePtr) (AH, te);
1367 }
1368 else
1369 Assert(false);
1370
1371 /* Return status to leader */
1372 buildWorkerResponse(AH, te, act, status, buf, sizeof(buf));
1373
1375
1376 /* command was pg_malloc'd and we are responsible for free()ing it. */
1377 free(command);
1378 }
1379}

References ACT_DUMP, ACT_RESTORE, Assert, buf, buildWorkerResponse(), fb(), free, getMessageFromLeader(), lockTableForWorker(), parseWorkerCommand(), sendMessageToLeader(), _archiveHandle::WorkerJobDumpPtr, and _archiveHandle::WorkerJobRestorePtr.

Referenced by RunWorker().

◆ WaitForTerminatingWorkers()

static void WaitForTerminatingWorkers ( ParallelState pstate)
static

Definition at line 448 of file parallel.c.

449{
450 while (!HasEveryWorkerTerminated(pstate))
451 {
452 ParallelSlot *slot = NULL;
453 int j;
454
455#ifndef WIN32
456 /* On non-Windows, use wait() to wait for next worker to end */
457 int status;
458 pid_t pid = wait(&status);
459
460 /* Find dead worker's slot, and clear the PID field */
461 for (j = 0; j < pstate->numWorkers; j++)
462 {
463 slot = &(pstate->parallelSlot[j]);
464 if (slot->pid == pid)
465 {
466 slot->pid = 0;
467 break;
468 }
469 }
470#else /* WIN32 */
471 /* On Windows, we must use WaitForMultipleObjects() */
473 int nrun = 0;
474 DWORD ret;
476
477 for (j = 0; j < pstate->numWorkers; j++)
478 {
480 {
481 lpHandles[nrun] = (HANDLE) pstate->parallelSlot[j].hThread;
482 nrun++;
483 }
484 }
486 Assert(ret != WAIT_FAILED);
489
490 /* Find dead worker's slot, and clear the hThread field */
491 for (j = 0; j < pstate->numWorkers; j++)
492 {
493 slot = &(pstate->parallelSlot[j]);
494 if (slot->hThread == hThread)
495 {
496 /* For cleanliness, close handles for dead threads */
497 CloseHandle((HANDLE) slot->hThread);
498 slot->hThread = (uintptr_t) INVALID_HANDLE_VALUE;
499 break;
500 }
501 }
502#endif /* WIN32 */
503
504 /* On all platforms, update workerStatus and te[] as well */
505 Assert(j < pstate->numWorkers);
507 pstate->te[j] = NULL;
508 }
509}

References Assert, fb(), HasEveryWorkerTerminated(), j, ParallelState::numWorkers, ParallelState::parallelSlot, pg_free(), pg_malloc_array, ParallelSlot::pid, ParallelState::te, WORKER_IS_RUNNING, ParallelSlot::workerStatus, and WRKR_TERMINATED.

Referenced by ParallelBackupEnd(), and ShutdownWorkersHard().

◆ WaitForWorkers()

void WaitForWorkers ( ArchiveHandle AH,
ParallelState pstate,
WFW_WaitOption  mode 
)

Definition at line 1451 of file parallel.c.

1452{
1453 bool do_wait = false;
1454
1455 /*
1456 * In GOT_STATUS mode, always block waiting for a message, since we can't
1457 * return till we get something. In other modes, we don't block the first
1458 * time through the loop.
1459 */
1460 if (mode == WFW_GOT_STATUS)
1461 {
1462 /* Assert that caller knows what it's doing */
1463 Assert(!IsEveryWorkerIdle(pstate));
1464 do_wait = true;
1465 }
1466
1467 for (;;)
1468 {
1469 /*
1470 * Check for status messages, even if we don't need to block. We do
1471 * not try very hard to reap all available messages, though, since
1472 * there's unlikely to be more than one.
1473 */
1474 if (ListenToWorkers(AH, pstate, do_wait))
1475 {
1476 /*
1477 * If we got a message, we are done by definition for GOT_STATUS
1478 * mode, and we can also be certain that there's at least one idle
1479 * worker. So we're done in all but ALL_IDLE mode.
1480 */
1481 if (mode != WFW_ALL_IDLE)
1482 return;
1483 }
1484
1485 /* Check whether we must wait for new status messages */
1486 switch (mode)
1487 {
1488 case WFW_NO_WAIT:
1489 return; /* never wait */
1490 case WFW_GOT_STATUS:
1491 Assert(false); /* can't get here, because we waited */
1492 break;
1493 case WFW_ONE_IDLE:
1494 if (GetIdleWorker(pstate) != NO_SLOT)
1495 return;
1496 break;
1497 case WFW_ALL_IDLE:
1498 if (IsEveryWorkerIdle(pstate))
1499 return;
1500 break;
1501 }
1502
1503 /* Loop back, and this time wait for something to happen */
1504 do_wait = true;
1505 }
1506}

References Assert, do_wait, GetIdleWorker(), IsEveryWorkerIdle(), ListenToWorkers(), mode, NO_SLOT, WFW_ALL_IDLE, WFW_GOT_STATUS, WFW_NO_WAIT, and WFW_ONE_IDLE.

Referenced by DispatchJobForTocEntry(), restore_toc_entries_parallel(), and WriteDataChunks().

Variable Documentation

◆ shutdown_info

ShutdownInformation shutdown_info
static

Definition at line 154 of file parallel.c.

Referenced by on_exit_close_archive(), ParallelBackupEnd(), and ParallelBackupStart().

◆ signal_info