Skip to content

Commit a330284

Browse files
committed
Improve backend architecture and memory safety
Backend improvements: - SDL: Add comprehensive error handling with proper resource cleanup - SDL: Implement smart conditional delay to prevent CPU busy-wait - SDL: Fix resource leaks in initialization and exit paths - fbdev: Fix critical memory leaks and invalid return statements - fbdev: Add NULL checks for screen creation - All backends: Standardize main loop using twin_dispatch_once() API refactoring: - Add twin_run() as unified application entry point - Hide platform-specific details from public API Performance: - Reduce idle CPU usage from 100% to <1% in SDL backend - Maintain zero-latency event handling and screen updates
1 parent fab4f4d commit a330284

File tree

8 files changed

+405
-50
lines changed

8 files changed

+405
-50
lines changed

apps/main.c

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,39 @@ static void sigint_handler(int sig)
8282
exit(1);
8383
}
8484

85+
/* Initialize demo applications based on build configuration.
86+
* Shared by both native and WebAssembly builds.
87+
*/
88+
static void init_demo_apps(twin_context_t *ctx)
89+
{
90+
twin_screen_t *screen = ctx->screen;
91+
#if defined(CONFIG_DEMO_MULTI)
92+
apps_multi_start(screen, "Demo", 100, 100, 400, 400);
93+
#endif
94+
#if defined(CONFIG_DEMO_HELLO)
95+
apps_hello_start(screen, "Hello, World", 0, 0, 200, 200);
96+
#endif
97+
#if defined(CONFIG_DEMO_CLOCK)
98+
apps_clock_start(screen, "Clock", 10, 10, 200, 200);
99+
#endif
100+
#if defined(CONFIG_DEMO_CALCULATOR)
101+
apps_calc_start(screen, "Calculator", 100, 100, 200, 200);
102+
#endif
103+
#if defined(CONFIG_DEMO_LINE)
104+
apps_line_start(screen, "Line", 0, 0, 200, 200);
105+
#endif
106+
#if defined(CONFIG_DEMO_SPLINE)
107+
apps_spline_start(screen, "Spline", 20, 20, 400, 400);
108+
#endif
109+
#if defined(CONFIG_DEMO_ANIMATION)
110+
apps_animation_start(screen, "Viewer", ASSET_PATH "nyancat.gif", 20, 20);
111+
#endif
112+
#if defined(CONFIG_DEMO_IMAGE)
113+
apps_image_start(screen, "Viewer", 20, 20);
114+
#endif
115+
twin_screen_set_active(screen, screen->top);
116+
}
117+
85118
int main(void)
86119
{
87120
tx = twin_create(WIDTH, HEIGHT);
@@ -106,34 +139,8 @@ int main(void)
106139
twin_screen_set_background(
107140
tx->screen, load_background(tx->screen, ASSET_PATH "/tux.png"));
108141

109-
#if defined(CONFIG_DEMO_MULTI)
110-
apps_multi_start(tx->screen, "Demo", 100, 100, 400, 400);
111-
#endif
112-
#if defined(CONFIG_DEMO_HELLO)
113-
apps_hello_start(tx->screen, "Hello, World", 0, 0, 200, 200);
114-
#endif
115-
#if defined(CONFIG_DEMO_CLOCK)
116-
apps_clock_start(tx->screen, "Clock", 10, 10, 200, 200);
117-
#endif
118-
#if defined(CONFIG_DEMO_CALCULATOR)
119-
apps_calc_start(tx->screen, "Calculator", 100, 100, 200, 200);
120-
#endif
121-
#if defined(CONFIG_DEMO_LINE)
122-
apps_line_start(tx->screen, "Line", 0, 0, 200, 200);
123-
#endif
124-
#if defined(CONFIG_DEMO_SPLINE)
125-
apps_spline_start(tx->screen, "Spline", 20, 20, 400, 400);
126-
#endif
127-
#if defined(CONFIG_DEMO_ANIMATION)
128-
apps_animation_start(tx->screen, "Viewer", ASSET_PATH "nyancat.gif", 20,
129-
20);
130-
#endif
131-
#if defined(CONFIG_DEMO_IMAGE)
132-
apps_image_start(tx->screen, "Viewer", 20, 20);
133-
#endif
134-
135-
twin_screen_set_active(tx->screen, tx->screen->top);
136-
twin_dispatch(tx);
142+
/* Start application with unified API (handles native and WebAssembly) */
143+
twin_run(tx, init_demo_apps);
137144

138145
return 0;
139146
}

backend/fbdev.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ twin_context_t *twin_fbdev_init(int width, int height)
236236
return NULL;
237237
ctx->priv = calloc(1, sizeof(twin_fbdev_t));
238238
if (!ctx->priv)
239-
return NULL;
239+
goto bail;
240240

241241
twin_fbdev_t *tx = ctx->priv;
242242

@@ -264,7 +264,7 @@ twin_context_t *twin_fbdev_init(int width, int height)
264264
/* Examine if framebuffer mapping is valid */
265265
if (tx->fb_base == MAP_FAILED) {
266266
log_error("Failed to map framebuffer memory");
267-
return;
267+
goto bail_vt_fd;
268268
}
269269

270270
const twin_put_span_t fbdev_put_spans[] = {
@@ -276,6 +276,10 @@ twin_context_t *twin_fbdev_init(int width, int height)
276276
ctx->screen = twin_screen_create(
277277
width, height, NULL, fbdev_put_spans[tx->fb_var.bits_per_pixel / 8 - 2],
278278
ctx);
279+
if (!ctx->screen) {
280+
log_error("Failed to create screen");
281+
goto bail_fb_unmap;
282+
}
279283

280284
/* Create Linux input system object */
281285
tx->input = twin_linux_input_create(ctx->screen);
@@ -294,6 +298,9 @@ twin_context_t *twin_fbdev_init(int width, int height)
294298

295299
bail_screen:
296300
twin_screen_destroy(ctx->screen);
301+
bail_fb_unmap:
302+
if (tx->fb_base != MAP_FAILED)
303+
munmap(tx->fb_base, tx->fb_len);
297304
bail_vt_fd:
298305
close(tx->vt_fd);
299306
bail_fb_fd:
@@ -327,9 +334,27 @@ static void twin_fbdev_exit(twin_context_t *ctx)
327334
free(ctx);
328335
}
329336

337+
/* Start function for fbdev backend
338+
* Note: fbdev uses Linux input system with background thread for events,
339+
* so we use the standard dispatcher for work queue and timeout processing.
340+
*/
341+
static void twin_fbdev_start(twin_context_t *ctx,
342+
void (*init_callback)(twin_context_t *))
343+
{
344+
if (init_callback)
345+
init_callback(ctx);
346+
347+
/* Use standard dispatcher to ensure work queue and timeouts run.
348+
* Events are handled by linux_input background thread.
349+
*/
350+
while (twin_dispatch_once(ctx))
351+
;
352+
}
353+
330354
/* Register the Linux framebuffer backend */
331355
const twin_backend_t g_twin_backend = {
332356
.init = twin_fbdev_init,
333357
.configure = twin_fbdev_configure,
358+
.start = twin_fbdev_start,
334359
.exit = twin_fbdev_exit,
335360
};

backend/headless.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,19 +401,42 @@ static twin_context_t *twin_headless_init_dummy(int width, int height)
401401
(void) height;
402402
return NULL;
403403
}
404+
405+
static void twin_headless_start_dummy(twin_context_t *ctx,
406+
void (*init_callback)(twin_context_t *))
407+
{
408+
(void) ctx;
409+
(void) init_callback;
410+
}
404411
#endif /* HAVE_POSIX_SHM */
405412

413+
#ifdef HAVE_POSIX_SHM
414+
/* Start function for headless backend */
415+
static void twin_headless_start(twin_context_t *ctx,
416+
void (*init_callback)(twin_context_t *))
417+
{
418+
if (init_callback)
419+
init_callback(ctx);
420+
421+
/* Use standard dispatcher to ensure work queue and timeouts run */
422+
while (twin_dispatch_once(ctx))
423+
;
424+
}
425+
#endif
426+
406427
/* Register the headless backend */
407428
const twin_backend_t g_twin_backend = {
408429
#ifdef HAVE_POSIX_SHM
409430
.init = twin_headless_init,
410431
.configure = twin_headless_configure,
411432
.poll = twin_headless_poll,
433+
.start = twin_headless_start,
412434
.exit = twin_headless_exit,
413435
#else
414436
.init = twin_headless_init_dummy,
415437
.configure = twin_headless_config_dummy,
416438
.poll = twin_headless_poll_dummy,
439+
.start = twin_headless_start_dummy,
417440
.exit = twin_headless_exit_dummy,
418441
#endif
419442
};

0 commit comments

Comments
 (0)