-
Notifications
You must be signed in to change notification settings - Fork 1
/
athr.c
170 lines (145 loc) · 4.64 KB
/
athr.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include "athr.h"
#include "athr_elapsed.h"
#include "athr_ema.h"
#include "athr_logger.h"
#include "athr_thread.h"
#include "athr_widget_bar.h"
#include "athr_widget_eta.h"
#include "athr_widget_main.h"
#include "athr_widget_perc.h"
#include "athr_widget_text.h"
#include <stdatomic.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define error(msg) athr_logger_error(athr_logger_format(msg))
static atomic_bool disable_thread = false;
static void lock(struct athr *at)
{
while (atomic_flag_test_and_set(&at->lock))
/* spin until the lock is acquired */;
}
static void unlock(struct athr *x) { atomic_flag_clear(&x->lock); }
static void update(struct athr *x)
{
lock(x);
uint_fast64_t consumed = atomic_load(&x->consumed);
if (consumed > x->total) consumed = x->total;
if (consumed == x->last_consumed) goto cleanup;
uint_fast64_t delta = consumed - x->last_consumed;
x->last_consumed = consumed;
if (athr_elapsed_stop(x->elapsed)) error("failed to elapsed_stop");
double seconds = ((double)athr_elapsed_milliseconds(x->elapsed)) / 1000.;
double progress = ((double)delta) / ((double)x->total);
if (progress < 0.005f && x->timestep < ATHR_TIMESTEP_LIMIT)
{
x->timestep += ATHR_TIMESTEP;
if (x->timestep > ATHR_TIMESTEP_LIMIT)
x->timestep = ATHR_TIMESTEP_LIMIT;
}
athr_ema_add(&x->speed, progress / seconds);
double consumed_fraction = ((double)consumed) / ((double)x->total);
x->main.super.vtable->update(&x->main.super, consumed_fraction,
athr_ema_get(&x->speed));
if (athr_elapsed_start(x->elapsed)) error("failed to elapsed_start");
cleanup:
unlock(x);
}
static void thread_start(void *args)
{
struct athr *at = (struct athr *)args;
while (!atomic_load(&at->stop) && !atomic_load(&disable_thread))
{
update(at);
if (athr_sleep(at->timestep)) error("failed to sleep");
}
}
int athr_start(struct athr *x, uint64_t total, const char *desc,
enum athr_option opts)
{
if (desc == NULL) desc = "";
x->timestep = ATHR_TIMESTEP;
x->total = total;
x->consumed = 0;
x->last_consumed = 0;
x->speed = ATHR_EMA_INIT;
x->elapsed = athr_elapsed_new();
x->total_elapsed = athr_elapsed_new();
if (!x->elapsed || !x->total_elapsed)
{
athr_elapsed_del(x->elapsed);
athr_elapsed_del(x->total_elapsed);
x->elapsed = NULL;
x->total_elapsed = NULL;
error("failed to allocate elapsed struct");
return 1;
}
if (athr_elapsed_start(x->elapsed) || athr_elapsed_start(x->total_elapsed))
{
athr_elapsed_del(x->elapsed);
athr_elapsed_del(x->total_elapsed);
x->elapsed = NULL;
x->total_elapsed = NULL;
error("failed to elapsed_start");
return 1;
}
x->opts = opts;
athr_widget_main_create(&x->main);
athr_widget_text_create(athr_widget_main_add_text(&x->main), desc);
if (opts & ATHR_PERC)
athr_widget_perc_create(athr_widget_main_add_perc(&x->main));
if (opts & ATHR_BAR)
athr_widget_bar_create(athr_widget_main_add_bar(&x->main));
if (opts & ATHR_ETA)
athr_widget_eta_create(athr_widget_main_add_eta(&x->main));
athr_widget_main_setup(&x->main);
atomic_store(&x->stop, false);
if (!atomic_load(&disable_thread))
{
int rc = athr_thread_create(&x->thr, thread_start, x);
if (rc)
{
athr_elapsed_del(x->elapsed);
athr_elapsed_del(x->total_elapsed);
x->elapsed = NULL;
x->total_elapsed = NULL;
}
return rc;
}
return 0;
}
void athr_eat(struct athr *x, uint64_t amount)
{
atomic_fetch_add(&x->consumed, amount);
if (atomic_load(&disable_thread)) update(x);
}
void athr_stop(struct athr *x)
{
atomic_store(&x->stop, true);
update(x);
athr_thread_join(&x->thr);
if (athr_elapsed_stop(x->total_elapsed)) error("failed to elapsed_stop");
double seconds = ((double)athr_elapsed_milliseconds(x->total_elapsed)) / 1000.;
x->main.super.vtable->finish(&x->main.super, seconds);
athr_canvas_close(&x->main.canvas);
athr_elapsed_del(x->elapsed);
athr_elapsed_del(x->total_elapsed);
x->elapsed = NULL;
x->total_elapsed = NULL;
}
void athr_stop_wait(struct athr *at)
{
atomic_store(&at->stop, true);
update(at);
athr_thread_join(&at->thr);
athr_canvas_close(&at->main.canvas);
}
void athr_disable_threading(bool disable)
{
atomic_store(&disable_thread, disable);
}
int athr_sleep(unsigned milliseconds)
{
return athr_elapsed_sleep((unsigned)milliseconds);
}