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:
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);
}