flst / upstream /emscripten /system /lib /wasmfs /thread_utils.h
arudradey's picture
Upload folder using huggingface_hub
00df61d verified
Raw
History Blame Contribute Delete
3.98 kB
/*
* Copyright 2021 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.
*/
#pragma once
#include <functional>
#include <thread>
#include <emscripten/proxying.h>
#include <emscripten/threading.h>
extern "C" {
void _wasmfs_thread_utils_heartbeat(em_proxying_queue* ctx);
}
namespace emscripten {
// Helper class for synchronously proxying work to a dedicated worker thread,
// including where the work is asynchronous.
class ProxyWorker {
// The queue we use to proxy work and the dedicated worker.
ProxyingQueue queue;
// Used to notify the calling thread once the worker has been started.
bool started = false;
std::mutex mutex;
std::condition_variable cond;
// Declare the thread last since it's dependent on the above member variables.
// Declaring it last isn't strictly needed since the thread is initialized in
// the body of the constructor, but is done out of caution.
std::thread thread;
public:
// Spawn the worker thread.
ProxyWorker() : queue() {
// Initialize the thread in the constructor to ensure the object has been
// fully constructed before thread starts using the object to avoid a data
// race. See #24370.
thread = std::thread([&]() {
// Notify the caller that we have started.
{
std::unique_lock<std::mutex> lock(mutex);
started = true;
}
cond.notify_all();
// Sometimes the main thread is spinning, waiting on a WasmFS lock held
// by a thread trying to proxy work to this dedicated worker. In that
// case, the proxying message won't be relayed by the main thread and
// the system will deadlock. This heartbeat ensures that proxying work
// eventually gets done so the thread holding the lock can make forward
// progress even if the main thread is blocked.
//
// TODO: Remove this once we can postMessage directly between workers
// without involving the main thread or once all browsers ship
// Atomics.waitAsync.
//
// Note that this requires adding _emscripten_proxy_execute_queue to
// EXPORTED_FUNCTIONS.
_wasmfs_thread_utils_heartbeat(queue.queue);
// Sit in the event loop performing work as it comes in.
emscripten_exit_with_live_runtime();
});
// Make sure the thread has actually started before returning. This allows
// subsequent code to assume the thread has already been spawned and not
// worry about potential deadlocks where it holds a lock while proxying an
// operation and meanwhile the main thread is blocked trying to acquire the
// same lock so is never able to spawn the worker thread.
//
// Unfortunately, this solution would cause the main thread to deadlock on
// itself, so for now assert that we are not on the main thread. In the
// future, we could provide an asynchronous version of this utility that
// calls a user callback once the worker has been started. This asynchronous
// version would be safe to use on the main thread.
assert(
!emscripten_is_main_browser_thread() &&
"cannot safely spawn dedicated workers from the main browser thread");
{
std::unique_lock<std::mutex> lock(mutex);
cond.wait(lock, [&]() { return started; });
}
}
// Kill the worker thread.
~ProxyWorker() {
pthread_cancel(thread.native_handle());
thread.join();
}
// Proxy synchronous work.
void operator()(const std::function<void()>& func) {
queue.proxySync(thread.native_handle(), func);
}
// Proxy asynchronous work that calls `finish()` on the ctx parameter to mark
// its end.
void operator()(const std::function<void(ProxyingQueue::ProxyingCtx)>& func) {
queue.proxySyncWithCtx(thread.native_handle(), func);
}
};
} // namespace emscripten