nissy-core

The "engine" of nissy, including the H48 optimal solver.
git clone https://git.tronto.net/nissy-core
Download | Log | Files | Refs | README | LICENSE

commit 52c74a1c33ce12b93ec5830f5a6348d9578c7c6e
parent 57f194ef57e393d84e2d3c73115ec528e02ea3c9
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Thu,  5 Jun 2025 08:37:34 +0200

Simplify callback business (web version)

Diffstat:
MREADME.md | 2++
Mbuild | 9+++++----
Mweb/adapter.cpp | 27++++++++++++++-------------
Dweb/callback.js | 52----------------------------------------------------
Aweb/http/start-server.sh | 8++++++++
Mweb/http/worker.mjs | 14++++++++------
Mweb/logging.h | 16+++++++++-------
7 files changed, 46 insertions(+), 82 deletions(-)

diff --git a/README.md b/README.md @@ -257,6 +257,8 @@ $ darkhttpd web/http/ \ --mimetypes web/http/mime ``` +The command above can also be run with the `web/http/start-server.sh` script. + ## Cube format This format is a "base 32" encoding of the cube. It is not meant to be diff --git a/build b/build @@ -139,12 +139,14 @@ CPPFLAGS="-std=c++20 -pthread" # The options below have to be adjusted when native WASM_SIMD is implemented. # Build flags for emscripten (WASM target) -WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread \ +WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread -mfpu=neon -mrelaxed-simd" WASMCPPFLAGS="-std=c++20 -pthread" WASMMFLAGS="-DTHREADS=$THREADS -DNEON" WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE - -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \ + -sEXPORTED_RUNTIME_METHODS=addFunction,UTF8ToString + -sALLOW_TABLE_GROWTH + -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS -sFETCH -sASYNCIFY -sLINKABLE -sEXPORT_ALL" if (command -v "python3-config" >/dev/null 2>&1) ; then @@ -284,8 +286,7 @@ build_web() { run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $(odflags) -c \ -o nissy.o src/nissy.c || exit 1 run $EMCC -lembind -lidbfs.js \ - $WASMCPPFLAGS $(odflags) $WASMLINKFLAGS \ - --js-library web/callback.js -o web/"$obj".mjs \ + $WASMCPPFLAGS $(odflags) $WASMLINKFLAGS -o web/"$obj".mjs \ cpp/nissy.cpp web/storage.cpp web/adapter.cpp nissy.o || exit 1 cp web/"$obj".mjs web/http/ cp web/"$obj".wasm web/http/ diff --git a/web/adapter.cpp b/web/adapter.cpp @@ -1,15 +1,10 @@ -extern "C" { - extern int addCallbackFunction(/* args intentionally unspecified */); - extern void callFunction(int, const char *); - extern int callFunctionInt(int); -} - #include "../cpp/nissy.h" #include "storage.h" #include "logging.h" #include <emscripten.h> #include <emscripten/bind.h> +#include <functional> #include <map> #include <set> #include <string> @@ -17,6 +12,8 @@ extern "C" { EM_ASYNC_JS(void, fake_async, (), {}); +std::map<std::string, nissy::solver> loaded_solvers; + const std::set<std::string> available_solvers { "h48h0k4", @@ -27,8 +24,6 @@ const std::set<std::string> available_solvers "h48h5k2", }; -std::map<std::string, nissy::solver> loaded_solvers; - bool is_solver_available(const std::string& name) { return available_solvers.contains(name); @@ -153,16 +148,20 @@ bool init_solver_generate(const std::string& name) int poll_status(void *arg) { - if (arg == NULL || *(int *)arg == -1) + if (arg == nullptr) return nissy::status::RUN.value; - return callFunctionInt(*(int *)arg); + std::function<int(void)> poll((int (*)(void))arg); + return poll(); } +// The parameter js_poll_status is of type int here, but actually it is a +// pointer to a JS function. The type will have to be changed to a 64-bit +// integer when we move to WASM64. nissy::solver::solve_result solve(std::string name, nissy::cube cube, nissy::nissflag nissflag, unsigned minmoves, unsigned maxmoves, unsigned maxsols, unsigned optimal, unsigned threads, - int poll_status_id) + int js_poll_status) { // Here we use a dirty trick to make this function always return the // same kind of JavaScript object. If we did not do this, the returned @@ -178,8 +177,11 @@ nissy::solver::solve_result solve(std::string name, {.err = nissy::error::INVALID_SOLVER}; } + // TODO: when running multiple solvers at the same time, we could use + // poll_status_id as intended (i.e. an id of some sort) return loaded_solvers.at(name).solve(cube, nissflag, minmoves, - maxmoves, maxsols, optimal, threads, poll_status, &poll_status_id); + maxmoves, maxsols, optimal, threads, + poll_status, &js_poll_status); } EMSCRIPTEN_BINDINGS(Nissy) @@ -244,5 +246,4 @@ EMSCRIPTEN_BINDINGS(Nissy) emscripten::return_value_policy::take_ownership()); emscripten::function("setLogger", &set_logger, emscripten::allow_raw_pointers()); - emscripten::function("addCallbackFunction", &addCallbackFunction); } diff --git a/web/callback.js b/web/callback.js @@ -1,52 +0,0 @@ -addToLibrary({ - -cbfl: [], - -validateCallbackId__deps: [ 'cbfl' ], -validateCallbackId: function(i) { - if (i < 0) { - console.log("--- WARNING ---"); - console.log("Trying to access callback function of invalid id " + i); - console.log("--- WARNING ---"); - return false; - } - - if (i >= _cbfl.length) { - console.log("--- WARNING ---"); - console.log("Trying to access callback function " + i + ", but only " - + _cbfl.length + " have been registered. This may be caused by a " - + "call outside of the main thread."); - console.log("--- WARNING ---"); - return false; - } - - return true; -}, - -addCallbackFunction__deps: [ 'cbfl' ], -addCallbackFunction: function(f) { - _cbfl.push(f) - return _cbfl.length - 1 -}, - -callFunction__deps: [ 'cbfl', 'validateCallbackId' ], -callFunction: function(id, arg) { - // This is a workaround related to usign WASM64 - // JavaScript's UTF8ToString expects a pointer argument, which for JS is - // of type "number", but WASM64 is passing a BigInt. See also: - // https://github.com/emscripten-core/emscripten/issues/21541 - // (but I could not make the suggested solution work in this case). - // TODO: check if there is a better workaround. - const non64_arg = Number(arg); - if (_validateCallbackId(id)) - _cbfl[id](UTF8ToString(non64_arg)); -}, - -callFunctionInt__deps: [ 'cbfl', 'validateCallbackId' ], -callFunctionInt: function(id) { - if (_validateCallbackId(id)) - return _cbfl[id](); - return 0; -}, - -}); diff --git a/web/http/start-server.sh b/web/http/start-server.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Run this script from the main folder (../../) + +darkhttpd web/http \ + --header 'Cross-Origin-Opener-Policy: same-origin' \ + --header 'Cross-Origin-Embedder-Policy: require-corp' \ + --mimetypes web/http/mime diff --git a/web/http/worker.mjs b/web/http/worker.mjs @@ -1,14 +1,16 @@ import Nissy from "./nissy_web_module.mjs" const nissy = await Nissy(); -const log = (msg) => postMessage({ command: "log", id: -1, object: msg }); -nissy.setLogger(nissy._addCallbackFunction(log)); -var solveStatus = nissy.statusRUN; // For now this is a global variable -const pollStatusCallback = nissy._addCallbackFunction(() => { -console.log("Calling pollstatus, returning " + solveStatus); -return solveStatus; +const log = (cstr) => postMessage({ + command: "log", id: -1, + object: nissy.UTF8ToString(cstr) }); +nissy.setLogger(nissy.addFunction(log, "vp")); + +var solveStatus = nissy.statusRUN; // For now this is a global variable +const pollStatus = () => solveStatus; +const pollStatusCallback = nissy.addFunction(pollStatus, "i"); const commands = [ { name: "load solver data", exec: loadSolverDataFromStorage }, diff --git a/web/logging.h b/web/logging.h @@ -1,11 +1,9 @@ -static int logger_id = -1; +static void (*js_log)(const char *) = nullptr; void log(std::string s) { - if (logger_id == -1) - return; - - callFunction(logger_id, s.c_str()); + if (js_log != nullptr) + js_log(s.c_str()); } void log_wrapper(const char *cstr, void *data) @@ -13,8 +11,12 @@ void log_wrapper(const char *cstr, void *data) log(cstr); } -void set_logger(int id) +/* +To receive a function pointer for JS, we use an int parameter. +This will have to be changed to a 64-bit integer when we move to WASM64. +*/ +void set_logger(int f) { - logger_id = id; + js_log = (void (*)(const char *))f; nissy::set_logger(log_wrapper, NULL); }