#author("2018-02-04T13:56:10+09:00","default:haikikyou","haikikyou")
#author("2018-02-04T13:56:20+09:00","default:haikikyou","haikikyou")
[[PostgreSQL/開発]]

#contents

* 概要 [#m85d25cf]

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

#ref(./postgresql-latch-overview.png)
* 定義 [#k8969dc3]

** 変数,エイリアス [#ta86900e]

** マクロ [#pe361ec9]

** 列挙型 [#i414147b]

** 構造体/共用体 [#ic3441f0]

*** Latch [#e97c7afa]

|~番号|~データ型|~フィールド|~説明|h
|1|sig_atomic_t|is_set|Latchがセットされているか|
|2|bool|is_shared|Latchが他のプロセスと共有された状態か。&br;初期時はこの状態。バックエンドが起動するとバックエンドローカルなLatchに設定される。&br;&label(warn){参考};[[InitPostmasterChild()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/init/miscinit.c;h=afbf8f86919fd4d742e8faa68a49b3cace1cf003;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06#l170]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};|
|3|int|owner_pid|Latchの所有者|
|4|HANDLE|event|監視するイベント(WIN32)|




** 関数 [#dd0e4593]



*** InitializeLatchSupport [#b5db6367]

#geshi(c){{{
extern void InitializeLatchSupport(void);
}}}

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

&label(warn){参考};[[InitPostmasterChild()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/init/miscinit.c;h=afbf8f86919fd4d742e8faa68a49b3cace1cf003;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06#l197]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};
*** InitLatch [#d423cbd9]

#geshi(c){{{
// 引数1:Latchの参照
extern void InitLatch(volatile Latch *latch);
}}}

- Latchの初期化を行なう。Latchのsetはfalse、所有者は呼び出し元プロセス(MyProcPid)となる。

&label(sample){サンプル}; 独自のLatchを使う(通常はMyLatchがあるのでそれを使う)

#geshi(c){{{
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 [#gd688850]

#geshi(c){{{
// 引数1:Latchの参照
extern void InitSharedLatch(volatile Latch *latch);
}}}

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

&label(warn){参考};[[InitSharedLatch()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/ipc/latch.c;h=4eb6e83682e7e20a52bd1b848715464cc5a7711e;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06#l252]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};
*** OwnLatch [#tba26765]

#geshi(c){{{
// 引数1:Latchの参照
extern void OwnLatch(volatile Latch *latch);
}}}

- Latchの所有者になる。
*** DisownLatch [#dcc68245]

#geshi(c){{{
// 引数1:Latchの参照
extern void DisownLatch(volatile Latch *latch);
}}}

- Latchの所有権を放棄する。
*** SetLatch [#g2641dcc]

#geshi(c){{{
// 引数1:Latchの参照
extern void SetLatch(volatile Latch *latch);
}}}

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

&label(warn){参考};[[SetLatch()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/ipc/latch.c;h=4eb6e83682e7e20a52bd1b848715464cc5a7711e;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06#l402]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};

&label(sample){サンプル}; シグナルハンドラー内でSetLatchして待ち状態のプロセスをwake upさせる。
#geshi(c){{{
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 [#b5a8208c]

#geshi(c){{{
// 引数1:Latchの参照
extern void ResetLatch(volatile Latch *latch);
}}}

- Latchのsetフラグをfalseにする。通常は、WaitLatchから戻った後にフラグをクリアするために呼ばれる。


*** CreateWaitEventSet [#wa959ebb]

#geshi(c){{{
// 引数1:メモリコンテキスト
// 引数2:イベント数
// 戻り値:待ち受けイベントに関する情報を格納するWaitEventSet構造体のポインタ
extern WaitEventSet *CreateWaitEventSet(MemoryContext context, int nevents);
}}}

- 指定されたメモリコンテキスト領域に、最大でnevents数までのイベントを登録できる領域を確保する。

&label(sample){サンプル};
#geshi(c){{{

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 [#ea60049f]

#geshi(c){{{
// 引数1:イベントセット構造体(WaitEventSet)の参照
extern void FreeWaitEventSet(WaitEventSet *set);
}}}

- CreateWaitEventSet関数で確保したメモリ領域を解放する。
*** AddWaitEventToSet [#r5f7af87]

#geshi(c){{{
// 引数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 [#dc2c40cc]

#geshi(c){{{
// 引数1:イベントセット構造体(WaitEventSet)の参照
// 引数2:イベントセット内でのイベントのposition(配列のインデックス)
// 引数3:書き換え後のイベント
// 引数4:イベントに紐付けられるLatchの参照
extern void ModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch);
}}}

- イベントマスクを修正する。
- eventsが&code(){WL_LATCH_SET};の場合、イベントにlatchが紐付けられる。

*** WaitEventSetWait [#ce970493]

#geshi(c){{{
// 引数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のイベントが起きるのを待つ。

&label(warn){参考};[[WaitEventSetWait()>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/ipc/latch.c;h=4eb6e83682e7e20a52bd1b848715464cc5a7711e;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06#l908]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};
*** WaitLatch [#oeee50b9]

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

- 指定した&code(){latch};に対するイベント(&code(){wakeEvents};)を待つ。
*** WaitLatchOrSocket [#a43b2d4e]

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

- 指定した&code(){latch};と&code(){sock};に対するイベント(&code(){wakeEvents};)を待つ。

* サンプルプログラム [#m2a7d8de]

#geshi(c){{
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);
}
}}

&label(memo){メモ};

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

#geshi(c){{{
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 */
	}
}
}}}
* 参考・関連 [#ue9266c6]

- [[EPOLL>https://linuxjm.osdn.jp/html/LDP_man-pages/man7/epoll.7.html]] - &size(11){&color(gray){https://linuxjm.osdn.jp/html/LDP_man-pages/man7/epoll.7.html};};
- [[storage/latch.h>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/storage/latch.h;h=a43193c916b5403f0c00825a257d782e16c02d82;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};
- [[storage/latch.c>https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/ipc/latch.c;h=4eb6e83682e7e20a52bd1b848715464cc5a7711e;hb=67854bc59a8b283f02f2a8f0c0095df639bedf06]] - &size(11){&color(gray){https://git.postgresql.org/gitweb/};};


&size(12){&color(white,orange){ 関連 };};

#related
* コメント [#v579259c]
- test -- [[test]] &new{2018-02-04 (日) 13:56:10};

#comment

PR

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP