概要 †
- PostgreSQL独自のプロセス同期の仕組み。
- pipeとイベント通知機能epollやpoll、WSAEventSelect(win)を使って実現されている。
- バックエンドプロセス内では、MyLatchという変数で自身が所有者であるLatchを使用することができる。
定義 †
変数,エイリアス †
マクロ †
列挙型 †
構造体/共用体 †
Latch †
番号 | データ型 | フィールド | 説明 |
---|---|---|---|
1 | sig_atomic_t | is_set | Latchがセットされているか |
2 | bool | is_shared | Latchが他のプロセスと共有された状態か。 初期時はこの状態。バックエンドが起動するとバックエンドローカルなLatchに設定される。 参考InitPostmasterChild() - https://git.postgresql.org/gitweb/ |
3 | int | owner_pid | Latchの所有者 |
4 | HANDLE | event | 監視するイベント(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);
- 指定した
latch
とsock
に対するイベント(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 */ } }
参考・関連 †
- EPOLL - https://linuxjm.osdn.jp/html/LDP_man-pages/man7/epoll.7.html
- storage/latch.h - https://git.postgresql.org/gitweb/
- storage/latch.c - https://git.postgresql.org/gitweb/
関連