mirror of
https://github.com/rkd77/elinks.git
synced 2024-10-07 04:53:37 -04:00
d6fd2ac31f
Previously, each progress timer function registered with start_update_progress() was directly used as the timer function of progress.timer, so it was responsible of erasing the expired timer ID from that member. Failing to do this could result in heap corruption. The progress timer functions normally fulfilled the requirement by calling update_progress(), but one such function upload_stat_timer() had to erase the timer ID on its own too. Now instead, there is a wrapper function progress_timeout(), which progress.c sets as the timer function of progress.timer. This wrapper erases the expired timer ID from progress.timer and then calls the progress timer function registered with start_update_progress(). So the progress timer function is no longer responsible of erasing the timer ID and there's no risk that it could fail to do that in some error situation. This commit introduces a new risk though. Previously, if the struct progress was freed while the timer was running, the (progress) timer function would still be called, and it would be able to detect that the progress pointer is NULL and recover from this situation. Now, the timer function progress_timeout() has a pointer to the struct progress and will dereference that pointer without being able to check whether the structure has been freed. Fortunately, done_progress() asserts that the timer is not running, so this should not occur.
132 lines
3.7 KiB
C
132 lines
3.7 KiB
C
/* Downloads progression stuff. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "network/progress.h"
|
|
#include "util/error.h"
|
|
#include "util/memory.h"
|
|
#include "util/time.h"
|
|
|
|
#define SPD_DISP_TIME ((milliseconds_T) 100)
|
|
#define CURRENT_SPD_AFTER ((milliseconds_T) 100)
|
|
|
|
|
|
int
|
|
has_progress(struct progress *progress)
|
|
{
|
|
timeval_T current_speed_after;
|
|
|
|
timeval_from_milliseconds(¤t_speed_after, CURRENT_SPD_AFTER);
|
|
|
|
return (timeval_cmp(&progress->elapsed, ¤t_speed_after) >= 0);
|
|
}
|
|
|
|
struct progress *
|
|
init_progress(off_t start)
|
|
{
|
|
struct progress *progress = mem_calloc(1, sizeof(*progress));
|
|
|
|
if (progress) {
|
|
progress->start = start;
|
|
progress->timer = TIMER_ID_UNDEF;
|
|
}
|
|
|
|
return progress;
|
|
}
|
|
|
|
void
|
|
done_progress(struct progress *progress)
|
|
{
|
|
assert(progress->timer == TIMER_ID_UNDEF);
|
|
mem_free(progress);
|
|
}
|
|
|
|
/** Timer callback for progress.timer. As explained in install_timer(),
|
|
* this function must erase the expired timer ID from all variables. */
|
|
static void
|
|
progress_timeout(void *progress_voidptr)
|
|
{
|
|
struct progress *const progress = progress_voidptr;
|
|
|
|
progress->timer = TIMER_ID_UNDEF;
|
|
/* The expired timer ID has now been erased. */
|
|
|
|
progress->timer_func(progress->timer_func_data);
|
|
}
|
|
|
|
/* Usually called from the timer callback of @progress->timer. */
|
|
void
|
|
update_progress(struct progress *progress, off_t loaded, off_t size, off_t pos)
|
|
{
|
|
off_t bytes_delta;
|
|
timeval_T now, elapsed, dis_b_max, dis_b_interval;
|
|
|
|
timeval_now(&now);
|
|
timeval_sub(&elapsed, &progress->last_time, &now);
|
|
timeval_copy(&progress->last_time, &now);
|
|
|
|
progress->loaded = loaded;
|
|
bytes_delta = progress->loaded - progress->last_loaded;
|
|
progress->last_loaded = progress->loaded;
|
|
|
|
timeval_add_interval(&progress->elapsed, &elapsed);
|
|
|
|
timeval_add_interval(&progress->dis_b, &elapsed);
|
|
timeval_from_milliseconds(&dis_b_max, mult_ms(SPD_DISP_TIME, CURRENT_SPD_SEC));
|
|
timeval_from_milliseconds(&dis_b_interval, SPD_DISP_TIME);
|
|
|
|
while (timeval_cmp(&progress->dis_b, &dis_b_max) >= 0) {
|
|
progress->cur_loaded -= progress->data_in_secs[0];
|
|
memmove(progress->data_in_secs, progress->data_in_secs + 1,
|
|
sizeof(*progress->data_in_secs) * (CURRENT_SPD_SEC - 1));
|
|
progress->data_in_secs[CURRENT_SPD_SEC - 1] = 0;
|
|
timeval_sub_interval(&progress->dis_b, &dis_b_interval);
|
|
}
|
|
progress->data_in_secs[CURRENT_SPD_SEC - 1] += bytes_delta;
|
|
progress->cur_loaded += bytes_delta;
|
|
|
|
progress->current_speed = progress->cur_loaded / (CURRENT_SPD_SEC * ((long) SPD_DISP_TIME) / 1000);
|
|
|
|
progress->pos = pos;
|
|
progress->size = size;
|
|
if (progress->size != -1 && progress->size < progress->pos)
|
|
progress->size = progress->pos;
|
|
|
|
progress->average_speed = timeval_div_off_t(progress->loaded, &progress->elapsed);
|
|
if (progress->average_speed) /* Division by zero risk */
|
|
timeval_from_seconds(&progress->estimated_time,
|
|
(progress->size - progress->pos) / progress->average_speed);
|
|
|
|
install_timer(&progress->timer, SPD_DISP_TIME,
|
|
progress_timeout, progress);
|
|
}
|
|
|
|
/*! Unlike in install_timer(), @a timer_func need not erase the
|
|
* expired timer ID from @a progress->timer. update_progress()
|
|
* installs the timer with a wrapper function that takes care of
|
|
* erasing the timer ID. */
|
|
void
|
|
start_update_progress(struct progress *progress, void (*timer_func)(void *),
|
|
void *timer_func_data)
|
|
{
|
|
if (!progress->valid) {
|
|
struct progress tmp;
|
|
|
|
/* Just copy useful fields from invalid progress. */
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
tmp.start = progress->start;
|
|
tmp.seek = progress->seek;
|
|
tmp.valid = 1;
|
|
|
|
memcpy(progress, &tmp, sizeof(*progress));
|
|
}
|
|
timeval_now(&progress->last_time);
|
|
progress->last_loaded = progress->loaded;
|
|
progress->timer_func = timer_func;
|
|
progress->timer_func_data = timer_func_data;
|
|
}
|