diff --git a/src/main.cpp b/src/main.cpp index 9a7376e..25384f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,11 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include +#include +#endif + #include "bitboard.h" #include "misc.h" #include "position.h" @@ -27,6 +32,21 @@ using namespace Stockfish; +#ifdef __EMSCRIPTEN__ +namespace { +std::unique_ptr wasmUci; +} + +extern "C" EMSCRIPTEN_KEEPALIVE void pikafish_command(const char* command) { + if (wasmUci && command) + wasmUci->command(command); +} + +extern "C" EMSCRIPTEN_KEEPALIVE double pikafish_heap_size() { + return static_cast(emscripten_get_heap_size()); +} +#endif + int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; @@ -37,7 +57,11 @@ int main(int argc, char* argv[]) { Tune::init(uci->engine_options()); +#ifdef __EMSCRIPTEN__ + wasmUci = std::move(uci); +#else uci->loop(); +#endif return 0; } diff --git a/src/numa.h b/src/numa.h index 99169c2..8b7a1e2 100644 --- a/src/numa.h +++ b/src/numa.h @@ -937,12 +937,17 @@ class NumaConfig { template void execute_on_numa_node(NumaIndex n, FuncT&& f) const { +#ifdef __EMSCRIPTEN__ + (void) n; + std::forward(f)(); +#else std::thread th([this, &f, n]() { bind_current_thread_to_numa_node(n); std::forward(f)(); }); th.join(); +#endif } std::vector> nodes; @@ -1394,7 +1399,11 @@ class LazyNumaReplicatedSystemWide: public NumaReplicatedBase { else { assert(cfg.num_numa_nodes() == 1); +#ifdef __EMSCRIPTEN__ + instances.emplace_back(SystemWideSharedConstant(std::move(*source))); +#else instances.emplace_back(SystemWideSharedConstant(*source, get_discriminator(0))); +#endif } } }; diff --git a/src/shm.h b/src/shm.h index 9bf13f2..61af6b2 100644 --- a/src/shm.h +++ b/src/shm.h @@ -478,6 +478,11 @@ struct SharedMemoryBackendFallback { SharedMemoryBackendFallback(const std::string&, const T& value) : fallback_object(make_unique_large_page(value)) {} +#ifdef __EMSCRIPTEN__ + SharedMemoryBackendFallback(const std::string&, T&& value) : + fallback_object(make_unique_large_page(std::move(value))) {} +#endif + void* get() const { return fallback_object.get(); } SharedMemoryBackendFallback(const SharedMemoryBackendFallback&) = delete; @@ -563,6 +568,11 @@ struct SystemWideSharedConstant { } } +#ifdef __EMSCRIPTEN__ + SystemWideSharedConstant(T&& value, std::size_t = 0) : + backend(SharedMemoryBackendFallback("", std::move(value))) {} +#endif + SystemWideSharedConstant(const SystemWideSharedConstant&) = delete; SystemWideSharedConstant& operator=(const SystemWideSharedConstant&) = delete; diff --git a/src/thread.cpp b/src/thread.cpp index 0ab073e..b709e1c 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -50,9 +50,19 @@ Thread::Thread(Search::SharedState& sharedState, idx(n), idxInNuma(numaN), totalNuma(totalNumaCount), - nthreads(sharedState.options["Threads"]), + nthreads(sharedState.options["Threads"]) +#ifndef __EMSCRIPTEN__ + , stdThread(&Thread::idle_loop, this) { +#else + { + numaAccessToken = binder(); + worker = make_unique_large_page( + sharedState, std::move(sm), n, idxInNuma, totalNuma, numaAccessToken); + searching = false; +#endif +#ifndef __EMSCRIPTEN__ wait_for_search_finished(); run_custom_job([this, &binder, &sharedState, &sm, n]() { @@ -65,6 +75,7 @@ Thread::Thread(Search::SharedState& sharedState, }); wait_for_search_finished(); +#endif } @@ -72,11 +83,15 @@ Thread::Thread(Search::SharedState& sharedState, // for its termination. Thread should be already waiting. Thread::~Thread() { +#ifdef __EMSCRIPTEN__ + return; +#else assert(!searching); exit = true; start_searching(); stdThread.join(); +#endif } // Wakes up the thread that will start the search @@ -94,12 +109,19 @@ void Thread::clear_worker() { // Blocks on the condition variable until the thread has finished searching void Thread::wait_for_search_finished() { +#ifdef __EMSCRIPTEN__ + return; +#else std::unique_lock lk(mutex); cv.wait(lk, [&] { return !searching; }); +#endif } // Launching a function in the thread void Thread::run_custom_job(std::function f) { +#ifdef __EMSCRIPTEN__ + f(); +#else { std::unique_lock lk(mutex); cv.wait(lk, [&] { return !searching; }); @@ -107,6 +129,7 @@ void Thread::run_custom_job(std::function f) { searching = true; } cv.notify_one(); +#endif } void Thread::ensure_network_replicated() { worker->ensure_network_replicated(); } diff --git a/src/uci.cpp b/src/uci.cpp index 19f55f0..ff7ffc9 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -85,17 +85,25 @@ void UCIEngine::init_search_update_listeners() { } void UCIEngine::loop() { - std::string token, cmd; + std::string cmd; for (int i = 1; i < cli.argc; ++i) cmd += std::string(cli.argv[i]) + " "; + bool quitting = false; do { if (cli.argc == 1 && !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication cmd = "quit"; + quitting = command(cmd); + } while (!quitting && cli.argc == 1); // The command-line arguments are one-shot +} + +bool UCIEngine::command(std::string_view command) { + std::string token, cmd(command); + std::istringstream is(cmd); token.clear(); // Avoid a stale if getline() returns nothing or a blank line @@ -173,7 +181,7 @@ void UCIEngine::loop() { sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl; - } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot + return token == "quit"; } Search::LimitsType UCIEngine::parse_limits(std::istream& is) { diff --git a/src/uci.h b/src/uci.h index 1c8c7c6..b4fb0d7 100644 --- a/src/uci.h +++ b/src/uci.h @@ -41,6 +41,7 @@ class UCIEngine { UCIEngine(int argc, char** argv); void loop(); + bool command(std::string_view command); static int to_cp(Value v, const Position& pos); static std::string format_score(const Score& s);