[インデックス 12626] ファイルの概要
本コミットは、Go言語のruntime/cgo
パッケージにおけるLinux環境でのシグナルマスキングに関する修正です。具体的には、pthread_create
呼び出し時に発生しうるシグナル関連の問題を解決し、スレッド作成の堅牢性を向上させることを目的としています。この修正は、GoプログラムがCgoを介してCライブラリと連携する際に、予期せぬシグナルによってスレッドの初期化が妨げられる可能性に対処しています。
コミット
- コミットハッシュ:
9eeb90945eb56edc9095c662741b89170e522419
- Author: Mikio Hara mikioh.mikioh@gmail.com
- Date: Wed Mar 14 13:07:25 2012 +0900
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9eeb90945eb56edc9095c662741b89170e522419
元コミット内容
runtime/cgo: linux signal masking
Fixes #3314.
Fixes #3101 (again).
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5820047
変更の背景
このコミットは、Go言語のruntime/cgo
がLinux上で新しいスレッドを作成する際に発生していた、シグナル関連のバグを修正するために導入されました。具体的には、以下の2つの問題(Go issue)を解決することを目的としています。
- Issue #3314: "runtime/cgo: pthread_create fails if a signal is pending" (保留中のシグナルがあると
pthread_create
が失敗する) - Issue #3101: "runtime/cgo: pthread_create fails if a signal is pending" (再度、保留中のシグナルがあると
pthread_create
が失敗する)
これらの問題は、pthread_create
が新しいスレッドを作成する際に、親スレッドに保留されているシグナルが子スレッドに継承され、そのシグナルがスレッドの初期化プロセスに干渉することで発生していました。特に、pthread_create
が内部的に使用するシステムコールが、シグナルによって中断される可能性がありました。これにより、スレッドの作成が失敗したり、予期せぬ動作を引き起こしたりする可能性がありました。
この修正は、以前にも同様の問題(Issue #3101)に対する試みがあったものの、完全に解決されていなかったため、「(again)」と付記されています。これは、シグナル処理の複雑さと、異なるLinuxカーネルバージョンや環境での挙動の違いが原因である可能性があります。
前提知識の解説
Cgo (Go言語とC言語の相互運用)
Cgoは、GoプログラムがC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Goはシステムプログラミング言語ですが、既存のCライブラリやOSのAPIを直接利用する必要がある場合にCgoが用いられます。Cgoを使用すると、GoのランタイムとCのランタイムが共存することになり、特にスレッド管理やシグナル処理において、両者の挙動の整合性を保つことが重要になります。
pthread_create
pthread_create
は、POSIXスレッド(pthreads)ライブラリで提供される関数で、新しいスレッドを作成するために使用されます。この関数は、新しいスレッドの属性(スタックサイズなど)を指定するpthread_attr_t
構造体と、新しいスレッドが実行を開始する関数へのポインタ、およびその関数に渡す引数を取ります。pthread_create
は、GoのランタイムがCgoを介してCのコードから新しいOSスレッドを起動する際に内部的に使用されることがあります。
Linuxシグナル処理
Linux(およびPOSIXシステム)におけるシグナルは、プロセスやスレッドに対して非同期的に通知されるイベントです。シグナルは、ハードウェア例外(例: ゼロ除算、不正なメモリアクセス)やソフトウェアイベント(例: ユーザーからのCtrl+C、タイマー満了、子プロセスの終了)によって生成されます。
- シグナルマスク: 各スレッドは、ブロックするシグナルのセット(シグナルマスク)を持っています。シグナルがブロックされている場合、そのシグナルはスレッドに配信されず、保留状態になります。シグナルがブロック解除されると、保留されていたシグナルが配信されます。
sigset_t
: シグナルのセットを表すデータ型です。sigfillset(sigset_t *set)
:set
が指すシグナルセットを、すべての標準シグナルを含むように初期化します。つまり、すべてのシグナルをブロック対象として設定します。sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
: 呼び出し元のスレッドのシグナルマスクを変更するために使用されます。how
: シグナルマスクの変更方法を指定します。SIG_SETMASK
:set
で指定されたシグナルセットを新しいシグナルマスクとして設定します。SIG_BLOCK
:set
で指定されたシグナルを現在のシグナルマスクに追加します。SIG_UNBLOCK
:set
で指定されたシグナルを現在のシグナルマスクから削除します。
set
:how
で指定された操作に使用されるシグナルセットへのポインタです。oldset
: 変更前のシグナルマスクを保存するためのポインタです。NULL
の場合、古いマスクは保存されません。
シグナルとスレッド作成の相互作用
pthread_create
のようなシステムコールは、シグナルによって中断される可能性があります。親スレッドに保留されているシグナルがある場合、新しい子スレッドは親スレッドのシグナルマスクを継承します。しかし、シグナルがpthread_create
の実行中に配信されると、そのシステムコールが中断され、スレッドの作成が失敗する可能性があります。これは、特にGoのランタイムがCgoを介して外部のCライブラリと連携し、そのCライブラリが新しいスレッドを生成するようなシナリオで問題となります。
技術的詳細
このコミットの技術的な核心は、pthread_create
を呼び出す直前にすべてのシグナルを一時的にブロックし、pthread_create
が完了した後に元のシグナルマスクを復元するというアプローチです。これにより、pthread_create
の実行中に予期せぬシグナルが配信され、スレッド作成プロセスが中断されることを防ぎます。
具体的な手順は以下の通りです。
- シグナルセットの準備:
sigset_t ign
とsigset_t oset
という2つのシグナルセット変数を宣言します。ign
はすべてのシグナルをブロックするために使用され、oset
はpthread_create
呼び出し前の元のシグナルマスクを保存するために使用されます。 - すべてのシグナルのブロック:
sigfillset(&ign)
を呼び出してign
にすべてのシグナルを含め、sigprocmask(SIG_SETMASK, &ign, &oset)
を呼び出して現在のスレッドのシグナルマスクをign
に設定します。これにより、pthread_create
が実行されている間、すべてのシグナルがブロックされ、保留状態になります。同時に、元のシグナルマスクがoset
に保存されます。 - スレッドの作成:
pthread_create
を呼び出して新しいスレッドを作成します。この間、シグナルはブロックされているため、pthread_create
はシグナルによる中断なしに安全に実行できます。 - 元のシグナルマスクの復元:
pthread_create
が成功したかどうかにかかわらず、sigprocmask(SIG_SETMASK, &oset, nil)
を呼び出して、スレッド作成前の元のシグナルマスクを復元します。これにより、シグナル処理の挙動がpthread_create
呼び出し前と同じ状態に戻ります。
このアプローチは、クリティカルセクション(ここではpthread_create
の呼び出し)をシグナルから保護するための標準的な手法です。これにより、GoのCgoランタイムがLinux上でより堅牢にスレッドを作成できるようになります。
コアとなるコードの変更箇所
変更はsrc/pkg/runtime/cgo/gcc_linux_386.c
とsrc/pkg/runtime/cgo/gcc_linux_amd64.c
の2つのファイルに適用されています。これらはそれぞれ32ビット(i386)と64ビット(amd64)のLinuxアーキテクチャ向けのCgoランタイムコードです。変更内容は両ファイルでほぼ同じです。
diff --git a/src/pkg/runtime/cgo/gcc_linux_386.c b/src/pkg/runtime/cgo/gcc_linux_386.c
index 8401a75caa..7d84acc11e 100644
--- a/src/pkg/runtime/cgo/gcc_linux_386.c
+++ b/src/pkg/runtime/cgo/gcc_linux_386.c
@@ -4,6 +4,7 @@
#include <pthread.h>
#include <string.h>
+#include <signal.h>
#include "libcgo.h"
static void *threadentry(void*);
@@ -26,10 +27,14 @@ void
libcgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
+\tsigset_t ign, oset;\n \tpthread_t p;\n \tsize_t size;\n \tint err;\n \n+\tsigfillset(&ign);\n+\tsigprocmask(SIG_SETMASK, &ign, &oset);\n+\n // Not sure why the memset is necessary here,
// but without it, we get a bogus stack size
// out of pthread_attr_getstacksize. C'est la Linux.
@@ -39,6 +44,9 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
err = pthread_create(&p, &attr, threadentry, ts);
+\n+\tsigprocmask(SIG_SETMASK, &oset, nil);\n+\n if (err != 0) {
fprintf(stderr, "runtime/cgo: pthread_create failed: %s\\n", strerror(err));
abort();
diff --git a/src/pkg/runtime/cgo/gcc_linux_amd64.c b/src/pkg/runtime/cgo/gcc_linux_amd64.c
index 6ce3333a85..28cbf78c52 100644
--- a/src/pkg/runtime/cgo/gcc_linux_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_linux_amd64.c
@@ -4,6 +4,7 @@
#include <pthread.h>
#include <string.h> // strerror
+#include <signal.h>
#include "libcgo.h"
static void* threadentry(void*);
@@ -26,14 +27,21 @@ void
libcgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
+\tsigset_t ign, oset;\n \tpthread_t p;\n \tsize_t size;\n \tint err;\n \n+\tsigfillset(&ign);\n+\tsigprocmask(SIG_SETMASK, &ign, &oset);\n+\n pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
err = pthread_create(&p, &attr, threadentry, ts);
+\n+\tsigprocmask(SIG_SETMASK, &oset, nil);\n+\n if (err != 0) {
fprintf(stderr, "runtime/cgo: pthread_create failed: %s\\n", strerror(err));
abort();
コアとなるコードの解説
変更されたコードは、libcgo_sys_thread_start
関数内にあります。この関数は、GoランタイムがCgoを介して新しいOSスレッドを起動する際に呼び出されます。
-
#include <signal.h>
:- シグナル処理に関連する関数(
sigset_t
,sigfillset
,sigprocmask
など)を使用するために、signal.h
ヘッダーファイルがインクルードされています。
- シグナル処理に関連する関数(
-
sigset_t ign, oset;
:ign
: すべてのシグナルをブロックするためのシグナルセットです。oset
:sigprocmask
呼び出し前の元のシグナルマスクを保存するためのシグナルセットです。
-
sigfillset(&ign);
:ign
シグナルセットを、すべての標準シグナルを含むように初期化します。これにより、ign
はすべてのシグナルをブロックするマスクとして機能するようになります。
-
sigprocmask(SIG_SETMASK, &ign, &oset);
:- この行が変更の核心です。
SIG_SETMASK
は、現在のスレッドのシグナルマスクをign
で指定されたセットに完全に置き換えることを意味します。- これにより、
pthread_create
が呼び出される直前に、すべてのシグナルが一時的にブロックされます。 - 同時に、変更前の元のシグナルマスクが
oset
に保存されます。これは、pthread_create
の呼び出し後に元の状態に戻すために必要です。
-
err = pthread_create(&p, &attr, threadentry, ts);
:- 新しいスレッドを作成する実際の呼び出しです。この呼び出しは、シグナルがブロックされている「安全な」期間中に実行されます。
-
sigprocmask(SIG_SETMASK, &oset, nil);
:pthread_create
の呼び出し後、元のシグナルマスクを復元します。oset
に保存されていたシグナルマスクが現在のスレッドのマスクとして設定されます。nil
は、この呼び出しで古いシグナルマスクを保存する必要がないことを示します。
この一連の操作により、pthread_create
の実行中にシグナルが配信されて処理を中断する可能性が排除され、スレッド作成の信頼性が向上します。
関連リンク
- Go Issue #3314: https://github.com/golang/go/issues/3314
- Go Issue #3101: https://github.com/golang/go/issues/3101
- Go CL 5820047: https://golang.org/cl/5820047 (Goのコードレビューシステムへのリンク)
pthread_create
man page:man pthread_create
sigprocmask
man page:man sigprocmask
sigsetops
man page:man sigsetops
(sigfillsetなどについて)
参考にした情報源リンク
- Go Issue Tracker
- Go Code Review
- POSIX Threads (pthreads) - Wikipedia
- Signals (operating systems) - Wikipedia
- Linux man pages
- The Linux Programming Interface by Michael Kerrisk (シグナル処理とスレッドに関する詳細な情報源)
[インデックス 12626] ファイルの概要
本コミットは、Go言語のruntime/cgo
パッケージにおけるLinux環境でのシグナルマスキングに関する修正です。具体的には、pthread_create
呼び出し時に発生しうるシグナル関連の問題を解決し、スレッド作成の堅牢性を向上させることを目的としています。この修正は、GoプログラムがCgoを介してCライブラリと連携する際に、予期せぬシグナルによってスレッドの初期化が妨げられる可能性に対処しています。
コミット
- コミットハッシュ:
9eeb90945eb56edc9095c662741b89170e522419
- Author: Mikio Hara mikioh.mikioh@gmail.com
- Date: Wed Mar 14 13:07:25 2012 +0900
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9eeb90945eb56edc9095c662741b89170e522419
元コミット内容
runtime/cgo: linux signal masking
Fixes #3314.
Fixes #3101 (again).
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5820047
変更の背景
このコミットは、Go言語のruntime/cgo
がLinux上で新しいスレッドを作成する際に発生していた、シグナル関連のバグを修正するために導入されました。具体的には、以下の2つの問題(Go issue)を解決することを目的としています。
- Issue #3314: "runtime/cgo: pthread_create fails if a signal is pending" (保留中のシグナルがあると
pthread_create
が失敗する) - Issue #3101: "runtime/cgo: pthread_create fails if a signal is pending" (再度、保留中のシグナルがあると
pthread_create
が失敗する)
これらの問題は、pthread_create
が新しいスレッドを作成する際に、親スレッドに保留されているシグナルが子スレッドに継承され、そのシグナルがスレッドの初期化プロセスに干渉することで発生していました。特に、pthread_create
が内部的に使用するシステムコールが、シグナルによって中断される可能性がありました。これにより、スレッドの作成が失敗したり、予期せぬ動作を引き起こしたりする可能性がありました。
この修正は、以前にも同様の問題(Issue #3101)に対する試みがあったものの、完全に解決されていなかったため、「(again)」と付記されています。これは、シグナル処理の複雑さと、異なるLinuxカーネルバージョンや環境での挙動の違いが原因である可能性があります。Goランタイムはプログラム起動時にシグナルハンドリングを制御し、独自のシグナルハンドラをインストールしますが、Cgoを介して非Goコードがpthread_create
のような関数で新しいスレッドを作成する場合、Goランタイムが管理するスレッドのシグナルマスクを変更すべきではありません。しかし、非Goコードが独自に作成したスレッドは、自由にシグナルマスクを設定できます。このコミットは、pthread_create
呼び出し時のシグナルマスクの挙動を適切に管理することで、この問題を解決しようとしています。
前提知識の解説
Cgo (Go言語とC言語の相互運用)
Cgoは、GoプログラムがC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Goはシステムプログラミング言語ですが、既存のCライブラリやOSのAPIを直接利用する必要がある場合にCgoが用いられます。Cgoを使用すると、GoのランタイムとCのランタイムが共存することになり、特にスレッド管理やシグナル処理において、両者の挙動の整合性を保つことが重要になります。Goの起動コードは、非Goコードの起動コードよりも先に実行され、シグナルハンドラを設定します。非Goコードが独自のシグナルハンドラをインストールしようとする場合、Goランタイムのハンドラと干渉しないように注意深く行う必要があります。
pthread_create
pthread_create
は、POSIXスレッド(pthreads)ライブラリで提供される関数で、新しいスレッドを作成するために使用されます。この関数は、新しいスレッドの属性(スタックサイズなど)を指定するpthread_attr_t
構造体と、新しいスレッドが実行を開始する関数へのポインタ、およびその関数に渡す引数を取ります。pthread_create
は、GoのランタイムがCgoを介してCのコードから新しいOSスレッドを起動する際に内部的に使用されることがあります。Linuxでは、pthread_create
で作成された新しいスレッドは、作成元のスレッドのシグナルマスクのコピーを継承します。ただし、保留中のシグナルセットは空です。
Linuxシグナル処理
Linux(およびPOSIXシステム)におけるシグナルは、プロセスやスレッドに対して非同期的に通知されるイベントです。シグナルは、ハードウェア例外(例: ゼロ除算、不正なメモリアクセス)やソフトウェアイベント(例: ユーザーからのCtrl+C、タイマー満了、子プロセスの終了)によって生成されます。
- シグナルマスク: 各スレッドは、ブロックするシグナルのセット(シグナルマスク)を持っています。シグナルがブロックされている場合、そのシグナルはスレッドに配信されず、保留状態になります。シグナルがブロック解除されると、保留されていたシグナルが配信されます。シグナルハンドラはプロセス内のすべてのスレッドで共有されますが、シグナルマスクはスレッドごとに独立しています。
sigset_t
: シグナルのセットを表すデータ型です。sigfillset(sigset_t *set)
:set
が指すシグナルセットを、すべての標準シグナルを含むように初期化します。つまり、すべてのシグナルをブロック対象として設定します。sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
: 呼び出し元のスレッドのシグナルマスクを変更するために使用されます。how
: シグナルマスクの変更方法を指定します。SIG_SETMASK
:set
で指定されたシグナルセットを新しいシグナルマスクとして設定します。SIG_BLOCK
:set
で指定されたシグナルを現在のシグナルマスクに追加します。SIG_UNBLOCK
:set
で指定されたシグナルを現在のシグナルマスクから削除します。
set
:how
で指定された操作に使用されるシグナルセットへのポインタです。oldset
: 変更前のシグナルマスクを保存するためのポインタです。NULL
の場合、古いマスクは保存されません。
シグナルとスレッド作成の相互作用
pthread_create
のようなシステムコールは、シグナルによって中断される可能性があります。親スレッドに保留されているシグナルがある場合、新しい子スレッドは親スレッドのシグナルマスクを継承します。しかし、シグナルがpthread_create
の実行中に配信されると、そのシステムコールが中断され、スレッドの作成が失敗する可能性があります。これは、特にGoのランタイムがCgoを介して外部のCライブラリと連携し、そのCライブラリが新しいスレッドを生成するようなシナリオで問題となります。
技術的詳細
このコミットの技術的な核心は、pthread_create
を呼び出す直前にすべてのシグナルを一時的にブロックし、pthread_create
が完了した後に元のシグナルマスクを復元するというアプローチです。これにより、pthread_create
の実行中に予期せぬシグナルが配信され、スレッド作成プロセスが中断されることを防ぎます。
具体的な手順は以下の通りです。
- シグナルセットの準備:
sigset_t ign
とsigset_t oset
という2つのシグナルセット変数を宣言します。ign
はすべてのシグナルをブロックするために使用され、oset
はpthread_create
呼び出し前の元のシグナルマスクを保存するために使用されます。 - すべてのシグナルのブロック:
sigfillset(&ign)
を呼び出してign
にすべてのシグナルを含め、sigprocmask(SIG_SETMASK, &ign, &oset)
を呼び出して現在のスレッドのシグナルマスクをign
に設定します。これにより、pthread_create
が実行されている間、すべてのシグナルがブロックされ、保留状態になります。同時に、元のシグナルマスクがoset
に保存されます。 - スレッドの作成:
pthread_create
を呼び出して新しいスレッドを作成します。この間、シグナルはブロックされているため、pthread_create
はシグナルによる中断なしに安全に実行できます。 - 元のシグナルマスクの復元:
pthread_create
が成功したかどうかにかかわらず、sigprocmask(SIG_SETMASK, &oset, nil)
を呼び出して、スレッド作成前の元のシグナルマスクを復元します。これにより、シグナル処理の挙動がpthread_create
呼び出し前と同じ状態に戻ります。
このアプローチは、クリティカルセクション(ここではpthread_create
の呼び出し)をシグナルから保護するための標準的な手法です。これにより、GoのCgoランタイムがLinux上でより堅牢にスレッドを作成できるようになります。
コアとなるコードの変更箇所
変更はsrc/pkg/runtime/cgo/gcc_linux_386.c
とsrc/pkg/runtime/cgo/gcc_linux_amd64.c
の2つのファイルに適用されています。これらはそれぞれ32ビット(i386)と64ビット(amd64)のLinuxアーキテクチャ向けのCgoランタイムコードです。変更内容は両ファイルでほぼ同じです。
diff --git a/src/pkg/runtime/cgo/gcc_linux_386.c b/src/pkg/runtime/cgo/gcc_linux_386.c
index 8401a75caa..7d84acc11e 100644
--- a/src/pkg/runtime/cgo/gcc_linux_386.c
+++ b/src/pkg/runtime/cgo/gcc_linux_386.c
@@ -4,6 +4,7 @@
#include <pthread.h>
#include <string.h>
+#include <signal.h>
#include "libcgo.h"
static void *threadentry(void*);
@@ -26,10 +27,14 @@ void
libcgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
+\tsigset_t ign, oset;\n \tpthread_t p;\n \tsize_t size;\n \tint err;\n \n+\tsigfillset(&ign);\n+\tsigprocmask(SIG_SETMASK, &ign, &oset);\n+\n // Not sure why the memset is necessary here,
// but without it, we get a bogus stack size
// out of pthread_attr_getstacksize. C'est la Linux.
@@ -39,6 +44,9 @@ libcgo_sys_thread_start(ThreadStart *ts)
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
err = pthread_create(&p, &attr, threadentry, ts);
+\n+\tsigprocmask(SIG_SETMASK, &oset, nil);\n+\n if (err != 0) {
fprintf(stderr, "runtime/cgo: pthread_create failed: %s\\n", strerror(err));
abort();
diff --git a/src/pkg/runtime/cgo/gcc_linux_amd64.c b/src/pkg/runtime/cgo/gcc_linux_amd64.c
index 6ce3333a85..28cbf78c52 100644
--- a/src/pkg/runtime/cgo/gcc_linux_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_linux_amd64.c
@@ -4,6 +4,7 @@
#include <pthread.h>
#include <string.h> // strerror
+#include <signal.h>
#include "libcgo.h"
static void* threadentry(void*);
@@ -26,14 +27,21 @@ void
libcgo_sys_thread_start(ThreadStart *ts)
{
pthread_attr_t attr;
+\tsigset_t ign, oset;\n \tpthread_t p;\n \tsize_t size;\n \tint err;\n \n+\tsigfillset(&ign);\n+\tsigprocmask(SIG_SETMASK, &ign, &oset);\n+\n pthread_attr_init(&attr);
pthread_attr_getstacksize(&attr, &size);
ts->g->stackguard = size;
err = pthread_create(&p, &attr, threadentry, ts);
+\n+\tsigprocmask(SIG_SETMASK, &oset, nil);\n+\n if (err != 0) {
fprintf(stderr, "runtime/cgo: pthread_create failed: %s\\n", strerror(err));
abort();
コアとなるコードの解説
変更されたコードは、libcgo_sys_thread_start
関数内にあります。この関数は、GoランタイムがCgoを介して新しいOSスレッドを起動する際に呼び出されます。
-
#include <signal.h>
:- シグナル処理に関連する関数(
sigset_t
,sigfillset
,sigprocmask
など)を使用するために、signal.h
ヘッダーファイルがインクルードされています。
- シグナル処理に関連する関数(
-
sigset_t ign, oset;
:ign
: すべてのシグナルをブロックするためのシグナルセットです。oset
:sigprocmask
呼び出し前の元のシグナルマスクを保存するためのシグナルセットです。
-
sigfillset(&ign);
:ign
シグナルセットを、すべての標準シグナルを含むように初期化します。これにより、ign
はすべてのシグナルをブロックするマスクとして機能するようになります。
-
sigprocmask(SIG_SETMASK, &ign, &oset);
:- この行が変更の核心です。
SIG_SETMASK
は、現在のスレッドのシグナルマスクをign
で指定されたセットに完全に置き換えることを意味します。- これにより、
pthread_create
が呼び出される直前に、すべてのシグナルが一時的にブロックされます。 - 同時に、変更前の元のシグナルマスクが
oset
に保存されます。これは、pthread_create
の呼び出し後に元の状態に戻すために必要です。
-
err = pthread_create(&p, &attr, threadentry, ts);
:- 新しいスレッドを作成する実際の呼び出しです。この呼び出しは、シグナルがブロックされている「安全な」期間中に実行されます。
-
sigprocmask(SIG_SETMASK, &oset, nil);
:pthread_create
の呼び出し後、元のシグナルマスクを復元します。oset
に保存されていたシグナルマスクが現在のスレッドのマスクとして設定されます。nil
は、この呼び出しで古いシグナルマスクを保存する必要がないことを示します。
この一連の操作により、pthread_create
の実行中にシグナルが配信されて処理を中断する可能性が排除され、スレッド作成の信頼性が向上します。
関連リンク
- Go Issue #3314: https://github.com/golang/go/issues/3314
- Go Issue #3101: https://github.com/golang/go/issues/3101
- Go CL 5820047: https://golang.org/cl/5820047 (Goのコードレビューシステムへのリンク)
pthread_create
man page:man pthread_create
sigprocmask
man page:man sigprocmask
sigsetops
man page:man sigsetops
(sigfillsetなどについて)
参考にした情報源リンク
- Go Issue Tracker
- Go Code Review
- POSIX Threads (pthreads) - Wikipedia
- Signals (operating systems) - Wikipedia
- Linux man pages
- The Linux Programming Interface by Michael Kerrisk (シグナル処理とスレッドに関する詳細な情報源)
- Go runtime and signal handling
- pthread_create and signal masking on Linux
- Signal Concepts - The GNU C Library