mirror of
https://github.com/fluencelabs/redis
synced 2025-04-25 10:32:14 +00:00
Modules shared API: initial core functions.
Based on ideas and code in PR #5560 by @MeirShpilraien.
This commit is contained in:
parent
4d747bb850
commit
d38d82af05
86
src/module.c
86
src/module.c
@ -47,9 +47,21 @@ struct RedisModule {
|
|||||||
int ver; /* Module version. We use just progressive integers. */
|
int ver; /* Module version. We use just progressive integers. */
|
||||||
int apiver; /* Module API version as requested during initialization.*/
|
int apiver; /* Module API version as requested during initialization.*/
|
||||||
list *types; /* Module data types. */
|
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;
|
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.*/
|
static dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/
|
||||||
|
|
||||||
/* Entries in the context->amqueue array, representing objects to free
|
/* 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->ver = ver;
|
||||||
module->apiver = apiver;
|
module->apiver = apiver;
|
||||||
module->types = listCreate();
|
module->types = listCreate();
|
||||||
|
module->usedby = listCreate();
|
||||||
|
module->using = listCreate();
|
||||||
ctx->module = module;
|
ctx->module = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4621,6 +4635,77 @@ void RM_GetRandomHexChars(char *dst, size_t len) {
|
|||||||
getRandomHexChars(dst,len);
|
getRandomHexChars(dst,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
|
* Modules API internals
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
@ -4900,6 +4985,7 @@ size_t moduleCount(void) {
|
|||||||
* file so that's easy to seek it to add new entries. */
|
* file so that's easy to seek it to add new entries. */
|
||||||
void moduleRegisterCoreAPI(void) {
|
void moduleRegisterCoreAPI(void) {
|
||||||
server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
|
server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
|
||||||
|
server.sharedapi = dictCreate(&moduleAPIDictType,NULL);
|
||||||
REGISTER_API(Alloc);
|
REGISTER_API(Alloc);
|
||||||
REGISTER_API(Calloc);
|
REGISTER_API(Calloc);
|
||||||
REGISTER_API(Realloc);
|
REGISTER_API(Realloc);
|
||||||
|
@ -954,7 +954,9 @@ struct redisServer {
|
|||||||
size_t initial_memory_usage; /* Bytes used after initialization. */
|
size_t initial_memory_usage; /* Bytes used after initialization. */
|
||||||
int always_show_logo; /* Show logo even for non-stdout logging. */
|
int always_show_logo; /* Show logo even for non-stdout logging. */
|
||||||
/* Modules */
|
/* 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. */
|
list *loadmodule_queue; /* List of modules to load at startup. */
|
||||||
int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a
|
int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a
|
||||||
client blocked on a module command needs
|
client blocked on a module command needs
|
||||||
|
Loading…
x
Reference in New Issue
Block a user