aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2022-06-16 19:17:57 +0200
committerbfredl <bjorn.linse@gmail.com>2022-07-18 14:08:44 +0200
commit67a04fe6cb0f6b0cd3d44ae37b7caddddda198ea (patch)
treebb7f51c543dd03e6303e91d81fc592b1283e03c3
parent1b462705d049fa0cf2bb99bae9112b84abea8d5a (diff)
downloadrneovim-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.c7
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c99
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h7
-rw-r--r--src/nvim/ui.c3
-rw-r--r--src/nvim/ui_client.c42
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;
}