#ifndef _SOUL_H_ #define _SOUL_H_ #include #include #include #include #include #include #include #include "soul_types.h" #include "soul_exports.h" #define MAX_QUEUED_ACTIONS 8 typedef void *dlhandle_t; /* Opaque state for a soul. Not to be touched by the harness (not that it * really can be.) */ struct SOUL; /* This structure represents an action requested by the soul for the harness. */ typedef struct { int (*action)(struct SOUL *requester, void *arg); void (*arg_dtor)(void *arg); union { void *ptr_arg; int int_arg; char *str_arg; }; } requested_action_t; /* * Structure for the soul. */ typedef struct SOUL { /* The argc this soul is loaded with. Typically the argc from main(). */ int argc; /* The argv this soul is loaded with. Typically the argv from main(). */ char **argv; /* Filename the soul is loaded from. */ char filename[PATH_MAX]; /* Opaque state of this soul. The state is usually some kind of pointer to * the soul state, but all the harness knows is the opaque state is a * pointer-sized piece of data. * * This opaque state is used in a linear pattern where the handlers take the * opaque state, maybe operate on it, and return a new opaque state, which is * then passed to the next handler, etc. It is on the soul to properly * manager the memory for this state and to destroy it upon teardown. * * It's guaranteed that this state is used linearly, meaning the harness gives * up all ownership to it once passed into a handler. */ opqst_t state; /* This soul's lock. This avoids potential issues with multiple threads * trying to change the opaque state at once which can lead to undesireable * outcomes. */ pthread_mutex_t lock; /** Set to not-zero if this soul is initialized, otherwise set to zero. */ int initialized; /* The handle to the shared library. */ dlhandle_t library_handle; /* Pointer to the soul name. This is in the shared library and a * null-terminated string. If the library does not have a soul name, this * will be NULL. */ const char *soul_name; /* Soul function table populated by the runtime loader. */ #define SOUL_FN_PTR(ret, name, args) ret (*name) args; ARKSOUL_EXPORTS(SOUL_FN_PTR) #undef SOUL_FN_PTR /* List of requested actions by the soul. 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[MAX_QUEUED_ACTIONS]; } soul_t; /* Reloads the soul. This tears down the existing soul, marshals the state * for it and reloads it. * * This function will call dlclose on the soul's library handle. */ int soul_hot_reload(int argc, char **argv, const char *filepath, soul_t *soul); /* * Like hot-reload, but uses the same parameters the soul was originally * loaded with. */ int soul_hot_reload_same_state(soul_t *soul); /* Starts a soul in a cold state. Called after load_soul_from_file. */ void soul_cold_start(soul_t *soul); /* Reads a soul from a filename. */ int load_soul_from_file(int argc, char **argv, const char *filename, soul_t *soul); void soul_run_requested_actions(soul_t *soul); #endif /* _SOUL_H_ */