task_wdt: Fix the way the kernel timer is used

Do not use periodic executions of the timer handler, as in certain
circumstances (the fallback hardware watchdog used, one or more
task_wdt channel activated but none of them being ever fed) this
would lead to no callback/reset being executed for any channel.
Instead, schedule the next timeout from the timer handler function
when the function is executed for the dummy background channel or
for a channel that was deleted.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
diff --git a/subsys/task_wdt/task_wdt.c b/subsys/task_wdt/task_wdt.c
index 24a6127..7121918 100644
--- a/subsys/task_wdt/task_wdt.c
+++ b/subsys/task_wdt/task_wdt.c
@@ -50,6 +50,40 @@
 static bool hw_wdt_started;
 #endif
 
+static void schedule_next_timeout(uint32_t current_ticks)
+{
+	int next_channel_id;	/* channel which will time out next */
+	int64_t next_timeout;   /* timeout in absolute ticks of this channel */
+
+#ifdef CONFIG_TASK_WDT_HW_FALLBACK
+	next_channel_id = TASK_WDT_BACKGROUND_CHANNEL;
+	next_timeout = current_ticks +
+		k_ms_to_ticks_ceil64(CONFIG_TASK_WDT_MIN_TIMEOUT);
+#else
+	next_channel_id = 0;
+	next_timeout = INT64_MAX;
+#endif
+
+	/* find minimum timeout of all channels */
+	for (int id = 0; id < ARRAY_SIZE(channels); id++) {
+		if (channels[id].reload_period != 0 &&
+		    channels[id].timeout_abs_ticks < next_timeout) {
+			next_channel_id = id;
+			next_timeout = channels[id].timeout_abs_ticks;
+		}
+	}
+
+	/* update task wdt kernel timer */
+	k_timer_user_data_set(&timer, (void *)next_channel_id);
+	k_timer_start(&timer, K_TIMEOUT_ABS_TICKS(next_timeout), K_FOREVER);
+
+#ifdef CONFIG_TASK_WDT_HW_FALLBACK
+	if (hw_wdt_dev) {
+		wdt_feed(hw_wdt_dev, hw_wdt_channel);
+	}
+#endif
+}
+
 /**
  * @brief Task watchdog timer callback.
  *
@@ -67,20 +101,20 @@
 static void task_wdt_trigger(struct k_timer *timer_id)
 {
 	int channel_id = (int)k_timer_user_data_get(timer_id);
+	bool bg_channel = IS_ENABLED(CONFIG_TASK_WDT_HW_FALLBACK) &&
+			  (channel_id == TASK_WDT_BACKGROUND_CHANNEL);
 
-#ifdef CONFIG_TASK_WDT_HW_FALLBACK
-	if (channel_id == TASK_WDT_BACKGROUND_CHANNEL) {
-		if (hw_wdt_dev) {
-			wdt_feed(hw_wdt_dev, hw_wdt_channel);
-		}
+	/* If the timeout expired for the background channel (so the hardware
+	 * watchdog needs to be fed) or for a channel that has been deleted,
+	 * only schedule a new timeout (the hardware watchdog, if used, will be
+	 * fed right after that new timeout is scheduled).
+	 */
+	if (bg_channel || channels[channel_id].reload_period == 0) {
+		schedule_next_timeout(sys_clock_tick_get());
 		return;
 	}
-#endif
 
-	if (channels[channel_id].reload_period == 0) {
-		/* channel was deleted */
-		return;
-	} else if (channels[channel_id].callback) {
+	if (channels[channel_id].callback) {
 		channels[channel_id].callback(channel_id,
 			channels[channel_id].user_data);
 	} else {
@@ -157,8 +191,6 @@
 int task_wdt_feed(int channel_id)
 {
 	int64_t current_ticks;
-	int next_channel_id;	/* channel which will time out next */
-	int64_t next_timeout;   /* timeout in absolute ticks of this channel */
 
 	if (channel_id < 0 || channel_id >= ARRAY_SIZE(channels)) {
 		return -EINVAL;
@@ -178,34 +210,7 @@
 	channels[channel_id].timeout_abs_ticks = current_ticks +
 		k_ms_to_ticks_ceil64(channels[channel_id].reload_period);
 
-#ifdef CONFIG_TASK_WDT_HW_FALLBACK
-	next_channel_id = TASK_WDT_BACKGROUND_CHANNEL;
-	next_timeout = current_ticks +
-		k_ms_to_ticks_ceil64(CONFIG_TASK_WDT_MIN_TIMEOUT);
-#else
-	next_channel_id = 0;
-	next_timeout = INT64_MAX;
-#endif
-
-	/* find minimum timeout of all channels */
-	for (int id = 0; id < ARRAY_SIZE(channels); id++) {
-		if (channels[id].reload_period != 0 &&
-		    channels[id].timeout_abs_ticks < next_timeout) {
-			next_channel_id = id;
-			next_timeout = channels[id].timeout_abs_ticks;
-		}
-	}
-
-	/* update task wdt kernel timer */
-	k_timer_user_data_set(&timer, (void *)next_channel_id);
-	k_timer_start(&timer, K_TIMEOUT_ABS_TICKS(next_timeout),
-		K_TIMEOUT_ABS_TICKS(next_timeout));
-
-#ifdef CONFIG_TASK_WDT_HW_FALLBACK
-	if (hw_wdt_dev) {
-		wdt_feed(hw_wdt_dev, hw_wdt_channel);
-	}
-#endif
+	schedule_next_timeout(current_ticks);
 
 	k_sched_unlock();