aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-02-14 10:39:01 -0700
committerJosh Rahm <joshuarahm@gmail.com>2024-02-14 10:39:01 -0700
commit020bc92281ae584dc97aa30e1a1ad2a5373335fd (patch)
tree1eb1036d55e116fc055feee6e9502b8445d89872
parent10a5272eaca6407982b3707027ea8704f3484377 (diff)
downloadwetterhorn-020bc92281ae584dc97aa30e1a1ad2a5373335fd.tar.gz
wetterhorn-020bc92281ae584dc97aa30e1a1ad2a5373335fd.tar.bz2
wetterhorn-020bc92281ae584dc97aa30e1a1ad2a5373335fd.zip
Framework for plugin to call into harness.
This is done by passing an interface to the plugin from the harness. The plugin can then request the harness do some things (such as reload), and the harness will do that.
-rw-r--r--harness/include/foreign_intf.h3
-rw-r--r--harness/include/plugin.h23
-rw-r--r--harness/src/main.c5
-rw-r--r--harness/src/plugin.c123
-rw-r--r--harness/src/wl.c25
-rw-r--r--harness/tools/genbuild.pl7
-rw-r--r--harness/tools/genintf.pl6
-rw-r--r--package.yaml2
-rw-r--r--src/Wetterhorn/Core.hs13
-rw-r--r--src/Wetterhorn/Core/ForeignInterface.hs16
-rw-r--r--src/Wetterhorn/FFI.hs18
-rw-r--r--src/harness_adapter.c13
12 files changed, 190 insertions, 64 deletions
diff --git a/harness/include/foreign_intf.h b/harness/include/foreign_intf.h
index 3a4b2a3..fc079e2 100644
--- a/harness/include/foreign_intf.h
+++ b/harness/include/foreign_intf.h
@@ -18,6 +18,9 @@ typedef struct FOREIGN_INTERFACE {
/* Requests the harness hot reload the current plugin. */
EXPORT(void (*request_hot_reload)(ctx_t ctx));
+ /* Requests the harness hot reload the current plugin. */
+ EXPORT(void (*do_log)(ctx_t ctx, const char* str));
+
} foreign_interface_t;
#undef EXPORT
diff --git a/harness/include/plugin.h b/harness/include/plugin.h
index db56845..1e4a161 100644
--- a/harness/include/plugin.h
+++ b/harness/include/plugin.h
@@ -7,7 +7,7 @@
#include <stdint.h>
#include <stdlib.h>
-#include "foreign_intf.h"
+#include <foreign_intf.h>
/*
* Marker macro to define what functions should be exported. This generates the
@@ -15,6 +15,12 @@
*/
#define EXPORT(a) a
+#define EXPORT_INCLUDE(a)
+
+EXPORT_INCLUDE(<foreign_intf.h>)
+
+#define MAX_QUEUED_ACTIONS 8
+
typedef void *dlhandle_t;
/* Opaque state for a plugin. Not to be touched by the harness (not that it
@@ -25,7 +31,9 @@ struct PLUGIN;
/* This structure represents an action requested by the plugin for the harness.
*/
typedef struct {
- int (*action)(struct PLUGIN *requester);
+ int (*action)(struct PLUGIN *requester, void* arg);
+ void (*arg_dtor)(void* arg);
+ void* arg;
} requested_action_t;
/*
@@ -62,6 +70,9 @@ typedef struct PLUGIN {
* outcomes. */
pthread_mutex_t lock;
+ /** Set to not-zero if this plugin is initialized, otherwise set to zero. */
+ int initialized;
+
/* The handle to the shared library. */
dlhandle_t library_handle;
@@ -73,7 +84,7 @@ typedef struct PLUGIN {
/** Intializes the plugin with the given argc/argv. This is the first thing
* called on the plugin and is called immediately after the library is loaded.
*/
- EXPORT(void (*plugin_load)(int argc, char **argv, foreign_interface_t *intf));
+ EXPORT(void (*plugin_load)(int argc, char** argv, foreign_interface_t *intf));
/* Start the plugin with the marshalled state from the previous plugin.
*
@@ -122,10 +133,11 @@ typedef struct PLUGIN {
* allowed at one time. That should be plenty. The actions should be flushed
* after each call to a handler anyway. */
size_t n_requested_actions;
- requested_action_t requested_actions[8];
+ requested_action_t requested_actions[MAX_QUEUED_ACTIONS];
} plugin_t;
#undef EXPORT
+#undef EXPORT_INCLUDE
/* Reloads the plugin. This tears down the existing plugin, marshals the state
* for it and reloads it.
@@ -141,6 +153,9 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
*/
int plugin_hot_reload_same_state(plugin_t *plugin);
+/* Starts a plugin in a cold state. Called after load_plugin_from_file. */
+void plugin_cold_start(plugin_t* plugin);
+
/* Reads a plugin from a filename. */
int load_plugin_from_file(int argc, char **argv, const char *filename,
plugin_t *plugin);
diff --git a/harness/src/main.c b/harness/src/main.c
index 995d11e..05b06ff 100644
--- a/harness/src/main.c
+++ b/harness/src/main.c
@@ -65,12 +65,11 @@ int main_(int argc, char **argv) {
}
plugin_t plugin;
- if (load_plugin_from_file(argv[1], &plugin)) {
+ if (load_plugin_from_file(argc, argv, argv[1], &plugin)) {
fprintf(stderr, "Exiting due to other failures.\n");
return 1;
}
- plugin.plugin_load(argc, argv);
- plugin.state = plugin.plugin_cold_start();
+ plugin_cold_start(&plugin);
run_plugin(argc, argv, &plugin);
diff --git a/harness/src/plugin.c b/harness/src/plugin.c
index 808088f..f1cc361 100644
--- a/harness/src/plugin.c
+++ b/harness/src/plugin.c
@@ -3,6 +3,7 @@
#include <ctype.h>
#include <dlfcn.h>
+#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -45,31 +46,68 @@ static void shx(uint8_t *state, uint32_t sz)
int load_plugin_from_dl_(dlhandle_t dl, plugin_t *plug);
+static void lock(plugin_t *plugin)
+{
+ pthread_mutex_lock(&plugin->lock);
+};
+
+static void unlock(plugin_t *plugin)
+{
+ pthread_mutex_unlock(&plugin->lock);
+};
+
+static int plugin_hot_reload_same_state_action_(plugin_t *plugin, void* ignore)
+{
+ return plugin_hot_reload_same_state(plugin);
+}
+
void do_request_hot_reload(void *plugv)
{
plugin_t *plugin = plugv;
- if (plugin->n_requested_actions < 8) {
- plugin->requested_actions[plugin->n_requested_actions++].action =
- plugin_hot_reload_same_state;
+ size_t n = plugin->n_requested_actions++;
+ if (n < 8) {
+ plugin->requested_actions[n].action = plugin_hot_reload_same_state_action_;
+ plugin->requested_actions[n].arg_dtor = NULL;
}
}
-int load_plugin_from_file(int argc, char **argv, const char *filename,
- plugin_t *plugin)
+static int plugin_do_log(plugin_t* plugin, void* chrs)
+{
+ char* str = chrs;
+ puts(str);
+ return 0;
+}
+
+void do_request_log(void *plugv, const char* str)
+{
+ plugin_t *plugin = plugv;
+
+ size_t n = plugin->n_requested_actions++;
+ if (n < 8) {
+ plugin->requested_actions[n].action = plugin_do_log;
+ plugin->requested_actions[n].arg = strdup(str);
+ plugin->requested_actions[n].arg_dtor = free;
+ }
+}
+
+static int load_plugin_from_file_(int argc, char **argv, const char *filename,
+ plugin_t *plugin)
{
dlhandle_t lib = dlopen(filename, RTLD_LAZY);
+ int ec = 0;
if (!lib) {
fprintf(stderr, "Failed to open library: %s: %s\n", filename, dlerror());
- return 1;
+ ec = 1;
+ goto end;
}
printf("Loading file.\n");
- int ec = load_plugin_from_dl_(lib, plugin);
+ ec = load_plugin_from_dl_(lib, plugin);
if (ec) {
- return ec;
+ goto end;
}
strncpy(plugin->filename, filename, sizeof(plugin->filename));
@@ -78,13 +116,43 @@ int load_plugin_from_file(int argc, char **argv, const char *filename,
plugin->foreign_intf.ctx = plugin;
plugin->foreign_intf.request_hot_reload = do_request_hot_reload;
- return 0;
+ plugin->foreign_intf.do_log = do_request_log;
+
+ plugin->plugin_load(plugin->argc, plugin->argv, &plugin->foreign_intf);
+end:
+ return ec;
+}
+
+int load_plugin_from_file(int argc, char **argv, const char *filename,
+ plugin_t *plugin)
+{
+ memset(plugin, 0, sizeof(*plugin));
+
+ pthread_mutexattr_t attr;
+ if (pthread_mutexattr_init(&attr)) {
+ perror("pthread_mutexattr_init");
+ return 1;
+ }
+
+ if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) {
+ perror("pthread_mutexattr_settype");
+ return 1;
+ }
+
+ if (pthread_mutex_init(&plugin->lock, &attr)) {
+ pthread_mutexattr_destroy(&attr);
+ perror("pthread_mutexattr_init");
+ return 1;
+ }
+ pthread_mutexattr_destroy(&attr);
+ return load_plugin_from_file_(argc, argv, filename, plugin);
}
int plugin_hot_reload_same_state(plugin_t *plugin)
{
- return plugin_hot_reload(plugin->argc, plugin->argv, plugin->filename,
- plugin);
+ char filename_cpy[PATH_MAX];
+ strncpy(filename_cpy, plugin->filename, sizeof(filename_cpy));
+ return plugin_hot_reload(plugin->argc, plugin->argv, filename_cpy, plugin);
}
int plugin_hot_reload(int argc, char **argv, const char *filepath,
@@ -95,7 +163,7 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
uint8_t *marshalled_state = NULL;
printf("Hot Reloading %s\n", plugin->plugin_name);
- pthread_mutex_lock(&plugin->lock);
+ lock(plugin);
printf("Marshalling state ...\n");
marshalled_state = plugin->plugin_marshal_state(plugin->state, &sz);
@@ -107,9 +175,11 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
shx(marshalled_state, sz);
printf("Unloading old library handle.\n");
- dlclose(plugin->library_handle);
+ if (dlclose(plugin->library_handle)) {
+ printf("Could not close library handle: %s\n", dlerror());
+ }
- if ((ec = load_plugin_from_file(argc, argv, filepath, plugin))) {
+ if ((ec = load_plugin_from_file_(argc, argv, filepath, plugin))) {
goto fail;
}
@@ -120,15 +190,32 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
fail:
free(marshalled_state);
- pthread_mutex_unlock(&plugin->lock);
+ unlock(plugin);
return ec;
}
void plugin_run_requested_actions(plugin_t *plugin)
{
+ lock(plugin);
+ requested_action_t requested_actions[MAX_QUEUED_ACTIONS];
+ size_t n_requested_actions = plugin->n_requested_actions;
+ memcpy(&requested_actions, plugin->requested_actions,
+ sizeof(requested_actions));
+ plugin->n_requested_actions = 0;
+ unlock(plugin);
+
size_t i;
- for (i = 0; i < plugin->n_requested_actions; ++i) {
- plugin->requested_actions[i].action(plugin);
+ for (i = 0; i < n_requested_actions; ++i) {
+ requested_actions[i].action(plugin, requested_actions[i].arg);
+ if (requested_actions[i].arg_dtor) {
+ requested_actions[i].arg_dtor(requested_actions[i].arg);
+ }
}
- plugin->n_requested_actions = 0;
+}
+
+void plugin_cold_start(plugin_t *plugin)
+{
+ lock(plugin);
+ plugin->state = plugin->plugin_cold_start();
+ unlock(plugin);
}
diff --git a/harness/src/wl.c b/harness/src/wl.c
index 53932e7..474123a 100644
--- a/harness/src/wl.c
+++ b/harness/src/wl.c
@@ -22,6 +22,7 @@
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
+#include <wlr/xwayland.h>
#include <xkbcommon/xkbcommon.h>
// This macro is responsible for calling a handler on a plugin. This macro will
@@ -81,9 +82,6 @@ struct tinywl_server {
struct wl_list outputs;
struct wl_listener new_output;
- int plugin_argc;
- char **plugin_argv;
- const char *plugin_fpath;
plugin_t plugin;
};
@@ -189,8 +187,7 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym)
wl_display_terminate(server->wl_display);
break;
case XKB_KEY_F5:
- plugin_hot_reload(server->plugin_argc, server->plugin_argv,
- server->plugin_fpath, &server->plugin);
+ plugin_hot_reload_same_state(&server->plugin);
break;
case XKB_KEY_F1:
/* Cycle to the next view */
@@ -256,8 +253,12 @@ static void server_new_keyboard(struct tinywl_server *server,
/* We need to prepare an XKB keymap and assign it to the keyboard. This
* assumes the defaults (e.g. layout = "us"). */
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ struct xkb_rule_names names = (struct xkb_rule_names) { 0 };
+ names.layout = "jr";
+ names.variant = "jdvprk";
+ names.options = "numpad:mac";
struct xkb_keymap *keymap =
- xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
wlr_keyboard_set_keymap(device->keyboard, keymap);
xkb_keymap_unref(keymap);
@@ -908,17 +909,13 @@ int main(int argc, char *argv[])
struct tinywl_server server;
- if (load_plugin_from_file(plugin, &server.plugin)) {
+ if (load_plugin_from_file(argc, argv, plugin, &server.plugin)) {
fprintf(stderr, "Failed to read plugin from file.\n");
return 1;
}
// server.plugin.plugin_metaload(argc, argv);
- server.plugin.plugin_load(argc, argv);
- server.plugin.state = server.plugin.plugin_cold_start();
- server.plugin_fpath = plugin;
- server.plugin_argv = argv;
- server.plugin_argc = argc;
+ plugin_cold_start(&server.plugin);
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
@@ -941,7 +938,8 @@ int main(int argc, char *argv[])
* to dig your fingers in and play with their behavior if you want. Note that
* the clients cannot set the selection directly without compositor approval,
* see the handling of the request_set_selection event below.*/
- wlr_compositor_create(server.wl_display, server.renderer);
+ struct wlr_compositor* compositor;
+ compositor = wlr_compositor_create(server.wl_display, server.renderer);
wlr_data_device_manager_create(server.wl_display);
/* Creates an output layout, which a wlroots utility for working with an
@@ -1048,6 +1046,7 @@ int main(int argc, char *argv[])
* loop configuration to listen to libinput events, DRM events, generate
* frame events at the refresh rate, and so on. */
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
+ wlr_xwayland_create(server.wl_display, compositor, false);
wl_display_run(server.wl_display);
/* Once wl_display_run returns, we shut down the server. */
diff --git a/harness/tools/genbuild.pl b/harness/tools/genbuild.pl
index 1b1a005..7020e81 100644
--- a/harness/tools/genbuild.pl
+++ b/harness/tools/genbuild.pl
@@ -5,6 +5,7 @@ $comment="";
print "#include <stdio.h>\n";
print "#include <dlfcn.h>\n";
print "#include <pthread.h>\n";
+print "#include <string.h>\n";
print "#include \"plugin.h\"\n\n";
print "int load_plugin_from_dl_(dlhandle_t dl, plugin_t* plug)\n";
@@ -34,11 +35,5 @@ while (<>) {
$comment="";
}
}
-print "\n if (!ret) {\n";
-print " if(pthread_mutex_init(&plug->lock, NULL)) {\n";
-print " fprintf(stderr, \"Failed to initalize plugin lock\");\n";
-print " ret |= 1;\n";
-print " }\n";
-print " }\n";
print "\n return ret;\n";
print "}\n";
diff --git a/harness/tools/genintf.pl b/harness/tools/genintf.pl
index 6e9d477..6f53951 100644
--- a/harness/tools/genintf.pl
+++ b/harness/tools/genintf.pl
@@ -17,10 +17,14 @@ while (<>) {
$comment="$comment$_";
}
- if (/^\s*EXPORT\(\s*((?:\w|\s*\*\s*)+)\s*\(\*(\w+)\)\s*\((.*)\)\);/) {
+ if (/^\s*EXPORT_INCLUDE\((.*)\)/) {
+ print "#include $1\n";
+ } elsif (/^\s*EXPORT\(\s*((?:\w|\s*\*\s*)+)\s*\(\*(\w+)\)\s*\((.*)\)\);/) {
print "$comment";
print "$1 $2($3);\n\n";
$comment="";
+ } elsif (/^\s*EXPORT\((.*)\)/) {
+ print "$1\n";
}
}
print "#endif /* _PLUG_INTF */\n";
diff --git a/package.yaml b/package.yaml
index 7ecbbc1..1232b90 100644
--- a/package.yaml
+++ b/package.yaml
@@ -44,6 +44,7 @@ ghc-options:
- -Wpartial-fields
- -Wredundant-constraints
- -XTupleSections
+- -XViewPatterns
- -fPIC
executables:
@@ -60,6 +61,7 @@ executables:
- -g3
- -shared
- -Iharness/build/
+ - -Iharness/include/
tests:
wetterhorn-test:
diff --git a/src/Wetterhorn/Core.hs b/src/Wetterhorn/Core.hs
index d3b3f56..8d4e6b7 100644
--- a/src/Wetterhorn/Core.hs
+++ b/src/Wetterhorn/Core.hs
@@ -15,14 +15,16 @@ module Wetterhorn.Core
readWState,
defaultConfig,
requestHotReload,
+ ctxConfig,
)
where
import Control.Arrow (first)
import Control.Exception
+import Control.Monad (when)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as CH
-import Foreign (Ptr, StablePtr, Word32, newStablePtr, ptrToIntPtr)
+import Foreign (Ptr, StablePtr, Word32, newStablePtr, ptrToIntPtr, castForeignPtr)
import Numeric (showHex)
import Text.Printf
import Wetterhorn.Core.ForeignInterface (ForeignInterface)
@@ -41,6 +43,11 @@ requestHotReload = do
fi <- ctxForeignInterface <$> getWContext
wio $ ForeignInterface.requestHotReload fi
+requestLog :: String -> W ()
+requestLog str = do
+ fi <- ctxForeignInterface <$> getWContext
+ wio $ ForeignInterface.requestLog fi str
+
initWetterhorn :: WConfig -> IO Wetterhorn
initWetterhorn conf = do
foreignInterface <- ForeignInterface.getForeignInterface
@@ -64,7 +71,9 @@ defaultConfig =
WConfig
{ keybindingHandler = \sym -> do
i <- incrementState
- wio (printf "[%d] Got key %d\n" i sym),
+ wio (printf "[%d] Got key: %d\n" i sym)
+ when (sym == 111) requestHotReload
+ when (sym == 112) (requestLog "Hey daddy ths is a log statement.\n"),
surfaceHandler = \state ptr -> wio (printf "Surface %s is %s\n" (showHex (ptrToIntPtr ptr) "") (show state))
}
diff --git a/src/Wetterhorn/Core/ForeignInterface.hs b/src/Wetterhorn/Core/ForeignInterface.hs
index acd98d6..de4779b 100644
--- a/src/Wetterhorn/Core/ForeignInterface.hs
+++ b/src/Wetterhorn/Core/ForeignInterface.hs
@@ -6,18 +6,24 @@ where
import Data.Void (Void)
import Foreign (Ptr, Storable (peekByteOff, sizeOf))
+import Foreign.C.String
import GHC.Exts (FunPtr)
type CtxT = Ptr Void
type ForeignCall = CtxT -> IO ()
+type ForeignCallStr = CtxT -> CString -> IO ()
+
foreign import ccall "get_foreign_interface" foreignInterfacePtr :: IO (Ptr ())
foreign import ccall "dynamic" toForeignCall :: FunPtr ForeignCall -> ForeignCall
+foreign import ccall "dynamic" toForeignCallStr :: FunPtr ForeignCallStr -> ForeignCallStr
+
data ForeignInterface = ForeignInterface
- { requestHotReload :: IO ()
+ { requestHotReload :: IO (),
+ requestLog :: String -> IO ()
}
getForeignInterface :: IO ForeignInterface
@@ -26,11 +32,11 @@ getForeignInterface = do
ctx <- peekByteOff ptr 0
requestHotReloadFn <- peekByteOff ptr (sizeOf ctx)
-
- let _ = (ctx :: CtxT)
- let _ = (requestHotReloadFn :: FunPtr ForeignCall)
+ doLogFn <- peekByteOff ptr (sizeOf ctx + sizeOf requestHotReloadFn)
return $
ForeignInterface
- { requestHotReload = toForeignCall requestHotReloadFn ctx
+ { requestHotReload = toForeignCall requestHotReloadFn ctx,
+ requestLog = \str ->
+ withCString str $ \cs -> toForeignCallStr doLogFn ctx cs
}
diff --git a/src/Wetterhorn/FFI.hs b/src/Wetterhorn/FFI.hs
index 1d74248..3221903 100644
--- a/src/Wetterhorn/FFI.hs
+++ b/src/Wetterhorn/FFI.hs
@@ -21,27 +21,27 @@ import Wetterhorn.Core
runForeign :: (WConfig -> W ()) -> Wetterhorn -> IO Wetterhorn
runForeign fn stblptr = do
- (conf, st) <- deRefStablePtr stblptr
+ (ctx, st) <- deRefStablePtr stblptr
freeStablePtr stblptr
- (_, state') <- runW (fn conf) (conf, st)
- newStablePtr (conf, state')
+ (_, state') <- runW (fn $ ctxConfig ctx) (ctx, st)
+ newStablePtr (ctx, state')
runForeignWithReturn :: (Storable a) => (WConfig -> W a) -> Ptr a -> Wetterhorn -> IO Wetterhorn
runForeignWithReturn fn ptr stableptr = do
- (conf, st) <- deRefStablePtr stableptr
+ (ctx, st) <- deRefStablePtr stableptr
freeStablePtr stableptr
- (val, state') <- runW (fn conf) (conf, st)
+ (val, state') <- runW (fn $ ctxConfig ctx) (ctx, st)
poke ptr val
- newStablePtr (conf, state')
+ newStablePtr (ctx, state')
runForeignWithReturn2 :: (Storable a, Storable b) => (WConfig -> W (a, b)) -> Ptr a -> Ptr b -> Wetterhorn -> IO Wetterhorn
runForeignWithReturn2 fn ptrA ptrB stableptr = do
- (conf, st) <- deRefStablePtr stableptr
+ (ctx, st) <- deRefStablePtr stableptr
freeStablePtr stableptr
- ((vA, vB), state') <- runW (fn conf) (conf, st)
+ ((vA, vB), state') <- runW (fn $ ctxConfig ctx) (ctx, st)
poke ptrA vA
poke ptrB vB
- newStablePtr (conf, state')
+ newStablePtr (ctx, state')
-- | This function is the implementation of the "hotstart" mechanism. It gives a
-- pointer to the previously marshalled state and the length of that array and
diff --git a/src/harness_adapter.c b/src/harness_adapter.c
index aa45ce6..8585d7e 100644
--- a/src/harness_adapter.c
+++ b/src/harness_adapter.c
@@ -5,7 +5,7 @@
#include "HsFFI.h"
#include "plugin_interface.h"
-#include <stdio.h>
+#include <unistd.h>
const char *plugin_name = "Wetterhorn";
@@ -16,11 +16,18 @@ void* get_foreign_interface()
return foreign_interface;
}
-void plugin_load(int argc, char **argv, void* fintf) {
+void plugin_load(int argc, char **argv, foreign_interface_t* fintf) {
hs_init(&argc, &argv);
foreign_interface = fintf;
}
-void plugin_teardown(opqst_t st) { hs_exit(); }
+void plugin_teardown(opqst_t st) {
+ hs_exit();
+
+ // There's a race condition between when this shared library is unloaded and
+ // when the haskell runtime actually exits. Don't have a good solution for
+ // this at the moment, so just sleep for a second.
+ sleep(1);
+}
static const char msg[] =
"Wetterhorn Plugin v 0.01\n\n"