diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c index b21eae71368..74b8a00c94b 100644 --- a/src/backend/postmaster/auxprocess.c +++ b/src/backend/postmaster/auxprocess.c @@ -71,7 +71,7 @@ AuxiliaryProcessMainCommon(void) BaseInit(); - ProcSignalInit(); + ProcSignalInit(false, 0); /* * Auxiliary processes don't run transactions, but they may need a diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c index fafe5feecc2..8d4589846a9 100644 --- a/src/backend/postmaster/launch_backend.c +++ b/src/backend/postmaster/launch_backend.c @@ -58,6 +58,7 @@ #include "storage/pg_shmem.h" #include "storage/pmsignal.h" #include "storage/proc.h" +#include "storage/procsignal.h" #include "tcop/backend_startup.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" @@ -94,7 +95,6 @@ typedef int InheritableSocket; typedef struct { char DataDir[MAXPGPATH]; - int32 MyCancelKey; int MyPMChildSlot; #ifndef WIN32 unsigned long UsedShmemSegID; @@ -104,7 +104,6 @@ typedef struct #endif void *UsedShmemSegAddr; slock_t *ShmemLock; - struct bkend *ShmemBackendArray; #ifdef USE_INJECTION_POINTS struct InjectionPointsCtl *ActiveInjectionPoints; #endif @@ -119,6 +118,7 @@ typedef struct PGPROC *AuxiliaryProcs; PGPROC *PreparedXactProcs; volatile PMSignalData *PMSignalState; + ProcSignalHeader *ProcSignal; pid_t PostmasterPid; TimestampTz PgStartTime; TimestampTz PgReloadTime; @@ -702,7 +702,6 @@ save_backend_variables(BackendParameters *param, ClientSocket *client_sock, strlcpy(param->DataDir, DataDir, MAXPGPATH); - param->MyCancelKey = MyCancelKey; param->MyPMChildSlot = MyPMChildSlot; #ifdef WIN32 @@ -712,7 +711,6 @@ save_backend_variables(BackendParameters *param, ClientSocket *client_sock, param->UsedShmemSegAddr = UsedShmemSegAddr; param->ShmemLock = ShmemLock; - param->ShmemBackendArray = ShmemBackendArray; #ifdef USE_INJECTION_POINTS param->ActiveInjectionPoints = ActiveInjectionPoints; @@ -729,6 +727,7 @@ save_backend_variables(BackendParameters *param, ClientSocket *client_sock, param->AuxiliaryProcs = AuxiliaryProcs; param->PreparedXactProcs = PreparedXactProcs; param->PMSignalState = PMSignalState; + param->ProcSignal = ProcSignal; param->PostmasterPid = PostmasterPid; param->PgStartTime = PgStartTime; @@ -965,7 +964,6 @@ restore_backend_variables(BackendParameters *param) SetDataDir(param->DataDir); - MyCancelKey = param->MyCancelKey; MyPMChildSlot = param->MyPMChildSlot; #ifdef WIN32 @@ -975,7 +973,6 @@ restore_backend_variables(BackendParameters *param) UsedShmemSegAddr = param->UsedShmemSegAddr; ShmemLock = param->ShmemLock; - ShmemBackendArray = param->ShmemBackendArray; #ifdef USE_INJECTION_POINTS ActiveInjectionPoints = param->ActiveInjectionPoints; @@ -992,6 +989,7 @@ restore_backend_variables(BackendParameters *param) AuxiliaryProcs = param->AuxiliaryProcs; PreparedXactProcs = param->PreparedXactProcs; PMSignalState = param->PMSignalState; + ProcSignal = param->ProcSignal; PostmasterPid = param->PostmasterPid; PgStartTime = param->PgStartTime; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 6f974a8d21a..02442a4b85a 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -168,7 +168,6 @@ typedef struct bkend { pid_t pid; /* process id of backend */ - int32 cancel_key; /* cancel key for cancels for this backend */ int child_slot; /* PMChildSlot for this backend, if any */ int bkend_type; /* child process flavor, see above */ bool dead_end; /* is it going to send an error and quit? */ @@ -178,10 +177,6 @@ typedef struct bkend static dlist_head BackendList = DLIST_STATIC_INIT(BackendList); -#ifdef EXEC_BACKEND -NON_EXEC_STATIC Backend *ShmemBackendArray; -#endif - BackgroundWorker *MyBgworkerEntry = NULL; @@ -413,7 +408,6 @@ static int ServerLoop(void); static int BackendStartup(ClientSocket *client_sock); static void report_fork_failure_to_client(ClientSocket *client_sock, int errnum); static CAC_state canAcceptConnections(int backend_type); -static bool RandomCancelKey(int32 *cancel_key); static void signal_child(pid_t pid, int signal); static void sigquit_child(pid_t pid); static bool SignalSomeChildren(int signal, int target); @@ -444,8 +438,6 @@ static void MaybeStartSlotSyncWorker(void); (pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY))) && \ PgArchCanRestart()) -#ifdef EXEC_BACKEND - #ifdef WIN32 #define WNOHANG 0 /* ignored, so any integer value will do */ @@ -462,10 +454,6 @@ typedef struct } win32_deadchild_waitinfo; #endif /* WIN32 */ -static void ShmemBackendArrayAdd(Backend *bn); -static void ShmemBackendArrayRemove(Backend *bn); -#endif /* EXEC_BACKEND */ - /* Macros to check exit status of a child process */ #define EXIT_STATUS_0(st) ((st) == 0) #define EXIT_STATUS_1(st) (WIFEXITED(st) && WEXITSTATUS(st) == 1) @@ -1826,65 +1814,6 @@ ServerLoop(void) } } -/* - * The client has sent a cancel request packet, not a normal - * start-a-new-connection packet. Perform the necessary processing. - * Nothing is sent back to the client. - */ -void -processCancelRequest(int backendPID, int32 cancelAuthCode) -{ - Backend *bp; - -#ifndef EXEC_BACKEND - dlist_iter iter; -#else - int i; -#endif - - /* - * See if we have a matching backend. In the EXEC_BACKEND case, we can no - * longer access the postmaster's own backend list, and must rely on the - * duplicate array in shared memory. - */ -#ifndef EXEC_BACKEND - dlist_foreach(iter, &BackendList) - { - bp = dlist_container(Backend, elem, iter.cur); -#else - for (i = MaxLivePostmasterChildren() - 1; i >= 0; i--) - { - bp = (Backend *) &ShmemBackendArray[i]; -#endif - if (bp->pid == backendPID) - { - if (bp->cancel_key == cancelAuthCode) - { - /* Found a match; signal that backend to cancel current op */ - ereport(DEBUG2, - (errmsg_internal("processing cancel request: sending SIGINT to process %d", - backendPID))); - signal_child(bp->pid, SIGINT); - } - else - /* Right PID, wrong key: no way, Jose */ - ereport(LOG, - (errmsg("wrong key in cancel request for process %d", - backendPID))); - return; - } -#ifndef EXEC_BACKEND /* make GNU Emacs 26.1 see brace balance */ - } -#else - } -#endif - - /* No matching backend */ - ereport(LOG, - (errmsg("PID %d in cancel request did not match any process", - backendPID))); -} - /* * canAcceptConnections --- check to see if database state allows connections * of the specified type. backend_type can be BACKEND_TYPE_NORMAL, @@ -2752,9 +2681,6 @@ CleanupBackgroundWorker(int pid, /* Get it out of the BackendList and clear out remaining data */ dlist_delete(&rw->rw_backend->elem); -#ifdef EXEC_BACKEND - ShmemBackendArrayRemove(rw->rw_backend); -#endif /* * It's possible that this background worker started some OTHER @@ -2840,9 +2766,6 @@ CleanupBackend(int pid, HandleChildCrash(pid, exitstatus, _("server process")); return; } -#ifdef EXEC_BACKEND - ShmemBackendArrayRemove(bp); -#endif } if (bp->bgworker_notify) { @@ -2910,9 +2833,6 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) */ (void) ReleasePostmasterChildSlot(rw->rw_child_slot); dlist_delete(&rw->rw_backend->elem); -#ifdef EXEC_BACKEND - ShmemBackendArrayRemove(rw->rw_backend); -#endif pfree(rw->rw_backend); rw->rw_backend = NULL; rw->rw_pid = 0; @@ -2945,9 +2865,6 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) if (!bp->dead_end) { (void) ReleasePostmasterChildSlot(bp->child_slot); -#ifdef EXEC_BACKEND - ShmemBackendArrayRemove(bp); -#endif } dlist_delete(iter.cur); pfree(bp); @@ -3560,24 +3477,9 @@ BackendStartup(ClientSocket *client_sock) return STATUS_ERROR; } - /* - * Compute the cancel key that will be assigned to this backend. The - * backend will have its own copy in the forked-off process' value of - * MyCancelKey, so that it can transmit the key to the frontend. - */ - if (!RandomCancelKey(&MyCancelKey)) - { - pfree(bn); - ereport(LOG, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("could not generate random cancel key"))); - return STATUS_ERROR; - } - /* Pass down canAcceptConnections state */ startup_data.canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL); bn->dead_end = (startup_data.canAcceptConnections != CAC_OK); - bn->cancel_key = MyCancelKey; /* * Unless it's a dead_end child, assign it a child slot number @@ -3621,11 +3523,6 @@ BackendStartup(ClientSocket *client_sock) bn->bkend_type = BACKEND_TYPE_NORMAL; /* Can change later to WALSND */ dlist_push_head(&BackendList, &bn->elem); -#ifdef EXEC_BACKEND - if (!bn->dead_end) - ShmemBackendArrayAdd(bn); -#endif - return STATUS_OK; } @@ -3861,15 +3758,6 @@ dummy_handler(SIGNAL_ARGS) { } -/* - * Generate a random cancel key. - */ -static bool -RandomCancelKey(int32 *cancel_key) -{ - return pg_strong_random(cancel_key, sizeof(int32)); -} - /* * Count up number of child processes of specified types (dead_end children * are always excluded). @@ -3970,25 +3858,9 @@ StartAutovacuumWorker(void) */ if (canAcceptConnections(BACKEND_TYPE_AUTOVAC) == CAC_OK) { - /* - * Compute the cancel key that will be assigned to this session. We - * probably don't need cancel keys for autovac workers, but we'd - * better have something random in the field to prevent unfriendly - * people from sending cancels to them. - */ - if (!RandomCancelKey(&MyCancelKey)) - { - ereport(LOG, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("could not generate random cancel key"))); - return; - } - bn = (Backend *) palloc_extended(sizeof(Backend), MCXT_ALLOC_NO_OOM); if (bn) { - bn->cancel_key = MyCancelKey; - /* Autovac workers are not dead_end and need a child slot */ bn->dead_end = false; bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot(); @@ -3999,9 +3871,6 @@ StartAutovacuumWorker(void) { bn->bkend_type = BACKEND_TYPE_AUTOVAC; dlist_push_head(&BackendList, &bn->elem); -#ifdef EXEC_BACKEND - ShmemBackendArrayAdd(bn); -#endif /* all OK */ return; } @@ -4133,11 +4002,10 @@ CreateOptsFile(int argc, char *argv[], char *fullprogname) /* * MaxLivePostmasterChildren * - * This reports the number of entries needed in per-child-process arrays - * (the PMChildFlags array, and if EXEC_BACKEND the ShmemBackendArray). - * These arrays include regular backends, autovac workers, walsenders + * This reports the number of entries needed in the per-child-process array + * (PMChildFlags). It includes regular backends, autovac workers, walsenders * and background workers, but not special children nor dead_end children. - * This allows the arrays to have a fixed maximum size, to wit the same + * This allows the array to have a fixed maximum size, to wit the same * too-many-children limit enforced by canAcceptConnections(). The exact value * isn't too critical as long as it's more than MaxBackends. */ @@ -4206,9 +4074,6 @@ do_start_bgworker(RegisteredBgWorker *rw) ReportBackgroundWorkerPID(rw); /* add new worker to lists of backends */ dlist_push_head(&BackendList, &rw->rw_backend->elem); -#ifdef EXEC_BACKEND - ShmemBackendArrayAdd(rw->rw_backend); -#endif return true; } @@ -4276,20 +4141,6 @@ assign_backendlist_entry(RegisteredBgWorker *rw) return false; } - /* - * Compute the cancel key that will be assigned to this session. We - * probably don't need cancel keys for background workers, but we'd better - * have something random in the field to prevent unfriendly people from - * sending cancels to them. - */ - if (!RandomCancelKey(&MyCancelKey)) - { - ereport(LOG, - (errcode(ERRCODE_INTERNAL_ERROR), - errmsg("could not generate random cancel key"))); - return false; - } - bn = palloc_extended(sizeof(Backend), MCXT_ALLOC_NO_OOM); if (bn == NULL) { @@ -4299,7 +4150,6 @@ assign_backendlist_entry(RegisteredBgWorker *rw) return false; } - bn->cancel_key = MyCancelKey; bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot(); bn->bkend_type = BACKEND_TYPE_BGWORKER; bn->dead_end = false; @@ -4459,46 +4309,6 @@ PostmasterMarkPIDForWorkerNotify(int pid) return false; } -#ifdef EXEC_BACKEND - -Size -ShmemBackendArraySize(void) -{ - return mul_size(MaxLivePostmasterChildren(), sizeof(Backend)); -} - -void -ShmemBackendArrayAllocation(void) -{ - Size size = ShmemBackendArraySize(); - - ShmemBackendArray = (Backend *) ShmemAlloc(size); - /* Mark all slots as empty */ - memset(ShmemBackendArray, 0, size); -} - -static void -ShmemBackendArrayAdd(Backend *bn) -{ - /* The array slot corresponding to my PMChildSlot should be free */ - int i = bn->child_slot - 1; - - Assert(ShmemBackendArray[i].pid == 0); - ShmemBackendArray[i] = *bn; -} - -static void -ShmemBackendArrayRemove(Backend *bn) -{ - int i = bn->child_slot - 1; - - Assert(ShmemBackendArray[i].pid == bn->pid); - /* Mark the slot as empty */ - ShmemBackendArray[i].pid = 0; -} -#endif /* EXEC_BACKEND */ - - #ifdef WIN32 /* diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index ca930af08f2..b6c3b169509 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -152,9 +152,6 @@ CalculateShmemSize(int *num_semaphores) size = add_size(size, WaitEventCustomShmemSize()); size = add_size(size, InjectionPointShmemSize()); size = add_size(size, SlotSyncShmemSize()); -#ifdef EXEC_BACKEND - size = add_size(size, ShmemBackendArraySize()); -#endif /* include additional requested shmem from preload libraries */ size = add_size(size, total_addin_request); @@ -244,14 +241,6 @@ CreateSharedMemoryAndSemaphores(void) /* Initialize subsystems */ CreateOrAttachShmemStructs(); -#ifdef EXEC_BACKEND - - /* - * Alloc the win32 shared backend array - */ - ShmemBackendArrayAllocation(); -#endif - /* Initialize dynamic shared memory facilities. */ dsm_postmaster_startup(shim); diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index 4ed9cedcdd4..27326ffcb28 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -47,9 +47,9 @@ * know the ProcNumber of the process you're signaling. (We do support * signaling without ProcNumber, but it's a bit less efficient.) * - * The flags are actually declared as "volatile sig_atomic_t" for maximum - * portability. This should ensure that loads and stores of the flag - * values are atomic, allowing us to dispense with any explicit locking. + * The fields in each slot are protected by a spinlock, pss_mutex. pss_pid can + * also be read without holding the spinlock, as a quick preliminary check + * when searching for a particular PID in the array. * * pss_signalFlags are intended to be set in cases where we don't need to * keep track of whether or not the target process has handled the signal, @@ -62,8 +62,13 @@ */ typedef struct { - volatile pid_t pss_pid; + pg_atomic_uint32 pss_pid; + bool pss_cancel_key_valid; + int32 pss_cancel_key; volatile sig_atomic_t pss_signalFlags[NUM_PROCSIGNALS]; + slock_t pss_mutex; /* protects the above fields */ + + /* Barrier-related fields (not protected by pss_mutex) */ pg_atomic_uint64 pss_barrierGeneration; pg_atomic_uint32 pss_barrierCheckMask; ConditionVariable pss_barrierCV; @@ -75,7 +80,7 @@ typedef struct * * psh_barrierGeneration is the highest barrier generation in existence. */ -typedef struct +typedef struct ProcSignalHeader { pg_atomic_uint64 psh_barrierGeneration; ProcSignalSlot psh_slot[FLEXIBLE_ARRAY_MEMBER]; @@ -96,7 +101,7 @@ typedef struct #define BARRIER_CLEAR_BIT(flags, type) \ ((flags) &= ~(((uint32) 1) << (uint32) (type))) -static ProcSignalHeader *ProcSignal = NULL; +NON_EXEC_STATIC ProcSignalHeader *ProcSignal = NULL; static ProcSignalSlot *MyProcSignalSlot = NULL; static bool CheckProcSignal(ProcSignalReason reason); @@ -141,7 +146,10 @@ ProcSignalShmemInit(void) { ProcSignalSlot *slot = &ProcSignal->psh_slot[i]; - slot->pss_pid = 0; + SpinLockInit(&slot->pss_mutex); + pg_atomic_init_u32(&slot->pss_pid, 0); + slot->pss_cancel_key_valid = false; + slot->pss_cancel_key = 0; MemSet(slot->pss_signalFlags, 0, sizeof(slot->pss_signalFlags)); pg_atomic_init_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX); pg_atomic_init_u32(&slot->pss_barrierCheckMask, 0); @@ -155,7 +163,7 @@ ProcSignalShmemInit(void) * Register the current process in the ProcSignal array */ void -ProcSignalInit(void) +ProcSignalInit(bool cancel_key_valid, int32 cancel_key) { ProcSignalSlot *slot; uint64 barrier_generation; @@ -167,9 +175,13 @@ ProcSignalInit(void) slot = &ProcSignal->psh_slot[MyProcNumber]; /* sanity check */ - if (slot->pss_pid != 0) + SpinLockAcquire(&slot->pss_mutex); + if (pg_atomic_read_u32(&slot->pss_pid) != 0) + { + SpinLockRelease(&slot->pss_mutex); elog(LOG, "process %d taking over ProcSignal slot %d, but it's not empty", MyProcPid, MyProcNumber); + } /* Clear out any leftover signal reasons */ MemSet(slot->pss_signalFlags, 0, NUM_PROCSIGNALS * sizeof(sig_atomic_t)); @@ -189,10 +201,12 @@ ProcSignalInit(void) barrier_generation = pg_atomic_read_u64(&ProcSignal->psh_barrierGeneration); pg_atomic_write_u64(&slot->pss_barrierGeneration, barrier_generation); - pg_memory_barrier(); - /* Mark slot with my PID */ - slot->pss_pid = MyProcPid; + slot->pss_cancel_key_valid = cancel_key_valid; + slot->pss_cancel_key = cancel_key; + pg_atomic_write_u32(&slot->pss_pid, MyProcPid); + + SpinLockRelease(&slot->pss_mutex); /* Remember slot location for CheckProcSignal */ MyProcSignalSlot = slot; @@ -210,6 +224,7 @@ ProcSignalInit(void) static void CleanupProcSignalState(int status, Datum arg) { + pid_t old_pid; ProcSignalSlot *slot = MyProcSignalSlot; /* @@ -221,25 +236,34 @@ CleanupProcSignalState(int status, Datum arg) MyProcSignalSlot = NULL; /* sanity check */ - if (slot->pss_pid != MyProcPid) + SpinLockAcquire(&slot->pss_mutex); + old_pid = pg_atomic_read_u32(&slot->pss_pid); + if (old_pid != MyProcPid) { /* * don't ERROR here. We're exiting anyway, and don't want to get into * infinite loop trying to exit */ + SpinLockRelease(&slot->pss_mutex); elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d", - MyProcPid, (int) (slot - ProcSignal->psh_slot), (int) slot->pss_pid); + MyProcPid, (int) (slot - ProcSignal->psh_slot), (int) old_pid); return; /* XXX better to zero the slot anyway? */ } + /* Mark the slot as unused */ + pg_atomic_write_u32(&slot->pss_pid, 0); + slot->pss_cancel_key_valid = false; + slot->pss_cancel_key = 0; + /* * Make this slot look like it's absorbed all possible barriers, so that * no barrier waits block on it. */ pg_atomic_write_u64(&slot->pss_barrierGeneration, PG_UINT64_MAX); - ConditionVariableBroadcast(&slot->pss_barrierCV); - slot->pss_pid = 0; + SpinLockRelease(&slot->pss_mutex); + + ConditionVariableBroadcast(&slot->pss_barrierCV); } /* @@ -262,21 +286,16 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber) { slot = &ProcSignal->psh_slot[procNumber]; - /* - * Note: Since there's no locking, it's possible that the target - * process detaches from shared memory and exits right after this - * test, before we set the flag and send signal. And the signal slot - * might even be recycled by a new process, so it's remotely possible - * that we set a flag for a wrong process. That's OK, all the signals - * are such that no harm is done if they're mistakenly fired. - */ - if (slot->pss_pid == pid) + SpinLockAcquire(&slot->pss_mutex); + if (pg_atomic_read_u32(&slot->pss_pid) == pid) { /* Atomically set the proper flag */ slot->pss_signalFlags[reason] = true; + SpinLockRelease(&slot->pss_mutex); /* Send signal */ return kill(pid, SIGUSR1); } + SpinLockRelease(&slot->pss_mutex); } else { @@ -293,14 +312,18 @@ SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber) { slot = &ProcSignal->psh_slot[i]; - if (slot->pss_pid == pid) + if (pg_atomic_read_u32(&slot->pss_pid) == pid) { - /* the above note about race conditions applies here too */ - - /* Atomically set the proper flag */ - slot->pss_signalFlags[reason] = true; - /* Send signal */ - return kill(pid, SIGUSR1); + SpinLockAcquire(&slot->pss_mutex); + if (pg_atomic_read_u32(&slot->pss_pid) == pid) + { + /* Atomically set the proper flag */ + slot->pss_signalFlags[reason] = true; + SpinLockRelease(&slot->pss_mutex); + /* Send signal */ + return kill(pid, SIGUSR1); + } + SpinLockRelease(&slot->pss_mutex); } } } @@ -368,13 +391,20 @@ EmitProcSignalBarrier(ProcSignalBarrierType type) for (int i = NumProcSignalSlots - 1; i >= 0; i--) { volatile ProcSignalSlot *slot = &ProcSignal->psh_slot[i]; - pid_t pid = slot->pss_pid; + pid_t pid = pg_atomic_read_u32(&slot->pss_pid); if (pid != 0) { - /* see SendProcSignal for details */ - slot->pss_signalFlags[PROCSIG_BARRIER] = true; - kill(pid, SIGUSR1); + SpinLockAcquire(&slot->pss_mutex); + pid = pg_atomic_read_u32(&slot->pss_pid); + if (pid != 0) + { + /* see SendProcSignal for details */ + slot->pss_signalFlags[PROCSIG_BARRIER] = true; + SpinLockRelease(&slot->pss_mutex); + kill(pid, SIGUSR1); + } + SpinLockRelease(&slot->pss_mutex); } } @@ -414,7 +444,7 @@ WaitForProcSignalBarrier(uint64 generation) WAIT_EVENT_PROC_SIGNAL_BARRIER)) ereport(LOG, (errmsg("still waiting for backend with PID %d to accept ProcSignalBarrier", - (int) slot->pss_pid))); + (int) pg_atomic_read_u32(&slot->pss_pid)))); oldval = pg_atomic_read_u64(&slot->pss_barrierGeneration); } ConditionVariableCancelSleep(); @@ -617,7 +647,11 @@ CheckProcSignal(ProcSignalReason reason) if (slot != NULL) { - /* Careful here --- don't clear flag if we haven't seen it set */ + /* + * Careful here --- don't clear flag if we haven't seen it set. + * pss_signalFlags is of type "volatile sig_atomic_t" to allow us to + * read it here safely, without holding the spinlock. + */ if (slot->pss_signalFlags[reason]) { slot->pss_signalFlags[reason] = false; @@ -678,3 +712,78 @@ procsignal_sigusr1_handler(SIGNAL_ARGS) SetLatch(MyLatch); } + +/* + * Send a query cancellation signal to backend. + * + * Note: This is called from a backend process before authentication. We + * cannot take LWLocks yet, but that's OK; we rely on atomic reads of the + * fields in the ProcSignal slots. + */ +void +SendCancelRequest(int backendPID, int32 cancelAuthCode) +{ + Assert(backendPID != 0); + + /* + * See if we have a matching backend. Reading the pss_pid and + * pss_cancel_key fields is racy, a backend might die and remove itself + * from the array at any time. The probability of the cancellation key + * matching wrong process is miniscule, however, so we can live with that. + * PIDs are reused too, so sending the signal based on PID is inherently + * racy anyway, although OS's avoid reusing PIDs too soon. + */ + for (int i = 0; i < NumProcSignalSlots; i++) + { + ProcSignalSlot *slot = &ProcSignal->psh_slot[i]; + bool match; + + if (pg_atomic_read_u32(&slot->pss_pid) != backendPID) + continue; + + /* Acquire the spinlock and re-check */ + SpinLockAcquire(&slot->pss_mutex); + if (pg_atomic_read_u32(&slot->pss_pid) != backendPID) + { + SpinLockRelease(&slot->pss_mutex); + continue; + } + else + { + match = slot->pss_cancel_key_valid && slot->pss_cancel_key == cancelAuthCode; + + SpinLockRelease(&slot->pss_mutex); + + if (match) + { + /* Found a match; signal that backend to cancel current op */ + ereport(DEBUG2, + (errmsg_internal("processing cancel request: sending SIGINT to process %d", + backendPID))); + + /* + * If we have setsid(), signal the backend's whole process + * group + */ +#ifdef HAVE_SETSID + kill(-backendPID, SIGINT); +#else + kill(backendPID, SIGINT); +#endif + } + else + { + /* Right PID, wrong key: no way, Jose */ + ereport(LOG, + (errmsg("wrong key in cancel request for process %d", + backendPID))); + } + return; + } + } + + /* No matching backend */ + ereport(LOG, + (errmsg("PID %d in cancel request did not match any process", + backendPID))); +} diff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.c index b840d95e4d1..9ad60a6dc7f 100644 --- a/src/backend/tcop/backend_startup.c +++ b/src/backend/tcop/backend_startup.c @@ -29,6 +29,7 @@ #include "replication/walsender.h" #include "storage/fd.h" #include "storage/ipc.h" +#include "storage/procsignal.h" #include "storage/proc.h" #include "tcop/backend_startup.h" #include "tcop/tcopprot.h" @@ -541,6 +542,11 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done) if (proto == CANCEL_REQUEST_CODE) { + /* + * The client has sent a cancel request packet, not a normal + * start-a-new-connection packet. Perform the necessary processing. + * Nothing is sent back to the client. + */ CancelRequestPacket *canc; int backendPID; int32 cancelAuthCode; @@ -556,7 +562,8 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done) backendPID = (int) pg_ntoh32(canc->backendPID); cancelAuthCode = (int32) pg_ntoh32(canc->cancelAuthCode); - processCancelRequest(backendPID, cancelAuthCode); + if (backendPID != 0) + SendCancelRequest(backendPID, cancelAuthCode); /* Not really an error, but we don't want to proceed further */ return STATUS_ERROR; } diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index ea517f4b9bb..2b393fd0430 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -4224,6 +4224,22 @@ PostgresMain(const char *dbname, const char *username) /* We need to allow SIGINT, etc during the initial transaction */ sigprocmask(SIG_SETMASK, &UnBlockSig, NULL); + /* + * Generate a random cancel key, if this is a backend serving a + * connection. InitPostgres() will advertise it in shared memory. + */ + Assert(!MyCancelKeyValid); + if (whereToSendOutput == DestRemote) + { + if (!pg_strong_random(&MyCancelKey, sizeof(int32))) + { + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random cancel key"))); + } + MyCancelKeyValid = true; + } + /* * General initialization. * @@ -4276,6 +4292,7 @@ PostgresMain(const char *dbname, const char *username) { StringInfoData buf; + Assert(MyCancelKeyValid); pq_beginmessage(&buf, PqMsg_BackendKeyData); pq_sendint32(&buf, (int32) MyProcPid); pq_sendint32(&buf, (int32) MyCancelKey); diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index 927bccfbea8..d8debd160e4 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -48,7 +48,8 @@ pg_time_t MyStartTime; TimestampTz MyStartTimestamp; struct ClientSocket *MyClientSocket; struct Port *MyProcPort; -int32 MyCancelKey; +bool MyCancelKeyValid = false; +int32 MyCancelKey = 0; int MyPMChildSlot; /* diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index fe703e4f19f..13524ea488a 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -683,7 +683,7 @@ InitPostgres(const char *in_dbname, Oid dboid, */ SharedInvalBackendInit(false); - ProcSignalInit(); + ProcSignalInit(MyCancelKeyValid, MyCancelKey); /* * Also set up timeout handlers needed for backend operation. We need diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 90f9b21b258..ac16233b71f 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -191,6 +191,7 @@ extern PGDLLIMPORT pg_time_t MyStartTime; extern PGDLLIMPORT TimestampTz MyStartTimestamp; extern PGDLLIMPORT struct Port *MyProcPort; extern PGDLLIMPORT struct Latch *MyLatch; +extern PGDLLIMPORT bool MyCancelKeyValid; extern PGDLLIMPORT int32 MyCancelKey; extern PGDLLIMPORT int MyPMChildSlot; diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index d19e103937d..4cc59a662ee 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -36,10 +36,6 @@ extern PGDLLIMPORT bool remove_temp_files_after_crash; extern PGDLLIMPORT bool send_abort_for_crash; extern PGDLLIMPORT bool send_abort_for_kill; -#ifdef EXEC_BACKEND -extern struct bkend *ShmemBackendArray; -#endif - #ifdef WIN32 extern PGDLLIMPORT HANDLE PostmasterHandle; #else @@ -69,14 +65,9 @@ extern bool PostmasterMarkPIDForWorkerNotify(int); extern void processCancelRequest(int backendPID, int32 cancelAuthCode); -#ifdef EXEC_BACKEND -extern Size ShmemBackendArraySize(void); -extern void ShmemBackendArrayAllocation(void); - #ifdef WIN32 extern void pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId); #endif -#endif /* defined in globals.c */ extern PGDLLIMPORT struct ClientSocket *MyClientSocket; diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h index 7d290ea7d05..2516869465d 100644 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@ -62,9 +62,10 @@ typedef enum extern Size ProcSignalShmemSize(void); extern void ProcSignalShmemInit(void); -extern void ProcSignalInit(void); +extern void ProcSignalInit(bool cancel_key_valid, int32 cancel_key); extern int SendProcSignal(pid_t pid, ProcSignalReason reason, ProcNumber procNumber); +extern void SendCancelRequest(int backendPID, int32 cancelAuthCode); extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type); extern void WaitForProcSignalBarrier(uint64 generation); @@ -72,4 +73,11 @@ extern void ProcessProcSignalBarrier(void); extern void procsignal_sigusr1_handler(SIGNAL_ARGS); +/* ProcSignalHeader is an opaque struct, details known only within procsignal.c */ +typedef struct ProcSignalHeader ProcSignalHeader; + +#ifdef EXEC_BACKEND +extern ProcSignalHeader *ProcSignal; +#endif + #endif /* PROCSIGNAL_H */