]> git.ipfire.org Git - pakfire.git/commitdiff
progressbar: Drop timer in favour of a render thread
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 9 Sep 2022 15:53:39 +0000 (15:53 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 9 Sep 2022 15:53:39 +0000 (15:53 +0000)
The timer might fire after the progressbar has been destroyed resulting
in random SEGV.

We are now launching one(!) thread which will perform the entire
rendering and run for as long as the progressbar is in running mode.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/progressbar.c

index 9ff9adb7baf6faac218f7779e504a092ebf45001..98031667f16d00d68cd0d6511d824688e046d7a7 100644 (file)
 
 #define DRAWS_PER_SECOND 20
 
-static const struct itimerspec TIMER = {
-       .it_interval = {
-               .tv_sec  = 0,
-               .tv_nsec = 1000000000 / DRAWS_PER_SECOND,
-       },
-       .it_value = {
-               .tv_sec  = 0,
-               .tv_nsec = 1000000000 / DRAWS_PER_SECOND,
-       },
+static const struct timespec TIMER = {
+       .tv_sec  = 0,
+       .tv_nsec = 1000000000 / DRAWS_PER_SECOND,
 };
 
 struct pakfire_progressbar_widget {
@@ -65,19 +59,20 @@ struct pakfire_progressbar {
        int nrefs;
        FILE* f;
 
-       enum pakfire_progressbar_status {
+       volatile enum pakfire_progressbar_status {
                PAKFIRE_PROGRESSBAR_INIT = 0,
                PAKFIRE_PROGRESSBAR_RUNNING,
                PAKFIRE_PROGRESSBAR_FINISHED,
        } status;
 
        // The progress
-       unsigned long value;
-       unsigned long value_max;
+       volatile unsigned long value;
+       volatile unsigned long value_max;
 
        struct timespec time_start;
-       timer_t timer;
-       pthread_mutex_t drawlock;
+
+       // Reference to the thread that is drawing the progress bar
+       pthread_t renderer;
 
        // Widgets
        STAILQ_HEAD(widgets, pakfire_progressbar_widget) widgets;
@@ -133,25 +128,12 @@ static void pakfire_progressbar_free_widgets(struct pakfire_progressbar* p) {
 }
 
 static void pakfire_progressbar_free(struct pakfire_progressbar* p) {
-       // Delete the timer
-       if (p->timer)
-               timer_delete(p->timer);
-
-       // Free muted
-       pthread_mutex_destroy(&p->drawlock);
-
        pakfire_progressbar_free_widgets(p);
        free(p);
 }
 
 static int pakfire_progressbar_draw(struct pakfire_progressbar* p) {
        struct pakfire_progressbar_widget* widget;
-       int r;
-
-       // Acquire the lock
-       r = pthread_mutex_lock(&p->drawlock);
-       if (r)
-               return r;
 
        // Update terminal size if not set
        if (!terminal.cols)
@@ -222,23 +204,11 @@ static int pakfire_progressbar_draw(struct pakfire_progressbar* p) {
        // Flush everything
        fflush(p->f);
 
-       // Release lock
-       pthread_mutex_unlock(&p->drawlock);
-
        return 0;
 }
 
-static void __pakfire_progressbar_draw(union sigval data) {
-       struct pakfire_progressbar* p = (struct pakfire_progressbar*)data.sival_ptr;
-
-       // Draw unconditionally
-       pakfire_progressbar_draw(p);
-}
-
 PAKFIRE_EXPORT int pakfire_progressbar_create(
                struct pakfire_progressbar** progressbar, FILE* f) {
-       int r;
-
        // Allocate main object
        struct pakfire_progressbar* p = calloc(1, sizeof(*p));
        if (!p)
@@ -254,38 +224,16 @@ PAKFIRE_EXPORT int pakfire_progressbar_create(
        // Store output
        p->f = stdout;
 
-       // Acquire lock
-       r = pthread_mutex_init(&p->drawlock, NULL);
-       if (r)
-               goto ERROR;
-
        // Register signal handler to update terminal size
        signal(SIGWINCH, pakfire_progressbar_update_terminal_size);
 
        // Setup widgets
        STAILQ_INIT(&p->widgets);
 
-       struct sigevent sigevent = {
-               .sigev_notify = SIGEV_THREAD,
-               .sigev_notify_function = __pakfire_progressbar_draw,
-               .sigev_value = {
-                       .sival_ptr = p,
-               },
-       };
-
-       // Create timer
-       r = timer_create(CLOCK_REALTIME, &sigevent, &p->timer);
-       if (r)
-               goto ERROR;
-
        // Done
        *progressbar = p;
-       return 0;
 
-ERROR:
-       pakfire_progressbar_free(p);
-
-       return r;
+       return 0;
 }
 
 PAKFIRE_EXPORT struct pakfire_progressbar* pakfire_progressbar_ref(struct pakfire_progressbar* p) {
@@ -312,7 +260,49 @@ static time_t pakfire_progressbar_elapsed_time(struct pakfire_progressbar* p) {
        return now.tv_sec - p->time_start.tv_sec;
 }
 
+static void* __pakfire_progressbar_renderer(void* __p) {
+       struct pakfire_progressbar* p = (struct pakfire_progressbar*)__p;
+       int r;
+
+       do {
+               // Redraw the progress bar
+               r = pakfire_progressbar_draw(p);
+               if (r)
+                       goto ERROR;
+
+               // Sleep for a short moment
+               nanosleep(&TIMER, NULL);
+
+       // Keep doing this for as long as we are running
+       } while (p->status == PAKFIRE_PROGRESSBAR_RUNNING);
+
+       // Set to maximum value
+       r = pakfire_progressbar_update(p, p->value_max);
+       if (r)
+               goto ERROR;
+
+       // Perform a final draw
+       r = pakfire_progressbar_draw(p);
+       if (r)
+               goto ERROR;
+
+       // Finish line
+       r = fputs("\n", p->f);
+       if (r <= 0 || r == EOF) {
+               r = 1;
+               goto ERROR;
+       }
+
+       // Success
+       r = 0;
+
+ERROR:
+       return (void *)(intptr_t)r;
+}
+
 PAKFIRE_EXPORT int pakfire_progressbar_start(struct pakfire_progressbar* p, unsigned long value) {
+       int r;
+
        if (p->status == PAKFIRE_PROGRESSBAR_RUNNING)
                return EINVAL;
 
@@ -322,11 +312,6 @@ PAKFIRE_EXPORT int pakfire_progressbar_start(struct pakfire_progressbar* p, unsi
        // Set maximum value
        pakfire_progressbar_set_max(p, value);
 
-       // Start timer
-       int r = timer_settime(p->timer, 0, &TIMER, NULL);
-       if (r)
-               return r;
-
        // Set start time
        r = clock_gettime(CLOCK_REALTIME, &p->time_start);
        if (r)
@@ -337,12 +322,8 @@ PAKFIRE_EXPORT int pakfire_progressbar_start(struct pakfire_progressbar* p, unsi
        if (r)
                return r;
 
-       // Perform an initial draw
-       r = pakfire_progressbar_draw(p);
-       if (r)
-               return r;
-
-       return r;
+       // Launch the renderer thread
+       return pthread_create(&p->renderer, NULL, __pakfire_progressbar_renderer, p);
 }
 
 PAKFIRE_EXPORT int pakfire_progressbar_update(struct pakfire_progressbar* p, unsigned long value) {
@@ -358,26 +339,21 @@ PAKFIRE_EXPORT int pakfire_progressbar_increment(struct pakfire_progressbar* p,
 }
 
 PAKFIRE_EXPORT int pakfire_progressbar_finish(struct pakfire_progressbar* p) {
+       int r;
+       int retval = 0;
+
        if (p->status != PAKFIRE_PROGRESSBAR_RUNNING)
                return EINVAL;
 
        // Set status
        p->status = PAKFIRE_PROGRESSBAR_FINISHED;
 
-       // Set to maximum value
-       int r = pakfire_progressbar_update(p, p->value_max);
+       // Wait until the render thread is done
+       r = pthread_join(p->renderer, (void**)&retval);
        if (r)
                return r;
 
-       // Perform a final draw
-       pakfire_progressbar_draw(p);
-
-       // Finish line
-       r = fputs("\n", p->f);
-       if (r <= 0 || r == EOF)
-               return 1;
-
-       return 0;
+       return retval;
 }
 
 PAKFIRE_EXPORT int pakfire_progressbar_reset(struct pakfire_progressbar* p) {
@@ -421,8 +397,6 @@ static int pakfire_progressbar_add_widget(struct pakfire_progressbar* p,
        return 0;
 }
 
-
-
 // String widget
 
 static const char* pakfire_progressbar_string(struct pakfire_progressbar* p,