Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
6fd4af1f
Commit
6fd4af1f
authored
Mar 14, 2019
by
antirez
Browse files
Merge branch 'sharedapi' into unstable
parents
2537b214
8a87de13
Changes
3
Show whitespace changes
Inline
Side-by-side
src/module.c
View file @
6fd4af1f
...
@@ -47,9 +47,21 @@ struct RedisModule {
...
@@ -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
...
@@ -701,6 +713,8 @@ void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int api
...
@@ -701,6 +713,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;
}
}
...
@@ -4623,6 +4637,121 @@ void RM_GetRandomHexChars(char *dst, size_t len) {
...
@@ -4623,6 +4637,121 @@ 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;
}
/* Remove all the APIs registered by the specified module. Usually you
* want this when the module is going to be unloaded. This function
* assumes that's caller responsibility to make sure the APIs are not
* used by other modules.
*
* The number of unregistered APIs is returned. */
int moduleUnregisterSharedAPI(RedisModule *module) {
int count = 0;
dictIterator *di = dictGetSafeIterator(server.sharedapi);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
const char *apiname = dictGetKey(de);
RedisModuleSharedAPI *sapi = dictGetVal(de);
if (sapi->module == module) {
dictDelete(server.sharedapi,apiname);
zfree(sapi);
count++;
}
}
dictReleaseIterator(di);
return count;
}
/* Remove the specified module as an user of APIs of ever other module.
* This is usually called when a module is unloaded.
*
* Returns the number of modules this module was using APIs from. */
int moduleUnregisterUsedAPI(RedisModule *module) {
listIter li;
listNode *ln;
int count = 0;
listRewind(module->using,&li);
while((ln = listNext(&li))) {
RedisModule *used = ln->value;
listNode *ln = listSearchKey(used->usedby,module);
if (ln) {
listDelNode(module->using,ln);
count++;
}
}
return count;
}
/* --------------------------------------------------------------------------
/* --------------------------------------------------------------------------
* Modules API internals
* Modules API internals
* -------------------------------------------------------------------------- */
* -------------------------------------------------------------------------- */
...
@@ -4767,6 +4896,8 @@ int moduleLoad(const char *path, void **module_argv, int module_argc) {
...
@@ -4767,6 +4896,8 @@ int moduleLoad(const char *path, void **module_argv, int module_argc) {
if (onload((void*)&ctx,module_argv,module_argc) == REDISMODULE_ERR) {
if (onload((void*)&ctx,module_argv,module_argc) == REDISMODULE_ERR) {
if (ctx.module) {
if (ctx.module) {
moduleUnregisterCommands(ctx.module);
moduleUnregisterCommands(ctx.module);
moduleUnregisterSharedAPI(ctx.module);
moduleUnregisterUsedAPI(ctx.module);
moduleFreeModuleStructure(ctx.module);
moduleFreeModuleStructure(ctx.module);
}
}
dlclose(handle);
dlclose(handle);
...
@@ -4796,14 +4927,17 @@ int moduleUnload(sds name) {
...
@@ -4796,14 +4927,17 @@ int moduleUnload(sds name) {
if (module == NULL) {
if (module == NULL) {
errno = ENOENT;
errno = ENOENT;
return REDISMODULE_ERR;
return REDISMODULE_ERR;
}
} else if (listLength(module->types)) {
if (listLength(module->types)) {
errno = EBUSY;
errno = EBUSY;
return REDISMODULE_ERR;
return REDISMODULE_ERR;
} else if (listLength(module->usedby)) {
errno = EPERM;
return REDISMODULE_ERR;
}
}
moduleUnregisterCommands(module);
moduleUnregisterCommands(module);
moduleUnregisterSharedAPI(module);
moduleUnregisterUsedAPI(module);
/* Remove any notification subscribers this module might have */
/* Remove any notification subscribers this module might have */
moduleUnsubscribeNotifications(module);
moduleUnsubscribeNotifications(module);
...
@@ -4884,7 +5018,12 @@ NULL
...
@@ -4884,7 +5018,12 @@ NULL
errmsg = "no such module with that name";
errmsg = "no such module with that name";
break;
break;
case EBUSY:
case EBUSY:
errmsg = "the module exports one or more module-side data types, can't unload";
errmsg = "the module exports one or more module-side data "
"types, can't unload";
break;
case EPERM:
errmsg = "the module exports APIs used by other modules. "
"Please unload them first and try again";
break;
break;
default:
default:
errmsg = "operation not possible.";
errmsg = "operation not possible.";
...
@@ -4909,6 +5048,7 @@ size_t moduleCount(void) {
...
@@ -4909,6 +5048,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);
...
@@ -5059,4 +5199,6 @@ void moduleRegisterCoreAPI(void) {
...
@@ -5059,4 +5199,6 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(DictPrev);
REGISTER_API(DictPrev);
REGISTER_API(DictCompareC);
REGISTER_API(DictCompareC);
REGISTER_API(DictCompare);
REGISTER_API(DictCompare);
REGISTER_API(ExportSharedAPI);
REGISTER_API(GetSharedAPI);
}
}
src/redismodule.h
View file @
6fd4af1f
...
@@ -335,6 +335,8 @@ void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t
...
@@ -335,6 +335,8 @@ void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t
void
REDISMODULE_API_FUNC
(
RedisModule_GetRandomHexChars
)(
char
*
dst
,
size_t
len
);
void
REDISMODULE_API_FUNC
(
RedisModule_GetRandomHexChars
)(
char
*
dst
,
size_t
len
);
void
REDISMODULE_API_FUNC
(
RedisModule_SetDisconnectCallback
)(
RedisModuleBlockedClient
*
bc
,
RedisModuleDisconnectFunc
callback
);
void
REDISMODULE_API_FUNC
(
RedisModule_SetDisconnectCallback
)(
RedisModuleBlockedClient
*
bc
,
RedisModuleDisconnectFunc
callback
);
void
REDISMODULE_API_FUNC
(
RedisModule_SetClusterFlags
)(
RedisModuleCtx
*
ctx
,
uint64_t
flags
);
void
REDISMODULE_API_FUNC
(
RedisModule_SetClusterFlags
)(
RedisModuleCtx
*
ctx
,
uint64_t
flags
);
int
REDISMODULE_API_FUNC
(
RedisModule_ExportSharedAPI
)(
RedisModuleCtx
*
ctx
,
const
char
*
apiname
,
void
*
func
);
void
*
REDISMODULE_API_FUNC
(
RedisModule_GetSharedAPI
)(
RedisModuleCtx
*
ctx
,
const
char
*
apiname
);
#endif
#endif
/* This is included inline inside each Redis module. */
/* This is included inline inside each Redis module. */
...
@@ -495,6 +497,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
...
@@ -495,6 +497,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
GetRandomBytes
);
REDISMODULE_GET_API
(
GetRandomBytes
);
REDISMODULE_GET_API
(
GetRandomHexChars
);
REDISMODULE_GET_API
(
GetRandomHexChars
);
REDISMODULE_GET_API
(
SetClusterFlags
);
REDISMODULE_GET_API
(
SetClusterFlags
);
REDISMODULE_GET_API
(
ExportSharedAPI
);
REDISMODULE_GET_API
(
GetSharedAPI
);
#endif
#endif
if
(
RedisModule_IsModuleNameBusy
&&
RedisModule_IsModuleNameBusy
(
name
))
return
REDISMODULE_ERR
;
if
(
RedisModule_IsModuleNameBusy
&&
RedisModule_IsModuleNameBusy
(
name
))
return
REDISMODULE_ERR
;
...
...
src/server.h
View file @
6fd4af1f
...
@@ -1026,7 +1026,9 @@ struct redisServer {
...
@@ -1026,7 +1026,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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment