83bool progress_start(
size_t total,
int threads,
const char *message);
95void progress_add(
size_t amount);
103void progress_flush(
void);
111bool progress_end(
void);
113#ifdef PROGRESS_EXTERN_DISABLE
114extern bool progress_disable;
118#if defined(PROGRESS_IMPLEMENTATION) && !defined(_PROGRESS_IMPLEMENTED)
119#define _PROGRESS_IMPLEMENTED
122#include <stdatomic.h>
125#ifndef PROGRESS_PRINT_H
126#define PROGRESS_PRINT_H 0
130#error "Include print.h before including progress.h"
132#undef PROGRESS_PRINT_H
133#define PROGRESS_PRINT_H 1
138#define progress_bar(pct, ...) \
140 printf("\r%3d%% ", pct); \
141 printf(__VA_ARGS__); \
149#define progress_err(...) perr(__VA_ARGS__)
151#define progress_err(...) fprintf(stderr, __VA_ARGS__)
157#define progress_dev(...) pdev(__VA_ARGS__)
159#define progress_dev(...) fprintf(stderr, __VA_ARGS__)
163static _Thread_local
size_t t_done;
165static _Alignas(64) _Atomic(
size_t) p_done;
166static thrd_t p_monitor_thrd;
168static
size_t p_total;
169static
size_t p_update_limit;
170static const
char *p_message;
172static _Atomic(
bool) p_running;
173static
bool progress_disable;
178static
int p_monitor(
void *arg)
181 progress_bar(0,
"%s", p_message);
183 struct timespec timeout;
186 while (atomic_load_explicit(&p_running, memory_order_acquire)) {
187 current = atomic_load_explicit(&p_done, memory_order_relaxed);
188 progress_bar((
int)(100 * current / p_total),
"%s", p_message);
190 if (timespec_get(&timeout, TIME_UTC) == 0) {
194 timeout.tv_nsec += 250000000;
195 if (timeout.tv_nsec >= 1000000000) {
197 timeout.tv_nsec -= 1000000000;
201 cnd_timedwait(&p_cond, &p_mutex, &timeout);
202 mtx_unlock(&p_mutex);
205 progress_bar(100,
"%s", p_message);
209bool progress_start(
size_t total,
int threads,
const char *message)
211 if (progress_disable)
214 if (atomic_load_explicit(&p_running, memory_order_relaxed))
215 goto p_monitor_running_error;
217 atomic_store_explicit(&p_running,
true, memory_order_relaxed);
218 atomic_store_explicit(&p_done, 0, memory_order_relaxed);
223 p_update_limit = total / ((size_t)threads * 100);
227 if (mtx_init(&p_mutex, mtx_plain) != thrd_success)
228 goto p_monitor_mtx_error;
229 if (cnd_init(&p_cond) != thrd_success)
230 goto p_monitor_cnd_error;
232 if (thrd_create(&p_monitor_thrd, p_monitor, NULL) == thrd_success)
235 cnd_destroy(&p_cond);
237 mtx_destroy(&p_mutex);
239 atomic_store_explicit(&p_running,
false, memory_order_relaxed);
240p_monitor_running_error:
241 progress_err(
"Failed to create a progress bar display");
245void progress_flush(
void)
247 if (progress_disable || !t_done)
250 atomic_fetch_add_explicit(&p_done, t_done, memory_order_relaxed);
254void progress_add(
size_t amount)
256 if (progress_disable || (t_done += amount) < p_update_limit)
262bool progress_end(
void)
264 if (progress_disable)
267 if (!atomic_load_explicit(&p_running, memory_order_relaxed)) {
268 progress_dev(
"Tried to end non-running progress monitor");
269 progress_err(
"Internal error during progress bar display");
274 atomic_store_explicit(&p_done, p_total, memory_order_relaxed);
275 atomic_store_explicit(&p_running,
false, memory_order_release);
277 thrd_join(p_monitor_thrd, NULL);
278 cnd_destroy(&p_cond);
279 mtx_destroy(&p_mutex);