Open5

WasmLinux: スレッド生成に対応する

okuokuokuoku

シグナルの前哨戦って事で。。

Muslは __clone 関数をarch毎に実装することを求めている。生の clone3 はそもそもarch毎に実装が違う。で、この __cloneposix_spawn と スレッドの生成専用となっている。

https://github.com/okuoku/wasmlinux-musl/blob/7d78063f0e5d094360553afbc8c194f3a3f262be/src/thread/pthread_create.c#L355

https://github.com/okuoku/wasmlinux-musl/blob/7d78063f0e5d094360553afbc8c194f3a3f262be/src/process/posix_spawn.c#L193-L194

この __clone はglibcのラッパ( clone(2) に言及がある)の仕様になっている。このwrapper自体はMuslにも実装されている:

https://github.com/okuoku/wasmlinux-musl/blob/7d78063f0e5d094360553afbc8c194f3a3f262be/src/linux/clone.c#L60

従来のfork実装は fork や旧 clone を直接使っている。

https://github.com/okuoku/wasmlinux-musl/blob/7d78063f0e5d094360553afbc8c194f3a3f262be/src/process/_Fork.c#L29-L43

https://github.com/okuoku/wasmlinux-musl/blob/7d78063f0e5d094360553afbc8c194f3a3f262be/src/process/vfork.c#L6-L14

okuokuokuoku

機能性の確認(pthread_create)

Muslが pthread_create で指定してくるCLONEのフラグは以下:

https://github.com/okuoku/wasmlinux-musl/blob/7d78063f0e5d094360553afbc8c194f3a3f262be/src/thread/pthread_create.c#L243-L245

スレッドで常識的に必要なもの: 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してるので実装する必要がある。

https://github.com/okuoku/wasmlinux-musl/blob/33a0c32349b54f1c304c1d995313cbe83a3c8709/src/internal/pthread_impl.h#L117-L123

CLONE_DETACHED は今は効果が無い。

https://github.com/lkl/linux/blob/3023e6f25fbf6d5f95b4e7ebd011fa688434ce5f/include/uapi/linux/sched.h#L25

okuokuokuoku

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 。

https://github.com/okuoku/wasmlinux-musl/blob/33a0c32349b54f1c304c1d995313cbe83a3c8709/src/thread/pthread_create.c#L303

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で固定されてないのか。。(なので変数を見ている)

https://github.com/okuoku/wasmlinux-musl/commit/515583869796953e5bbec1472ff9963598bdef6e

ついでに、 __get_tp() も実装しておいた。

https://github.com/okuoku/wasmlinux-musl/commit/52cf4694f70a43dab1e2d71be6776dde3150b8d5

ここまでで、 pthread_create(3) で clone(2) してくるところまでは動作確認できた。

okuokuokuoku

とりあえず動いた

https://github.com/okuoku/lkl-wasm/commit/641273a496a2c4fcf8637c719e1538d1a903404a

#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呼びまくってデバッグできなくなる。