aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AsyncEventLoop.cpp94
-rw-r--r--src/AsyncEventLoop.hpp40
-rw-r--r--src/LuaRuntime.cpp75
-rw-r--r--src/LuaRuntime.hpp8
-rw-r--r--src/utils.hpp2
5 files changed, 184 insertions, 35 deletions
diff --git a/src/AsyncEventLoop.cpp b/src/AsyncEventLoop.cpp
new file mode 100644
index 0000000..360a45e
--- /dev/null
+++ b/src/AsyncEventLoop.cpp
@@ -0,0 +1,94 @@
+#include <QtCore>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <uv.h>
+
+#include "AsyncEventLoop.hpp"
+
+AsyncEventLoop::AsyncEventLoop() {
+ // UV Loop
+ loop = (uv_loop_t *)malloc(sizeof(uv_loop_t));
+ uv_loop_init(loop);
+
+ uv_async_init(loop, &asyncHandle, AsyncEventLoop::asyncHandleCallback);
+ asyncHandle.data = this;
+
+ loopThread = std::thread(&AsyncEventLoop::runLoop, this);
+
+ // Wait for thread to start
+ while (!isLoopRunning)
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+}
+
+void AsyncEventLoop::processTasks() {
+ std::queue<std::function<void()>> tasks;
+
+ {
+ std::lock_guard<std::mutex> lock(tasksQueueMutex);
+ tasksQueue.swap(tasks);
+ }
+
+ while (!tasks.empty()) {
+ auto task = std::move(tasks.front());
+ tasks.pop();
+ task();
+ }
+}
+
+void AsyncEventLoop::runLoop() {
+ isLoopRunning = true;
+ while (isLoopRunning) {
+ int result = uv_run(loop, UV_RUN_ONCE);
+ if (result == 0)
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ uv_run(loop, UV_RUN_NOWAIT);
+}
+
+AsyncEventLoop::~AsyncEventLoop() {
+ if (!isLoopRunning)
+ return;
+ isLoopRunning = false;
+
+ // Clear the tasks queue
+ {
+ std::lock_guard<std::mutex> lock(tasksQueueMutex);
+ std::queue<std::function<void()>>().swap(tasksQueue);
+ }
+
+ // Wake it up. Stab it to death.
+ uv_async_send(&asyncHandle);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ uv_stop(loop);
+
+ // Close all handles
+ AsyncEventLoop::closeHandle((uv_handle_t *)&asyncHandle);
+ uv_walk(loop, AsyncEventLoop::closeHandle, nullptr);
+ while (uv_run(loop, UV_RUN_ONCE) != 0)
+ ;
+ // TODO: Fix pending handler case (setTimeout(100) wait(20) close() -> error)
+
+ qDebug() << "join start";
+ if (loopThread.joinable())
+ loopThread.join();
+ qDebug() << "join done";
+
+ while (uv_loop_close(loop) == UV_EBUSY) {
+ uv_walk(loop, AsyncEventLoop::closeHandle, nullptr);
+ uv_run(loop, UV_RUN_NOWAIT);
+ }
+ free(loop);
+ loop = nullptr;
+}
+
+void AsyncEventLoop::asyncHandleCallback(uv_async_t *handle) {
+ auto *runtime = static_cast<AsyncEventLoop *>(handle->data);
+ runtime->processTasks();
+}
+
+void AsyncEventLoop::closeHandle(uv_handle_t *handle, void *arg) {
+ if (!uv_is_closing(handle)) {
+ uv_close(handle, [](uv_handle_t *h) { h->data = nullptr; });
+ }
+}
diff --git a/src/AsyncEventLoop.hpp b/src/AsyncEventLoop.hpp
new file mode 100644
index 0000000..ebe2cad
--- /dev/null
+++ b/src/AsyncEventLoop.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <queue>
+#include <thread>
+#include <uv.h>
+
+#include "utils.hpp"
+
+class AsyncEventLoop {
+public:
+ AsyncEventLoop();
+ ~AsyncEventLoop();
+
+ DEFINE_GETTER(getUVLoop, loop)
+
+ template <typename F> void queueTask(F &&task) {
+ {
+ std::lock_guard<std::mutex> lock(tasksQueueMutex);
+ tasksQueue.push(std::forward<F>(task));
+ }
+ uv_async_send(&asyncHandle);
+ }
+
+protected:
+ void runLoop();
+ void processTasks();
+ static void asyncHandleCallback(uv_async_t *handle);
+ static void closeHandle(uv_handle_t *handle, void *arg = nullptr);
+
+private:
+ uv_loop_t *loop;
+ std::thread loopThread;
+ uv_async_t asyncHandle;
+ std::atomic<bool> isLoopRunning = false;
+ std::queue<std::function<void()>> tasksQueue;
+ std::mutex tasksQueueMutex;
+};
diff --git a/src/LuaRuntime.cpp b/src/LuaRuntime.cpp
index d89f7dc..e61fbf5 100644
--- a/src/LuaRuntime.cpp
+++ b/src/LuaRuntime.cpp
@@ -4,16 +4,16 @@ extern "C" {
#include <luv.h>
}
+#include "AsyncEventLoop.hpp"
#include "LuaRuntime.hpp"
+const char *uv_global_name = "uv";
+const char *web_global_name = "web";
+
LuaRuntime::LuaRuntime() {
state = luaL_newstate();
luaL_openlibs(state);
- // Load `uv` (luv)
- luaopen_luv(state);
- lua_setglobal(state, "uv");
-
// Load `web`
luaL_Reg weblib[] = {
{"open", &LuaRuntime::lua_onUrlOpen},
@@ -21,39 +21,42 @@ LuaRuntime::LuaRuntime() {
{NULL, NULL},
};
luaL_newlib(state, weblib);
- lua_setglobal(state, "web");
+ lua_setglobal(state, web_global_name);
+}
+
+void LuaRuntime::startEventLoop() {
+ if (eventLoop != nullptr)
+ stopEventLoop();
+
+ // Init event loop
+ eventLoop = new AsyncEventLoop();
- // auto pp = R"(
- // print('Hello -- ');
- // local h = uv.fs_open('foobar', 'w', tonumber('644', 8), function(err, h)
- // assert(not err, err);
- // print('inside');
- // print(h);
- // print(err);
- // uv.fs_write(h, 'Hello world');
- // end);
- // print(h);
- // uv.run();
- // print('-- end');
- // )";
- // auto pp = R"(
- // print('Hello -- ');
- // local t = uv.new_timer();
- // uv.timer_start(t, 4000, 0, function()
- // print('after time')
- // end);
- // uv.run();
- // print('-- end');
- // )";
- // if (luaL_dostring(state, pp)) {
- // qDebug() << "Lua Error: " << lua_tostring(state, -1);
- // } else {
- // qDebug() << "succ";
- // }
+ // Load `uv` (luv)
+ luv_set_loop(state, eventLoop->getUVLoop());
+ luaopen_luv(state);
+ lua_setglobal(state, uv_global_name);
+}
+
+void LuaRuntime::stopEventLoop() {
+ if (eventLoop == nullptr)
+ return;
+ delete eventLoop;
+ eventLoop = nullptr;
+
+ // Clear the uv global
+ lua_pushnil(state);
+ lua_setglobal(state, uv_global_name);
+ lua_gc(state, LUA_GCCOLLECT, 0);
}
void LuaRuntime::evaluate(QString code) {
- luaL_dostring(state, code.toStdString().c_str());
+ eventLoop->queueTask([this, code]() {
+ if (luaL_dostring(state, code.toStdString().c_str())) {
+ qDebug() << "Lua Error: " << lua_tostring(state, -1);
+ lua_pop(state, 1);
+ } else
+ qDebug() << "done";
+ });
}
int LuaRuntime::lua_onUrlOpen(lua_State *state) {
@@ -70,4 +73,8 @@ int LuaRuntime::lua_onUrlTabOpen(lua_State *state) {
return 1;
}
-LuaRuntime::~LuaRuntime() { lua_close(state); }
+LuaRuntime::~LuaRuntime() {
+ stopEventLoop();
+ lua_close(state);
+ state = nullptr;
+}
diff --git a/src/LuaRuntime.hpp b/src/LuaRuntime.hpp
index 5bfc82c..443dec5 100644
--- a/src/LuaRuntime.hpp
+++ b/src/LuaRuntime.hpp
@@ -1,7 +1,9 @@
#pragma once
+
#include <QtCore>
#include <lua.hpp>
+#include "AsyncEventLoop.hpp"
#include "widgets/WebViewStack.hpp"
class LuaRuntime : public QObject {
@@ -15,6 +17,11 @@ public:
void evaluate(QString code);
+ void stopEventLoop();
+ void startEventLoop();
+
+ DELEGATE(eventLoop, queueTask, queueTask)
+
signals:
void urlOpened(QString url, OpenType openType);
@@ -26,4 +33,5 @@ protected:
private:
lua_State *state;
+ AsyncEventLoop *eventLoop = nullptr;
};
diff --git a/src/utils.hpp b/src/utils.hpp
index 0650ac2..fbe5597 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -8,4 +8,4 @@
template <typename Arg> void METHOD(Arg val) { PROPERTY = val; }
#define DEFINE_GETTER(METHOD, EXPR) \
- template <typename Arg> decltype(auto) METHOD() { return EXPR; }
+ decltype(auto) METHOD() { return EXPR; }