Sanity
======

The Sanity task is a software watchdog task, which runs periodically to
check system state, and ensure that everything is still operating
properly.

In a typical system design, there are multiple stages of watchdog:

-  Internal Watchdog

-  External Watchdog

-  Sanity Watchdog

The *Internal Watchdog* is typically an MCU watchdog, which is tickled
in the core of the OS. The internal watchdog is tickled frequently, and
is meant to be an indicator the OS is running.

The *External Watchdog* is a watchdog that's typically run slower. The
purpose of an external watchdog is to provide the system with a hard
reset when it has lost its mind.

The *Sanity Watchdog* is the least frequently run watchdog, and is meant
as an application watchdog.

This document is about the operation of the Mynewt Sanity Watchdog.

Description
-----------

Sanity Task
-----------

Mynewt OS uses the OS Idle task to check sanity. The :c:macro:`SANITY_INTERVAL`
syscfg setting specifies the interval in seconds to perform the sanity
checks.

By default, every operating system task provides the frequency it will
check in with the sanity task, with the ``sanity_itvl`` parameter in the
c:func:`os_task_init()` function:

.. code:: c

    int os_task_init(struct os_task *t, char *name, os_task_func_t func, 
        void *arg, uint8_t prio, os_time_t sanity_itvl, os_stack_t *bottom,
        uint16_t stack_size);

c:var:`sanity_itvl` is the time in OS time ticks that the task being created
must register in with the sanity task.

Checking in with Sanity Task
-----------------------------

The task must then register in with the sanity task every
``sanity_itvl`` seconds. In order to do that, the task should call the
:c:func:`os_sanity_task_checkin` function, which will reset the sanity check
associated with this task. Here is an example of a task that uses a
callout to checkin with the sanity task every 50 seconds:

.. code:: c

    #define TASK1_SANITY_CHECKIN_ITVL (50 * OS_TICKS_PER_SEC) 
    struct os_eventq task1_evq;

    static void
    task1(void *arg)
    {
        struct os_task *t;
        struct os_event *ev;
        struct os_callout c;
        
        /* Get current OS task */
        t = os_sched_get_current_task();

        /* Initialize the event queue. */
        os_eventq_init(&task1_evq);

        /* Initialize the callout */
        os_callout_init(&c, &task1_evq, NULL);

        /* reset the callout to checkin with the sanity task 
         * in 50 seconds to kick off timing.
         */
        os_callout_reset(&c, TASK1_SANITY_CHECKIN_ITVL);

        while (1) {
            ev = os_eventq_get(&task1_evq);

            /* The sanity timer has reset */
            if (ev->ev_arg == &c) {
                os_sanity_task_checkin(t);
            } else {
                /* not expecting any other events */
                assert(0);
            }
        }
        
        /* Should never reach */
        assert(0);
    }

Registering a Custom Sanity Check
-----------------------------------

If a particular task wants to further hook into the sanity framework to
perform other checks during the sanity task's operation, it can do so by
registering a :c:type:`struct os_sanity_check` using the
:c:func:`os_sanity_check_register()` function.

.. code:: c

    static int 
    mymodule_perform_sanity_check(struct os_sanity_check *sc, void *arg)
    {
        /* Perform your checking here.  In this case, we check if there 
         * are available buffers in mymodule, and return 0 (all good)
         * if true, and -1 (error) if not.
         */
        if (mymodule_has_buffers()) {
            return (0);
        } else {
            return (-1);
        }
    }

    static int 
    mymodule_register_sanity_check(void)
    {
        struct os_sanity_check sc;

        os_sanity_check_init(&sc);
        /* Only assert() if mymodule_perform_sanity_check() fails 50 
         * times.  SANITY_TASK_INTERVAL is defined by the user, and 
         * is the frequency at which the sanity_task runs in seconds.
         */
        OS_SANITY_CHECK_SETFUNC(&sc, mymodule_perform_sanity_check, NULL, 
            50 * SANITY_TASK_INTERVAL);

        rc = os_sanity_check_register(&sc);
        if (rc != 0) {
            goto err;
        } 

        return (0);
    err:
        return (rc);
    }

In the above example, every time the custom sanity check
``mymodule_perform_sanity_check`` returns successfully (0), the sanity
check is reset. In the c:macro:`OS_SANITY_CHECK_SETFUNC` macro, the sanity
checkin interval is specified as 50 \* :c:macro:`SANITY_TASK_INTERVAL` (which is
the interval at which the sanity task runs.) This means that the
``mymodule_perform_sanity_check()`` function needs to fail 50 times
consecutively before the sanity task will crash the system.

**TIP:** When checking things like memory buffers, which can be
temporarily be exhausted, it's a good idea to have the sanity check fail
multiple consecutive times before crashing the system. This will avoid
crashing for temporary failures.

API
-----------------

.. doxygengroup:: OSSanity
    :content-only:
    :members:
