- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2018-01-29T12:19:19+00: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);
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 ret;
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]
#comment