summaryrefslogtreecommitdiff
path: root/src/common/app.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 13:14:28 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 13:14:28 +0300
commit71091ca35176f62469311fbf02f6e9333b5f6f0b (patch)
tree4e804e60338b99556d05ad3d50680e54e912cb89 /src/common/app.c
parentf6a42623ec1e3bc718ae685ddbb0be118364121e (diff)
fix(app): free app surfaces only when no callback runs
app_runner_unregister_surface freed the runner's entry at once. A frame or event callback that destroyed its own surface returned into code that still read that entry: app_runner_surface_draw read as->pacing, and the input loop read as->surface and as->next. yasl-app-test hit this under ASan. Unregister now only clears the entry's surface. The run loop frees dead entries before each iteration and at shutdown, when no callback runs. The unused frame_in_progress flag is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/common/app.c')
-rw-r--r--src/common/app.c51
1 files changed, 29 insertions, 22 deletions
diff --git a/src/common/app.c b/src/common/app.c
index 5350f85..1916c5b 100644
--- a/src/common/app.c
+++ b/src/common/app.c
@@ -14,6 +14,8 @@ struct user_event_node {
struct user_event_node *next;
};
+/* `surface` is NULL once the surface is destroyed. A callback may still
+ * hold the entry then, so only app_runner_free_dead() frees it. */
struct app_surface {
struct yasl_surface *surface;
struct app_runner *runner;
@@ -24,7 +26,6 @@ struct app_surface {
struct yasl_vector2i last_size;
uint32_t last_scale_120;
uint64_t frame_index;
- bool frame_in_progress;
bool presented;
struct app_surface *next;
};
@@ -49,7 +50,6 @@ static void app_runner_surface_draw(struct yasl_surface *s, struct yasl_frame *f
if (!ar || !ar->desc || !ar->desc->frame)
return;
- as->frame_in_progress = true;
as->presented = false;
struct yasl_frame_context ctx = {
@@ -75,11 +75,8 @@ static void app_runner_surface_draw(struct yasl_surface *s, struct yasl_frame *f
ar->desc->frame(ar->desc->userdata, s, &ctx);
- if (as->pacing == YASL_PACING_CONTINUOUS && ar->running) {
+ if (as->surface && as->pacing == YASL_PACING_CONTINUOUS && ar->running)
yasl_surface_schedule_redraw(s);
- }
-
- as->frame_in_progress = false;
}
static void app_runner_surface_configure(struct yasl_surface *s, struct yasl_geometry geom, void *ud)
@@ -104,6 +101,8 @@ static void app_runner_surface_configure(struct yasl_surface *s, struct yasl_geo
};
if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
ar->running = false;
+ if (!as->surface)
+ return;
}
struct yasl_event ev = {
@@ -117,7 +116,8 @@ static void app_runner_surface_configure(struct yasl_surface *s, struct yasl_geo
if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
ar->running = false;
- yasl_surface_schedule_redraw(s);
+ if (as->surface)
+ yasl_surface_schedule_redraw(s);
}
static void app_runner_surface_close(struct yasl_surface *s, void *ud)
@@ -172,19 +172,23 @@ void app_runner_unregister_surface(void *runner, struct yasl_surface *s)
return;
struct app_runner *ar = runner;
struct app_surface *as = s->app_surface;
+ as->surface = NULL;
+ s->app_surface = NULL;
+ ar->surface_count--;
+}
- struct app_surface **curr = &ar->surfaces;
- while (*curr) {
- if (*curr == as) {
- *curr = as->next;
- break;
+/* Call only when no callback runs. */
+static void app_runner_free_dead(struct app_runner *ar)
+{
+ for (struct app_surface **p = &ar->surfaces; *p;) {
+ struct app_surface *as = *p;
+ if (as->surface) {
+ p = &as->next;
+ continue;
}
- curr = &(*curr)->next;
+ *p = as->next;
+ free(as);
}
-
- ar->surface_count--;
- s->app_surface = NULL;
- free(as);
}
bool yasl_frame_present_cpu(struct yasl_frame_context *ctx,
@@ -343,7 +347,8 @@ static void app_runner_dispatch_input_events(struct app_runner *ar)
{
for (struct app_surface *as = ar->surfaces; as && ar->running; as = as->next) {
struct yasl_input_event ie;
- while (yasl_surface_input_poll(as->surface, &ie) && ar->running) {
+ while (as->surface && yasl_surface_input_poll(as->surface, &ie) &&
+ ar->running) {
if (!ar->desc->event)
continue;
@@ -473,6 +478,7 @@ int yasl_run(const struct yasl_app_desc *desc)
bool has_frame_pacing = (caps & YASL_CAP_FRAME_PACING) != 0;
while (runner.running) {
+ app_runner_free_dead(&runner);
if (runner.had_surfaces && runner.surface_count == 0) {
runner.running = false;
break;
@@ -503,7 +509,7 @@ int yasl_run(const struct yasl_app_desc *desc)
if (!has_frame_pacing && any_continuous && runner.running) {
for (struct app_surface *as = runner.surfaces; as; as = as->next) {
- if (as->pacing == YASL_PACING_CONTINUOUS) {
+ if (as->surface && as->pacing == YASL_PACING_CONTINUOUS) {
yasl_surface_schedule_redraw(as->surface);
}
}
@@ -515,10 +521,11 @@ int yasl_run(const struct yasl_app_desc *desc)
}
/* Destroy any surfaces that were not explicitly destroyed by the application */
- while (runner.surfaces) {
- struct yasl_surface *s = runner.surfaces->surface;
- yasl_surface_destroy(s);
+ for (struct app_surface *as = runner.surfaces; as; as = as->next) {
+ if (as->surface)
+ yasl_surface_destroy(as->surface);
}
+ app_runner_free_dead(&runner);
d->app_runner = NULL;