wasm-micro-runtime icon indicating copy to clipboard operation
wasm-micro-runtime copied to clipboard

wasm C code can't get a pointer-value immediately, have to wait some time

Open tkernelcn opened this issue 2 years ago • 5 comments

source code:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h> 

#define MAX_NUM_THREADS 3

_Noreturn void pthread_exit(void *);

void *thread_routine(void *arg)
{
    struct timespec ts;
    int i = 0;
    pthread_t *tid = (pthread_t *)arg;
    //sleep(1);
    printf("Enter thread %08x\n", (int)*tid);
    for(i = 0; i < 10; i++)
    {
        clock_gettime(CLOCK_REALTIME, &ts);  
        printf("%llds %ldns: thread %08x running i=%d\n", ts.tv_sec, ts.tv_nsec, (int)*tid, i);
        sleep(1);
    }
    pthread_exit(NULL);
    return NULL;
}

int main(int argc, char** argv)
{
    pthread_t tids[MAX_NUM_THREADS];
    setbuf(stdout, NULL);//output immediately

    for (int i = 0; i < MAX_NUM_THREADS; i++) {
        if (pthread_create(&tids[i], NULL, thread_routine, &tids[i]) != 0) {
            printf("Thread creation failed\n");
        }
    }
    printf("pthread_join\n");
    for (int i = 0; i < MAX_NUM_THREADS; i++) {
        if (pthread_join(tids[i], NULL) != 0) {
            printf("Thread join failed\n");
        }
    }

    printf("before sleep\n");
    sleep(3);
    printf("Exit\n");

    return 0;
}

build:

opt/wasi-sdk/bin/clang --target=wasm32-wasi -pthread -O3  -Wl,--allow-undefined,--no-check-features -Wl,--export=__heap_base,--export=__data_end -Wl,--export=malloc -Wl,--export=free main.c -o test.wasm

run:

$ ./iwasm --max-threads=20 --heap-size=65535 test.wasm
Enter thread 00000000                                                                    //===>thread id not get correct value
1704705385s 805131021ns: thread 00000001 running i=0
Enter thread 00000000                                                                    //===>thread id not get correct value
1704705385s 805513255ns: thread 00000002 running i=0
Enter thread 00000000                                                                    //===>thread id not get correct value
1704705385s 805665156ns: thread 00000003 running i=0
1704705386s 808273644ns: thread 00000001 running i=1
1704705386s 811101859ns: thread 00000003 running i=1
1704705386s 819979233ns: thread 00000002 running i=1
1704705387s 814727723ns: thread 00000001 running i=2
1704705387s 822503027ns: thread 00000003 running i=2
1704705387s 835201966ns: thread 00000002 running i=2
1704705388s 818195647ns: thread 00000001 running i=3
1704705388s 829340960ns: thread 00000003 running i=3
1704705388s 854175241ns: thread 00000002 running i=3
1704705389s 823698363ns: thread 00000001 running i=4
1704705389s 838319743ns: thread 00000003 running i=4
1704705389s 875401961ns: thread 00000002 running i=4
1704705390s 828702692ns: thread 00000001 running i=5
1704705390s 847107535ns: thread 00000003 running i=5
1704705390s 878263286ns: thread 00000002 running i=5
1704705391s 834154848ns: thread 00000001 running i=6
1704705391s 855453612ns: thread 00000003 running i=6
1704705391s 912887171ns: thread 00000002 running i=6
1704705392s 841931376ns: thread 00000001 running i=7
1704705392s 865333238ns: thread 00000003 running i=7
1704705392s 931382623ns: thread 00000002 running i=7
1704705393s 848815045ns: thread 00000001 running i=8
1704705393s 872374532ns: thread 00000003 running i=8
1704705393s 968670058ns: thread 00000002 running i=8
1704705394s 858598053ns: thread 00000001 running i=9
1704705394s 885874963ns: thread 00000003 running i=9
1704705394s 983285488ns: thread 00000002 running i=9
before sleep
Exit

if add a sleep(1) before print thread id, the result is correct now

pthread_join
Enter thread 00000002                                                                    //===>thread id is correct
1704705801s 280416341ns: thread 00000002 running i=0
Enter thread 00000003                                                                    //===>thread id is correct
1704705801s 281247387ns: thread 00000003 running i=0
Enter thread 00000001                                                                    //===>thread id is correct
1704705801s 292010354ns: thread 00000001 running i=0
1704705802s 280998016ns: thread 00000002 running i=1
1704705802s 282319397ns: thread 00000003 running i=1
1704705802s 294618323ns: thread 00000001 running i=1
1704705803s 281254384ns: thread 00000002 running i=2
1704705803s 287189411ns: thread 00000003 running i=2
...

so It's very strange, anybody know this? Thanks.

tkernelcn avatar Jan 08 '24 09:01 tkernelcn

WAMR generates the thread ID in the spawn-thread implementation but is wasi-libc who sets the pthread_t * here. after calling wasi_thread_spawn. The simplest solution would be to create independent args:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define MAX_NUM_THREADS 3

void *thread_routine(void *arg) {
    printf("Enter thread %d\n", *((int *)arg));
    return NULL;
}

int main(int argc, char** argv) {
    pthread_t tids[MAX_NUM_THREADS];
    int buf[MAX_NUM_THREADS];
    setbuf(stdout, NULL);

    for (int i = 0; i < MAX_NUM_THREADS; i++) {
        buf[i] = i;
        if (pthread_create(&tids[i], NULL, thread_routine, &buf[i]) != 0) {
            printf("Thread creation failed\n");
        }
    }
    printf("pthread_join\n");
    for (int i = 0; i < MAX_NUM_THREADS; i++) {
        if (pthread_join(tids[i], NULL) != 0) {
            printf("Thread join failed\n");
        }
    }
    printf("Exit\n");
    return 0;
}

Output:

Enter thread 0
Enter thread 1
Enter thread 2
pthread_join
Exit

tonibofarull avatar Jan 08 '24 15:01 tonibofarull

hello @tonibofarull , Thanks for your reply and solution share another test result: when I set iwasm main thread higher priority than default user thread, It works fine. so I think maybe have some synchronization issue in wasm, what do you think?

Thanks.

tkernelcn avatar Jan 09 '24 01:01 tkernelcn

in the wasi-libc: int __pthread_create

ret = __wasi_thread_spawn((void *) args); //will let os task switch to run new thread

//but after that value changed: *res = new;

so if target thread priority lower than current thread, new thread will not start immediately, the *res = new; will first execute so currently this dependent on priority except have some api just create thread but not start, after some work then enable it running

tkernelcn avatar Jan 09 '24 01:01 tkernelcn

Interesting! While we find a better solution, if you want to keep passing tids in args, you can synchronize the threads yourself at the WASM app level. I'm studying right now the internals of wasi-threads.

tonibofarull avatar Jan 09 '24 13:01 tonibofarull

in fact, there is no requirement about this, just a interesting finding to your team, if it can make the wasm improve stability, I will very happy.

tkernelcn avatar Jan 10 '24 01:01 tkernelcn