WasmLinux: スレッド生成に対応する
シグナルの前哨戦って事で。。
Muslは __clone
関数をarch毎に実装することを求めている。生の clone3
はそもそもarch毎に実装が違う。で、この __clone
は posix_spawn
と スレッドの生成専用となっている。
この __clone
はglibcのラッパ( clone(2) に言及がある)の仕様になっている。このwrapper自体はMuslにも実装されている:
従来のfork実装は fork
や旧 clone
を直接使っている。
__clone
呼ぶ準備
とりあえず glibc ラッパと同様の機能性をランタイム側に作り込むことにする。簡単のために、可変長引数の部分はMusl内部で事前に展開しておく。
機能性の確認(pthread_create)
Muslが pthread_create
で指定してくるCLONEのフラグは以下:
スレッドで常識的に必要なもの: CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
これらは要するに新規スレッドの常識的なパラメタと言える。全てを呼出し元と共有する。これらはこのまま渡す。
CLONE_CHILD_CLEARTID
はfutex起床をセットする(引数 ctid
)。 これはランタイムでエミュレートする。 一旦カーネルに渡してみる。
CLONE_PARENT_SETTID
は呼出し元に tid
を返却する(引数 ptid
)。 これもランタイムでエミュレートする。 一旦カーネルに渡してみる。
CLONE_SETTLS
は新規スレッドにTLSポインタをセットする。データは TP_ADJ
マクロの返値、つまり struct pthread
へのポインタになる。LKL的なTLSにセットするとかで良いのかな。カーネルに渡す必要は無いはず。設定したデータを返却する __get_tp
は今stubしてるので実装する必要がある。
CLONE_DETACHED
は今は効果が無い。
mmap
に失敗する
...まぁnommuだしな。。いや MAP_ANON
は出来るはず。。
* thread #28, name = 'runner', stop reason = breakpoint 2.1
frame #0: 0x000000000269cf42 runner`w2c_user_0x5F_mmap(instance=0x00000000029b0260, var_p0=0, var_p1=0, var_p2=3, var_p3=34, var_p4=4294967295, var_p5=0) at user.c:11227:7
引数が、 0, 0, 3, 34, -1, 0 。
size
がゼロって事は、 __default_stacksize
がおかしい。。?でもステップ実行する限りはちゃんとデフォルトの1MiBになっている:
Process 21692 stopped
* thread #28, name = 'runner', stop reason = step over
frame #0: 0x000000000269fab8 runner`w2c_user_0x5F_pthread_create(instance=0x00000000029b0260, var_p0=1042546660, var_p1=0, var_p2=3, var_p3=0) at user.c:12227:39
12224 var_i2 = 3000u; // ★ このオフセットに __default_stacksize がある
12225 var_i1 += var_i2;
12226 var_i1 = i32_load(instance->w2c_env_memory, (u64)(var_i1));
-> 12227 i32_store(instance->w2c_env_memory, (u64)(var_i0) + 168, var_i1);
12228 var_i0 = var_l4;
12229 var_i1 = var_l5;
12230 var_i2 = 3004u;
(lldb) p var_i1
(u32) $3 = 131072
どうも PAGESIZE
を設定していないとこうなるようだ。。というわけで設定しておいた。というかARMはPagesizeがABIで固定されてないのか。。(なので変数を見ている)
ついでに、 __get_tp()
も実装しておいた。
ここまでで、 pthread_create
(3) で clone
(2) してくるところまでは動作確認できた。
とりあえず動いた
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
static void*
thr_test(void* bogus){
int counter;
counter = 0;
for(;;){
fprintf(stderr, "Print from worker thread... %d\n", counter);
counter++;
sleep(2);
if(counter == 2){
//return 0;
}
}
return 0;
}
int
__original_main(int ac, char** av, char** envp){
int count = 0;
fprintf(stderr, "Hello, world!\n");
int r;
void* p;
pthread_t thr;
r = pthread_create(&thr, 0, thr_test, 0);
//pthread_join(thr, &p);
for(;;){
fprintf(stderr, "Sleep...%d\n", count);
count++;
sleep(1);
}
return 0;
}
ただし pthread_join
を掛けるとfutex呼びまくってデバッグできなくなる。