From 31f91bd483797feb411077da0e351ccfae9ecc10 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 15:37:09 +1000 Subject: Improve resize performance by partially flushing the transaction queue When interactively resizing some views (eg. Nautilus), new transactions are added to the queue faster than the client can process them. Previously, we would wait for the entire queue to be ready before applying any of them, but in this case the transactions would time out, giving the client choppy performance. This changes the queue handling so it applies the transactions up to the first waiting transaction, without waiting for the entire queue to be ready. --- sway/desktop/transaction.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 19f41efc..2a89880a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -222,24 +222,16 @@ static void transaction_apply(struct sway_transaction *transaction) { } } -/** - * For simplicity, we only progress the queue if it can be completely flushed. - */ static void transaction_progress_queue() { - // We iterate this list in reverse because we're more likely to find a - // waiting transactions at the end of the list. - for (int i = server.transactions->length - 1; i >= 0; --i) { - struct sway_transaction *transaction = server.transactions->items[i]; + while (server.transactions->length) { + struct sway_transaction *transaction = server.transactions->items[0]; if (transaction->num_waiting) { return; } - } - for (int i = 0; i < server.transactions->length; ++i) { - struct sway_transaction *transaction = server.transactions->items[i]; transaction_apply(transaction); transaction_destroy(transaction); + list_del(server.transactions, 0); } - server.transactions->length = 0; idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); } -- cgit v1.2.3 From 27a20a488465468511de9b2307941ac1bc4db8bf Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 25 Jul 2018 20:56:23 +1000 Subject: Allow containers to be fullscreen --- sway/desktop/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 2a89880a..ee7a0704 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -110,6 +110,7 @@ static void copy_pending_state(struct sway_container *container, state->swayc_y = container->y; state->swayc_width = container->width; state->swayc_height = container->height; + state->is_fullscreen = container->is_fullscreen; state->has_gaps = container->has_gaps; state->current_gaps = container->current_gaps; state->gaps_inner = container->gaps_inner; @@ -122,7 +123,6 @@ static void copy_pending_state(struct sway_container *container, state->view_y = view->y; state->view_width = view->width; state->view_height = view->height; - state->is_fullscreen = view->is_fullscreen; state->border = view->border; state->border_thickness = view->border_thickness; state->border_top = view->border_top; -- cgit v1.2.3 From 073ac425d5bf6f6393eb91d9b5f84e3caa68f511 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 15:19:14 +1000 Subject: Fix use after free in transactions In set_instructions_ready, calling set_instruction_ready may cause any number of transactions to get applied, which removes them from the list being iterated. The iteration variables need to be adjusted accordingly. --- sway/desktop/transaction.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ee7a0704..0a24c4fc 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -364,7 +364,13 @@ static void set_instructions_ready(struct sway_view *view, int index) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; if (!instruction->ready) { + // set_instruction_ready can remove instructions from the list we're + // iterating + size_t length = view->swayc->instructions->length; set_instruction_ready(instruction); + size_t num_removed = length - view->swayc->instructions->length; + i -= num_removed; + index -= num_removed; } } } -- cgit v1.2.3 From 52cf410d3cfcf0cae81b47c90097867c4e4d8564 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 17:43:18 +1000 Subject: Second attempt at fixing transaction use-after-free The solution used in 073ac425d5bf6f6393eb91d9b5f84e3caa68f511 doesn't work in all cases because the freed instruction might be ahead in the list, not necessarily behind. The new solution delays running the queue until after the loop has finished iterating, thus avoiding the problem completely. --- sway/desktop/transaction.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 0a24c4fc..ccda1963 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -344,13 +344,11 @@ static void set_instruction_ready( } - // If all views are ready, apply the transaction. // If the transaction has timed out then its num_waiting will be 0 already. if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { if (!txn_debug) { wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); - transaction_progress_queue(); } } } @@ -364,15 +362,10 @@ static void set_instructions_ready(struct sway_view *view, int index) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; if (!instruction->ready) { - // set_instruction_ready can remove instructions from the list we're - // iterating - size_t length = view->swayc->instructions->length; set_instruction_ready(instruction); - size_t num_removed = length - view->swayc->instructions->length; - i -= num_removed; - index -= num_removed; } } + transaction_progress_queue(); } void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { -- cgit v1.2.3 From 32663b7b013e9c0fd37c1c86d6c26bc3156e1c3a Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 09:22:37 +1000 Subject: Handle out-of-fd situations gracefully for transaction and urgent timers --- sway/desktop/transaction.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ccda1963..a9c9cb58 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -316,7 +316,14 @@ static void transaction_commit(struct sway_transaction *transaction) { // Set up a timer which the views must respond within transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, handle_timeout, transaction); - wl_event_source_timer_update(transaction->timer, txn_timeout_ms); + if (transaction->timer) { + wl_event_source_timer_update(transaction->timer, txn_timeout_ms); + } else { + wlr_log(WLR_ERROR, "Unable to create transaction timer. " + "There might not be any available file descriptors. " + "Some imperfect frames might be rendered."); + handle_timeout(transaction); + } } // The debug tree shows the pending/live tree. Here is a good place to -- cgit v1.2.3 From d6daf10cad540f7581e9a325a041333d1113cae5 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 10:00:04 +1000 Subject: Show errno description in log --- sway/desktop/transaction.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index a9c9cb58..17e3f467 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -319,9 +319,9 @@ static void transaction_commit(struct sway_transaction *transaction) { if (transaction->timer) { wl_event_source_timer_update(transaction->timer, txn_timeout_ms); } else { - wlr_log(WLR_ERROR, "Unable to create transaction timer. " - "There might not be any available file descriptors. " - "Some imperfect frames might be rendered."); + wlr_log(WLR_ERROR, "Unable to create transaction timer (%s). " + "Some imperfect frames might be rendered.", + strerror(errno)); handle_timeout(transaction); } } -- cgit v1.2.3 From a4bcddcfdc67ef64edf3737342a99c4e41cae6d4 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 10:24:04 +1000 Subject: Include errno.h --- sway/desktop/transaction.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 17e3f467..7975366e 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include #include #include #include -- cgit v1.2.3 From d10ccc1eb144e4de2477398f6b11753f6b7df70b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 1 Aug 2018 16:23:11 +1000 Subject: Correctly track saved surfaces during multiple transactions Fixes #2364. Suppose a view is 600px wide, and we tell it to resize to 601px during a resize operation. We create a transaction, save the 600px buffer and send the configure. This buffer is saved into the associated instruction, and is rendered while we wait for the view to commit a 601px buffer. Before the view commits the 601px buffer, suppose we tell it to resize to 602px. The new transaction will also save the buffer, but it's still the 600px buffer because we haven't received a new one yet. Then suppose the view commits its original 601px buffer. This completes the first transaction, so we apply the 601px width to the container. There's still the second (now only) transaction remaining, so we render the saved buffer from that. But this is still the 600px buffer, and we believe it's 601px. Whoops. The problem here is we can't stack buffers like this. So this commit removes the saved buffer from the instructions, places it in the view instead, and re-saves the latest buffer every time the view completes a transaction and still has further pending transactions. As saved buffers are now specific to views rather than instructions, the functions for saving and removing the saved buffer have been moved to view.c. The calls to save and restore the buffer have been relocated to more appropriate functions too, favouring transaction_commit and transaction_apply rather than transaction_add_container and transaction_destroy. --- sway/desktop/transaction.c | 51 +++++++++++----------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7975366e..94070363 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -41,8 +41,6 @@ struct sway_transaction_instruction { struct sway_transaction *transaction; struct sway_container *container; struct sway_container_state state; - struct wlr_buffer *saved_buffer; - int saved_buffer_width, saved_buffer_height; uint32_t serial; bool ready; }; @@ -57,27 +55,6 @@ static struct sway_transaction *transaction_create() { return transaction; } -static void remove_saved_view_buffer( - struct sway_transaction_instruction *instruction) { - if (instruction->saved_buffer) { - wlr_buffer_unref(instruction->saved_buffer); - instruction->saved_buffer = NULL; - } -} - -static void save_view_buffer(struct sway_view *view, - struct sway_transaction_instruction *instruction) { - if (!sway_assert(instruction->saved_buffer == NULL, - "Didn't expect instruction to have a saved buffer already")) { - remove_saved_view_buffer(instruction); - } - if (view->surface && wlr_surface_has_buffer(view->surface)) { - instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); - instruction->saved_buffer_width = view->surface->current.width; - instruction->saved_buffer_height = view->surface->current.height; - } -} - static void transaction_destroy(struct sway_transaction *transaction) { // Free instructions for (int i = 0; i < transaction->instructions->length; ++i) { @@ -93,7 +70,6 @@ static void transaction_destroy(struct sway_transaction *transaction) { if (con->destroying && !con->instructions->length) { container_free(con); } - remove_saved_view_buffer(instruction); free(instruction); } list_free(transaction->instructions); @@ -158,9 +134,6 @@ static void transaction_add_container(struct sway_transaction *transaction, copy_pending_state(container, &instruction->state); - if (container->type == C_VIEW) { - save_view_buffer(container->sway_view, instruction); - } list_add(transaction->instructions, instruction); } @@ -220,6 +193,15 @@ static void transaction_apply(struct sway_transaction *transaction) { memcpy(&container->current, &instruction->state, sizeof(struct sway_container_state)); + + if (container->type == C_VIEW) { + if (container->sway_view->saved_buffer) { + view_remove_saved_buffer(container->sway_view); + } + if (container->instructions->length > 1) { + view_save_buffer(container->sway_view); + } + } } } @@ -294,6 +276,9 @@ static void transaction_commit(struct sway_transaction *transaction) { // mapping and its default geometry doesn't intersect an output. struct timespec when; wlr_surface_send_frame_done(con->sway_view->surface, &when); + if (!con->sway_view->saved_buffer) { + view_save_buffer(con->sway_view); + } } list_add(con->instructions, instruction); } @@ -400,18 +385,6 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, } } -struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, - int *width, int *height) { - struct sway_transaction_instruction *instruction = - view->swayc->instructions->items[0]; - if (!instruction->saved_buffer) { - return NULL; - } - *width = instruction->saved_buffer_width; - *height = instruction->saved_buffer_height; - return instruction->saved_buffer->texture; -} - void transaction_commit_dirty(void) { if (!server.dirty_containers->length) { return; -- cgit v1.2.3 From 8314019f660cd28fc8cdb634f82b437105074258 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 2 Aug 2018 20:54:03 +1000 Subject: Fix race condition crashes when unmapping views This fixes two issues which were both introduced in #2396. First issue: The PR changes the location of the buffer save to transaction_apply, but puts it inside the should_configure block. For unmapping (destroying) views, should_configure returns false so it wasn't saving the buffer. If a frame was rendered between the unmap and the transaction applying then it would result in a crash. Second issue: If a destroying view is involved in two transactions, we must not release the buffer between the transactions because there is no live buffer to grab any more. --- sway/desktop/transaction.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'sway/desktop/transaction.c') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 94070363..4e6af86a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -195,11 +195,18 @@ static void transaction_apply(struct sway_transaction *transaction) { sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - if (container->sway_view->saved_buffer) { - view_remove_saved_buffer(container->sway_view); - } - if (container->instructions->length > 1) { - view_save_buffer(container->sway_view); + if (container->destroying) { + if (container->instructions->length == 1 && + container->sway_view->saved_buffer) { + view_remove_saved_buffer(container->sway_view); + } + } else { + if (container->sway_view->saved_buffer) { + view_remove_saved_buffer(container->sway_view); + } + if (container->instructions->length > 1) { + view_save_buffer(container->sway_view); + } } } } @@ -276,9 +283,9 @@ static void transaction_commit(struct sway_transaction *transaction) { // mapping and its default geometry doesn't intersect an output. struct timespec when; wlr_surface_send_frame_done(con->sway_view->surface, &when); - if (!con->sway_view->saved_buffer) { - view_save_buffer(con->sway_view); - } + } + if (con->type == C_VIEW && !con->sway_view->saved_buffer) { + view_save_buffer(con->sway_view); } list_add(con->instructions, instruction); } -- cgit v1.2.3