commit 8d3fc9f5f9c0593fcae9c451efe6c89c64dcbe84
parent cdf46d85efe7fa54e8c302a255214341c567b4af
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date: Tue, 29 Apr 2025 10:08:04 +0200
Added stop / pause / resume solve to API
Diffstat:
16 files changed, 213 insertions(+), 51 deletions(-)
diff --git a/configure.sh b/configure.sh
@@ -85,6 +85,7 @@ validatethreads
validatearch
STD="-std=c11"
+POSIX="-D_POSIX_C_SOURCE=199309L"
WFLAGS="-pedantic -Wall -Wextra"
WNOFLAGS="-Wno-unused-parameter -Wno-unused-function -Wno-unknown-pragmas"
WNOFLAGS="$WNOFLAGS -Wno-unused-command-line-argument"
@@ -99,8 +100,8 @@ if [ -n "$SANITIZE" ]; then
fi
LIBS="-lpthread"
-CFLAGS="$STD $LIBS $WFLAGS $WNOFLAGS $AVX -O3 -fPIC -D$ARCH"
-DBGFLAGS="$STD $LIBS $WFLAGS $WNOFLAGS $SAN $AVX -g3 -DDEBUG -fPIC -D$ARCH"
+CFLAGS="$STD $POSIX $LIBS $WFLAGS $WNOFLAGS $AVX -O3 -fPIC -D$ARCH"
+DBGFLAGS="$STD $POSIX $LIBS $WFLAGS $WNOFLAGS $SAN $AVX -g3 -DDEBUG -fPIC -D$ARCH"
WASMFLAGS="$STD $LIBS $WFLAGS $WNOFLAGS -O3 -fPIC"
MACROS="-DTHREADS=$THREADS"
diff --git a/cpp/examples/solve_h48h3k2.cpp b/cpp/examples/solve_h48h3k2.cpp
@@ -68,7 +68,7 @@ int main() {
// Solve
auto solve_result = h48h3k2.solve(c, nissy::nissflag::NORMAL,
- 0, maxmoves, 1, 20, 8);
+ 0, maxmoves, 1, 20, 8, NULL, NULL);
// Write the result
if (!solve_result.err.ok()) {
diff --git a/cpp/nissy.cpp b/cpp/nissy.cpp
@@ -20,7 +20,8 @@ extern "C" {
long long nissy_checkdata(unsigned long long, const unsigned char *);
long long nissy_solve(const char *, const char *, unsigned, unsigned,
unsigned, unsigned, unsigned, unsigned, unsigned long long,
- const unsigned char *, unsigned, char *, long long *);
+ const unsigned char *, unsigned, char *, long long *,
+ int (*)(void *), void *);
long long nissy_countmoves(const char *);
long long nissy_setlogger(void (*)(const char *, void *), void *);
}
@@ -47,6 +48,10 @@ namespace nissy {
const error error::OPTIONS{-80};
const error error::UNKNOWN{-999};
+ const status status::run{0};
+ const status status::stop{1};
+ const status status::pause{2};
+
namespace size {
constexpr size_t CUBE = 22;
constexpr size_t TRANSFORMATION = 12;
@@ -154,7 +159,8 @@ namespace nissy {
solver::solve_result
solver::solve(const cube& cube, nissflag niss, unsigned minmoves,
unsigned maxmoves, unsigned maxsols, unsigned optimal,
- unsigned threads)
+ unsigned threads, int (*poll_status)(void *),
+ void *poll_status_data)
{
solver::solve_result result;
@@ -171,7 +177,8 @@ namespace nissy {
name.c_str(), niss.value, minmoves, maxmoves, maxsols,
optimal, threads, data.size(),
reinterpret_cast<const unsigned char *>(data.data()), len,
- csols.data(), result.stats.data());
+ csols.data(), result.stats.data(), poll_status,
+ poll_status_data);
result.err = error{err};
if (err < 0)
diff --git a/cpp/nissy.h b/cpp/nissy.h
@@ -46,6 +46,15 @@ namespace nissy {
static const error UNKNOWN;
};
+ class status {
+ public:
+ int value;
+
+ static const status run;
+ static const status stop;
+ static const status pause;
+ };
+
class cube {
public:
cube();
@@ -86,7 +95,8 @@ namespace nissy {
void unload_data();
solve_result solve(const cube&, nissflag, unsigned minmoves,
unsigned maxmoves, unsigned maxsols, unsigned optimal,
- unsigned threads);
+ unsigned threads, int (*poll_status)(void *),
+ void *poll_status_data);
static std::variant<solver, error> get(const std::string&);
private:
diff --git a/python/nissy_module.c b/python/nissy_module.c
@@ -300,7 +300,7 @@ solve(PyObject *self, PyObject *args)
result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves,
maxsolutions, optimal, threads, data->ob_alloc,
(unsigned char *)data->ob_bytes, MAX_SOLUTIONS_SIZE, solutions,
- stats);
+ stats, NULL, NULL);
Py_END_ALLOW_THREADS
if(!check_error(result)) {
@@ -350,9 +350,9 @@ static PyMethodDef nissy_methods[] = {
{ "getcube", getcube, METH_VARARGS, getcube_doc },
{ "solverinfo", solverinfo, METH_VARARGS, solverinfo_doc },
{ "gendata", gendata, METH_VARARGS, gendata_doc },
- { "checkdata", checkdata, METH_VARARGS, checkdata_doc },
+ { "checkdata", _checkdata, METH_VARARGS, checkdata_doc },
{ "solve", solve, METH_VARARGS, solve_doc },
- { "countmoves", countmoves, METH_VARARGS, countmoves_doc },
+ { "countmoves", _countmoves, METH_VARARGS, countmoves_doc },
{ NULL, NULL, 0, NULL }
};
diff --git a/shell/shell.c b/shell/shell.c
@@ -435,7 +435,7 @@ solve_exec(args_t *args)
ret = nissy_solve(
args->cube, args->str_solver, nissflag, args->minmoves,
args->maxmoves, args->maxsolutions, args->optimal, args->threads,
- size, buf, SOLUTIONS_BUFFER_SIZE, solutions, stats);
+ size, buf, SOLUTIONS_BUFFER_SIZE, solutions, stats, NULL, NULL);
free(buf);
diff --git a/src/nissy.c b/src/nissy.c
@@ -460,7 +460,9 @@ nissy_solve(
const unsigned char data[data_size],
unsigned sols_size,
char sols[sols_size],
- long long stats[static NISSY_SIZE_SOLVE_STATS]
+ long long stats[static NISSY_SIZE_SOLVE_STATS],
+ int (*poll_status)(void *),
+ void *poll_status_data
)
{
oriented_cube_t oc;
@@ -475,14 +477,23 @@ nissy_solve(
oc = readcube(cube);
-/* TODO: solve should handle oriented cubes */
-
if (!isconsistent(oc)) {
LOG("[solve] Error: cube is invalid\n");
return NISSY_ERROR_INVALID_CUBE;
}
-/* TODO: checks for minmoves, maxmoves, nissflag */
+ if (maxmoves > 20) {
+ LOG("[solve] 'maxmoves' larger than 20 not supported yet, "
+ "setting it to 20\n");
+ maxmoves = 20;
+ }
+
+ if (minmoves > maxmoves) {
+ LOG("[solve] value provided for 'minmoves' (%u) is larger "
+ "than that provided for 'maxmoves' (%u), setting "
+ "'minmoves' to %u\n", minmoves, maxmoves, maxmoves);
+ minmoves = maxmoves;
+ }
if (maxsols == 0) {
LOG("[solve] 'maxsols' is 0, returning no solution\n");
@@ -505,11 +516,12 @@ nissy_solve(
if (parse_ret != NISSY_OK)
return parse_ret;
return solve_h48(oc, minmoves, maxmoves, maxsols,
- optimal, t, data_size, data, sols_size, sols, stats);
+ optimal, t, data_size, data, sols_size, sols, stats,
+ poll_status, poll_status_data);
} else if (!strncmp(solver, "coord_", 6)) {
return solve_coord_dispatch(oc, solver + 6, nissflag,
minmoves, maxmoves, maxsols, optimal, t, data_size, data,
- sols_size, sols);
+ sols_size, sols, poll_status, poll_status_data);
} else {
LOG("[solve] Error: unknown solver '%s'\n", solver);
return NISSY_ERROR_INVALID_SOLVER;
diff --git a/src/nissy.h b/src/nissy.h
@@ -37,6 +37,11 @@ for example 'rotation UF' or 'mirrored BL'.
#define NISSY_NISSFLAG_ALL \
(NISSY_NISSFLAG_NORMAL | NISSY_NISSFLAG_INVERSE | NISSY_NISSFLAG_MIXED)
+/* Status for stopping / pausing / resuming a solver */
+#define NISSY_STATUS_RUN 0
+#define NISSY_STATUS_STOP 1
+#define NISSY_STATUS_PAUSE 2
+
/* The solved cube */
#define NISSY_SOLVED_CUBE "ABCDEFGH=ABCDEFGHIJKL=A"
@@ -296,24 +301,32 @@ nissy_checkdata(
Solve the given cube using the given solver and options.
Parameters:
- cube - The cube to solver.
- solver - The name of the solver.
- nissflag - The flags for NISS (linear, inverse, mixed, or combinations).
- minmoves - The minimum number of moves for a solution.
- maxmoves - The maximum number of moves for a solution.
- maxsols - The maximum number of solutions.
- optimal - The maximum number of moves above the optimal solution length.
- threads - The number of threads to use. Must be less than or equalt to
- the value of the compile-time constant THREADS. If set to 0,
- the default value THREADS will be used.
- data_size - The size of the data buffer.
- data - The data for the solver. Can be computed with gendata.
- This buffer must have 8-byte alignment.
- sols_size - The size of the solutions buffer.
- sols - The return parameter for the solutions. The solutions are
- separated by a '\n' (newline) and a '\0' (NULL character)
- terminates the list.
- stats - An array to store some statistics about the solve.
+ cube - The cube to solver.
+ solver - The name of the solver.
+ nissflag - The flags for NISS (linear, inverse, mixed, or
+ combinations; see the constants at the top of this file).
+ minmoves - The minimum number of moves for a solution.
+ maxmoves - The maximum number of moves for a solution.
+ maxsols - The maximum number of solutions.
+ optimal - The maximum number of moves above the optimal solution.
+ threads - The number of threads to use. Must be less than or equal
+ to the value of the compile-time constant THREADS. If set
+ to 0, the default value THREADS will be used.
+ data_size - The size of the data buffer.
+ data - The data for the solver. Can be computed with gendata.
+ This buffer must have 8-byte alignment.
+ sols_size - The size of the solutions buffer.
+ sols - The return parameter for the solutions. The solutions are
+ separated by a '\n' (newline) and a '\0' (NULL character)
+ terminates the list.
+ stats - An array to store some statistics about the solve.
+ poll_status - A callback function that should return the current
+ requested status for the solver (e.g. run, stop, pause,
+ resume; see the constants at the top of this file). The
+ way this status is polled and honored is solver-specific.
+ If this parameter is NULL, the status will always assumed
+ to be "run".
+ poll_status_data - Auxiliary data for the poll_status callback function.
Return values:
NISSY_OK - Cube solved succesfully.
@@ -340,7 +353,9 @@ nissy_solve(
const unsigned char data[data_size],
unsigned sols_size,
char sols[sols_size],
- long long stats[static NISSY_SIZE_SOLVE_STATS]
+ long long stats[static NISSY_SIZE_SOLVE_STATS],
+ int (*poll_status)(void *),
+ void *poll_status_data
);
/*
diff --git a/src/solvers/coord/solve.h b/src/solvers/coord/solve.h
@@ -14,10 +14,10 @@ typedef struct {
STATIC int64_t solve_coord(oriented_cube_t, coord_t [static 1], uint8_t,
uint8_t, uint8_t, uint8_t, uint64_t, uint8_t, uint8_t, uint64_t,
- const unsigned char *, size_t n, char [n]);
+ const unsigned char *, size_t n, char [n], int (*)(void *), void *);
STATIC int64_t solve_coord_dispatch(oriented_cube_t, const char *, uint8_t,
uint8_t, uint8_t, uint64_t, uint8_t, uint8_t, uint64_t,
- const unsigned char *, size_t n, char [n]);
+ const unsigned char *, size_t n, char [n], int (*)(void *), void *);
STATIC bool coord_solution_admissible(const dfsarg_solve_coord_t [static 1]);
STATIC bool solve_coord_dfs_stop(const dfsarg_solve_coord_t [static 1]);
STATIC bool coord_continue_onnormal(const dfsarg_solve_coord_t [static 1]);
@@ -211,7 +211,9 @@ solve_coord_dispatch(
uint64_t data_size,
const unsigned char *data,
size_t solutions_size,
- char sols[solutions_size]
+ char sols[solutions_size],
+ int (*poll_status)(void *),
+ void *poll_status_data
)
{
coord_t *coord;
@@ -233,7 +235,7 @@ solve_coord_dispatch(
return solve_coord(oc, coord, axis, nissflag, minmoves, maxmoves,
maxsolutions, optimal, threads, data_size, data,
- solutions_size, sols);
+ solutions_size, sols, poll_status, poll_status_data);
}
STATIC int64_t
@@ -250,7 +252,9 @@ solve_coord(
uint64_t data_size,
const unsigned char *data,
size_t solutions_size,
- char sols[solutions_size]
+ char sols[solutions_size],
+ int (*poll_status)(void *),
+ void *poll_status_data
)
{
int8_t d;
diff --git a/src/solvers/h48/solve.h b/src/solvers/h48/solve.h
@@ -35,6 +35,10 @@ typedef struct {
solve_h48_task_t *tasks;
int thread_id;
pthread_mutex_t *solutions_mutex;
+ int (*poll_status)(void *);
+ void *poll_status_data;
+ _Atomic bool cancelled;
+ _Atomic bool cantsleep;
} dfsarg_solve_h48_t;
typedef struct {
@@ -50,11 +54,12 @@ STATIC_INLINE bool solve_h48_stop(dfsarg_solve_h48_t [static 1]);
STATIC int64_t solve_h48_maketasks(
dfsarg_solve_h48_t [static 1], dfsarg_solve_h48_maketasks_t [static 1],
solve_h48_task_t [static STARTING_CUBES], int [static 1]);
+STATIC bool solve_h48_runthread_continue(dfsarg_solve_h48_t *);
STATIC void *solve_h48_runthread(void *);
STATIC int64_t solve_h48_dfs(dfsarg_solve_h48_t [static 1]);
STATIC int64_t solve_h48(oriented_cube_t, uint8_t, uint8_t, uint8_t, uint8_t,
uint8_t, uint64_t, const unsigned char *, size_t n, char [n],
- long long [static NISSY_SIZE_SOLVE_STATS]);
+ long long [static NISSY_SIZE_SOLVE_STATS], int (*)(void *), void *);
STATIC_INLINE bool
solve_h48_stop(dfsarg_solve_h48_t arg[static 1])
@@ -232,16 +237,42 @@ solve_h48_dfs(dfsarg_solve_h48_t arg[static 1])
return ret;
}
+STATIC bool
+solve_h48_runthread_continue(dfsarg_solve_h48_t *arg)
+{
+ int status;
+
+ for (status = NISSY_STATUS_PAUSE; status == NISSY_STATUS_PAUSE; ) {
+ status = arg->poll_status == NULL ? NISSY_STATUS_RUN :
+ arg->poll_status(arg->poll_status_data);
+ msleep(500);
+ }
+
+ return status == NISSY_STATUS_RUN;
+}
+
STATIC void *
solve_h48_runthread(void *arg)
{
- int i, j;
+ int i, j, status;
solve_h48_task_t task;
dfsarg_solve_h48_t *dfsarg;
dfsarg = (dfsarg_solve_h48_t *)arg;
for (i = dfsarg->thread_id; i < dfsarg->ntasks; i += dfsarg->threads) {
+ status = dfsarg->poll_status == NULL ? NISSY_STATUS_RUN :
+ dfsarg->poll_status(dfsarg->poll_status_data);
+ switch (status) {
+ case NISSY_STATUS_STOP:
+ goto solve_h48_runthread_cancel;
+ case NISSY_STATUS_PAUSE:
+ if (!NISSY_CANSLEEP)
+ goto solve_h48_runthread_cantsleep;
+ if (!solve_h48_runthread_continue(dfsarg))
+ goto solve_h48_runthread_cancel;
+ }
+
task = dfsarg->tasks[i];
solution_moves_reset(dfsarg->solution_moves);
@@ -265,6 +296,12 @@ solve_h48_runthread(void *arg)
}
return NULL;
+
+solve_h48_runthread_cantsleep:
+ dfsarg->cantsleep = true;
+solve_h48_runthread_cancel:
+ dfsarg->cancelled = true;
+ return NULL;
}
STATIC int64_t
@@ -351,9 +388,12 @@ solve_h48(
const unsigned char *data,
size_t solutions_size,
char solutions[solutions_size],
- long long stats[static NISSY_SIZE_SOLVE_STATS]
+ long long stats[static NISSY_SIZE_SOLVE_STATS],
+ int (*poll_status)(void *),
+ void *poll_status_data
)
{
+ bool anycancelled, anycantsleep;
int i, ntasks, eoesep_table_index;
int8_t d;
dfsarg_solve_h48_t arg[THREADS];
@@ -434,6 +474,10 @@ solve_h48(
.threads = threads,
.thread_id = i,
.solutions_mutex = &solutions_mutex,
+ .poll_status = poll_status,
+ .poll_status_data = poll_status_data,
+ .cancelled = false,
+ .cantsleep = false,
};
}
@@ -460,9 +504,10 @@ solve_h48(
LOG("[H48 solve] Prepared %d tasks\n", ntasks);
+ anycancelled = anycantsleep = false;
for (
d = MAX(minmoves, STARTING_MOVES + 1);
- !solutions_done(&sollist, &settings, d);
+ !(solutions_done(&sollist, &settings, d) || anycancelled);
d++
) {
if (d >= 15)
@@ -474,8 +519,22 @@ solve_h48(
pthread_create(
&thread[i], NULL, solve_h48_runthread, &arg[i]);
}
- for (i = 0; i < threads; i++)
+ for (i = 0; i < threads; i++) {
pthread_join(thread[i], NULL);
+ anycancelled = anycancelled || arg[i].cancelled;
+ anycantsleep = anycantsleep || arg[i].cantsleep;
+ }
+ }
+
+ if (anycantsleep) {
+ LOG("[H48 solve] Received pause request, but this feature is "
+ "not available on this system. "
+ "Taking it as a stop request.\n");
+ }
+
+ if (anycancelled) {
+ LOG("[H48 solve] Received stop request, ending solution "
+ "search early.\n");
}
solve_h48_done:
diff --git a/src/utils/sleep.h b/src/utils/sleep.h
@@ -0,0 +1,48 @@
+STATIC void msleep(int);
+
+#if defined(WIN32)
+
+#define NISSY_CANSLEEP true
+
+STATIC void
+msleep(int milliseconds)
+{
+ Sleep(milliseconds);
+}
+
+#elif defined(__wasm__)
+
+#include <emscripten.h>
+
+#define NISSY_CANSLEEP true
+
+STATIC void
+msleep(int milliseconds)
+{
+ emscripten_sleep(milliseconds);
+}
+
+#elif defined(__unix__)
+
+#include <time.h>
+
+#define NISSY_CANSLEEP true
+
+STATIC void
+msleep(int milliseconds)
+{
+ struct timespec t;
+ t.tv_sec = milliseconds / 1000;
+ t.tv_nsec = (milliseconds % 1000) * 1000000;
+ nanosleep(&t, NULL);
+}
+
+#else
+
+#define NISSY_CANSLEEP false
+STATIC void
+msleep(int milliseconds)
+{
+}
+
+#endif
diff --git a/src/utils/utils.h b/src/utils/utils.h
@@ -1,3 +1,4 @@
#include "dbg_log.h"
#include "constants.h"
#include "math.h"
+#include "sleep.h"
diff --git a/tools/300_solve_small/solve_small.c b/tools/300_solve_small/solve_small.c
@@ -37,7 +37,8 @@ void run(void) {
continue;
}
n = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL,
- 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats);
+ 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats,
+ NULL, NULL);
if (n == 0)
printf("No solution found\n");
else
diff --git a/tools/301_solve_file/solve_file.c b/tools/301_solve_file/solve_file.c
@@ -24,7 +24,8 @@ void run(void) {
continue;
}
nsols = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL,
- 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats);
+ 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats,
+ NULL, NULL);
if (nsols == 0)
printf("No solution found\n");
else
diff --git a/tools/302_solve_multisol/solve_multisol.c b/tools/302_solve_multisol/solve_multisol.c
@@ -30,7 +30,8 @@ void run(void) {
continue;
}
n = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL,
- 0, 20, nsol, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats);
+ 0, 20, nsol, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats,
+ NULL, NULL);
if (n == 0)
printf("No solution found\n");
else
diff --git a/tools/400_solvetest/solve_test.c b/tools/400_solvetest/solve_test.c
@@ -57,7 +57,8 @@ void run(void) {
continue;
}
n = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL,
- 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats);
+ 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats,
+ NULL, NULL);
if (n == 0) {
printf("Error: no solution\n");
return;
@@ -78,7 +79,8 @@ void run(void) {
continue;
}
n = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL,
- 0, 20, 100, 0, 0, size, buf, SOL_BUFFER_LEN, sol, stats);
+ 0, 20, 100, 0, 0, size, buf, SOL_BUFFER_LEN, sol, stats,
+ NULL, NULL);
if (check_all(sol, s[i].solutions)) {
printf("All solutions are correct\n");
} else {