📄 async.c
字号:
scan = heap_beginscan(lRel, SnapshotNow, 1, key); while ((lTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) simple_heap_delete(lRel, &lTuple->t_self); heap_endscan(scan); heap_close(lRel, ExclusiveLock);}/* *-------------------------------------------------------------- * Async_UnlistenOnExit * * Clean up the pg_listener table at backend exit. * * This is executed if we have done any LISTENs in this backend. * It might not be necessary anymore, if the user UNLISTENed everything, * but we don't try to detect that case. * * Results: * XXX * * Side effects: * pg_listener is updated if necessary. * *-------------------------------------------------------------- */static voidAsync_UnlistenOnExit(int code, Datum arg){ /* * We need to start/commit a transaction for the unlisten, but if there is * already an active transaction we had better abort that one first. * Otherwise we'd end up committing changes that probably ought to be * discarded. */ AbortOutOfAnyTransaction(); /* Now we can do the unlisten */ StartTransactionCommand(); Async_UnlistenAll(); CommitTransactionCommand();}/* *-------------------------------------------------------------- * AtPrepare_Notify * * This is called at the prepare phase of a two-phase * transaction. Save the state for possible commit later. *-------------------------------------------------------------- */voidAtPrepare_Notify(void){ ListCell *p; foreach(p, pendingNotifies) { const char *relname = (const char *) lfirst(p); RegisterTwoPhaseRecord(TWOPHASE_RM_NOTIFY_ID, 0, relname, strlen(relname) + 1); } /* * We can clear the state immediately, rather than needing a separate * PostPrepare call, because if the transaction fails we'd just discard * the state anyway. */ ClearPendingNotifies();}/* *-------------------------------------------------------------- * AtCommit_Notify * * This is called at transaction commit. * * If there are outbound notify requests in the pendingNotifies list, * scan pg_listener for matching tuples, and either signal the other * backend or send a message to our own frontend. * * NOTE: we are still inside the current transaction, therefore can * piggyback on its committing of changes. * * Results: * XXX * * Side effects: * Tuples in pg_listener that have matching relnames and other peoples' * listenerPIDs are updated with a nonzero notification field. * *-------------------------------------------------------------- */voidAtCommit_Notify(void){ Relation lRel; TupleDesc tdesc; HeapScanDesc scan; HeapTuple lTuple, rTuple; Datum value[Natts_pg_listener]; char repl[Natts_pg_listener], nulls[Natts_pg_listener]; if (pendingNotifies == NIL) return; /* no NOTIFY statements in this transaction */ /* * NOTIFY is disabled if not normal processing mode. This test used to be * in xact.c, but it seems cleaner to do it here. */ if (!IsNormalProcessingMode()) { ClearPendingNotifies(); return; } if (Trace_notify) elog(DEBUG1, "AtCommit_Notify"); /* preset data to update notify column to MyProcPid */ nulls[0] = nulls[1] = nulls[2] = ' '; repl[0] = repl[1] = repl[2] = ' '; repl[Anum_pg_listener_notify - 1] = 'r'; value[0] = value[1] = value[2] = (Datum) 0; value[Anum_pg_listener_notify - 1] = Int32GetDatum(MyProcPid); lRel = heap_open(ListenerRelationId, ExclusiveLock); tdesc = RelationGetDescr(lRel); scan = heap_beginscan(lRel, SnapshotNow, 0, NULL); while ((lTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(lTuple); char *relname = NameStr(listener->relname); int32 listenerPID = listener->listenerpid; if (!AsyncExistsPendingNotify(relname)) continue; if (listenerPID == MyProcPid) { /* * Self-notify: no need to bother with table update. Indeed, we * *must not* clear the notification field in this path, or we * could lose an outside notify, which'd be bad for applications * that ignore self-notify messages. */ if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: notifying self"); NotifyMyFrontEnd(relname, listenerPID); } else { if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: notifying pid %d", listenerPID); /* * If someone has already notified this listener, we don't bother * modifying the table, but we do still send a SIGUSR2 signal, * just in case that backend missed the earlier signal for some * reason. It's OK to send the signal first, because the other * guy can't read pg_listener until we unlock it. */ if (kill(listenerPID, SIGUSR2) < 0) { /* * Get rid of pg_listener entry if it refers to a PID that no * longer exists. Presumably, that backend crashed without * deleting its pg_listener entries. This code used to only * delete the entry if errno==ESRCH, but as far as I can see * we should just do it for any failure (certainly at least * for EPERM too...) */ simple_heap_delete(lRel, &lTuple->t_self); } else if (listener->notification == 0) { HTSU_Result result; ItemPointerData update_ctid; TransactionId update_xmax; rTuple = heap_modifytuple(lTuple, tdesc, value, nulls, repl); /* * We cannot use simple_heap_update here because the tuple * could have been modified by an uncommitted transaction; * specifically, since UNLISTEN releases exclusive lock on the * table before commit, the other guy could already have tried * to unlisten. There are no other cases where we should be * able to see an uncommitted update or delete. Therefore, our * response to a HeapTupleBeingUpdated result is just to * ignore it. We do *not* wait for the other guy to commit * --- that would risk deadlock, and we don't want to block * while holding the table lock anyway for performance * reasons. We also ignore HeapTupleUpdated, which could occur * if the other guy commits between our heap_getnext and * heap_update calls. */ result = heap_update(lRel, &lTuple->t_self, rTuple, &update_ctid, &update_xmax, GetCurrentCommandId(), InvalidSnapshot, false /* no wait for commit */ ); switch (result) { case HeapTupleSelfUpdated: /* Tuple was already updated in current command? */ elog(ERROR, "tuple already updated by self"); break; case HeapTupleMayBeUpdated: /* done successfully */#ifdef NOT_USED /* currently there are no indexes */ CatalogUpdateIndexes(lRel, rTuple);#endif break; case HeapTupleBeingUpdated: /* ignore uncommitted tuples */ break; case HeapTupleUpdated: /* ignore just-committed tuples */ break; default: elog(ERROR, "unrecognized heap_update status: %u", result); break; } } } } heap_endscan(scan); /* * We do NOT release the lock on pg_listener here; we need to hold it * until end of transaction (which is about to happen, anyway) to ensure * that notified backends see our tuple updates when they look. Else they * might disregard the signal, which would make the application programmer * very unhappy. */ heap_close(lRel, NoLock); ClearPendingNotifies(); if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: done");}/* *-------------------------------------------------------------- * AtAbort_Notify * * This is called at transaction abort. * * Gets rid of pending outbound notifies that we would have executed * if the transaction got committed. * * Results: * XXX * *-------------------------------------------------------------- */voidAtAbort_Notify(void){ ClearPendingNotifies();}/* * AtSubStart_Notify() --- Take care of subtransaction start. * * Push empty state for the new subtransaction. */voidAtSubStart_Notify(void){ MemoryContext old_cxt; /* Keep the list-of-lists in TopTransactionContext for simplicity */ old_cxt = MemoryContextSwitchTo(TopTransactionContext); upperPendingNotifies = lcons(pendingNotifies, upperPendingNotifies); Assert(list_length(upperPendingNotifies) == GetCurrentTransactionNestLevel() - 1); pendingNotifies = NIL; MemoryContextSwitchTo(old_cxt);}/* * AtSubCommit_Notify() --- Take care of subtransaction commit. * * Reassign all items in the pending notifies list to the parent transaction. */voidAtSubCommit_Notify(void){ List *parentPendingNotifies; parentPendingNotifies = (List *) linitial(upperPendingNotifies); upperPendingNotifies = list_delete_first(upperPendingNotifies); Assert(list_length(upperPendingNotifies) == GetCurrentTransactionNestLevel() - 2); /* * We could try to eliminate duplicates here, but it seems not worthwhile. */ pendingNotifies = list_concat(parentPendingNotifies, pendingNotifies);}/* * AtSubAbort_Notify() --- Take care of subtransaction abort. */voidAtSubAbort_Notify(void){ int my_level = GetCurrentTransactionNestLevel(); /* * All we have to do is pop the stack --- the notifies made in this * subxact are no longer interesting, and the space will be freed when * CurTransactionContext is recycled. * * This routine could be called more than once at a given nesting level if * there is trouble during subxact abort. Avoid dumping core by using * GetCurrentTransactionNestLevel as the indicator of how far we need to * prune the list. */ while (list_length(upperPendingNotifies) > my_level - 2) { pendingNotifies = (List *) linitial(upperPendingNotifies); upperPendingNotifies = list_delete_first(upperPendingNotifies); }}/* *-------------------------------------------------------------- * NotifyInterruptHandler * * This is the signal handler for SIGUSR2. * * If we are idle (notifyInterruptEnabled is set), we can safely invoke * ProcessIncomingNotify directly. Otherwise, just set a flag * to do it later.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -