/*---------------- * BitmapShouldInitializeSharedState * * The first process to come here and see the state to the BM_INITIAL * will become the leader for the parallel bitmap scan and will be * responsible for populating the TIDBitmap. The other processes will * be blocked by the condition variable until the leader wakes them up. * --------------- */ static bool BitmapShouldInitializeSharedState(ParallelBitmapHeapState *pstate) { SharedBitmapState state; while (1) { SpinLockAcquire(&pstate->mutex); state = pstate->state; if (pstate->state == BM_INITIAL) pstate->state = BM_INPROGRESS; SpinLockRelease(&pstate->mutex); /* Exit if bitmap is done, or if we're the leader. */ if (state != BM_INPROGRESS) break; /* Wait for the leader to wake us up. */ ConditionVariableSleep(&pstate->cv, WAIT_EVENT_PARALLEL_BITMAP_SCAN); } ConditionVariableCancelSleep(); return (state == BM_INITIAL); }
/* * _bt_parallel_seize() -- Begin the process of advancing the scan to a new * page. Other scans must wait until we call bt_parallel_release() or * bt_parallel_done(). * * The return value is true if we successfully seized the scan and false * if we did not. The latter case occurs if no pages remain for the current * set of scankeys. * * If the return value is true, *pageno returns the next or current page * of the scan (depending on the scan direction). An invalid block number * means the scan hasn't yet started, and P_NONE means we've reached the end. * The first time a participating process reaches the last page, it will return * true and set *pageno to P_NONE; after that, further attempts to seize the * scan will return false. * * Callers should ignore the value of pageno if the return value is false. */ bool _bt_parallel_seize(IndexScanDesc scan, BlockNumber *pageno) { BTScanOpaque so = (BTScanOpaque) scan->opaque; BTPS_State pageStatus; bool exit_loop = false; bool status = true; ParallelIndexScanDesc parallel_scan = scan->parallel_scan; BTParallelScanDesc btscan; *pageno = P_NONE; btscan = (BTParallelScanDesc) OffsetToPointer((void *) parallel_scan, parallel_scan->ps_offset); while (1) { SpinLockAcquire(&btscan->btps_mutex); pageStatus = btscan->btps_pageStatus; if (so->arrayKeyCount < btscan->btps_arrayKeyCount) { /* Parallel scan has already advanced to a new set of scankeys. */ status = false; } else if (pageStatus == BTPARALLEL_DONE) { /* * We're done with this set of scankeys. This may be the end, or * there could be more sets to try. */ status = false; } else if (pageStatus != BTPARALLEL_ADVANCING) { /* * We have successfully seized control of the scan for the purpose * of advancing it to a new page! */ btscan->btps_pageStatus = BTPARALLEL_ADVANCING; *pageno = btscan->btps_scanPage; exit_loop = true; } SpinLockRelease(&btscan->btps_mutex); if (exit_loop || !status) break; ConditionVariableSleep(&btscan->btps_cv, WAIT_EVENT_BTREE_PAGE); } ConditionVariableCancelSleep(); return status; }
/* * Find a previously created slot and mark it as used by this backend. */ void ReplicationSlotAcquire(const char *name, bool nowait) { ReplicationSlot *slot; int active_pid; int i; retry: Assert(MyReplicationSlot == NULL); /* * Search for the named slot and mark it active if we find it. If the * slot is already active, we exit the loop with active_pid set to the PID * of the backend that owns it. */ active_pid = 0; slot = NULL; LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); for (i = 0; i < max_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; if (s->in_use && strcmp(name, NameStr(s->data.name)) == 0) { /* * This is the slot we want. We don't know yet if it's active, so * get ready to sleep on it in case it is. (We may end up not * sleeping, but we don't want to do this while holding the * spinlock.) */ ConditionVariablePrepareToSleep(&s->active_cv); SpinLockAcquire(&s->mutex); active_pid = s->active_pid; if (active_pid == 0) active_pid = s->active_pid = MyProcPid; SpinLockRelease(&s->mutex); slot = s; break; } } LWLockRelease(ReplicationSlotControlLock); /* If we did not find the slot, error out. */ if (slot == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("replication slot \"%s\" does not exist", name))); /* * If we found the slot but it's already active in another backend, we * either error out or retry after a short wait, as caller specified. */ if (active_pid != MyProcPid) { if (nowait) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("replication slot \"%s\" is active for PID %d", name, active_pid))); /* Wait here until we get signaled, and then restart */ ConditionVariableSleep(&slot->active_cv, WAIT_EVENT_REPLICATION_SLOT_DROP); ConditionVariableCancelSleep(); goto retry; } else ConditionVariableCancelSleep(); /* no sleep needed after all */ /* Let everybody know we've modified this slot */ ConditionVariableBroadcast(&slot->active_cv); /* We made this slot active, so it's ours now. */ MyReplicationSlot = slot; }
/* * Arrive at this barrier, wait for all other attached participants to arrive * too and then return. Increments the current phase. The caller must be * attached. * * While waiting, pg_stat_activity shows a wait_event_class and wait_event * controlled by the wait_event_info passed in, which should be a value from * one of the WaitEventXXX enums defined in pgstat.h. * * Return true in one arbitrarily chosen participant. Return false in all * others. The return code can be used to elect one participant to execute a * phase of work that must be done serially while other participants wait. */ bool BarrierArriveAndWait(Barrier *barrier, uint32 wait_event_info) { bool release = false; bool elected; int start_phase; int next_phase; SpinLockAcquire(&barrier->mutex); start_phase = barrier->phase; next_phase = start_phase + 1; ++barrier->arrived; if (barrier->arrived == barrier->participants) { release = true; barrier->arrived = 0; barrier->phase = next_phase; barrier->elected = next_phase; } SpinLockRelease(&barrier->mutex); /* * If we were the last expected participant to arrive, we can release our * peers and return true to indicate that this backend has been elected to * perform any serial work. */ if (release) { ConditionVariableBroadcast(&barrier->condition_variable); return true; } /* * Otherwise we have to wait for the last participant to arrive and * advance the phase. */ elected = false; ConditionVariablePrepareToSleep(&barrier->condition_variable); for (;;) { /* * We know that phase must either be start_phase, indicating that we * need to keep waiting, or next_phase, indicating that the last * participant that we were waiting for has either arrived or detached * so that the next phase has begun. The phase cannot advance any * further than that without this backend's participation, because * this backend is attached. */ SpinLockAcquire(&barrier->mutex); Assert(barrier->phase == start_phase || barrier->phase == next_phase); release = barrier->phase == next_phase; if (release && barrier->elected != next_phase) { /* * Usually the backend that arrives last and releases the other * backends is elected to return true (see above), so that it can * begin processing serial work while it has a CPU timeslice. * However, if the barrier advanced because someone detached, then * one of the backends that is awoken will need to be elected. */ barrier->elected = barrier->phase; elected = true; } SpinLockRelease(&barrier->mutex); if (release) break; ConditionVariableSleep(&barrier->condition_variable, wait_event_info); } ConditionVariableCancelSleep(); return elected; }