changeset 487:630881a97943

WIP, rework effect with one timer, untested
author Louis Opter <kalessin@kalessin.fr>
date Sun, 10 Jul 2016 17:34:04 -0700
parents 5afd4568a2f6
children 3dff3076c229
files add_power_transition.patch
diffstat 1 files changed, 203 insertions(+), 90 deletions(-) [+]
line wrap: on
line diff
--- a/add_power_transition.patch	Sat Jul 09 23:40:12 2016 -0700
+++ b/add_power_transition.patch	Sun Jul 10 17:34:04 2016 -0700
@@ -1,5 +1,5 @@
 # HG changeset patch
-# Parent  a3f7765c23828e1238a17d89252445769fb928db
+# Parent  37eb064ab54b68f370e4a251a7f93d514f4fe4fe
 Add a transition argument to the power functions
 
 Unlike LIFX's implementation, lightsd will properly get the bulbs to
@@ -25,6 +25,101 @@
  ADD_SUBDIRECTORY(lifx)
  
  # 2.8.11 is the first version with TARGET_INCLUDE_DIRECTORIES:
+diff --git a/compat/Darwin/time_monotonic.c b/compat/Darwin/time_monotonic.c
+--- a/compat/Darwin/time_monotonic.c
++++ b/compat/Darwin/time_monotonic.c
+@@ -22,6 +22,7 @@
+ #include <mach/mach_time.h>
+ #include <sys/time.h>
+ #include <assert.h>
++#include <limits.h>
+ #include <stdint.h>
+ 
+ #include "time_monotonic.h"
+@@ -40,3 +41,21 @@
+ 
+     return time * timebase.numer / timebase.denom / MSECS_IN_NSEC;
+ }
++
++int
++lgtd_time_mono_cmp(lgtd_time_mono_t t1, lgtd_time_mono_t t2, int error_margin)
++{
++    assert(error_margin >= 0);
++
++    int cmp;
++    lgtd_time_mono_t diff;
++    if (t1 > t2) {
++        diff = t1 - t2;
++        cmp = 1;
++    } else {
++        diff = t2 - t1;
++        cmp = -1;
++    }
++
++    return diff <= INT_MAX && (int)diff <= error_margin ? 0 : cmp;
++}
+diff --git a/compat/Darwin/time_monotonic.h b/compat/Darwin/time_monotonic.h
+--- a/compat/Darwin/time_monotonic.h
++++ b/compat/Darwin/time_monotonic.h
+@@ -20,3 +20,10 @@
+ typedef uint64_t lgtd_time_mono_t;
+ 
+ lgtd_time_mono_t lgtd_time_monotonic_msecs(void);
++
++// Returns:
++//
++// - 0 if abs(t1 - t2) <= error_margin;
++// - -1 if t2 > t1;
++// - 1 if t1 > t2.
++int lgtd_time_mono_cmp(lgtd_time_mono_t, lgtd_time_mono_t, int);
+diff --git a/compat/generic/time_monotonic.c b/compat/generic/time_monotonic.c
+--- a/compat/generic/time_monotonic.c
++++ b/compat/generic/time_monotonic.c
+@@ -15,6 +15,8 @@
+ // You should have received a copy of the GNU General Public License
+ // along with lighstd.  If not, see <http://www.gnu.org/licenses/>.
+ 
++#include <assert.h>
++#include <limits.h>
+ #include <stdint.h>
+ #include <time.h>
+ 
+@@ -27,3 +29,21 @@
+     clock_gettime(CLOCK_MONOTONIC, &tp);
+     return tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
+ }
++
++int
++lgtd_time_mono_cmp(lgtd_time_mono_t t1, lgtd_time_mono_t t2, int error_margin)
++{
++    assert(error_margin >= 0);
++
++    int cmp;
++    lgtd_time_mono_t diff;
++    if (t1 > t2) {
++        diff = t1 - t2;
++        cmp = 1;
++    } else {
++        diff = t2 - t1;
++        cmp = -1;
++    }
++
++    return diff <= INT_MAX && (int)diff <= error_margin ? 0 : cmp;
++}
+diff --git a/compat/generic/time_monotonic.h b/compat/generic/time_monotonic.h
+--- a/compat/generic/time_monotonic.h
++++ b/compat/generic/time_monotonic.h
+@@ -20,3 +20,10 @@
+ typedef uint64_t lgtd_time_mono_t;
+ 
+ lgtd_time_mono_t lgtd_time_monotonic_msecs(void);
++
++// Returns:
++//
++// - 0 if abs(t1 - t2) <= error_margin;
++// - -1 if t2 > t1;
++// - 1 if t1 > t2.
++int lgtd_time_mono_cmp(lgtd_time_mono_t, lgtd_time_mono_t, int);
 diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
 --- a/core/CMakeLists.txt
 +++ b/core/CMakeLists.txt
@@ -59,7 +154,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/core/effect.c
-@@ -0,0 +1,281 @@
+@@ -0,0 +1,300 @@
 +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr>
 +//
 +// This file is part of lighstd.
@@ -104,9 +199,6 @@
 +
 +    lgtd_info("ending effect %s, id=%p", effect->name, effect);
 +    LIST_REMOVE(effect, link);
-+    if (effect->duration_timer) {
-+        lgtd_timer_stop(effect->duration_timer);
-+    }
 +    if (effect->timer) {
 +        lgtd_timer_stop(effect->timer);
 +    }
@@ -114,25 +206,80 @@
 +}
 +
 +static void
++lgtd_effect_duration_callback(struct lgtd_timer *timer, union lgtd_timer_ctx ctx)
++{
++    (void)timer;
++
++    struct lgtd_effect *effect = (struct lgtd_effect *)ctx.as_ptr;
++
++    lgtd_time_mono_t now = lgtd_time_monotonic_msecs();
++    lgtd_time_mono_t ends_at = effect->ends_at;
++
++    // Reschedule the timer if it fired off too early:
++    int error_margin = LGTD_EFFECT_TIMER_RESCHEDULE_THRESHOLD_MS;
++    if (lgtd_time_mono_cmp(now, ends_at, error_margin) > 0) {
++        lgtd_time_mono_t timeout = ends_at - now;
++        lgtd_info(
++            "re-scheduling duration callback for effect %s, id=%p in %jums "
++            "(now=%ju, ends_at=%ju)",
++            effect->name, effect, (uintmax_t)timeout,
++            (uintmax_t)now, (uintmax_t)ends_at
++        );
++        struct timeval tv = LGTD_MSECS_TO_TIMEVAL(timeout);
++        if (!lgtd_timer_reschedule(timer, &tv)) {
++            lgtd_warn(
++                "can't re-schedule the duration callback for effect %s, id=%p",
++                effect->name, effect
++            );
++            lgtd_effect_stop(effect);
++        }
++        return;
++    }
++
++    if (effect->duration_cb) {
++        int error_margin = LGTD_EFFECT_STALE_THRESHOLD_MS;
++        if (!lgtd_time_mono_cmp(ends_at, now, error_margin)) {
++            lgtd_info(
++                "calling duration callback for effect %s, id=%p",
++                effect->name, effect
++            );
++            effect->duration_cb(effect);
++        } else {
++            lgtd_warnx(
++                "not calling duration callback for stale effect %s created "
++                "%jums ago, id=%p, duration=%jums",
++                effect->name, (uintmax_t)(now - effect->created_at), effect,
++                (uintmax_t)(effect->ends_at - effect->created_at)
++            );
++        }
++    }
++    lgtd_effect_stop(effect);
++}
++
++static void
 +lgtd_effect_timer_callback(struct lgtd_timer *timer, union lgtd_timer_ctx ctx)
 +{
 +    (void)timer;
 +
 +    struct lgtd_effect *effect = (struct lgtd_effect *)ctx.as_ptr;
 +
-+    lgtd_time_mono_t now = lgtd_time_monotonic_msecs();
-+
 +    if (effect->apply_left < 0) {
-+        lgtd_info(
-+            "ignoring extraneous timer callback for effect %s, id=%p, "
-+            "now=%ju, apply_left=%d",
-+            effect->name, effect, (uintmax_t)now, effect->apply_left
++        lgtd_warnx(
++            "stopping runaway effect %s, id=%p, apply_left=%d",
++            effect->name, effect, effect->apply_left
 +        );
++        lgtd_effect_stop(effect);
++#ifndef NDEBUG
++        abort();
++#endif
 +        return;
 +    }
 +
-+    if (effect->ends_at && now > effect->ends_at
-+        && now - effect->ends_at > LGTD_EFFECT_STALE_THRESHOLD_MS) {
++    lgtd_time_mono_t now = lgtd_time_monotonic_msecs();
++    lgtd_time_mono_t ends_at = effect->ends_at;
++
++    int error_margin = LGTD_EFFECT_STALE_THRESHOLD_MS;
++    if (ends_at && lgtd_time_mono_cmp(now, ends_at, error_margin) > 0) {
 +        // check if the effect is stale (e.g: the computer went to sleep)
 +        // and stop it if it is the case:
 +        lgtd_warnx(
@@ -152,62 +299,41 @@
 +    assert(effect->apply_cb);
 +    effect->apply_cb(effect);
 +    effect->apply_cnt++;
-+    if (effect->apply_left == 0) {
-+        lgtd_timer_stop(effect->timer);
-+        effect->timer = NULL;
-+    }
-+    effect->apply_left--;
-+}
-+
-+static void
-+lgtd_effect_duration_callback(struct lgtd_timer *timer, union lgtd_timer_ctx ctx)
-+{
-+    (void)timer;
-+
-+    struct lgtd_effect *effect = (struct lgtd_effect *)ctx.as_ptr;
-+
-+    lgtd_time_mono_t now = lgtd_time_monotonic_msecs();
-+
-+    if (now < effect->ends_at
-+        && effect->ends_at - now >= LGTD_EFFECT_TIMER_RESCHEDULE_THRESHOLD_MS) {
-+        lgtd_time_mono_t timeout = effect->ends_at - now;
-+        lgtd_info(
-+            "re-scheduling duration callback for effect %s, id=%p in %jums "
-+            "(now=%ju, ends_at=%ju)",
-+            effect->name, effect, (uintmax_t)timeout,
-+            (uintmax_t)now, (uintmax_t)effect->ends_at
-+        );
-+        struct timeval tv = LGTD_MSECS_TO_TIMEVAL(timeout);
-+        lgtd_timer_reschedule(timer, &tv);
-+        if (effect->timer && effect->apply_left >= 0) {
-+            lgtd_info(
-+                "force-calling timer callback after rescheduling the duration "
-+                "callback for effect %s, id=%p",
-+                effect->name, effect
-+            );
-+            lgtd_effect_timer_callback(effect->timer, ctx);
-+        }
-+        return;
-+    }
-+
-+    if (effect->duration_cb) {
-+        if (effect->ends_at < now || // let's not go negative with unsigned ints
-+            effect->ends_at - now < LGTD_EFFECT_STALE_THRESHOLD_MS) {
++    if (ends_at && effect->apply_left-- == 0) {
++        error_margin = LGTD_EFFECT_TIMER_RESCHEDULE_THRESHOLD_MS;
++        if (lgtd_time_mono_cmp(now, ends_at, error_margin) <= 0) {
++            // now <= ends_at and we aren't stale, call the duration callback rn
 +            lgtd_info(
 +                "calling duration callback for effect %s, id=%p",
 +                effect->name, effect
 +            );
 +            effect->duration_cb(effect);
-+        } else {
-+            lgtd_warnx(
-+                "not calling duration callback for stale effect %s created "
-+                "%jums ago, id=%p, duration=%jums",
-+                effect->name, (uintmax_t)(now - effect->created_at), effect,
-+                (uintmax_t)(effect->ends_at - effect->created_at)
++            lgtd_effect_stop(effect);
++            return;
++        }
++
++        // now > ends_at, schedule the duration callback
++        lgtd_timer_stop(timer);
++        lgtd_time_mono_t wait = now - ends_at;
++        effect->timer = lgtd_timer_start(
++            LGTD_TIMER_DEFAULT_FLAGS,
++            wait,
++            lgtd_effect_duration_callback,
++            ctx
++        );
++        if (!effect->timer) {
++            lgtd_warn(
++                "can't schedule the duration callback for effect %s, id=%p",
++                effect->name, effect
 +            );
++            lgtd_effect_stop(effect);
++            return;
 +        }
++        lgtd_info(
++            "scheduled duration callback for effect %s, id=%p, in %jums",
++            effect->name, effect, (uintmax_t)wait
++        );
 +    }
-+    lgtd_effect_stop(effect);
 +}
 +
 +struct lgtd_effect *
@@ -239,20 +365,7 @@
 +    effect->ctx = ctx;
 +    LIST_INSERT_HEAD(&lgtd_effects, effect, link);
 +
-+    union lgtd_timer_ctx timer_ctx = { .as_ptr = effect };
-+
-+    if (duration) {
-+        effect->duration_timer = lgtd_timer_start(
-+            LGTD_TIMER_DEFAULT_FLAGS,
-+            duration,
-+            lgtd_effect_duration_callback,
-+            timer_ctx
-+        );
-+        if (!effect->duration_timer) {
-+            goto err;
-+        }
-+    }
-+
++    void (*timer_cb)(struct lgtd_timer *, union lgtd_timer_ctx) = NULL;
 +    if (timer_ms) { // periodic or delayed effect
 +        assert(apply_cb);
 +        if (!apply_cb) {
@@ -271,16 +384,11 @@
 +        }
 +        if (duration && (timer_flags & LGTD_TIMER_PERSISTENT)) {
 +            effect->apply_left = duration / timer_ms;
-+        } else if (!(timer_flags & LGTD_TIMER_PERSISTENT)) {
-+            effect->apply_left = 1;
 +        }
-+        timer_flags |= LGTD_TIMER_EVENT_PRIORITY_HIGHEST;
-+        effect->timer = lgtd_timer_start(
-+            timer_flags, timer_ms, lgtd_effect_timer_callback, timer_ctx
-+        );
-+        if (!effect->timer) {
-+            goto err;
++        if (timer_flags & LGTD_TIMER_ACTIVATE_NOW) {
++            effect->apply_left++;
 +        }
++        timer_cb = lgtd_effect_timer_callback;
 +        if (duration) {
 +            lgtd_info(
 +                "starting effect %s, id=%p, created_at=%jums, "
@@ -318,14 +426,20 @@
 +            lgtd_effect_stop(effect);
 +            return NULL;
 +        }
++        timer_ms = duration;
++        timer_flags = LGTD_TIMER_DEFAULT_FLAGS;
++        timer_cb = lgtd_effect_duration_callback;
 +    }
 +
-+    return effect;
++    union lgtd_timer_ctx timer_ctx = { .as_ptr = effect };
++    effect->timer = lgtd_timer_start(
++        timer_flags, timer_ms, timer_cb, timer_ctx
++    );
++    if (effect->timer) {
++        return effect;
++    }
 +
 +err:
-+    if (effect->duration_timer) {
-+        lgtd_timer_stop(effect->duration_timer);
-+    }
 +    if (effect->timer) {
 +        lgtd_timer_stop(effect->timer);
 +    }
@@ -345,7 +459,7 @@
 new file mode 100644
 --- /dev/null
 +++ b/core/effect.h
-@@ -0,0 +1,64 @@
+@@ -0,0 +1,63 @@
 +// Copyright (c) 2015, Louis Opter <kalessin@kalessin.fr>
 +//
 +// This file is part of lighstd.
@@ -375,9 +489,8 @@
 +    const char                      *name;
 +    lgtd_time_mono_t                created_at;
 +    lgtd_time_mono_t                ends_at;
-+    struct lgtd_timer               *duration_timer;
++    struct lgtd_timer               *timer;
 +    void                            (*duration_cb)(const struct lgtd_effect *);
-+    struct lgtd_timer               *timer;
 +    void                            (*apply_cb)(const struct lgtd_effect *);
 +    // how many times apply_cb will be called again or 0 if your effect
 +    // is infinite (duration == 0 and timer_ms > 0) or doesn't use a