diff options
author | bfredl <bjorn.linse@gmail.com> | 2022-06-16 19:17:57 +0200 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2022-07-18 14:08:44 +0200 |
commit | 67a04fe6cb0f6b0cd3d44ae37b7caddddda198ea (patch) | |
tree | bb7f51c543dd03e6303e91d81fc592b1283e03c3 | |
parent | 1b462705d049fa0cf2bb99bae9112b84abea8d5a (diff) | |
download | rneovim-67a04fe6cb0f6b0cd3d44ae37b7caddddda198ea.tar.gz rneovim-67a04fe6cb0f6b0cd3d44ae37b7caddddda198ea.tar.bz2 rneovim-67a04fe6cb0f6b0cd3d44ae37b7caddddda198ea.zip |
perf(ui): unpack a single ui event at a time, instead of a "redraw" batch
This reduces the memory overhead for large redraw batches, as a much smaller
prefix of the api object buffer is used and needs to be hot in cache.
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 7 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/unpacker.c | 99 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/unpacker.h | 7 | ||||
-rw-r--r-- | src/nvim/ui.c | 3 | ||||
-rw-r--r-- | src/nvim/ui_client.c | 42 |
5 files changed, 122 insertions, 36 deletions
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de01443313..0bb5d800f1 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -239,7 +239,12 @@ static void parse_msgpack(Channel *channel) { Unpacker *p = channel->rpc.unpacker; while (unpacker_advance(p)) { - if (p->type == kMessageTypeResponse) { + if (p->is_ui) { + if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { + p->ui_handler.fn(p->result.data.array); + } + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + } else if (p->type == kMessageTypeResponse) { ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); if (p->request_id != frame->request_id) { char buf[256]; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 26c1843026..efbb3110d9 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -292,7 +292,18 @@ bool unpacker_advance(Unpacker *p) if (!unpacker_parse_header(p)) { return false; } - p->state = p->type == kMessageTypeResponse ? 1 : 2; + if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) { + p->state = 10; + } else { + p->state = p->type == kMessageTypeResponse ? 1 : 2; + arena_start(&p->arena, &p->reuse_blk); + } + } + + if (p->state == 10 || p->state == 11) { + if (!unpacker_parse_redraw(p)) { + return false; + } arena_start(&p->arena, &p->reuse_blk); } @@ -310,13 +321,91 @@ rerun: return false; } - if (p->state == 1) { + switch (p->state) { + case 1: p->error = p->result; p->state = 2; goto rerun; - } else { - assert(p->state == 2); + case 2: p->state = 0; + p->is_ui = false; + return true; + case 12: + p->ncalls--; + if (p->ncalls > 0) { + p->state = 12; + } else if (p->nevents > 0) { + p->state = 11; + } else { + p->state = 0; + } + // TODO: fold into p->type + p->is_ui = true; + return true; + default: + abort(); + } +} + +// note: first {11} is not saved as a state +// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] +// +bool unpacker_parse_redraw(Unpacker *p) +{ + mpack_token_t tok; + int result; + + const char *data = p->read_ptr; + size_t size = p->read_size; + switch (p->state) { + case 10: + result = mpack_rtoken(&data, &size, &tok); + if (result == MPACK_EOF) { + return false; + } else if (result || tok.type != MPACK_TOKEN_ARRAY) { + p->state = -1; + return false; + } + + p->nevents = (int)tok.length; + FALLTHROUGH; + + case 11: + result = mpack_rtoken(&data, &size, &tok); + if (result == MPACK_EOF) { + return false; + } else if (result || tok.type != MPACK_TOKEN_ARRAY) { + abort(); + } + + p->ncalls = (int)tok.length; + if (p->ncalls-- == 0) { + p->state = -1; + return false; + } + + result = mpack_rtoken(&data, &size, &tok); + if (result == MPACK_EOF) { + return false; + } else if (result || (tok.type != MPACK_TOKEN_STR && tok.type != MPACK_TOKEN_BIN)) { + abort(); + } + + if (tok.length > size) { + return false; + } + + p->ui_handler = ui_client_get_redraw_handler(data, tok.length, NULL); + data += tok.length; + size -= tok.length; + + p->nevents--; + p->read_ptr = data; + p->read_size = size; + p->state = 12; + return true; + + default: + abort(); } - return true; } diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index e0dc6f0a68..3a5476cd21 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -32,8 +32,13 @@ struct Unpacker { Error unpack_error; Arena arena; - // one lenght free-list of reusable blocks + // one length free-list of reusable blocks ArenaMem reuse_blk; + + bool is_ui; + int nevents; + int ncalls; + UIClientHandler ui_handler; }; // unrecovareble error. unpack_error should be set! diff --git a/src/nvim/ui.c b/src/nvim/ui.c index a49e9df9ee..27fa1c402c 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -348,7 +348,8 @@ void ui_attach_impl(UI *ui, uint64_t chanid) if (ui_count == MAX_UI_COUNT) { abort(); } - if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { + if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug] + && !ui_client_channel_id) { ui_comp_attach(ui); } diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index be01538f67..5f4cb63f87 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -46,37 +46,23 @@ void ui_client_init(uint64_t chan) ui_client_channel_id = chan; } -/// Handler for "redraw" events sent by the NVIM server -/// -/// This function will be called by handle_request (in msgpack_rpc/channel.c) -/// The individual ui_events sent by the server are individually handled -/// by their respective handlers defined in ui_events_client.generated.h -/// -/// @note The "flush" event is called only once and only after handling all -/// the other events -/// @param channel_id: The id of the rpc channel -/// @param uidata: The dense array containing the ui_events sent by the server -/// @param[out] err Error details, if any -Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error) { - for (size_t i = 0; i < args.size; i++) { - Array call = args.items[i].data.array; - String name = call.items[0].data.string; - - int hash = ui_client_handler_hash(name.data, name.size); - if (hash < 0) { - ELOG("No ui client handler for %s", name.size ? name.data : "<empty>"); - continue; - } - UIClientHandler handler = event_handlers[hash]; - - // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); - DLOG("Invoke ui client handler for %s", name.data); - for (size_t j = 1; j < call.size; j++) { - handler.fn(call.items[j].data.array); - } + int hash = ui_client_handler_hash(name, name_len); + if (hash < 0) { + return (UIClientHandler){ NULL, NULL }; } + return event_handlers[hash]; +} +/// Placeholder for _sync_ requests with 'redraw' method name +/// +/// async 'redraw' events, which are expected when nvim acts as an ui client. +/// get handled in msgpack_rpc/unpacker.c and directy dispatched to handlers +/// of specific ui events, like ui_client_event_grid_resize and so on. +Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error) +{ + api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request"); return NIL; } |