diff options
author | Josh Rahm <rahm@google.com> | 2024-02-13 17:53:30 -0700 |
---|---|---|
committer | Josh Rahm <rahm@google.com> | 2024-02-13 17:54:04 -0700 |
commit | 10a5272eaca6407982b3707027ea8704f3484377 (patch) | |
tree | 413ebfa9cffe703e596d33d7fd5e06249d7af477 | |
parent | d065af8c16bcb8ef54024c0f2082d827f83f37f9 (diff) | |
download | wetterhorn-10a5272eaca6407982b3707027ea8704f3484377.tar.gz wetterhorn-10a5272eaca6407982b3707027ea8704f3484377.tar.bz2 wetterhorn-10a5272eaca6407982b3707027ea8704f3484377.zip |
WIP: Working on the foreign interface.
-rw-r--r-- | harness/include/foreign_intf.h | 25 | ||||
-rw-r--r-- | harness/include/plugin.h | 48 | ||||
-rw-r--r-- | harness/src/plugin.c | 50 | ||||
-rw-r--r-- | harness/src/wl.c | 1 | ||||
-rw-r--r-- | harness/tools/genbuild.pl | 3 | ||||
-rw-r--r-- | src/Wetterhorn/Core.hs | 35 | ||||
-rw-r--r-- | src/Wetterhorn/Core/ForeignInterface.hs | 36 | ||||
-rw-r--r-- | src/harness_adapter.c | 12 |
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[] = |