PostgreSQL/開発

概要

  • PostgreSQL独自のプロセス同期の仕組み。
  • pipeとイベント通知機能epollやpoll、WSAEventSelect(win)を使って実現されている。
  • バックエンドプロセス内では、MyLatchという変数で自身が所有者であるLatchを使用することができる。
postgresql-latch-overview.png

定義

変数,エイリアス

マクロ

列挙型

構造体/共用体

Latch

番号データ型フィールド説明
1sig_atomic_tis_setLatchがセットされているか
2boolis_sharedLatchが他のプロセスと共有された状態か。
初期時はこの状態。バックエンドが起動するとバックエンドローカルなLatchに設定される。
参考InitPostmasterChild() - https://git.postgresql.org/gitweb/
3intowner_pidLatchの所有者
4HANDLEevent監視するイベント(WIN32)

関数

InitializeLatchSupport

extern void InitializeLatchSupport(void);
  • pipeを作成しLatchを使えるようにする。通常はバックエンドの初期化時に呼ばれているため、改めてプラグイン内などで呼ぶ必要はない。

参考InitPostmasterChild() - https://git.postgresql.org/gitweb/

InitLatch

// 引数1:Latchの参照
extern void InitLatch(volatile Latch *latch);
  • Latchの初期化を行なう。Latchのsetはfalse、所有者は呼び出し元プロセス(MyProcPid)となる。

サンプル 独自のLatchを使う(通常はMyLatchがあるのでそれを使う)

volatile Latch OriginalLatch;

void myworker()
{
	InitLatch(&OriginalLatch);

	int rc;

	rc = WaitLatch(&OriginalLatch,
		       WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
		       10 * 1000L,
		       PG_WAIT_EXTENSION);

	ResetLatch(&OriginalLatch);

	if (rc & WL_POSTMASTER_DEATH)
		proc_exit(1);

	/* Do something */
}

InitSharedLatch

// 引数1:Latchの参照
extern void InitSharedLatch(volatile Latch *latch);
  • 他のプロセスからsetされる共有Latchの初期化を行なう。初期化時は、Owner不在となっており、OwnLatch関数で自プロセスに関連付けする(所有者になる)。OwnerでないLatchに対するSetLatchでは、Ownerに対して、SIGUSR1シグナルが送信される。また、Owner不在のLatchに対するSetLatchでは、Latchをsetするだけで、pipeへの書き込みはしない。

参考InitSharedLatch() - https://git.postgresql.org/gitweb/

OwnLatch

// 引数1:Latchの参照
extern void OwnLatch(volatile Latch *latch);
  • Latchの所有者になる。

DisownLatch

// 引数1:Latchの参照
extern void DisownLatch(volatile Latch *latch);
  • Latchの所有権を放棄する。

SetLatch

// 引数1:Latchの参照
extern void SetLatch(volatile Latch *latch);
  • Latchにsetフラグを立てる。もしLatchで待ち状態のプロセスであれば(通常は時プロセスでシグナルハンドラ内からSetLatchされた状態)、pipeにダミーデータを書き込みイベントを発生させる。

参考SetLatch() - https://git.postgresql.org/gitweb/

サンプル シグナルハンドラー内でSetLatchして待ち状態のプロセスをwake upさせる。

static volatile sig_atomic_t got_sighup = false;

static void
myworker_sighup(SIGNAL_ARGS)
{
	int save_errno = errno;

	got_sighup = true;
	SetLatch(MyLatch); // waked up the waiting process

	errno = save_errno;
}


void myworker()
{
	int rc;
	/* Waiting for the latch to be expired */
	rc = WaitLatch(MyLatch,
		       WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
		       10 * 1000L,
		       PG_WAIT_EXTENSION);

	ResetLatch(MyLatch);

	if (rc & WL_POSTMASTER_DEATH)
		proc_exit(1);

	/* Do something */
}

ResetLatch

// 引数1:Latchの参照
extern void ResetLatch(volatile Latch *latch);
  • Latchのsetフラグをfalseにする。通常は、WaitLatchから戻った後にフラグをクリアするために呼ばれる。

CreateWaitEventSet

// 引数1:メモリコンテキスト
// 引数2:イベント数
// 戻り値:待ち受けイベントに関する情報を格納するWaitEventSet構造体のポインタ
extern WaitEventSet *CreateWaitEventSet(MemoryContext context, int nevents);
  • 指定されたメモリコンテキスト領域に、最大でnevents数までのイベントを登録できる領域を確保する。

サンプル

void event_set_sample()
{
	int ret = 0;
	int rc;
	WaitEvent	event;

	/* Allocate memory for WaitEventSet which can keep a maximum of 3 events */
	WaitEventSet *set = CreateWaitEventSet(CurrentMemoryContext, 3);

	/* Add an event for WL_LATCH_SET */
	AddWaitEventToSet(set, WL_LATCH_SET, PGINVALID_SOCKET,
					  MyLatch, NULL);

	/* Wait for a event to occur. */
	rc = WaitEventSetWait(set, 10 * 1000L, &event, 1, PG_WAIT_EXTENSION);

	/* Check event flag */
	if (rc == 0)
		ret |= WL_TIMEOUT;
	else
	{
		ret |= event.events & (WL_LATCH_SET |
				       WL_POSTMASTER_DEATH |
				       WL_SOCKET_MASK);
	}

	/* Free memory */
	FreeWaitEventSet(set);
}

FreeWaitEventSet

// 引数1:イベントセット構造体(WaitEventSet)の参照
extern void FreeWaitEventSet(WaitEventSet *set);
  • CreateWaitEventSet関数で確保したメモリ領域を解放する。

AddWaitEventToSet

// 引数1:イベントセット構造体(WaitEventSet)の参照
// 引数2:追加するイベント
// 引数3:監視するファイルディスクリプタ。
//        WL_LATCH_SETまたはWL_POSTMASTER_DEATHの時は、通常PGINVALID_SOCKETをセットする。
// 引数4:イベントに紐付けられるLatchの参照
// 引数5:イベントに紐付けられる、ユーザー指定可能な任意データ
// 戻り値:
extern int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data);
  • イベントセット(WaitEventSet)に1イベント(events)を追加する。
  • 指定可能なイベントは以下
    • WL_LATCH_SET
    • WL_POSTMASTER_DEATH
    • WL_SOCKET_READABLE
    • WL_SOCKET_WRITEABLE
    • WL_SOCKET_CONNECTED

ModifyWaitEvent

// 引数1:イベントセット構造体(WaitEventSet)の参照
// 引数2:イベントセット内でのイベントのposition(配列のインデックス)
// 引数3:書き換え後のイベント
// 引数4:イベントに紐付けられるLatchの参照
extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
  • イベントマスクを修正する。
  • eventsがWL_LATCH_SETの場合、イベントにlatchが紐付けられる。

WaitEventSetWait

// 引数1:イベントセット構造体(WaitEventSet)の参照
// 引数2:タイムアウト(msec)
// 引数3:発生したイベント情報を格納するWaitEvent構造体の参照
// 引数4:返される最大イベント数
// 引数5:pgstatにreportする情報
// 戻り値:発生したイベントフラグ
extern int WaitEventSetWait(WaitEventSet *set, long timeout,
				 WaitEvent *occurred_events, int nevents,
				 uint32 wait_event_info);
  • setのイベントが起きるのを待つ。

参考WaitEventSetWait() - https://git.postgresql.org/gitweb/

WaitLatch

// 引数1:監視したいLatchの参照
// 引数2:タイムアウト(msec)
// 引数3:pgstatにreportする情報
// 戻り値:発生したイベントフラグ
extern int WaitLatch(volatile Latch *latch, int wakeEvents, long timeout, uint32 wait_event_info);
  • 指定したlatchに対するイベント(wakeEvents)を待つ。

WaitLatchOrSocket

// 引数1:監視したいLatchの参照
// 引数2:タイムアウト(msec)
// 引数3:イベント監視するファイルディスクリプタ
// 引数4:pgstatにreportする情報
// 戻り値:発生したイベントフラグ
extern int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, long timeout, uint32 wait_event_info);
  • 指定したlatchsockに対するイベント(wakeEvents)を待つ。

サンプルプログラム

static volatile sig_atomic_t got_sighup = false;

static void
myworker_sighup(SIGNAL_ARGS)
{
	int save_errno = errno;

	got_sighup = true;
	/* MyLatchでwaitするプロセスをwake upする */
	SetLatch(MyLatch);

	errno = save_errno;
}

void
myworker(Datum main_arg)
{
	int arg = DatumGetInt32(main_arg);

	/* We're now ready to receive signals */
	BackgroundWorkerUnblockSignals();

	for (;;)
	{
		int ret;
		int rc;
		/* MyLatchがSetLatchされる、タイムアウトする、またはpostmasterが死ぬイベントを待つ。10秒でタイムアウト。 */
		rc = WaitLatch(MyLatch,
			       WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
			       10 * 1000L,
			       PG_WAIT_EXTENSION);
		/* MyLatchのsetフラグをクリアする */
		ResetLatch(MyLatch);

		/* Emergency bailout if postmaster has died */
		if (rc & WL_POSTMASTER_DEATH)
			proc_exit(1);

		CHECK_FOR_INTERRUPTS();

		/*
		 * In case of a SIGHUP, just reload the configuration.
		 */
		if (got_sighup)
		{
			got_sighup = false;
			ProcessConfigFile(PGC_SIGHUP);
		}

		/* Do something */

	}

	proc_exit(1);
}

メモ

MyLatch以外のLatchを使うことも可能である。例えば、WaitLatchでMyLatchではないLatchを渡しており、シグナルハンドラでMyLatchにSetLatchするような場合。この場合、myworker_sighup関数でMyLatchがsetされるが、WaitLatchからはWL_LATCH_SETイベントでは戻らない。SetLatchでpipeへの書き込みイベントは起きるが、WaitLatchで渡しているOriginalLatchはsetされていないためである。

static void
myworker_sighup(SIGNAL_ARGS)
{
	int save_errno = errno;

	got_sighup = true;
	/* MyLatchでwaitするプロセスをwake upする */
	SetLatch(MyLatch);

	errno = save_errno;
}

/* MyLatchとは別のLatch */
volatile Latch OriginalLatch;

void
myworker(Datum main_arg)
{
	int arg = DatumGetInt32(main_arg);

	/* We're now ready to receive signals */
	BackgroundWorkerUnblockSignals();

	for (;;)
	{
		int rc;
		/* MyLatchではないLatchでイベントを待つ */
		rc = WaitLatch(&OriginalLatch,
			       WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
			       10 * 1000L,
			       PG_WAIT_EXTENSION);
		/* ResetLatch(&OriginalLatch); */
		/* Do something */
	}
}

参考・関連

 関連

コメント



添付ファイル: filepostgresql-latch-overview.png 106件 [詳細]

PR

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-02-04 (日) 13:56:20 (472d)
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP