Skip to content

Commit

Permalink
Queues_get_next_datachange now returns a timestamp
Browse files Browse the repository at this point in the history
Before this commit, this function returned a time (in microseconds) to
be slept until the next update; this is harder to manage and slightly
less robust than returning a timestamp.
  • Loading branch information
tobast authored and fwsmit committed Mar 8, 2023
1 parent 37a7c63 commit 836fa0f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 29 deletions.
7 changes: 5 additions & 2 deletions src/dunst.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,13 @@ static gboolean run(void *data)
}

if (active) {
gint64 timeout_at = queues_get_next_datachange(now);
// Previous computations may have taken time, update `now`
// This might mean that `timeout_at` is now before `now`, so
// we have to make sure that `sleep` is still positive.
now = time_monotonic_now();
gint64 sleep = queues_get_next_datachange(now);
gint64 timeout_at = now + sleep;
gint64 sleep = timeout_at - now;
sleep = MAX(sleep, 1000); // Sleep at least 1ms

LOG_D("Sleeping for %li ms", sleep/1000);

Expand Down
20 changes: 10 additions & 10 deletions src/queues.c
Original file line number Diff line number Diff line change
Expand Up @@ -553,20 +553,20 @@ void queues_update(struct dunst_status status, gint64 time)
/* see queues.h */
gint64 queues_get_next_datachange(gint64 time)
{
gint64 sleep = G_MAXINT64;
gint64 until_next_second = S2US(1) - (time % S2US(1));
gint64 wakeup_time = G_MAXINT64;
gint64 next_second = time + S2US(1) - (time % S2US(1));

for (GList *iter = g_queue_peek_head_link(displayed); iter;
iter = iter->next) {
struct notification *n = iter->data;
gint64 ttl = n->timeout - (time - n->start);
gint64 timeout_ts = n->timestamp + n->timeout;

if (n->timeout > 0 && n->locked == 0) {
if (ttl > 0)
sleep = MIN(sleep, ttl);
if (timeout_ts > time)
wakeup_time = MIN(wakeup_time, timeout_ts);
else
// while we're processing, the notification already timed out
return 0;
// while we're processing or while locked, the notification already timed out
return time;
}

if (settings.show_age_threshold >= 0) {
Expand All @@ -579,14 +579,14 @@ gint64 queues_get_next_datachange(gint64 time)
* will change at once, and that at most one
* update will occur each second for this
* purpose. */
sleep = MIN(sleep, until_next_second);
wakeup_time = MIN(wakeup_time, next_second);
}
else
sleep = MIN(sleep, settings.show_age_threshold - age);
wakeup_time = MIN(wakeup_time, n->timestamp + settings.show_age_threshold);
}
}

return sleep != G_MAXINT64 ? sleep : -1;
return wakeup_time != G_MAXINT64 ? wakeup_time : -1;
}


Expand Down
2 changes: 1 addition & 1 deletion src/queues.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ void queues_update(struct dunst_status status, gint64 time);
*
* @param time the current time
*
* @return the distance to the next event in the queue, which forces
* @return the timestamp of the next event in the queue, which forces
* an update visible to the user. This may be:
* - notification hits timeout
* - notification's age second changes
Expand Down
43 changes: 27 additions & 16 deletions test/queues.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,24 +475,27 @@ TEST test_datachange_endless(void)

TEST test_datachange_endless_agethreshold(void)
{
settings.show_age_threshold = S2US(5);
const gint64 AGE_THRESHOLD = S2US(5);
settings.show_age_threshold = AGE_THRESHOLD;

gint64 cur_time = 0;
queues_init();

struct notification *n = test_notification("n", 0);
n->timestamp = cur_time;

queues_notification_insert(n);
queues_update(STATUS_NORMAL, time_monotonic_now());
queues_update(STATUS_NORMAL, cur_time);

ASSERT_IN_RANGEm("Age threshold is activated and the next wakeup should be less than a second away",
S2US(1)/2, queues_get_next_datachange(time_monotonic_now() + S2US(4)), S2US(1)/2);
AGE_THRESHOLD - S2US(1)/2, queues_get_next_datachange(cur_time + AGE_THRESHOLD - S2US(1)), S2US(1)/2);

ASSERT_IN_RANGEm("Age threshold is activated and the next wakeup should be less than the age threshold",
settings.show_age_threshold/2, queues_get_next_datachange(time_monotonic_now()), settings.show_age_threshold/2);
AGE_THRESHOLD / 2, queues_get_next_datachange(cur_time), AGE_THRESHOLD/2);

settings.show_age_threshold = S2US(0);
ASSERT_IN_RANGEm("Age threshold is activated and the next wakeup should be less than a second away",
S2US(1)/2, queues_get_next_datachange(time_monotonic_now()), S2US(1)/2);
S2US(1)/2, queues_get_next_datachange(cur_time), S2US(1)/2);

queues_teardown();
PASS();
Expand Down Expand Up @@ -520,7 +523,7 @@ TEST test_datachange_agethreshold_at_second(void)

// The next update should be when age must be first displayed, at T=5.5s
ASSERTm("Age threshold is activated, first wakeup should be at 5.5s",
cur_time + queues_get_next_datachange(cur_time) == S2US(5) + 500000);
queues_get_next_datachange(cur_time) == S2US(5) + 500000);

const int NB_MICROSECS = 6;
const gint64 MICROSECS[] = {
Expand All @@ -529,7 +532,7 @@ TEST test_datachange_agethreshold_at_second(void)
for(gint64 base_time = S2US(5); base_time <= S2US(7); base_time += S2US(1)) {
for(int musec_id = 0; musec_id < NB_MICROSECS; ++musec_id) {
cur_time = base_time + MICROSECS[musec_id];
gint64 next_wakeup = cur_time + queues_get_next_datachange(cur_time);
gint64 next_wakeup = queues_get_next_datachange(cur_time);
ASSERTm("Age threshold is activated, next wakeup should be at next turn of second",
next_wakeup == base_time + S2US(1));
}
Expand All @@ -543,19 +546,21 @@ TEST test_datachange_queues(void)
{
queues_init();

gint64 cur_time = 0;
struct notification *n = test_notification("n", 10);
n->timestamp = cur_time;

queues_notification_insert(n);
ASSERTm("The inserted notification is inside the waiting queue, so it should get ignored.",
queues_get_next_datachange(time_monotonic_now()) < S2US(0));
queues_get_next_datachange(cur_time) < 0);

queues_update(STATUS_NORMAL, time_monotonic_now());
ASSERT_IN_RANGEm("The notification has to get closed in less than its timeout",
S2US(10)/2, queues_get_next_datachange(time_monotonic_now()), S2US(10)/2);
S2US(10)/2, queues_get_next_datachange(cur_time), S2US(10)/2);

queues_notification_close(n, REASON_UNDEF);
ASSERTm("The inserted notification is inside the history queue, so it should get ignored",
queues_get_next_datachange(time_monotonic_now()) < S2US(0));
queues_get_next_datachange(cur_time) < 0);

queues_teardown();
PASS();
Expand All @@ -565,23 +570,29 @@ TEST test_datachange_ttl(void)
{
struct notification *n;
queues_init();
gint64 cur_time = 0;

n = test_notification("n1", 15);
n->timestamp = cur_time;

queues_notification_insert(n);
queues_update(STATUS_NORMAL, time_monotonic_now());
queues_update(STATUS_NORMAL, cur_time);
ASSERT_IN_RANGEm("The notification has to get closed in less than its timeout.",
n->timeout/2, queues_get_next_datachange(time_monotonic_now()), n->timeout/2);
n->timeout/2, queues_get_next_datachange(cur_time), n->timeout/2);

cur_time += 500; // microseconds
n = test_notification("n2", 10);
n->timestamp = cur_time;

queues_notification_insert(n);
queues_update(STATUS_NORMAL, time_monotonic_now());
queues_update(STATUS_NORMAL, cur_time);
ASSERT_IN_RANGEm("The timeout of the second notification has to get used as sleep time now.",
n->timeout/2, queues_get_next_datachange(time_monotonic_now()), n->timeout/2);
cur_time + n->timeout/2, queues_get_next_datachange(cur_time), n->timeout/2);

cur_time += S2US(10);

ASSERT_EQm("The notification already timed out. You have to answer with 0.",
S2US(0), queues_get_next_datachange(time_monotonic_now() + S2US(10)));
ASSERT_EQm("The notification already timed out. You have to answer with the current time.",
cur_time, queues_get_next_datachange(cur_time));

queues_teardown();
PASS();
Expand Down

0 comments on commit 836fa0f

Please sign in to comment.