Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -4003,7 +4003,7 @@ LibraryManager.library = {
#if USE_PTHREADS
// Pthreads need their clocks synchronized to the execution of the main thread, so give them a special form of the function.
"if (ENVIRONMENT_IS_PTHREAD) {\n" +
" _emscripten_get_now = function() { return performance['now']() - __performance_now_clock_drift; };\n" +
" _emscripten_get_now = function() { return performance['now']() - Module['__performance_now_clock_drift']; };\n" +
"} else " +
#endif
"if (typeof dateNow !== 'undefined') {\n" +
Expand Down
1 change: 0 additions & 1 deletion src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ var wasmModule;
// to avoid accessing the global scope.
var threadInfoStruct = 0;
var selfThreadId = 0;
var __performance_now_clock_drift = 0;
#if WASM_BACKEND
var tempDoublePtr = 0;
#endif
Expand Down
8 changes: 4 additions & 4 deletions src/shell_pthreads.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
var ENVIRONMENT_IS_PTHREAD = Module['ENVIRONMENT_IS_PTHREAD'] || false;
if (ENVIRONMENT_IS_PTHREAD) {
// Grab imports from the pthread to local scope.
buffer = {{{EXPORT_NAME}}}['buffer'];
tempDoublePtr = {{{EXPORT_NAME}}}['tempDoublePtr'];
DYNAMIC_BASE = {{{EXPORT_NAME}}}['DYNAMIC_BASE'];
DYNAMICTOP_PTR = {{{EXPORT_NAME}}}['DYNAMICTOP_PTR'];
buffer = Module['buffer'];
tempDoublePtr = Module['tempDoublePtr'];
DYNAMIC_BASE = Module['DYNAMIC_BASE'];
DYNAMICTOP_PTR = Module['DYNAMICTOP_PTR'];
// Note that not all runtime fields are imported above. Values for STACK_BASE, STACKTOP and STACK_MAX are not yet known at worker.js load time.
// These will be filled in at pthread startup time (the 'run' message for a pthread - pthread start establishes the stack frame)
}
Expand Down
12 changes: 5 additions & 7 deletions src/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ var tempDoublePtr = 0; // A temporary memory area for global float and double ma

var noExitRuntime;

// performance.now() is specced to return a wallclock time in msecs since that Web Worker/main thread launched. However for pthreads this can cause
// subtle problems in emscripten_get_now() as this essentially would measure time from pthread_create(), meaning that the clocks between each threads
// would be wildly out of sync. Therefore sync all pthreads to the clock on the main browser thread, so that different threads see a somewhat
// coherent clock across each of them (+/- 0.1msecs in testing)
var __performance_now_clock_drift = 0;

// Cannot use console.log or console.error in a web worker, since that would risk a browser deadlock! https://bugzilla.mozilla.org/show_bug.cgi?id=1049091
// Therefore implement custom logging facility for threads running in a worker, which queue the messages to main thread to print.
var Module = {};
Expand Down Expand Up @@ -163,7 +157,11 @@ this.onmessage = function(e) {
} else if (e.data.cmd === 'objectTransfer') {
PThread.receiveObjectTransfer(e.data);
} else if (e.data.cmd === 'run') { // This worker was idle, and now should start executing its pthread entry point.
__performance_now_clock_drift = performance.now() - e.data.time; // Sync up to the clock of the main thread.
// performance.now() is specced to return a wallclock time in msecs since that Web Worker/main thread launched. However for pthreads this can cause
// subtle problems in emscripten_get_now() as this essentially would measure time from pthread_create(), meaning that the clocks between each threads
// would be wildly out of sync. Therefore sync all pthreads to the clock on the main browser thread, so that different threads see a somewhat
// coherent clock across each of them (+/- 0.1msecs in testing)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you wrap these comments at 80?

Module['__performance_now_clock_drift'] = performance.now() - e.data.time;
threadInfoStruct = e.data.threadInfoStruct;
Module['__register_pthread_ptr'](threadInfoStruct, /*isMainBrowserThread=*/0, /*isMainRuntimeThread=*/0); // Pass the thread address inside the asm.js scope to store it for fast access that avoids the need for a FFI out.
selfThreadId = e.data.selfThreadId;
Expand Down
87 changes: 87 additions & 0 deletions tests/pthread/test_pthread_reltime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2019 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.

#include <iostream>
#include <pthread.h>
#include <emscripten.h>

typedef std::chrono::milliseconds::rep TimePoint;

TimePoint now() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think i'd find this test more readable and use fewer lines of code is it was just C and used clock_gettime.. but thats probably personal preference so I won't ask you to change that.


static TimePoint ping, pong;

static std::mutex mutex;
static std::condition_variable cond_var;

void *thread_main(void *arg) {
std::cout << "running thread ..." << std::endl;

std::unique_lock<std::mutex> lock(mutex);

cond_var.wait(lock);

// Measure time in the pthread.
pong = now();

std::cout << "pong - ping: " << (now() - ping) << std::endl;

cond_var.notify_one();

return NULL;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all these extra newlines?

}

EMSCRIPTEN_KEEPALIVE
extern "C" int notify() {
{
std::unique_lock<std::mutex> lock(mutex);

std::cout << "notifying ..." << std::endl;

ping = now();

cond_var.notify_one();
}
{
std::unique_lock<std::mutex> lock(mutex);

cond_var.wait(lock);

TimePoint last = now();

std::cout << "last - pong: " << (last - ping) << std::endl;

// Time measured on a worker should be relative to the main thread,
// so that things are basically monotonic.

REPORT_RESULT(int(pong >= ping) + 2 * int(last >= pong));
}

return 0;
}

int main() {
std::cout << "running main ..." << std::endl;

EM_ASM({
setTimeout(function() {
Module._notify();
});
});

pthread_attr_t attr;
pthread_attr_init(&attr);

pthread_t thread;
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to set PTHREAD_CREATE_DETACHED here? Maybe we can just remove these attrs completely and pass NULL to keep the test cleaner.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we cannot join the thread in this case, it seems best to explicitly detach it. Can try to remove, if you insist.

int error = pthread_create(&thread, &attr, thread_main, NULL);
if (error)
abort();

return 0;
}
7 changes: 7 additions & 0 deletions tests/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4567,6 +4567,13 @@ def run(emcc_args=[]):
run(['-s', 'ASSERTIONS=1'])
run(['-s', 'PROXY_TO_PTHREAD=1'])

# Tests that time in a pthread is relative to the main thread, so measurements
# on different threads are still monotonic, as if checking a single central
# clock.
@requires_threads
def test_pthread_reltime(self):
self.btest(path_from_root('tests', 'pthread', 'test_pthread_reltime.cpp'), expected='3', args=['-s', 'USE_PTHREADS=1', '-s', 'PTHREAD_POOL_SIZE=1'])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this in node now too? We should probably have a plan for centralizing the thread tests that run in both browser and node. Perhaps test_threads.py?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me, but since it seems like a larger undertaking I hope this does not block landing this fix.


# Tests that it is possible to load the main .js file of the application manually via a Blob URL, and still use pthreads.
@requires_threads
def test_load_js_from_blob_with_pthreads(self):
Expand Down