From 8d3fc9f5f9c0593fcae9c451efe6c89c64dcbe84 Mon Sep 17 00:00:00 2001 From: Sebastiano Tronto Date: Tue, 29 Apr 2025 10:08:04 +0200 Subject: Added stop / pause / resume solve to API --- src/nissy.c | 24 ++++++++++++----- src/nissy.h | 53 +++++++++++++++++++++++------------- src/solvers/coord/solve.h | 14 ++++++---- src/solvers/h48/solve.h | 69 +++++++++++++++++++++++++++++++++++++++++++---- src/utils/sleep.h | 48 +++++++++++++++++++++++++++++++++ src/utils/utils.h | 1 + 6 files changed, 174 insertions(+), 35 deletions(-) create mode 100644 src/utils/sleep.h (limited to 'src') diff --git a/src/nissy.c b/src/nissy.c index 2dd5a41..acf6ebe 100644 --- 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 index 5800490..2fa4a75 100644 --- 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 index 48cc704..8eb3e9d 100644 --- 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 index e3ef9b9..f2967ed 100644 --- 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 new file mode 100644 index 0000000..d618e9b --- /dev/null +++ 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 + +#define NISSY_CANSLEEP true + +STATIC void +msleep(int milliseconds) +{ + emscripten_sleep(milliseconds); +} + +#elif defined(__unix__) + +#include + +#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 index ce4355e..d14b285 100644 --- 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" -- cgit v1.3