void __wait(volatile int *addr, volatile int *waiters, int val, int priv) { int spins=10000; if (priv) priv = 128; priv=0; while (spins--) { if (*addr==val) a_spin(); else return; } if (waiters) a_inc(waiters); while (*addr==val) { #ifdef __EMSCRIPTEN__ if (pthread_self()->cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS) { // Must wait in slices in case this thread is cancelled in between. int e; do { if (_pthread_isduecanceled(pthread_self())) { if (waiters) a_dec(waiters); return; } e = emscripten_futex_wait((void*)addr, val, 100); } while(e == -ETIMEDOUT); } else { // Can wait in one go. emscripten_futex_wait((void*)addr, val, INFINITY); } #else __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0); #endif } if (waiters) a_dec(waiters); }
static int do_wait(volatile int *addr, int val, clockid_t clk, const struct timespec *at, int priv) { int r; struct timespec to, *top=0; if (at) { if (at->tv_nsec >= 1000000000UL) return EINVAL; if (clock_gettime(clk, &to)) return EINVAL; to.tv_sec = at->tv_sec - to.tv_sec; if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) { to.tv_sec--; to.tv_nsec += 1000000000; } if (to.tv_sec < 0) return ETIMEDOUT; top = &to; } #ifdef __EMSCRIPTEN__ if (1 || pthread_self()->cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS) { do { if (_pthread_isduecanceled(pthread_self())) { // Emscripten-specific return value: The wait was canceled by user calling // pthread_cancel() for this thread, and the caller needs to cooperatively // cancel execution. return ECANCELED; } // Must wait in slices in case this thread is cancelled in between. double waitMsecs = at ? _pthread_msecs_until(at) : INFINITY; if (waitMsecs <= 0) { r = ETIMEDOUT; break; } if (waitMsecs > 100) waitMsecs = 100; r = -emscripten_futex_wait((void*)addr, val, waitMsecs); // Assist other threads by executing proxied operations that are effectively singlethreaded. if (emscripten_is_main_runtime_thread()) emscripten_main_thread_process_queued_calls(); } while(r == ETIMEDOUT); } else { // Can wait in one go. double waitMsecs = at ? _pthread_msecs_until(at) : INFINITY; r = -emscripten_futex_wait((void*)addr, val, waitMsecs); } #else r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT, val, top); #endif if (r == EINTR || r == EINVAL || r == ETIMEDOUT) return r; return 0; }
EMSCRIPTEN_RESULT emscripten_fetch_wait(emscripten_fetch_t *fetch, double timeoutMsecs) { #if __EMSCRIPTEN_PTHREADS__ if (!fetch) return EMSCRIPTEN_RESULT_INVALID_PARAM; uint32_t proxyState = emscripten_atomic_load_u32(&fetch->__proxyState); if (proxyState == 2) return EMSCRIPTEN_RESULT_SUCCESS; // already finished. if (proxyState != 1) return EMSCRIPTEN_RESULT_INVALID_PARAM; // the fetch should be ongoing? // #ifdef FETCH_DEBUG EM_ASM({ console.log('fetch: emscripten_fetch_wait..') }); // #endif // TODO: timeoutMsecs is currently ignored. Return EMSCRIPTEN_RESULT_TIMED_OUT on timeout. while(proxyState == 1/*sent to proxy worker*/) { emscripten_futex_wait(&fetch->__proxyState, proxyState, 100 /*TODO HACK:Sleep sometimes doesn't wake up?*/);//timeoutMsecs); proxyState = emscripten_atomic_load_u32(&fetch->__proxyState); } // #ifdef FETCH_DEBUG EM_ASM({ console.log('fetch: emscripten_fetch_wait done..') }); // #endif if (proxyState == 2) return EMSCRIPTEN_RESULT_SUCCESS; else return EMSCRIPTEN_RESULT_FAILED; #else EM_ASM({ console.error('fetch: emscripten_fetch_wait is not available when building without pthreads!') }); return EMSCRIPTEN_RESULT_FAILED; #endif }
int main() { if (!emscripten_has_threading_support()) { #ifdef REPORT_RESULT REPORT_RESULT(0); #endif printf("Skipped: Threading is not supported.\n"); return 0; } pthread_t thread; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); int rc = pthread_create(&thread, &attr, ThreadMain, 0); assert(rc == 0); rc = emscripten_futex_wait(&main_thread_wait_val, 1, 15 * 1000); if (rc != 0) { printf("ERROR! futex wait timed out!\n"); result = 2; #ifdef REPORT_RESULT REPORT_RESULT(result); #endif exit(1); } pthread_attr_destroy(&attr); pthread_join(thread, 0); #ifdef REPORT_RESULT REPORT_RESULT(result); #endif }
void *mandelbrot_thread(void *arg) { int idx = (int)arg; for(;;) { emscripten_futex_wait(&tasksPending[idx], 0, INFINITY); emscripten_atomic_store_u32(&tasksPending[idx], 0); double t0 = emscripten_get_now(); int ni; if (use_sse) ni = ComputeMandelbrot_SSE(mandelReal, mandelImag, outputImage, sizeof(float)*W, sizeof(uint32_t)*W, 0, idx, numTasks, W, H, left, top, incrX, incrY, numItersDoneOnCanvas, numItersPerFrame); else ni = ComputeMandelbrot(mandelReal, mandelImag, outputImage, sizeof(float)*W, sizeof(uint32_t)*W, 0, idx, numTasks, W, H, left, top, incrX, incrY, numItersDoneOnCanvas, numItersPerFrame); //emscripten_atomic_add_u32(&numIters, ni); double t1 = emscripten_get_now(); numIters[idx] += ni; timeSpentInMandelbrot[idx] += t1-t0; emscripten_atomic_add_u32(&tasksDone, 1); emscripten_futex_wake(&tasksDone, 9999); } }