aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2024-02-13 17:53:30 -0700
committerJosh Rahm <rahm@google.com>2024-02-13 17:54:04 -0700
commit10a5272eaca6407982b3707027ea8704f3484377 (patch)
tree413ebfa9cffe703e596d33d7fd5e06249d7af477
parentd065af8c16bcb8ef54024c0f2082d827f83f37f9 (diff)
downloadwetterhorn-10a5272eaca6407982b3707027ea8704f3484377.tar.gz
wetterhorn-10a5272eaca6407982b3707027ea8704f3484377.tar.bz2
wetterhorn-10a5272eaca6407982b3707027ea8704f3484377.zip
WIP: Working on the foreign interface.
-rw-r--r--harness/include/foreign_intf.h25
-rw-r--r--harness/include/plugin.h48
-rw-r--r--harness/src/plugin.c50
-rw-r--r--harness/src/wl.c1
-rw-r--r--harness/tools/genbuild.pl3
-rw-r--r--src/Wetterhorn/Core.hs35
-rw-r--r--src/Wetterhorn/Core/ForeignInterface.hs36
-rw-r--r--src/harness_adapter.c12
8 files changed, 188 insertions, 22 deletions
diff --git a/harness/include/foreign_intf.h b/harness/include/foreign_intf.h
new file mode 100644
index 0000000..3a4b2a3
--- /dev/null
+++ b/harness/include/foreign_intf.h
@@ -0,0 +1,25 @@
+/* Contains a structure, which contains functions to back-call into
+ * the harness code. */
+
+#ifndef __FOREIGN_INTERFACE
+#define __FOREIGN_INTERFACE
+
+#define EXPORT(a) a
+
+typedef void *ctx_t;
+
+typedef struct FOREIGN_INTERFACE {
+ /* DO NOT ADD ANY UNEXPORTED VARIABLES HERE */
+
+ /* The context, which needs to be passed to each function. This context is
+ * opaque to the plugin and should not be changed. */
+ EXPORT(ctx_t ctx);
+
+ /* Requests the harness hot reload the current plugin. */
+ EXPORT(void (*request_hot_reload)(ctx_t ctx));
+
+} foreign_interface_t;
+
+#undef EXPORT
+
+#endif /* __FOREIGN_INTERFACE */
diff --git a/harness/include/plugin.h b/harness/include/plugin.h
index 615c3db..db56845 100644
--- a/harness/include/plugin.h
+++ b/harness/include/plugin.h
@@ -2,8 +2,12 @@
#define _PLUGIN_H_
#include <dlfcn.h>
+#include <linux/limits.h>
#include <pthread.h>
#include <stdint.h>
+#include <stdlib.h>
+
+#include "foreign_intf.h"
/*
* Marker macro to define what functions should be exported. This generates the
@@ -17,10 +21,29 @@ typedef void *dlhandle_t;
* really can be.) */
typedef void *opqst_t;
+struct PLUGIN;
+/* This structure represents an action requested by the plugin for the harness.
+ */
+typedef struct {
+ int (*action)(struct PLUGIN *requester);
+} requested_action_t;
+
/*
* Structure for the plugin.
*/
typedef struct PLUGIN {
+ /* The argc this plugin is loaded with. Typically the argc from main(). */
+ int argc;
+
+ /* The argv this plugin is loaded with. Typically the argv from main(). */
+ char **argv;
+
+ /* Filename the plugin is loaded from. */
+ char filename[PATH_MAX];
+
+ /* Interface to the harness that this plugin can use. */
+ foreign_interface_t foreign_intf;
+
/* Opaque state of this plugin. The state is usually some kind of pointer to
* the plugin state, but all the harness knows is the opaque state is a
* pointer-sized piece of data.
@@ -50,7 +73,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));
+ EXPORT(void (*plugin_load)(int argc, char **argv, foreign_interface_t *intf));
/* Start the plugin with the marshalled state from the previous plugin.
*
@@ -95,22 +118,33 @@ typedef struct PLUGIN {
EXPORT(opqst_t (*plugin_handle_surface_unmap)(void *surface, opqst_t));
EXPORT(opqst_t (*plugin_handle_surface_destroy)(void *surface, opqst_t));
+ /* List of requested actions by the plugin. Right now there is a maximum of 8
+ * 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];
} plugin_t;
-/** Loads a plugin from the dynamic library handle. Returns a non-zero error
- * code on error. */
-int load_plugin_from_dl(dlhandle_t library, plugin_t *out);
+#undef EXPORT
/* Reloads the plugin. This tears down the existing plugin, marshals the state
* for it and reloads it.
*
- * This function will call dlclose on the plugin's library handle if it is not
- * the same as 'library'.
+ * This function will call dlclose on the plugin's library handle.
*/
int plugin_hot_reload(int argc, char **argv, const char *filepath,
plugin_t *plugin);
+/*
+ * Like hot-reload, but uses the same parameters the plugin was originally
+ * loaded with.
+ */
+int plugin_hot_reload_same_state(plugin_t *plugin);
+
/* Reads a plugin from a filename. */
-int load_plugin_from_file(const char *filename, plugin_t *plugin);
+int load_plugin_from_file(int argc, char **argv, const char *filename,
+ plugin_t *plugin);
+
+void plugin_run_requested_actions(plugin_t *plugin);
#endif /* _PLUGIN_H_ */
diff --git a/harness/src/plugin.c b/harness/src/plugin.c
index bb32b02..808088f 100644
--- a/harness/src/plugin.c
+++ b/harness/src/plugin.c
@@ -1,9 +1,11 @@
#include "plugin.h"
+#include "foreign_intf.h"
#include <ctype.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
static void shx(uint8_t *state, uint32_t sz)
{
@@ -41,7 +43,20 @@ static void shx(uint8_t *state, uint32_t sz)
}
}
-int load_plugin_from_file(const char *filename, plugin_t *plugin)
+int load_plugin_from_dl_(dlhandle_t dl, plugin_t *plug);
+
+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;
+ }
+}
+
+int load_plugin_from_file(int argc, char **argv, const char *filename,
+ plugin_t *plugin)
{
dlhandle_t lib = dlopen(filename, RTLD_LAZY);
@@ -51,7 +66,25 @@ int load_plugin_from_file(const char *filename, plugin_t *plugin)
}
printf("Loading file.\n");
- return load_plugin_from_dl(lib, plugin);
+ int ec = load_plugin_from_dl_(lib, plugin);
+
+ if (ec) {
+ return ec;
+ }
+
+ strncpy(plugin->filename, filename, sizeof(plugin->filename));
+ plugin->argc = argc;
+ plugin->argv = argv;
+
+ plugin->foreign_intf.ctx = plugin;
+ plugin->foreign_intf.request_hot_reload = do_request_hot_reload;
+ return 0;
+}
+
+int plugin_hot_reload_same_state(plugin_t *plugin)
+{
+ return plugin_hot_reload(plugin->argc, plugin->argv, plugin->filename,
+ plugin);
}
int plugin_hot_reload(int argc, char **argv, const char *filepath,
@@ -76,12 +109,12 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
printf("Unloading old library handle.\n");
dlclose(plugin->library_handle);
- if ((ec = load_plugin_from_file(filepath, plugin))) {
+ if ((ec = load_plugin_from_file(argc, argv, filepath, plugin))) {
goto fail;
}
printf("Loading plugin ...\n");
- plugin->plugin_load(argc, argv);
+ plugin->plugin_load(plugin->argc, plugin->argv, &plugin->foreign_intf);
printf("Hot starting plugin ...\n");
plugin->state = plugin->plugin_hot_start(marshalled_state, sz);
@@ -90,3 +123,12 @@ fail:
pthread_mutex_unlock(&plugin->lock);
return ec;
}
+
+void plugin_run_requested_actions(plugin_t *plugin)
+{
+ size_t i;
+ for (i = 0; i < plugin->n_requested_actions; ++i) {
+ plugin->requested_actions[i].action(plugin);
+ }
+ plugin->n_requested_actions = 0;
+}
diff --git a/harness/src/wl.c b/harness/src/wl.c
index 7e89c21..53932e7 100644
--- a/harness/src/wl.c
+++ b/harness/src/wl.c
@@ -39,6 +39,7 @@
pthread_mutex_lock(&pl__->lock); \
pl__->state = pl__->member(__VA_ARGS__, pl__->state); \
pthread_mutex_unlock(&pl__->lock); \
+ plugin_run_requested_actions(pl__); \
} while (0)
/* For brevity's sake, struct members are annotated where they are used. */
diff --git a/harness/tools/genbuild.pl b/harness/tools/genbuild.pl
index b17ab6a..1b1a005 100644
--- a/harness/tools/genbuild.pl
+++ b/harness/tools/genbuild.pl
@@ -7,12 +7,13 @@ print "#include <dlfcn.h>\n";
print "#include <pthread.h>\n";
print "#include \"plugin.h\"\n\n";
-print "int load_plugin_from_dl(dlhandle_t dl, plugin_t* plug)\n";
+print "int load_plugin_from_dl_(dlhandle_t dl, plugin_t* plug)\n";
print "{\n";
print " void* ptr;\n";
print " int ret = 0;\n";
print "\n";
print " const char** name = dlsym(dl, \"plugin_name\");\n";
+print " memset(plug, 0, sizeof(*plug));\n";
print " if (name) {\n";
print " plug->plugin_name = *name;\n";
print " } else {\n";
diff --git a/src/Wetterhorn/Core.hs b/src/Wetterhorn/Core.hs
index 33ce78d..d3b3f56 100644
--- a/src/Wetterhorn/Core.hs
+++ b/src/Wetterhorn/Core.hs
@@ -14,6 +14,7 @@ module Wetterhorn.Core
incrementState,
readWState,
defaultConfig,
+ requestHotReload,
)
where
@@ -24,13 +25,26 @@ import qualified Data.ByteString.Char8 as CH
import Foreign (Ptr, StablePtr, Word32, newStablePtr, ptrToIntPtr)
import Numeric (showHex)
import Text.Printf
+import Wetterhorn.Core.ForeignInterface (ForeignInterface)
+import qualified Wetterhorn.Core.ForeignInterface as ForeignInterface
--- This is this opaque state presented to the harness.
-type Wetterhorn = StablePtr (WConfig, WState)
+data WContext = WContext
+ { ctxForeignInterface :: ForeignInterface,
+ ctxConfig :: WConfig
+ }
+
+-- This is the OpaqueState passed to the harness.
+type Wetterhorn = StablePtr (WContext, WState)
+
+requestHotReload :: W ()
+requestHotReload = do
+ fi <- ctxForeignInterface <$> getWContext
+ wio $ ForeignInterface.requestHotReload fi
initWetterhorn :: WConfig -> IO Wetterhorn
initWetterhorn conf = do
- newStablePtr (conf, WState "this is a string" 0)
+ foreignInterface <- ForeignInterface.getForeignInterface
+ newStablePtr (WContext foreignInterface conf, WState "this is a string" 0)
data WState = WState
{ someString :: String,
@@ -49,8 +63,8 @@ defaultConfig :: WConfig
defaultConfig =
WConfig
{ keybindingHandler = \sym -> do
- i <- incrementState
- wio (printf "[%d] Got key %d\n" i sym),
+ i <- incrementState
+ wio (printf "[%d] Got key %d\n" i sym),
surfaceHandler = \state ptr -> wio (printf "Surface %s is %s\n" (showHex (ptrToIntPtr ptr) "") (show state))
}
@@ -62,7 +76,7 @@ readWState bs =
let _ = (e :: SomeException) in return (WState "" 0)
)
-newtype W a = W ((WConfig, WState) -> IO (a, WState))
+newtype W a = W ((WContext, WState) -> IO (a, WState))
instance Functor W where
fmap mfn (W fn) = W $ fmap (first mfn) <$> fn
@@ -79,17 +93,20 @@ instance Monad W where
let W fntob = fnmb a
fntob (config, state')
+getWContext :: W WContext
+getWContext = W pure
+
getWConfig :: W WConfig
-getWConfig = W pure
+getWConfig = ctxConfig <$> getWContext
getWState :: W WState
getWState = W $ \(_, s) -> pure (s, s)
-runW :: W a -> (WConfig, WState) -> IO (a, WState)
+runW :: W a -> (WContext, WState) -> IO (a, WState)
runW (W fn) = fn
incrementState :: W Int
-incrementState = W $ \(conf, WState s i) -> return (i, WState s (i + 1))
+incrementState = W $ \(_, WState s i) -> return (i, WState s (i + 1))
wio :: IO a -> W a
wio fn = W $ \(_, b) -> fn >>= \a -> return (a, b)
diff --git a/src/Wetterhorn/Core/ForeignInterface.hs b/src/Wetterhorn/Core/ForeignInterface.hs
new file mode 100644
index 0000000..acd98d6
--- /dev/null
+++ b/src/Wetterhorn/Core/ForeignInterface.hs
@@ -0,0 +1,36 @@
+module Wetterhorn.Core.ForeignInterface
+ ( getForeignInterface,
+ ForeignInterface (..),
+ )
+where
+
+import Data.Void (Void)
+import Foreign (Ptr, Storable (peekByteOff, sizeOf))
+import GHC.Exts (FunPtr)
+
+type CtxT = Ptr Void
+
+type ForeignCall = CtxT -> IO ()
+
+foreign import ccall "get_foreign_interface" foreignInterfacePtr :: IO (Ptr ())
+
+foreign import ccall "dynamic" toForeignCall :: FunPtr ForeignCall -> ForeignCall
+
+data ForeignInterface = ForeignInterface
+ { requestHotReload :: IO ()
+ }
+
+getForeignInterface :: IO ForeignInterface
+getForeignInterface = do
+ ptr <- foreignInterfacePtr
+
+ ctx <- peekByteOff ptr 0
+ requestHotReloadFn <- peekByteOff ptr (sizeOf ctx)
+
+ let _ = (ctx :: CtxT)
+ let _ = (requestHotReloadFn :: FunPtr ForeignCall)
+
+ return $
+ ForeignInterface
+ { requestHotReload = toForeignCall requestHotReloadFn ctx
+ }
diff --git a/src/harness_adapter.c b/src/harness_adapter.c
index 9684921..aa45ce6 100644
--- a/src/harness_adapter.c
+++ b/src/harness_adapter.c
@@ -9,7 +9,17 @@
const char *plugin_name = "Wetterhorn";
-void plugin_load(int argc, char **argv) { hs_init(&argc, &argv); }
+void* foreign_interface;
+
+void* get_foreign_interface()
+{
+ return foreign_interface;
+}
+
+void plugin_load(int argc, char **argv, void* fintf) {
+ hs_init(&argc, &argv);
+ foreign_interface = fintf;
+}
void plugin_teardown(opqst_t st) { hs_exit(); }
static const char msg[] =