diff options
author | Thiago de Arruda <tpadilha84@gmail.com> | 2014-10-20 20:07:01 -0300 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-10-21 11:05:49 -0300 |
commit | 72e3e57bf1aa128b02724e853365f65fd9451f0b (patch) | |
tree | 3367aec22475458ea3ed4f3ae9db3a735b08c163 /src | |
parent | 72f028abcb167b2ca7e2d6d770af81a18ef58a0a (diff) | |
download | rneovim-72e3e57bf1aa128b02724e853365f65fd9451f0b.tar.gz rneovim-72e3e57bf1aa128b02724e853365f65fd9451f0b.tar.bz2 rneovim-72e3e57bf1aa128b02724e853365f65fd9451f0b.zip |
msgpack-rpc: Allow selective deferral API calls
Since all API functions now run immediately after a msgpack-rpc request is
parsed by libuv callbacks, a mechanism was added to override this behavior and
allow certain functions to run in Nvim main loop.
The mechanism is simple: Any API function tagged with the FUNC_ATTR_DEFERRED (a
"dummy" attribute only used by msgpack-gen.lua) will be called when Nvim main
loop receives a K_EVENT key.
To implement this mechanism it was necessary some restructuration on the
msgpack-rpc modules, especially in the msgpack-gen.lua script.
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/func_attr.h | 1 | ||||
-rw-r--r-- | src/nvim/map.c | 3 | ||||
-rw-r--r-- | src/nvim/map.h | 2 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 99 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/defs.h | 15 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/helpers.c | 44 |
6 files changed, 114 insertions, 50 deletions
diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index c75d0ab312..519f61c763 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -179,6 +179,7 @@ #endif #ifdef DEFINE_FUNC_ATTRIBUTES + #define FUNC_ATTR_DEFERRED #define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC #define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x) #define FUNC_ATTR_ALLOC_SIZE_PROD(x,y) REAL_FATTR_ALLOC_SIZE_PROD(x,y) diff --git a/src/nvim/map.c b/src/nvim/map.c index 24a869e2e6..3f485cb952 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -108,4 +108,5 @@ MAP_IMPL(cstr_t, uint64_t, DEFAULT_INITIALIZER) MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) -MAP_IMPL(String, rpc_method_handler_fn, DEFAULT_INITIALIZER) +#define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .defer = false} +MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 78f4218a72..5ade6dcf15 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -25,7 +25,7 @@ MAP_DECLS(cstr_t, uint64_t) MAP_DECLS(cstr_t, ptr_t) MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) -MAP_DECLS(String, rpc_method_handler_fn) +MAP_DECLS(String, MsgpackRpcRequestHandler) #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 91c26ca21e..6ddda10c5f 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -5,6 +5,8 @@ #include <uv.h> #include <msgpack.h> +#include "nvim/lib/klist.h" + #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/msgpack_rpc/channel.h" @@ -52,6 +54,17 @@ typedef struct { kvec_t(ChannelCallFrame *) call_stack; } Channel; +typedef struct { + Channel *channel; + MsgpackRpcRequestHandler handler; + Array args; + uint64_t request_id; +} RequestEvent; + +#define RequestEventFreer(x) +KMEMPOOL_INIT(RequestEventPool, RequestEvent, RequestEventFreer) +kmempool_t(RequestEventPool) *request_event_pool = NULL; + static uint64_t next_id = 1; static PMap(uint64_t) *channels = NULL; static PMap(cstr_t) *event_strings = NULL; @@ -64,6 +77,7 @@ static msgpack_sbuffer out_buffer; /// Initializes the module void channel_init(void) { + request_event_pool = kmp_init(RequestEventPool); channels = pmap_new(uint64_t)(); event_strings = pmap_new(cstr_t)(); msgpack_sbuffer_init(&out_buffer); @@ -352,12 +366,7 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) goto end; } - // Perform the call - WBuffer *resp = msgpack_rpc_call(channel->id, &unpacked.data, &out_buffer); - // write the response - if (!channel_write(channel, resp)) { - goto end; - } + handle_request(channel, &unpacked.data); } if (result == MSGPACK_UNPACK_NOMEM_ERROR) { @@ -387,6 +396,84 @@ end: } } +static void handle_request(Channel *channel, msgpack_object *request) + FUNC_ATTR_NONNULL_ALL +{ + uint64_t request_id; + Error error = ERROR_INIT; + msgpack_rpc_validate(&request_id, request, &error); + + if (error.set) { + // Validation failed, send response with error + channel_write(channel, + serialize_response(request_id, &error, NIL, &out_buffer)); + return; + } + + // Retrieve the request handler + MsgpackRpcRequestHandler handler; + msgpack_object method = request->via.array.ptr[2]; + + if (method.type == MSGPACK_OBJECT_BIN || method.type == MSGPACK_OBJECT_STR) { + handler = msgpack_rpc_get_handler_for(method.via.bin.ptr, + method.via.bin.size); + } else { + handler.fn = msgpack_rpc_handle_missing_method; + handler.defer = false; + } + + Array args; + msgpack_rpc_to_array(request->via.array.ptr + 3, &args); + + if (kv_size(channel->call_stack) || !handler.defer) { + call_request_handler(channel, handler, args, request_id); + return; + } + + // Defer calling the request handler. + RequestEvent *event_data = kmp_alloc(RequestEventPool, request_event_pool); + event_data->channel = channel; + event_data->handler = handler; + event_data->args = args; + event_data->request_id = request_id; + event_push((Event) { + .handler = on_request_event, + .data = event_data + }); +} + +static void on_request_event(Event event) +{ + RequestEvent *e = event.data; + call_request_handler(e->channel, e->handler, e->args, e->request_id); + kmp_free(RequestEventPool, request_event_pool, e); +} + +static void call_request_handler(Channel *channel, + MsgpackRpcRequestHandler handler, + Array args, + uint64_t request_id) +{ + Error error = ERROR_INIT; + Object result = handler.fn(channel->id, request_id, args, &error); + // send the response + msgpack_packer response; + msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write); + + if (error.set) { + ELOG("Error dispatching msgpack-rpc call: %s(request: id %" PRIu64 ")", + error.msg, + request_id); + channel_write(channel, + serialize_response(request_id, &error, NIL, &out_buffer)); + } + + DLOG("Successfully completed mspgack-rpc call(request id: %" PRIu64 ")", + request_id); + channel_write(channel, + serialize_response(request_id, &error, result, &out_buffer)); +} + static bool channel_write(Channel *channel, WBuffer *buffer) { bool success; diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h index 5eec4ced54..13067fb7b4 100644 --- a/src/nvim/msgpack_rpc/defs.h +++ b/src/nvim/msgpack_rpc/defs.h @@ -6,9 +6,15 @@ /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// functions of this type. -typedef Object (*rpc_method_handler_fn)(uint64_t channel_id, - msgpack_object *req, - Error *error); +typedef struct { + Object (*fn)(uint64_t channel_id, + uint64_t request_id, + Array args, + Error *error); + bool defer; // Should the call be deferred to the main loop? This should + // be true if the function mutates editor data structures such + // as buffers, windows, tabs, or if it executes vimscript code. +} MsgpackRpcRequestHandler; /// Initializes the msgpack-rpc method table void msgpack_rpc_init_method_table(void); @@ -31,4 +37,7 @@ Object msgpack_rpc_dispatch(uint64_t channel_id, Error *error) FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); +MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, + size_t name_len) + FUNC_ATTR_NONNULL_ARG(1); #endif // NVIM_MSGPACK_RPC_DEFS_H diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 4b96e4985e..6be221b912 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -140,10 +140,13 @@ bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) case MSGPACK_OBJECT_EXT: switch (obj->via.ext.type) { case kObjectTypeBuffer: + arg->type = kObjectTypeBuffer; return msgpack_rpc_to_buffer(obj, &arg->data.buffer); case kObjectTypeWindow: + arg->type = kObjectTypeWindow; return msgpack_rpc_to_window(obj, &arg->data.window); case kObjectTypeTabpage: + arg->type = kObjectTypeTabpage; return msgpack_rpc_to_tabpage(obj, &arg->data.tabpage); } default: @@ -292,44 +295,6 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) } } -/// Validates the basic structure of the msgpack-rpc call and fills `res` -/// with the basic response structure. -/// -/// @param channel_id The channel id -/// @param req The parsed request object -/// @param res A packer that contains the response -WBuffer *msgpack_rpc_call(uint64_t channel_id, - msgpack_object *req, - msgpack_sbuffer *sbuffer) - FUNC_ATTR_NONNULL_ARG(2) - FUNC_ATTR_NONNULL_ARG(3) -{ - uint64_t response_id; - Error error = ERROR_INIT; - msgpack_rpc_validate(&response_id, req, &error); - - if (error.set) { - return serialize_response(response_id, &error, NIL, sbuffer); - } - - // dispatch the call - Object rv = msgpack_rpc_dispatch(channel_id, req, &error); - // send the response - msgpack_packer response; - msgpack_packer_init(&response, sbuffer, msgpack_sbuffer_write); - - if (error.set) { - ELOG("Error dispatching msgpack-rpc call: %s(request: id %" PRIu64 ")", - error.msg, - response_id); - return serialize_response(response_id, &error, NIL, sbuffer); - } - - DLOG("Successfully completed mspgack-rpc call(request id: %" PRIu64 ")", - response_id); - return serialize_response(response_id, &error, rv, sbuffer); -} - /// Finishes the msgpack-rpc call with an error message. /// /// @param msg The error message @@ -348,7 +313,8 @@ void msgpack_rpc_error(char *msg, msgpack_packer *res) /// Handler executed when an invalid method name is passed Object msgpack_rpc_handle_missing_method(uint64_t channel_id, - msgpack_object *req, + uint64_t request_id, + Array args, Error *error) { snprintf(error->msg, sizeof(error->msg), "Invalid method name"); |