summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2018-12-20 12:06:24 +0100
committerantirez <antirez@gmail.com>2018-12-20 17:57:35 +0100
commit27f6e9bb9b6614bf4e49d9c53087f21de09cdb1a (patch)
treed4dd959517fcbc896bbf30826c0a70d72524436a
parent850b64c1166a1c36e9aa1b12a265b49982d776a0 (diff)
downloadredis-27f6e9bb9b6614bf4e49d9c53087f21de09cdb1a.tar.gz
Modules shared API: initial core functions.
Based on ideas and code in PR #5560 by @MeirShpilraien.
-rw-r--r--src/module.c86
-rw-r--r--src/server.h4
2 files changed, 89 insertions, 1 deletions
diff --git a/src/module.c b/src/module.c
index 20d159d33..2914b5903 100644
--- a/src/module.c
+++ b/src/module.c
@@ -47,9 +47,21 @@ struct RedisModule {
int ver; /* Module version. We use just progressive integers. */
int apiver; /* Module API version as requested during initialization.*/
list *types; /* Module data types. */
+ list *usedby; /* List of modules using APIs from this one. */
+ list *using; /* List of modules we use some APIs of. */
};
typedef struct RedisModule RedisModule;
+/* This represents a shared API. Shared APIs will be used to populate
+ * the server.sharedapi dictionary, mapping names of APIs exported by
+ * modules for other modules to use, to their structure specifying the
+ * function pointer that can be called. */
+struct RedisModuleSharedAPI {
+ void *func;
+ RedisModule *module;
+};
+typedef struct RedisModuleSharedAPI RedisModuleSharedAPI;
+
static dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/
/* Entries in the context->amqueue array, representing objects to free
@@ -700,6 +712,8 @@ void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int api
module->ver = ver;
module->apiver = apiver;
module->types = listCreate();
+ module->usedby = listCreate();
+ module->using = listCreate();
ctx->module = module;
}
@@ -4616,6 +4630,77 @@ void RM_GetRandomHexChars(char *dst, size_t len) {
}
/* --------------------------------------------------------------------------
+ * Modules API exporting / importing
+ * -------------------------------------------------------------------------- */
+
+/* This function is called by a module in order to export some API with a
+ * given name. Other modules will be able to use this API by calling the
+ * symmetrical function RM_GetSharedAPI() and casting the return value to
+ * the right function pointer.
+ *
+ * The function will return REDISMODULE_OK if the name is not already taken,
+ * otherwise REDISMODULE_ERR will be returned and no operation will be
+ * performed.
+ *
+ * IMPORTANT: the apiname argument should be a string literal with static
+ * lifetime. The API relies on the fact that it will always be valid in
+ * the future. */
+int RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) {
+ RedisModuleSharedAPI *sapi = zmalloc(sizeof(*sapi));
+ sapi->module = ctx->module;
+ sapi->func = func;
+ if (dictAdd(server.sharedapi, (char*)apiname, sapi) != DICT_OK) {
+ zfree(sapi);
+ return REDISMODULE_ERR;
+ }
+ return REDISMODULE_OK;
+}
+
+/* Request an exported API pointer. The return value is just a void pointer
+ * that the caller of this function will be required to cast to the right
+ * function pointer, so this is a private contract between modules.
+ *
+ * If the requested API is not available then NULL is returned. Because
+ * modules can be loaded at different times with different order, this
+ * function calls should be put inside some module generic API registering
+ * step, that is called every time a module attempts to execute a
+ * command that requires external APIs: if some API cannot be resolved, the
+ * command should return an error.
+ *
+ * Here is an exmaple:
+ *
+ * int ... myCommandImplementation() {
+ * if (getExternalAPIs() == 0) {
+ * reply with an error here if we cannot have the APIs
+ * }
+ * // Use the API:
+ * myFunctionPointer(foo);
+ * }
+ *
+ * And the function registerAPI() is:
+ *
+ * int getExternalAPIs(void) {
+ * static int api_loaded = 0;
+ * if (api_loaded != 0) return 1; // APIs already resolved.
+ *
+ * myFunctionPointer = RedisModule_GetOtherModuleAPI("...");
+ * if (myFunctionPointer == NULL) return 0;
+ *
+ * return 1;
+ * }
+ */
+void *RM_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname) {
+ dictEntry *de = dictFind(server.sharedapi, apiname);
+ if (de == NULL) return NULL;
+ RedisModuleSharedAPI *sapi = dictGetVal(de);
+ if (listSearchKey(sapi->module->usedby,ctx->module) == NULL) {
+ listAddNodeTail(sapi->module->usedby,ctx->module);
+ listAddNodeTail(ctx->module->using,sapi->module);
+ }
+ return sapi->func;
+}
+
+/* --------------------------------------------------------------------------
* Modules API internals
* -------------------------------------------------------------------------- */
@@ -4894,6 +4979,7 @@ size_t moduleCount(void) {
* file so that's easy to seek it to add new entries. */
void moduleRegisterCoreAPI(void) {
server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
+ server.sharedapi = dictCreate(&moduleAPIDictType,NULL);
REGISTER_API(Alloc);
REGISTER_API(Calloc);
REGISTER_API(Realloc);
diff --git a/src/server.h b/src/server.h
index da4c6d45a..3c2ecdd23 100644
--- a/src/server.h
+++ b/src/server.h
@@ -954,7 +954,9 @@ struct redisServer {
size_t initial_memory_usage; /* Bytes used after initialization. */
int always_show_logo; /* Show logo even for non-stdout logging. */
/* Modules */
- dict *moduleapi; /* Exported APIs dictionary for modules. */
+ dict *moduleapi; /* Exported core APIs dictionary for modules. */
+ dict *sharedapi; /* Like moduleapi but containing the APIs that
+ modules share with each other. */
list *loadmodule_queue; /* List of modules to load at startup. */
int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a
client blocked on a module command needs