/*-------------------------------------------------------------------------
 *
 * pg_version_compatibility.h
 *    Forward-compatibility helpers to allow limited use of new
 *    server functions when compiling against old PostgreSQL versions
 *
 * The idea is that you write your extension against the latest PostgreSQL
 * server version, and this file provides compatibility macros so that the
 * source code still compiles against older PostgreSQL versions with minimal
 * changes.
 *
 * Usage
 * -----
 *
 * Copy this file to your project.
 *
 * The latest version of this file can be found at: XXX
 *
 * - PostgreSQL doesn't make any guarantees on backwards- or forwards-
 *   compatibility of interfaces that extensions might use. It's on a best
 *   effort basis.
 *
 * - This file is not comprehensive. It only covers some common
 *   incompatibilities that are easy and safe to implement in a compatible
 *   manner.
 *
 * - See XXX for more version-specific guides on updating
 *
 * - This file may be updated time to time, even between PostgreSQL
 *   releases. It's not necessary to regularly watch for updates, but if
 *   you're doing new development and run into version incompatibilities with
 *   old server versions, you might want to check for updates.
 *
 *
 * License
 * -------
 *
 * This file is released under the PostgreSQL license: XXX
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_VERSION_COMPATIBILITY_H
#define PG_VERSION_COMPATIBILITY_H

/* The latest version is PostgreSQL 18 */

/*
 * Version 16 -> 17: "Proc number" as a concept existed in previous versions
 * too, but there was no typedef for it.
 */
#if PG_VERSION_NUM < 170000
typedef int ProcNumber;
#endif

/*
 * Version 17 -> 18: Latches were replaced with Interrupts
 */
#if PG_VERSION_NUM >= 180000
#include "storage/interrupt.h"
#else
#include "storage/latch.h"
#include "storage/proc.h"

typedef enum
{
	/*
	 * Raising INTERRUPT_GENERAL_WAKEUP is equivalent to setting the
	 * per-process latch in older versions.  There was direct no counterpart
	 * for other interrupts, so define only this one.
	 */
	INTERRUPT_GENERAL_WAKEUP,
} InterruptType;

/*
 * The WaitLatch WL_* flags were repurposed for WaitInterrupts, except that
 * WL_LATCH_SET was renamed to WL_INTERRUPT.
 */
#define WL_INTERRUPT WL_LATCH_SET

static inline void
RaiseInterrupt(InterruptType reason)
{
	Assert(reason == INTERRUPT_GENERAL_WAKEUP);
	SetLatch(MyLatch);
}

static inline void
SendInterrupt(InterruptType reason, ProcNumber pgprocno)
{
	PGPROC	   *proc;

	Assert(reason == INTERRUPT_GENERAL_WAKEUP);

	proc = GetPGProcByNumber(pgprocno);
	SetLatch(&proc->procLatch);
}

static inline int
WaitInterrupt(uint32 interruptMask, int wakeEvents, long timeout,
			  uint32 wait_event_info)
{
	Assert(interruptMask == (1 << INTERRUPT_GENERAL_WAKEUP) || interruptMask == 0);

	if ((interruptMask & (1 << INTERRUPT_GENERAL_WAKEUP)) == 0)
		wakeEvents &= ~WL_INTERRUPT;

	return WaitLatch(MyLatch, wakeEvents, timeout, wait_event_info);
}

static inline int
WaitInterruptOrSocket(uint32 interruptMask, int wakeEvents, pgsocket sock,
		      long timeout, uint32 wait_event_info)
{
	Assert(interruptMask == (1 << INTERRUPT_GENERAL_WAKEUP) || interruptMask == 0);

	if ((interruptMask & (1 << INTERRUPT_GENERAL_WAKEUP)) == 0)
		wakeEvents &= ~WL_INTERRUPT;

	return WaitLatchOrSocket(MyLatch, wakeEvents, timeout, sock, wait_event_info);
}

static inline void
ClearInterrupt(InterruptType reason)
{
	Assert(reason == INTERRUPT_GENERAL_WAKEUP);
	SetLatch(MyLatch);
}

static inline bool
ConsumeInterrupt(InterruptType reason)
{
	if (likely(!MyLatch->is_set))
		return false;

	ClearInterrupt(reason);

	return true;
}

#endif

#endif /* PG_VERSION_COMPATIBILITY_H */
