+/*
+ * Maximum size of a NOTIFY payload, including terminating NULL. This
+ * must be kept small enough so that a notification message fits on one
+ * SLRU page.
+ */
+#define NOTIFY_PAYLOAD_MAX_LENGTH 8000
+
+/*
+ * Struct representing an entry in the global notify queue
+ *
+ * This struct declaration has the maximal length, but in a real queue entry
+ * the data area is only big enough for the actual channel and payload strings
+ * (each null-terminated). AsyncQueueEntryEmptySize is the minimum possible
+ * entry size, if both channel and payload strings are empty (but note it
+ * doesn't include alignment padding).
+ *
+ * The "length" field should always be rounded up to the next QUEUEALIGN
+ * multiple so that all fields are properly aligned.
+ */
+typedef struct AsyncQueueEntry
+{
+ int length; /* total allocated length of entry */
+ Oid dboid; /* sender's database OID */
+ TransactionId xid; /* sender's XID */
+ int32 srcPid; /* sender's PID */
+ char data[NAMEDATALEN + NOTIFY_PAYLOAD_MAX_LENGTH];
+} AsyncQueueEntry;
+
+/* Currently, no field of AsyncQueueEntry requires more than int alignment */
+#define QUEUEALIGN(len) INTALIGN(len)
+
+#define AsyncQueueEntryEmptySize (offsetof(AsyncQueueEntry, data) + 2)
+
+/*
+ * Struct describing a queue position, and assorted macros for working with it
+ */
+typedef struct QueuePosition
+{
+ int page; /* SLRU page number */
+ int offset; /* byte offset within page */
+} QueuePosition;
+
+#define QUEUE_POS_PAGE(x) ((x).page)
+#define QUEUE_POS_OFFSET(x) ((x).offset)
+
+#define SET_QUEUE_POS(x,y,z) \
+ do { \
+ (x).page = (y); \
+ (x).offset = (z); \
+ } while (0)
+
+#define QUEUE_POS_EQUAL(x,y) \
+ ((x).page == (y).page && (x).offset == (y).offset)
+
+/* choose logically smaller QueuePosition */
+#define QUEUE_POS_MIN(x,y) \
+ (asyncQueuePagePrecedesLogically((x).page, (y).page) ? (x) : \
+ (x).page != (y).page ? (y) : \
+ (x).offset < (y).offset ? (x) : (y))
+
+/*
+ * Struct describing a listening backend's status
+ */
+typedef struct QueueBackendStatus
+{
+ int32 pid; /* either a PID or InvalidPid */
+ QueuePosition pos; /* backend has read queue up to here */
+} QueueBackendStatus;
+
+#define InvalidPid (-1)
+
+/*
+ * Shared memory state for LISTEN/NOTIFY (excluding its SLRU stuff)
+ *
+ * The AsyncQueueControl structure is protected by the AsyncQueueLock.
+ *
+ * When holding the lock in SHARED mode, backends may only inspect their own
+ * entries as well as the head and tail pointers. Consequently we can allow a
+ * backend to update its own record while holding only SHARED lock (since no
+ * other backend will inspect it).
+ *
+ * When holding the lock in EXCLUSIVE mode, backends can inspect the entries
+ * of other backends and also change the head and tail pointers.
+ *
+ * In order to avoid deadlocks, whenever we need both locks, we always first
+ * get AsyncQueueLock and then AsyncCtlLock.
+ *
+ * Each backend uses the backend[] array entry with index equal to its
+ * BackendId (which can range from 1 to MaxBackends). We rely on this to make
+ * SendProcSignal fast.
+ */
+typedef struct AsyncQueueControl
+{
+ QueuePosition head; /* head points to the next free location */
+ QueuePosition tail; /* the global tail is equivalent to the
+ tail of the "slowest" backend */
+ TimestampTz lastQueueFillWarn; /* time of last queue-full msg */
+ QueueBackendStatus backend[1]; /* actually of length MaxBackends+1 */
+ /* DO NOT ADD FURTHER STRUCT MEMBERS HERE */
+} AsyncQueueControl;
+
+static AsyncQueueControl *asyncQueueControl;
+
+#define QUEUE_HEAD (asyncQueueControl->head)
+#define QUEUE_TAIL (asyncQueueControl->tail)
+#define QUEUE_BACKEND_PID(i) (asyncQueueControl->backend[i].pid)
+#define QUEUE_BACKEND_POS(i) (asyncQueueControl->backend[i].pos)
+
+/*
+ * The SLRU buffer area through which we access the notification queue
+ */
+static SlruCtlData AsyncCtlData;
+
+#define AsyncCtl (&AsyncCtlData)
+#define QUEUE_PAGESIZE BLCKSZ
+#define QUEUE_FULL_WARN_INTERVAL 5000 /* warn at most once every 5s */
+
+/*
+ * slru.c currently assumes that all filenames are four characters of hex
+ * digits. That means that we can use segments 0000 through FFFF.
+ * Each segment contains SLRU_PAGES_PER_SEGMENT pages which gives us
+ * the pages from 0 to SLRU_PAGES_PER_SEGMENT * 0x10000 - 1.
+ *
+ * It's of course possible to enhance slru.c, but this gives us so much
+ * space already that it doesn't seem worth the trouble.
+ *
+ * The most data we can have in the queue at a time is QUEUE_MAX_PAGE/2
+ * pages, because more than that would confuse slru.c into thinking there
+ * was a wraparound condition. With the default BLCKSZ this means there
+ * can be up to 8GB of queued-and-not-read data.
+ *
+ * Note: it's possible to redefine QUEUE_MAX_PAGE with a smaller multiple of
+ * SLRU_PAGES_PER_SEGMENT, for easier testing of queue-full behaviour.
+ */
+#define QUEUE_MAX_PAGE (SLRU_PAGES_PER_SEGMENT * 0x10000 - 1)
+
+/*
+ * listenChannels identifies the channels we are actually listening to
+ * (ie, have committed a LISTEN on). It is a simple list of channel names,
+ * allocated in TopMemoryContext.
+ */
+static List *listenChannels = NIL; /* list of C strings */
+