From 52aa0cac10acda7fb5920b1752b6e74c0d09e8f4 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Sat, 11 Apr 2026 09:50:59 +0200 Subject: [PATCH] Alive thread implemented --- main.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++--- rkt_protocol.h | 2 + rktwebview.cpp | 74 +++++++++++++++++++++++++++++++------ rktwebview_qt.cpp | 10 +++++ rktwebview_qt.h | 3 ++ rktwebview_test.cpp | 5 ++- shm.cpp | 33 ++++++++++++++--- shm.h | 6 ++- shmqueue.cpp | 18 +++++---- shmqueue.h | 5 ++- 10 files changed, 215 insertions(+), 31 deletions(-) diff --git a/main.cpp b/main.cpp index 6e55848..b09d260 100644 --- a/main.cpp +++ b/main.cpp @@ -26,13 +26,26 @@ public: ShmQueue *result_queue; ShmQueue *event_queue; Rktwebview_qt *webview_handler; + bool quit; // QThread interface protected: void run(); }; +class Alive : public QThread +{ +public: + Handler *handler; + ShmQueue *alive_queue; + bool alive_timeout; + +protected: + void run(); +}; + static Handler *_handler; +static Alive *_alive; static void event_cb(rkt_data_t *data) { @@ -58,7 +71,7 @@ int main(int argc, char *argv[]) INFO1("Starting at %s", ctime(&my_time)); } - if (argc < 6) { + if (argc < 7) { ERROR1("%s: wrong number of arguments\n", me); exit(1); } @@ -68,14 +81,16 @@ int main(int argc, char *argv[]) const char *cmd_slot_str = argv[3]; const char *res_slot_str = argv[4]; const char *evt_slot_str = argv[5]; + const char *alive_slot_str = argv[6]; size_t shm_size = atoi(shm_size_str); int cmd_slot = atoi(cmd_slot_str); int res_slot = atoi(res_slot_str); int evt_slot = atoi(evt_slot_str); + int alive_slot = atoi(alive_slot_str); - MKLOGSTMT(LOG_INFO, fprintf(stderr, "%s %s %s %s %s %s\n", me, shm_name, shm_size_str, cmd_slot_str, res_slot_str, evt_slot_str)); - MKLOGSTMT(LOG_INFO, fprintf(stderr, "%s %s %ld %d %d %d\n", me, shm_name, shm_size, cmd_slot, res_slot, evt_slot)); + MKLOGSTMT(LOG_INFO, fprintf(stderr, "%s %s %s %s %s %s %s\n", me, shm_name, shm_size_str, cmd_slot_str, res_slot_str, evt_slot_str, alive_slot_str)); + MKLOGSTMT(LOG_INFO, fprintf(stderr, "%s %s %ld %d %d %d %d\n", me, shm_name, shm_size, cmd_slot, res_slot, evt_slot, alive_slot)); if (!(shm_size > 0 && cmd_slot > 0 && res_slot > 0 && evt_slot > 0)) { ERROR1("%s: Invalid shm size or slots\n", me); @@ -91,14 +106,38 @@ int main(int argc, char *argv[]) handler->webview_handler = new Rktwebview_qt(argc, argv); handler->start(); + Alive *alive = new Alive(); + _alive = alive; + alive->handler = handler; + alive->alive_timeout = false; + alive->alive_queue = new ShmQueue(handler->shm, alive_slot, false); + alive->start(); + handler->webview_handler->initApp(); handler->webview_handler->execApp(); INFO0("waiting for thread to end\n"); handler->wait(); + INFO0("Handler thread stopped\n"); + alive->wait(); + INFO0("Alive thread stopped\n"); + + if (alive->alive_timeout) { + // take ownership over all queues and SHM, because we didn't get an alive message + // and probably the racket side has crashed because of that. + ERROR0("Taking ownership of shared memory, shared queues and shared semaphores\n"); + handler->command_queue->takeOwnership(); + handler->result_queue->takeOwnership(); + handler->event_queue->takeOwnership(); + handler->shm->takeOwnership(); + alive->alive_queue->takeOwnership(); + } INFO0("cleaning up shm\n"); + delete alive->alive_queue; + delete alive; + delete handler->webview_handler; delete handler->command_queue; delete handler->result_queue; @@ -116,14 +155,28 @@ int main(int argc, char *argv[]) void Handler::run() { - bool quit = false; + int wait_ms = 10 * 1000; // 10 seconds. while (!quit) { int cmd; std::string data; - command_queue->dequeue(cmd, data, true); + bool something_came = command_queue->dequeue(cmd, data, wait_ms); + if (!something_came) { + DEBUG1("No command received last %d seconds\n", wait_ms / 1000); + cmd = CMD_NOOP; + } QJsonObject data_obj = QJsonDocument::fromJson(data.c_str()).object(); switch(cmd) { + case CMD_NOOP: { + if (quit) { + DEBUG1("Alive timeout, quit = %d\n", quit); + webview_handler->closeAllWindows(); + DEBUG0("Closed all windows\n"); + webview_handler->rktQuit(); + DEBUG0("Quit application\n"); + } + } + break; case CMD_QUIT: { INFO0("Got quit message\n"); webview_handler->rktQuit(); @@ -341,4 +394,31 @@ void Handler::run() } } } + DEBUG0("Exiting handler thread\n"); +} + +void Alive::run() +{ + int wait_ms = 10 * 1000; + int ping_no; + std::string data; + bool go_on = true; + while (go_on) { + bool something_came = alive_queue->dequeue(ping_no, data, wait_ms); + if (!something_came) { + ERROR0("No alive message received, stopping alive loop and quitting rktwebview_prg\n"); + handler->quit = true; + handler->command_queue->enqueue(CMD_NOOP); + alive_timeout = true; + go_on = false; + } else { + if (ping_no == CMD_ALIVE_QUIT) { + DEBUG0("Got Quit ping for alive thread\n"); + go_on = false; + } else { + DEBUG1("Got alive ping: %d\n", ping_no); + } + } + } + DEBUG0("Exiting alive thread\n"); } diff --git a/rkt_protocol.h b/rkt_protocol.h index d9f40b0..08e1cf3 100644 --- a/rkt_protocol.h +++ b/rkt_protocol.h @@ -30,7 +30,9 @@ #define CMD_INFO 27 // arguments: none #define CMD_SET_ICON 28 // arguments: wv: int, icon:string (absolute filename to icon in png/jpg/svg) +#define CMD_NOOP 33621 #define RESULT_QUIT 36379 +#define CMD_ALIVE_QUIT -38383 #endif // RKT_PROTOCOL_H diff --git a/rktwebview.cpp b/rktwebview.cpp index 482823a..ebad7d9 100644 --- a/rktwebview.cpp +++ b/rktwebview.cpp @@ -28,6 +28,7 @@ #define COMMAND_SLOT 1 #define COMMAND_RESULT_SLOT 2 #define EVENT_SLOT 3 +#define ALIVE_SLOT 4 //#define DEBUG @@ -42,6 +43,7 @@ typedef struct { ShmQueue *command_queue; ShmQueue *command_result_queue; ShmQueue *event_queue; + ShmQueue *alive_queue; #ifdef _WIN32 HANDLE rkt_webview_prg_pid; #else @@ -59,6 +61,8 @@ typedef struct { std::thread *guard_thread; bool guard_go_on; + bool alive_go_on; + std::thread *alive_thread; } Handle_t; Handle_t *handler = nullptr; @@ -96,11 +100,14 @@ bool runRktWebview(Handle_t *handler) char command_slot[10]; char command_result_slot[10]; char event_slot[10]; + char alive_slot[10]; sprintf(shm_size_str, "%d", static_cast(handler->shm_size)); sprintf(command_slot, "%d", COMMAND_SLOT); sprintf(command_result_slot, "%d", COMMAND_RESULT_SLOT); sprintf(event_slot, "%d", EVENT_SLOT); + sprintf(alive_slot, "%d", ALIVE_SLOT); + // run rktwebview_prg using the environment variable RKT_WEBVIEW_PRG #ifdef _WIN32 @@ -137,7 +144,7 @@ bool runRktWebview(Handle_t *handler) DWORD flags = CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS; - std::string cmdargs = std::string("") + handler->name + " " + shm_size_str + " " + command_slot + " " + command_result_slot + " " + event_slot; + std::string cmdargs = std::string("") + handler->name + " " + shm_size_str + " " + command_slot + " " + command_result_slot + " " + event_slot + " " + alive_slot; std::string exe = std::string(rkt_webview_prg_path); std::string dir = basedir(exe); @@ -178,13 +185,16 @@ bool runRktWebview(Handle_t *handler) ///////////////////////////////////////////////////////////////////// #define EVT_GUARD_STOP -93273 +#define WAIT_ON_EVENT_MS (10 * 1000) +#define ALIVE_MESSAGE_INTERVAL_S 5 // Should be smaller than 10 seconds +#define MAX_WAIT_RESULT (10 * 1000) // Maximum wait in milliseconds for a result void rkt_evt_guard(void) { while(handler->guard_go_on) { int wv; std::string data; - if (handler->event_queue->dequeue(wv, data, true)) { + if (handler->event_queue->dequeue(wv, data, WAIT_ON_EVENT_MS)) { if (wv != EVT_GUARD_STOP) { handler->evt_queue->enqueue(wv, data); handler->evt_cb(1); @@ -193,6 +203,27 @@ void rkt_evt_guard(void) } } + +void queue_alive_message() +{ + int seconds = 0; + int ping = 0; + DEBUG0("Starting queue_alive thread\n"); + while(handler->alive_go_on) { + std::this_thread::sleep_for(std::chrono::seconds(1)); // one second + seconds += 1; + //DEBUG2("seconds = %d, %d\n", seconds, ALIVE_MESSAGE_INTERVAL_S); + if (seconds >= ALIVE_MESSAGE_INTERVAL_S && handler->alive_go_on) { + seconds = 0; + ++ping; + DEBUG1("Pinging with %d\n", ping); + handler->alive_queue->enqueue(ping); + if (ping > 1000000000) { ping = 0; } + } + } + handler->alive_queue->enqueue(CMD_ALIVE_QUIT); +} + void rkt_webview_cleanup() { if (handler != nullptr) { @@ -212,15 +243,22 @@ void rkt_webview_cleanup() handler->guard_thread = nullptr; } + handler->alive_go_on = false; + handler->alive_thread->join(); + delete handler->alive_thread; + handler->alive_thread = nullptr; + INFO0("Sending quit message\n"); handler->command_queue->enqueue(CMD_QUIT); INFO0("Message sent\n"); bool stopped = false; while(!stopped) { - int cmd; + int cmd = -1; std::string s; INFO0("Getting result of quit message\n"); - handler->command_result_queue->dequeue(cmd, s, true); + if (!handler->command_result_queue->dequeue(cmd, s, MAX_WAIT_RESULT)) { + ERROR0("Other side has probably stopped working, no result on quit message\n"); + } INFO1("got %d\n", cmd); if (cmd == RESULT_QUIT) { stopped = true; @@ -228,16 +266,23 @@ void rkt_webview_cleanup() } } + // Cleanup shared memory delete handler->event_queue; delete handler->command_result_queue; delete handler->command_queue; + delete handler->alive_queue; delete handler->shm; + + // Cleanup Memory Queue delete handler->evt_queue; + + // Cleanup Handler delete handler; handler = nullptr; } } + void rkt_webview_init() { if (handler == nullptr) { @@ -269,6 +314,8 @@ void rkt_webview_init() handler->command_result_queue = new ShmQueue(handler->shm, COMMAND_RESULT_SLOT, true); handler->event_queue = new ShmQueue(handler->shm, EVENT_SLOT, true); + handler->alive_queue = new ShmQueue(handler->shm, ALIVE_SLOT, true); + // Start rktwebview_prg application with the right information #ifndef DEBUG handler->rkt_webview_prg_started = runRktWebview(handler); @@ -278,6 +325,10 @@ void rkt_webview_init() handler->evt_cb = nullptr; handler->evt_queue = new MemQueue(); handler->guard_go_on = false; + + handler->alive_go_on = true; + handler->alive_thread = new std::thread(queue_alive_message); + } else { handler->function_calls++; } @@ -339,7 +390,7 @@ rkt_wv_context_t rkt_webview_new_context(const char *boilerplate_js, const char int result; std::string json_result; - handler->command_result_queue->dequeue(result, json_result, true); + while (!handler->command_result_queue->dequeue(result, json_result, MAX_WAIT_RESULT)) {} return result; } @@ -356,7 +407,7 @@ int rkt_webview_create(rkt_wv_context_t context, rktwebview_t parent) int result; std::string json_result; - handler->command_result_queue->dequeue(result, json_result, true); + while (!handler->command_result_queue->dequeue(result, json_result, MAX_WAIT_RESULT)) {} return result; } @@ -383,7 +434,7 @@ void rkt_webview_close(rktwebview_t wv) handler->command_queue->enqueue(cmd, j.dump()); \ int result; \ std::string json_result; \ - handler->command_result_queue->dequeue(result, json_result, true); \ + while (!handler->command_result_queue->dequeue(result, json_result, MAX_WAIT_RESULT)) {} \ result_t r = static_cast(result); \ return r; @@ -427,7 +478,7 @@ rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js) int result; std::string json_result; - handler->command_result_queue->dequeue(result, json_result, true); + while (!handler->command_result_queue->dequeue(result, json_result, MAX_WAIT_RESULT)) {} rkt_data_t *r = new rkt_data_t(); r->kind = rkt_data_kind_t::js_result; @@ -501,8 +552,9 @@ result_t rkt_webview_show_normal(rktwebview_t w) void rkt_webview_set_loglevel(rkt_webview_loglevel_t l) { + setLogLevel(l); // library side auto f = [l]() { - CMDRES0(CMD_SET_LOGLEVEL, static_cast(l)); + CMDRES0(CMD_SET_LOGLEVEL, static_cast(l)); // webview process side }; f(); } @@ -605,7 +657,7 @@ rkt_data_t *rkt_webview_get_event() } else { int wv; std::string data; - if (handler->event_queue->dequeue(wv, data, false)) { + if (handler->event_queue->dequeue(wv, data, 0)) { //fprintf(stderr, "got event %d %s\n", wv, data.c_str()); rkt_data_t *d = reinterpret_cast(malloc(sizeof(rkt_data_t))); d->kind = rkt_data_kind_t::event; @@ -657,7 +709,7 @@ rkt_data_t *rkt_webview_info() int open_windows_result; std::string none; - handler->command_result_queue->dequeue(open_windows_result, none, true); + while (!handler->command_result_queue->dequeue(open_windows_result, none, MAX_WAIT_RESULT)) {} d->data.metrics.open_windows = open_windows_result; d->data.metrics.function_calls = handler->function_calls; diff --git a/rktwebview_qt.cpp b/rktwebview_qt.cpp index f19d605..78e182a 100644 --- a/rktwebview_qt.cpp +++ b/rktwebview_qt.cpp @@ -473,6 +473,16 @@ int Rktwebview_qt::nextHandle() return h; } +void Rktwebview_qt::closeAllWindows() +{ + auto wvs = _views.keys(); + int i; + for(i = 0; i < wvs.size(); i++) { + int wv = wvs[i]; + rktWebViewClose(wv); + } +} + rkt_wv_context_t Rktwebview_qt::newContext(const QString &boilerplate_js, bool has_pem, const QString &optional_server_cert_pem) { Command c(COMMAND_NEW_CONTEXT); diff --git a/rktwebview_qt.h b/rktwebview_qt.h index 11f6eb0..0069a0e 100644 --- a/rktwebview_qt.h +++ b/rktwebview_qt.h @@ -77,6 +77,9 @@ public: public: int nextHandle(); +public: + void closeAllWindows(); + public: rkt_wv_context_t newContext(const QString &boilerplate_js, bool has_pem, const QString &optional_server_cert_pem); int rktWebViewCreate(rkt_wv_context_t context, rktwebview_t parent, event_cb_t js_evt_cb); diff --git a/rktwebview_test.cpp b/rktwebview_test.cpp index 030ec62..ff7e2c5 100644 --- a/rktwebview_test.cpp +++ b/rktwebview_test.cpp @@ -36,6 +36,8 @@ int main(int argc, char *argv[]) #endif int context = rkt_webview_new_context("", nullptr); + rkt_webview_set_loglevel(rkt_webview_loglevel_t::log_debug); + rkt_webview_register_evt_callback(evt_cb); int wv = rkt_webview_create(context, 0); @@ -50,6 +52,7 @@ int main(int argc, char *argv[]) rkt_webview_set_url(wv, "https://wikipedia.org"); d = rkt_webview_info(); + fprintf(stderr, "%s\n", d->data.metrics.log_file); rkt_webview_free_data(d); while(rkt_webview_events_waiting() > 0) { @@ -57,7 +60,7 @@ int main(int argc, char *argv[]) rkt_webview_free_data(d); } #ifdef _WIN32 - Sleep(10); + Sleep(30000); #else sleep(10); #endif diff --git a/shm.cpp b/shm.cpp index d4a6d5d..7ca975b 100644 --- a/shm.cpp +++ b/shm.cpp @@ -211,6 +211,11 @@ public: return new ShmSem(name, owner); } +public: + void takeOwnership() { + _owner = true; + } + public: ShmApiBase(const char *name, size_t size, bool owner) { char *buf = reinterpret_cast(malloc(strlen(name) + 50)); @@ -520,11 +525,15 @@ public: ReleaseSemaphore(_sem, 1, NULL); } - void wait() { - DWORD r = WaitForSingleObject(_sem, INFINITE); + bool wait(int ms) { + DWORD r = WaitForSingleObject(_sem, ms); if (r != WAIT_OBJECT_0) { - ERROR1("sem_wait error: %ld\n", r); + if (r != WAIT_TIMEOUT) { + ERROR1("sem_wait error: %ld\n", r); + } + return false; } + return true; } bool trywait() { @@ -535,6 +544,10 @@ public: return r == WAIT_OBJECT_0; } + void takeOwnership() { + _owner = true; + } + public: ShmSemApi(const char *n, bool owner) { _name = _strdup(n); @@ -610,6 +623,11 @@ void *Shm::ref(int place) return _shm_api->ref(place); } +void Shm::takeOwnership() +{ + _shm_api->takeOwnership(); +} + void Shm::info(int &usage, int &free_depth, int &free_size, int &item_depth, int &item_size, double &usage_factor) { _shm_api->info(usage, free_depth, free_size, item_depth, item_size, usage_factor); @@ -635,14 +653,19 @@ void ShmSem::post() { _api->post(); } -void ShmSem::wait() { - _api->wait(); +bool ShmSem::wait(int ms) { + return _api->wait(ms); } bool ShmSem::trywait() { return _api->trywait(); } +void ShmSem::takeOwnership() +{ + _api->takeOwnership(); +} + ShmSem::ShmSem(const char *n, bool owner) { _api = new ShmSemApi(n, owner); } diff --git a/shm.h b/shm.h index 32b9fc9..d1cfd42 100644 --- a/shm.h +++ b/shm.h @@ -17,8 +17,9 @@ private: public: void post(); - void wait(); + bool wait(int ms); bool trywait(); + void takeOwnership(); public: ShmSem(const char *n, bool owner); @@ -52,6 +53,9 @@ public: public: void *ref(int place); +public: + void takeOwnership(); + public: void info(int &usage, int &free_depth, int &free_size, int &item_depth, int &item_size, double &usage_factor); diff --git a/shmqueue.cpp b/shmqueue.cpp index e0e255c..514d418 100644 --- a/shmqueue.cpp +++ b/shmqueue.cpp @@ -23,6 +23,11 @@ ShmQueue::ShmQueue(Shm *shm, ShmSlot slot, bool owner) _queue_sem = shm->sem(buf, owner); } +void ShmQueue::takeOwnership() +{ + _queue_sem->takeOwnership(); +} + ShmQueue::~ShmQueue() { if (_owner) { @@ -83,15 +88,14 @@ void ShmQueue::enqueue(int cmd) enqueue(cmd, s); } -bool ShmQueue::dequeue(int &cmd, std::string &json_data, bool wait) + +bool ShmQueue::dequeue(int &cmd, std::string &json_data, int wait_ms) { - if (wait) { - _queue_sem->wait(); - } else { - if (!_queue_sem->trywait()) { - return false; - } + bool something_came = _queue_sem->wait(wait_ms); + if (!something_came) { + return false; } + _shm->lock(); ShmPlace item_place = _queue->first; diff --git a/shmqueue.h b/shmqueue.h index 61176fe..082544e 100644 --- a/shmqueue.h +++ b/shmqueue.h @@ -37,10 +37,13 @@ public: int depth(); public: - bool dequeue(int &cmd, std::string &json_data, bool wait = false); + bool dequeue(int &cmd, std::string &json_data, int wait_ms = 0); void enqueue(int cmd, const std::string &json_data); void enqueue(int cmd); +public: + void takeOwnership(); + public: ShmQueue(Shm *shm, ShmSlot slot, bool owner); ~ShmQueue();