mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-11-22 07:02:28 +00:00
output: fix leak of empty back buffer lock
This refactors output_ensure_buffer() to not mutate the state passed,
making the previous subtle behavior much more explicit.
Fixes: d483dd2f
("output: add wlr_output_commit_state")
Closes: #3442
This commit is contained in:
parent
5cca72958a
commit
0deef6fe44
@ -13,6 +13,6 @@ struct wlr_drm_format *output_pick_format(struct wlr_output *output,
|
||||
const struct wlr_drm_format_set *display_formats, uint32_t format);
|
||||
void output_clear_back_buffer(struct wlr_output *output);
|
||||
bool output_ensure_buffer(struct wlr_output *output,
|
||||
struct wlr_output_state *state);
|
||||
const struct wlr_output_state *state, bool *new_back_buffer);
|
||||
|
||||
#endif
|
||||
|
@ -677,22 +677,29 @@ static bool output_basic_test(struct wlr_output *output,
|
||||
|
||||
bool wlr_output_test_state(struct wlr_output *output,
|
||||
const struct wlr_output_state *state) {
|
||||
bool had_buffer = state->committed & WLR_OUTPUT_STATE_BUFFER;
|
||||
|
||||
// Duplicate the state because we might mutate it in output_ensure_buffer
|
||||
struct wlr_output_state pending = *state;
|
||||
if (!output_basic_test(output, &pending)) {
|
||||
return false;
|
||||
}
|
||||
if (!output_ensure_buffer(output, &pending)) {
|
||||
if (!output_basic_test(output, state)) {
|
||||
return false;
|
||||
}
|
||||
if (!output->impl->test) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool success = output->impl->test(output, &pending);
|
||||
if (!had_buffer) {
|
||||
bool new_back_buffer = false;
|
||||
if (!output_ensure_buffer(output, state, &new_back_buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a shallow copy of the state with the new buffer
|
||||
// potentially included to pass to the backend.
|
||||
struct wlr_output_state copy = *state;
|
||||
if (new_back_buffer) {
|
||||
assert((copy.committed & WLR_OUTPUT_STATE_BUFFER) == 0);
|
||||
copy.committed |= WLR_OUTPUT_STATE_BUFFER;
|
||||
copy.buffer = output->back_buffer;
|
||||
}
|
||||
|
||||
bool success = output->impl->test(output, ©);
|
||||
if (new_back_buffer) {
|
||||
output_clear_back_buffer(output);
|
||||
}
|
||||
return success;
|
||||
@ -710,13 +717,23 @@ bool wlr_output_commit_state(struct wlr_output *output,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Duplicate the state because we might mutate it in output_ensure_buffer
|
||||
struct wlr_output_state pending = *state;
|
||||
if (!output_ensure_buffer(output, &pending)) {
|
||||
bool new_back_buffer = false;
|
||||
if (!output_ensure_buffer(output, state, &new_back_buffer)) {
|
||||
output_clear_back_buffer(output);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a shallow copy of the state with the new back buffer
|
||||
// potentially included to pass to the backend.
|
||||
struct wlr_output_state pending = *state;
|
||||
if (new_back_buffer) {
|
||||
assert((pending.committed & WLR_OUTPUT_STATE_BUFFER) == 0);
|
||||
pending.committed |= WLR_OUTPUT_STATE_BUFFER;
|
||||
// Lock the buffer to ensure it stays valid past the
|
||||
// output_clear_back_buffer() call below.
|
||||
pending.buffer = wlr_buffer_lock(output->back_buffer);
|
||||
}
|
||||
|
||||
if ((pending.committed & WLR_OUTPUT_STATE_BUFFER) &&
|
||||
output->idle_frame != NULL) {
|
||||
wl_event_source_remove(output->idle_frame);
|
||||
@ -746,6 +763,9 @@ bool wlr_output_commit_state(struct wlr_output *output,
|
||||
|
||||
if (!output->impl->commit(output, &pending)) {
|
||||
wlr_buffer_unlock(back_buffer);
|
||||
if (new_back_buffer) {
|
||||
wlr_buffer_unlock(pending.buffer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -820,8 +840,9 @@ bool wlr_output_commit_state(struct wlr_output *output,
|
||||
};
|
||||
wlr_signal_emit_safe(&output->events.commit, &event);
|
||||
|
||||
if (back_buffer != NULL) {
|
||||
wlr_buffer_unlock(back_buffer);
|
||||
wlr_buffer_unlock(back_buffer);
|
||||
if (new_back_buffer) {
|
||||
wlr_buffer_unlock(pending.buffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -99,7 +99,7 @@ static bool output_create_swapchain(struct wlr_output *output,
|
||||
}
|
||||
|
||||
static bool output_attach_back_buffer(struct wlr_output *output,
|
||||
struct wlr_output_state *state, int *buffer_age) {
|
||||
const struct wlr_output_state *state, int *buffer_age) {
|
||||
assert(output->back_buffer == NULL);
|
||||
|
||||
if (!output_create_swapchain(output, state, true)) {
|
||||
@ -151,11 +151,11 @@ bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age) {
|
||||
return output_attach_render(output, &output->pending, buffer_age);
|
||||
}
|
||||
|
||||
static bool output_attach_empty_buffer(struct wlr_output *output,
|
||||
struct wlr_output_state *state) {
|
||||
static bool output_attach_empty_back_buffer(struct wlr_output *output,
|
||||
const struct wlr_output_state *state) {
|
||||
assert(!(state->committed & WLR_OUTPUT_STATE_BUFFER));
|
||||
|
||||
if (!output_attach_render(output, state, NULL)) {
|
||||
if (!output_attach_back_buffer(output, state, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -170,8 +170,28 @@ static bool output_attach_empty_buffer(struct wlr_output *output,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool output_test_with_back_buffer(struct wlr_output *output,
|
||||
const struct wlr_output_state *state) {
|
||||
assert(output->impl->test != NULL);
|
||||
|
||||
// Create a shallow copy of the state with the empty back buffer included
|
||||
// to pass to the backend.
|
||||
struct wlr_output_state copy = *state;
|
||||
assert((copy.committed & WLR_OUTPUT_STATE_BUFFER) == 0);
|
||||
copy.committed |= WLR_OUTPUT_STATE_BUFFER;
|
||||
assert(output->back_buffer != NULL);
|
||||
copy.buffer = output->back_buffer;
|
||||
|
||||
return output->impl->test(output, ©);
|
||||
}
|
||||
|
||||
// This function may attach a new, empty back buffer if necessary.
|
||||
// If so, the new_back_buffer out parameter will be set to true.
|
||||
bool output_ensure_buffer(struct wlr_output *output,
|
||||
struct wlr_output_state *state) {
|
||||
const struct wlr_output_state *state,
|
||||
bool *new_back_buffer) {
|
||||
assert(*new_back_buffer == false);
|
||||
|
||||
// If we're lighting up an output or changing its mode, make sure to
|
||||
// provide a new buffer
|
||||
bool needs_new_buffer = false;
|
||||
@ -196,15 +216,16 @@ bool output_ensure_buffer(struct wlr_output *output,
|
||||
|
||||
wlr_log(WLR_DEBUG, "Attaching empty buffer to output for modeset");
|
||||
|
||||
if (!output_attach_empty_buffer(output, state)) {
|
||||
goto error;
|
||||
if (!output_attach_empty_back_buffer(output, state)) {
|
||||
return false;
|
||||
}
|
||||
if (!output->impl->test || output->impl->test(output, state)) {
|
||||
|
||||
if (output_test_with_back_buffer(output, state)) {
|
||||
*new_back_buffer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
output_clear_back_buffer(output);
|
||||
state->committed &= ~WLR_OUTPUT_STATE_BUFFER;
|
||||
|
||||
if (output->swapchain->format->len == 0) {
|
||||
return false;
|
||||
@ -217,17 +238,18 @@ bool output_ensure_buffer(struct wlr_output *output,
|
||||
if (!output_create_swapchain(output, state, false)) {
|
||||
return false;
|
||||
}
|
||||
if (!output_attach_empty_buffer(output, state)) {
|
||||
goto error;
|
||||
}
|
||||
if (!output->impl->test(output, state)) {
|
||||
goto error;
|
||||
}
|
||||
return true;
|
||||
|
||||
error:
|
||||
if (!output_attach_empty_back_buffer(output, state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (output_test_with_back_buffer(output, state)) {
|
||||
*new_back_buffer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
output_clear_back_buffer(output);
|
||||
state->committed &= ~WLR_OUTPUT_STATE_BUFFER;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user