LCOV - code coverage report
Current view: top level - src/backend/storage/aio - method_worker.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 177 185 95.7 %
Date: 2026-01-04 04:18:12 Functions: 16 16 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * method_worker.c
       4             :  *    AIO - perform AIO using worker processes
       5             :  *
       6             :  * IO workers consume IOs from a shared memory submission queue, run
       7             :  * traditional synchronous system calls, and perform the shared completion
       8             :  * handling immediately.  Client code submits most requests by pushing IOs
       9             :  * into the submission queue, and waits (if necessary) using condition
      10             :  * variables.  Some IOs cannot be performed in another process due to lack of
      11             :  * infrastructure for reopening the file, and must processed synchronously by
      12             :  * the client code when submitted.
      13             :  *
      14             :  * So that the submitter can make just one system call when submitting a batch
      15             :  * of IOs, wakeups "fan out"; each woken IO worker can wake two more. XXX This
      16             :  * could be improved by using futexes instead of latches to wake N waiters.
      17             :  *
      18             :  * This method of AIO is available in all builds on all operating systems, and
      19             :  * is the default.
      20             :  *
      21             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      22             :  * Portions Copyright (c) 1994, Regents of the University of California
      23             :  *
      24             :  * IDENTIFICATION
      25             :  *    src/backend/storage/aio/method_worker.c
      26             :  *
      27             :  *-------------------------------------------------------------------------
      28             :  */
      29             : 
      30             : #include "postgres.h"
      31             : 
      32             : #include "libpq/pqsignal.h"
      33             : #include "miscadmin.h"
      34             : #include "port/pg_bitutils.h"
      35             : #include "postmaster/auxprocess.h"
      36             : #include "postmaster/interrupt.h"
      37             : #include "storage/aio.h"
      38             : #include "storage/aio_internal.h"
      39             : #include "storage/aio_subsys.h"
      40             : #include "storage/io_worker.h"
      41             : #include "storage/ipc.h"
      42             : #include "storage/latch.h"
      43             : #include "storage/proc.h"
      44             : #include "tcop/tcopprot.h"
      45             : #include "utils/injection_point.h"
      46             : #include "utils/memdebug.h"
      47             : #include "utils/ps_status.h"
      48             : #include "utils/wait_event.h"
      49             : 
      50             : 
      51             : /* How many workers should each worker wake up if needed? */
      52             : #define IO_WORKER_WAKEUP_FANOUT 2
      53             : 
      54             : 
      55             : typedef struct PgAioWorkerSubmissionQueue
      56             : {
      57             :     uint32      size;
      58             :     uint32      head;
      59             :     uint32      tail;
      60             :     int         sqes[FLEXIBLE_ARRAY_MEMBER];
      61             : } PgAioWorkerSubmissionQueue;
      62             : 
      63             : typedef struct PgAioWorkerSlot
      64             : {
      65             :     Latch      *latch;
      66             :     bool        in_use;
      67             : } PgAioWorkerSlot;
      68             : 
      69             : typedef struct PgAioWorkerControl
      70             : {
      71             :     uint64      idle_worker_mask;
      72             :     PgAioWorkerSlot workers[FLEXIBLE_ARRAY_MEMBER];
      73             : } PgAioWorkerControl;
      74             : 
      75             : 
      76             : static size_t pgaio_worker_shmem_size(void);
      77             : static void pgaio_worker_shmem_init(bool first_time);
      78             : 
      79             : static bool pgaio_worker_needs_synchronous_execution(PgAioHandle *ioh);
      80             : static int  pgaio_worker_submit(uint16 num_staged_ios, PgAioHandle **staged_ios);
      81             : 
      82             : 
      83             : const IoMethodOps pgaio_worker_ops = {
      84             :     .shmem_size = pgaio_worker_shmem_size,
      85             :     .shmem_init = pgaio_worker_shmem_init,
      86             : 
      87             :     .needs_synchronous_execution = pgaio_worker_needs_synchronous_execution,
      88             :     .submit = pgaio_worker_submit,
      89             : };
      90             : 
      91             : 
      92             : /* GUCs */
      93             : int         io_workers = 3;
      94             : 
      95             : 
      96             : static int  io_worker_queue_size = 64;
      97             : static int  MyIoWorkerId;
      98             : static PgAioWorkerSubmissionQueue *io_worker_submission_queue;
      99             : static PgAioWorkerControl *io_worker_control;
     100             : 
     101             : 
     102             : static size_t
     103        6468 : pgaio_worker_queue_shmem_size(int *queue_size)
     104             : {
     105             :     /* Round size up to next power of two so we can make a mask. */
     106        6468 :     *queue_size = pg_nextpower2_32(io_worker_queue_size);
     107             : 
     108       12936 :     return offsetof(PgAioWorkerSubmissionQueue, sqes) +
     109        6468 :         sizeof(int) * *queue_size;
     110             : }
     111             : 
     112             : static size_t
     113        6468 : pgaio_worker_control_shmem_size(void)
     114             : {
     115        6468 :     return offsetof(PgAioWorkerControl, workers) +
     116             :         sizeof(PgAioWorkerSlot) * MAX_IO_WORKERS;
     117             : }
     118             : 
     119             : static size_t
     120        4212 : pgaio_worker_shmem_size(void)
     121             : {
     122             :     size_t      sz;
     123             :     int         queue_size;
     124             : 
     125        4212 :     sz = pgaio_worker_queue_shmem_size(&queue_size);
     126        4212 :     sz = add_size(sz, pgaio_worker_control_shmem_size());
     127             : 
     128        4212 :     return sz;
     129             : }
     130             : 
     131             : static void
     132        2256 : pgaio_worker_shmem_init(bool first_time)
     133             : {
     134             :     bool        found;
     135             :     int         queue_size;
     136             : 
     137        2256 :     io_worker_submission_queue =
     138        2256 :         ShmemInitStruct("AioWorkerSubmissionQueue",
     139             :                         pgaio_worker_queue_shmem_size(&queue_size),
     140             :                         &found);
     141        2256 :     if (!found)
     142             :     {
     143        2256 :         io_worker_submission_queue->size = queue_size;
     144        2256 :         io_worker_submission_queue->head = 0;
     145        2256 :         io_worker_submission_queue->tail = 0;
     146             :     }
     147             : 
     148        2256 :     io_worker_control =
     149        2256 :         ShmemInitStruct("AioWorkerControl",
     150             :                         pgaio_worker_control_shmem_size(),
     151             :                         &found);
     152        2256 :     if (!found)
     153             :     {
     154        2256 :         io_worker_control->idle_worker_mask = 0;
     155       74448 :         for (int i = 0; i < MAX_IO_WORKERS; ++i)
     156             :         {
     157       72192 :             io_worker_control->workers[i].latch = NULL;
     158       72192 :             io_worker_control->workers[i].in_use = false;
     159             :         }
     160             :     }
     161        2256 : }
     162             : 
     163             : static int
     164     1178866 : pgaio_worker_choose_idle(void)
     165             : {
     166             :     int         worker;
     167             : 
     168     1178866 :     if (io_worker_control->idle_worker_mask == 0)
     169       26088 :         return -1;
     170             : 
     171             :     /* Find the lowest bit position, and clear it. */
     172     1152778 :     worker = pg_rightmost_one_pos64(io_worker_control->idle_worker_mask);
     173     1152778 :     io_worker_control->idle_worker_mask &= ~(UINT64_C(1) << worker);
     174             :     Assert(io_worker_control->workers[worker].in_use);
     175             : 
     176     1152778 :     return worker;
     177             : }
     178             : 
     179             : static bool
     180     1145234 : pgaio_worker_submission_queue_insert(PgAioHandle *ioh)
     181             : {
     182             :     PgAioWorkerSubmissionQueue *queue;
     183             :     uint32      new_head;
     184             : 
     185     1145234 :     queue = io_worker_submission_queue;
     186     1145234 :     new_head = (queue->head + 1) & (queue->size - 1);
     187     1145234 :     if (new_head == queue->tail)
     188             :     {
     189           0 :         pgaio_debug(DEBUG3, "io queue is full, at %u elements",
     190             :                     io_worker_submission_queue->size);
     191           0 :         return false;           /* full */
     192             :     }
     193             : 
     194     1145234 :     queue->sqes[queue->head] = pgaio_io_get_id(ioh);
     195     1145234 :     queue->head = new_head;
     196             : 
     197     1145234 :     return true;
     198             : }
     199             : 
     200             : static int
     201     1888502 : pgaio_worker_submission_queue_consume(void)
     202             : {
     203             :     PgAioWorkerSubmissionQueue *queue;
     204             :     int         result;
     205             : 
     206     1888502 :     queue = io_worker_submission_queue;
     207     1888502 :     if (queue->tail == queue->head)
     208      952848 :         return -1;              /* empty */
     209             : 
     210      935654 :     result = queue->sqes[queue->tail];
     211      935654 :     queue->tail = (queue->tail + 1) & (queue->size - 1);
     212             : 
     213      935654 :     return result;
     214             : }
     215             : 
     216             : static uint32
     217     1868706 : pgaio_worker_submission_queue_depth(void)
     218             : {
     219             :     uint32      head;
     220             :     uint32      tail;
     221             : 
     222     1868706 :     head = io_worker_submission_queue->head;
     223     1868706 :     tail = io_worker_submission_queue->tail;
     224             : 
     225     1868706 :     if (tail > head)
     226        1114 :         head += io_worker_submission_queue->size;
     227             : 
     228             :     Assert(head >= tail);
     229             : 
     230     1868706 :     return head - tail;
     231             : }
     232             : 
     233             : static bool
     234     1154680 : pgaio_worker_needs_synchronous_execution(PgAioHandle *ioh)
     235             : {
     236             :     return
     237     1154680 :         !IsUnderPostmaster
     238     1147538 :         || ioh->flags & PGAIO_HF_REFERENCES_LOCAL
     239     2302218 :         || !pgaio_io_can_reopen(ioh);
     240             : }
     241             : 
     242             : static void
     243     1144064 : pgaio_worker_submit_internal(int num_staged_ios, PgAioHandle **staged_ios)
     244             : {
     245             :     PgAioHandle *synchronous_ios[PGAIO_SUBMIT_BATCH_SIZE];
     246     1144064 :     int         nsync = 0;
     247     1144064 :     Latch      *wakeup = NULL;
     248             :     int         worker;
     249             : 
     250             :     Assert(num_staged_ios <= PGAIO_SUBMIT_BATCH_SIZE);
     251             : 
     252     1144064 :     LWLockAcquire(AioWorkerSubmissionQueueLock, LW_EXCLUSIVE);
     253     2289298 :     for (int i = 0; i < num_staged_ios; ++i)
     254             :     {
     255             :         Assert(!pgaio_worker_needs_synchronous_execution(staged_ios[i]));
     256     1145234 :         if (!pgaio_worker_submission_queue_insert(staged_ios[i]))
     257             :         {
     258             :             /*
     259             :              * We'll do it synchronously, but only after we've sent as many as
     260             :              * we can to workers, to maximize concurrency.
     261             :              */
     262           0 :             synchronous_ios[nsync++] = staged_ios[i];
     263           0 :             continue;
     264             :         }
     265             : 
     266     1145234 :         if (wakeup == NULL)
     267             :         {
     268             :             /* Choose an idle worker to wake up if we haven't already. */
     269     1144070 :             worker = pgaio_worker_choose_idle();
     270     1144070 :             if (worker >= 0)
     271     1131380 :                 wakeup = io_worker_control->workers[worker].latch;
     272             : 
     273     1144070 :             pgaio_debug_io(DEBUG4, staged_ios[i],
     274             :                            "choosing worker %d",
     275             :                            worker);
     276             :         }
     277             :     }
     278     1144064 :     LWLockRelease(AioWorkerSubmissionQueueLock);
     279             : 
     280     1144064 :     if (wakeup)
     281     1131380 :         SetLatch(wakeup);
     282             : 
     283             :     /* Run whatever is left synchronously. */
     284     1144064 :     if (nsync > 0)
     285             :     {
     286           0 :         for (int i = 0; i < nsync; ++i)
     287             :         {
     288           0 :             pgaio_io_perform_synchronously(synchronous_ios[i]);
     289             :         }
     290             :     }
     291     1144064 : }
     292             : 
     293             : static int
     294     1144064 : pgaio_worker_submit(uint16 num_staged_ios, PgAioHandle **staged_ios)
     295             : {
     296     2289298 :     for (int i = 0; i < num_staged_ios; i++)
     297             :     {
     298     1145234 :         PgAioHandle *ioh = staged_ios[i];
     299             : 
     300     1145234 :         pgaio_io_prepare_submit(ioh);
     301             :     }
     302             : 
     303     1144064 :     pgaio_worker_submit_internal(num_staged_ios, staged_ios);
     304             : 
     305     1144064 :     return num_staged_ios;
     306             : }
     307             : 
     308             : /*
     309             :  * on_shmem_exit() callback that releases the worker's slot in
     310             :  * io_worker_control.
     311             :  */
     312             : static void
     313        3488 : pgaio_worker_die(int code, Datum arg)
     314             : {
     315        3488 :     LWLockAcquire(AioWorkerSubmissionQueueLock, LW_EXCLUSIVE);
     316             :     Assert(io_worker_control->workers[MyIoWorkerId].in_use);
     317             :     Assert(io_worker_control->workers[MyIoWorkerId].latch == MyLatch);
     318             : 
     319        3488 :     io_worker_control->idle_worker_mask &= ~(UINT64_C(1) << MyIoWorkerId);
     320        3488 :     io_worker_control->workers[MyIoWorkerId].in_use = false;
     321        3488 :     io_worker_control->workers[MyIoWorkerId].latch = NULL;
     322        3488 :     LWLockRelease(AioWorkerSubmissionQueueLock);
     323        3488 : }
     324             : 
     325             : /*
     326             :  * Register the worker in shared memory, assign MyIoWorkerId and register a
     327             :  * shutdown callback to release registration.
     328             :  */
     329             : static void
     330        3488 : pgaio_worker_register(void)
     331             : {
     332        3488 :     MyIoWorkerId = -1;
     333             : 
     334             :     /*
     335             :      * XXX: This could do with more fine-grained locking. But it's also not
     336             :      * very common for the number of workers to change at the moment...
     337             :      */
     338        3488 :     LWLockAcquire(AioWorkerSubmissionQueueLock, LW_EXCLUSIVE);
     339             : 
     340        7994 :     for (int i = 0; i < MAX_IO_WORKERS; ++i)
     341             :     {
     342        7994 :         if (!io_worker_control->workers[i].in_use)
     343             :         {
     344             :             Assert(io_worker_control->workers[i].latch == NULL);
     345        3488 :             io_worker_control->workers[i].in_use = true;
     346        3488 :             MyIoWorkerId = i;
     347        3488 :             break;
     348             :         }
     349             :         else
     350             :             Assert(io_worker_control->workers[i].latch != NULL);
     351             :     }
     352             : 
     353        3488 :     if (MyIoWorkerId == -1)
     354           0 :         elog(ERROR, "couldn't find a free worker slot");
     355             : 
     356        3488 :     io_worker_control->idle_worker_mask |= (UINT64_C(1) << MyIoWorkerId);
     357        3488 :     io_worker_control->workers[MyIoWorkerId].latch = MyLatch;
     358        3488 :     LWLockRelease(AioWorkerSubmissionQueueLock);
     359             : 
     360        3488 :     on_shmem_exit(pgaio_worker_die, 0);
     361        3488 : }
     362             : 
     363             : static void
     364        2254 : pgaio_worker_error_callback(void *arg)
     365             : {
     366             :     ProcNumber  owner;
     367             :     PGPROC     *owner_proc;
     368             :     int32       owner_pid;
     369        2254 :     PgAioHandle *ioh = arg;
     370             : 
     371        2254 :     if (!ioh)
     372           0 :         return;
     373             : 
     374             :     Assert(ioh->owner_procno != MyProcNumber);
     375             :     Assert(MyBackendType == B_IO_WORKER);
     376             : 
     377        2254 :     owner = ioh->owner_procno;
     378        2254 :     owner_proc = GetPGProcByNumber(owner);
     379        2254 :     owner_pid = owner_proc->pid;
     380             : 
     381        2254 :     errcontext("I/O worker executing I/O on behalf of process %d", owner_pid);
     382             : }
     383             : 
     384             : void
     385        3488 : IoWorkerMain(const void *startup_data, size_t startup_data_len)
     386             : {
     387             :     sigjmp_buf  local_sigjmp_buf;
     388        3488 :     PgAioHandle *volatile error_ioh = NULL;
     389        3488 :     ErrorContextCallback errcallback = {0};
     390        3488 :     volatile int error_errno = 0;
     391             :     char        cmd[128];
     392             : 
     393        3488 :     MyBackendType = B_IO_WORKER;
     394        3488 :     AuxiliaryProcessMainCommon();
     395             : 
     396        3488 :     pqsignal(SIGHUP, SignalHandlerForConfigReload);
     397        3488 :     pqsignal(SIGINT, die);      /* to allow manually triggering worker restart */
     398             : 
     399             :     /*
     400             :      * Ignore SIGTERM, will get explicit shutdown via SIGUSR2 later in the
     401             :      * shutdown sequence, similar to checkpointer.
     402             :      */
     403        3488 :     pqsignal(SIGTERM, SIG_IGN);
     404             :     /* SIGQUIT handler was already set up by InitPostmasterChild */
     405        3488 :     pqsignal(SIGALRM, SIG_IGN);
     406        3488 :     pqsignal(SIGPIPE, SIG_IGN);
     407        3488 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
     408        3488 :     pqsignal(SIGUSR2, SignalHandlerForShutdownRequest);
     409             : 
     410             :     /* also registers a shutdown callback to unregister */
     411        3488 :     pgaio_worker_register();
     412             : 
     413        3488 :     sprintf(cmd, "%d", MyIoWorkerId);
     414        3488 :     set_ps_display(cmd);
     415             : 
     416        3488 :     errcallback.callback = pgaio_worker_error_callback;
     417        3488 :     errcallback.previous = error_context_stack;
     418        3488 :     error_context_stack = &errcallback;
     419             : 
     420             :     /* see PostgresMain() */
     421        3488 :     if (sigsetjmp(local_sigjmp_buf, 1) != 0)
     422             :     {
     423           2 :         error_context_stack = NULL;
     424           2 :         HOLD_INTERRUPTS();
     425             : 
     426           2 :         EmitErrorReport();
     427             : 
     428             :         /*
     429             :          * In the - very unlikely - case that the IO failed in a way that
     430             :          * raises an error we need to mark the IO as failed.
     431             :          *
     432             :          * Need to do just enough error recovery so that we can mark the IO as
     433             :          * failed and then exit (postmaster will start a new worker).
     434             :          */
     435           2 :         LWLockReleaseAll();
     436             : 
     437           2 :         if (error_ioh != NULL)
     438             :         {
     439             :             /* should never fail without setting error_errno */
     440             :             Assert(error_errno != 0);
     441             : 
     442           2 :             errno = error_errno;
     443             : 
     444           2 :             START_CRIT_SECTION();
     445           2 :             pgaio_io_process_completion(error_ioh, -error_errno);
     446           2 :             END_CRIT_SECTION();
     447             :         }
     448             : 
     449           2 :         proc_exit(1);
     450             :     }
     451             : 
     452             :     /* We can now handle ereport(ERROR) */
     453        3488 :     PG_exception_stack = &local_sigjmp_buf;
     454             : 
     455        3488 :     sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
     456             : 
     457     1891944 :     while (!ShutdownRequestPending)
     458             :     {
     459             :         uint32      io_index;
     460             :         Latch      *latches[IO_WORKER_WAKEUP_FANOUT];
     461     1888502 :         int         nlatches = 0;
     462     1888502 :         int         nwakeups = 0;
     463             :         int         worker;
     464             : 
     465             :         /*
     466             :          * Try to get a job to do.
     467             :          *
     468             :          * The lwlock acquisition also provides the necessary memory barrier
     469             :          * to ensure that we don't see an outdated data in the handle.
     470             :          */
     471     1888502 :         LWLockAcquire(AioWorkerSubmissionQueueLock, LW_EXCLUSIVE);
     472     1888502 :         if ((io_index = pgaio_worker_submission_queue_consume()) == -1)
     473             :         {
     474             :             /*
     475             :              * Nothing to do.  Mark self idle.
     476             :              *
     477             :              * XXX: Invent some kind of back pressure to reduce useless
     478             :              * wakeups?
     479             :              */
     480      952848 :             io_worker_control->idle_worker_mask |= (UINT64_C(1) << MyIoWorkerId);
     481             :         }
     482             :         else
     483             :         {
     484             :             /* Got one.  Clear idle flag. */
     485      935654 :             io_worker_control->idle_worker_mask &= ~(UINT64_C(1) << MyIoWorkerId);
     486             : 
     487             :             /* See if we can wake up some peers. */
     488      935654 :             nwakeups = Min(pgaio_worker_submission_queue_depth(),
     489             :                            IO_WORKER_WAKEUP_FANOUT);
     490      957052 :             for (int i = 0; i < nwakeups; ++i)
     491             :             {
     492       34796 :                 if ((worker = pgaio_worker_choose_idle()) < 0)
     493       13398 :                     break;
     494       21398 :                 latches[nlatches++] = io_worker_control->workers[worker].latch;
     495             :             }
     496             :         }
     497     1888502 :         LWLockRelease(AioWorkerSubmissionQueueLock);
     498             : 
     499     1909900 :         for (int i = 0; i < nlatches; ++i)
     500       21398 :             SetLatch(latches[i]);
     501             : 
     502     1888502 :         if (io_index != -1)
     503             :         {
     504      935654 :             PgAioHandle *ioh = NULL;
     505             : 
     506      935654 :             ioh = &pgaio_ctl->io_handles[io_index];
     507      935654 :             error_ioh = ioh;
     508      935654 :             errcallback.arg = ioh;
     509             : 
     510      935654 :             pgaio_debug_io(DEBUG4, ioh,
     511             :                            "worker %d processing IO",
     512             :                            MyIoWorkerId);
     513             : 
     514             :             /*
     515             :              * Prevent interrupts between pgaio_io_reopen() and
     516             :              * pgaio_io_perform_synchronously() that otherwise could lead to
     517             :              * the FD getting closed in that window.
     518             :              */
     519      935654 :             HOLD_INTERRUPTS();
     520             : 
     521             :             /*
     522             :              * It's very unlikely, but possible, that reopen fails. E.g. due
     523             :              * to memory allocations failing or file permissions changing or
     524             :              * such.  In that case we need to fail the IO.
     525             :              *
     526             :              * There's not really a good errno we can report here.
     527             :              */
     528      935654 :             error_errno = ENOENT;
     529      935654 :             pgaio_io_reopen(ioh);
     530             : 
     531             :             /*
     532             :              * To be able to exercise the reopen-fails path, allow injection
     533             :              * points to trigger a failure at this point.
     534             :              */
     535      935654 :             INJECTION_POINT("aio-worker-after-reopen", ioh);
     536             : 
     537      935652 :             error_errno = 0;
     538      935652 :             error_ioh = NULL;
     539             : 
     540             :             /*
     541             :              * As part of IO completion the buffer will be marked as NOACCESS,
     542             :              * until the buffer is pinned again - which never happens in io
     543             :              * workers. Therefore the next time there is IO for the same
     544             :              * buffer, the memory will be considered inaccessible. To avoid
     545             :              * that, explicitly allow access to the memory before reading data
     546             :              * into it.
     547             :              */
     548             : #ifdef USE_VALGRIND
     549             :             {
     550             :                 struct iovec *iov;
     551             :                 uint16      iov_length = pgaio_io_get_iovec_length(ioh, &iov);
     552             : 
     553             :                 for (int i = 0; i < iov_length; i++)
     554             :                     VALGRIND_MAKE_MEM_UNDEFINED(iov[i].iov_base, iov[i].iov_len);
     555             :             }
     556             : #endif
     557             : 
     558             :             /*
     559             :              * We don't expect this to ever fail with ERROR or FATAL, no need
     560             :              * to keep error_ioh set to the IO.
     561             :              * pgaio_io_perform_synchronously() contains a critical section to
     562             :              * ensure we don't accidentally fail.
     563             :              */
     564      935652 :             pgaio_io_perform_synchronously(ioh);
     565             : 
     566      935652 :             RESUME_INTERRUPTS();
     567      935652 :             errcallback.arg = NULL;
     568             :         }
     569             :         else
     570             :         {
     571      952848 :             WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, -1,
     572             :                       WAIT_EVENT_IO_WORKER_MAIN);
     573      952812 :             ResetLatch(MyLatch);
     574             :         }
     575             : 
     576     1888464 :         CHECK_FOR_INTERRUPTS();
     577             : 
     578     1888456 :         if (ConfigReloadPending)
     579             :         {
     580         416 :             ConfigReloadPending = false;
     581         416 :             ProcessConfigFile(PGC_SIGHUP);
     582             :         }
     583             :     }
     584             : 
     585        3442 :     error_context_stack = errcallback.previous;
     586        3442 :     proc_exit(0);
     587             : }
     588             : 
     589             : bool
     590      257658 : pgaio_workers_enabled(void)
     591             : {
     592      257658 :     return io_method == IOMETHOD_WORKER;
     593             : }

Generated by: LCOV version 1.16