Really like this change Chris.  I think its going to make it way easier to
create task-less portable modules.


On 11/8/16, 5:36 PM, "Christopher Collins" <[email protected]> wrote:

>Hello all,
>
>Recently a pretty big change was made to Mynewt's event queue model.
>The Mynewt documentation gives a good overview of how event queues used
>to work
>(http://mynewt.apache.org/develop/os/core_os/event_queue/event_queue/).
>
>Here is the old os_event struct:
>
>    struct os_event {
>        uint8_t ev_queued;
>        uint8_t ev_type;
>        void *ev_arg;
>        STAILQ_ENTRY(os_event) ev_next;
>    };
>
>Here is the new one:
>
>    typedef void os_event_fn(struct os_event *ev);
>
>    struct os_event {
>        uint8_t ev_queued;
>        os_event_fn *ev_cb;
>        void *ev_arg;
>        STAILQ_ENTRY(os_event) ev_next;
>    };
>
>The difference: the ev_type field was replaced with a callback function
>pointer.  A task handler using the old event queue code would dispatch
>events based on the event's type.  For example, here is the relevant
>part of the old newtmgr task handler:
>
>    while (1) {
>        ev = os_eventq_get(&g_nmgr_evq);
>        switch (ev->ev_type) {
>            case OS_EVENT_T_MQUEUE_DATA:
>                nt = (struct nmgr_transport *) ev->ev_arg;
>                nmgr_process(nt);
>                break;
>            case OS_EVENT_T_TIMER:
>                ocf = (struct os_callout_func *)ev;
>                ocf->cf_func(CF_ARG(ocf));
>                break;
>        }
>    }
>
>Now that the ev_type field has been replaced with a callback function
>pointer, the dispatch logic is moved out of the task handler and gets
>built into each event.  A task handler now just pulls an event off its
>queue and blindly calls its callback function.  A helper function was
>added to do just this: os_eventq_run().  As an example, the task handler
>for the bleprph application is below:
>
>    static void
>    bleprph_task_handler(void *unused)
>    {
>        while (1) {
>            os_eventq_run(&bleprph_evq);
>        }
>    }
>
>The callback associated with an event is specified when the event gets
>initialized.  For example, here are some statically-initialized events
>in the nimble host:
>
>    static void ble_hs_event_tx_notify(struct os_event *ev);
>    static void ble_hs_event_reset(struct os_event *ev);
>
>    /** OS event - triggers tx of pending notifications and indications.
>*/
>    static struct os_event ble_hs_ev_tx_notifications = {
>        .ev_cb = ble_hs_event_tx_notify,
>    };
>
>    /** OS event - triggers a full reset. */
>    static struct os_event ble_hs_ev_reset = {
>        .ev_cb = ble_hs_event_reset,
>    };
>
>As indicated, the callback function receives a single parameter: a
>pointer to the event being processed.  If the event is allocated
>dynamically, the callback function probably frees the event.
>
>The above text summarizes what was changed.  Now I want to explain the
>rationale.  This change was motivated by a desire to reduce the required
>number of tasks in a Mynewt application.  Each extraneous task strains a
>device's RAM because it requires the allocation of a dedicated stack.
>An application was forced to use a lot of tasks because library packages
>would create their own task at initialization time.  For example, nearly
>all of the sample apps use the shell and newtmgr packages, both of which
>used a dedicated task.
>
>Most of the packages that create a dedicated task don't actually need
>one.  These packages don't have any real-time timing requirements, and
>therefore no need to preempt the system.  The only reason these packages
>were creating a task was to simplify their design and API.  If these
>packages could just "piggyback" on some application task, they could
>still benefit from a task-oriented design without the cost of a
>dedicated task.  However, there is a problem with this idea.  If the
>dedicated tasks are eliminated and the work is moved into an application
>task, how does the application task know what to do with events meant
>for a library?  Even if the application developer is willing to add all
>the necessary cases to the task's event dispatch switch, the event types
>are ambiguous, since the per-user event type number space is assumed to
>be unique for each task.
>
>The solution to this problem is to replace the event type with a
>callback function pointer.  Now, the task handler doesn't have to know
>whether it just dequeued a shell event or a newtmgr event, it just calls
>the associated function.
>
>Packages with timing requirements should still create a dedicated task.
>For example, the nimble controller is not affected by this change.
>Packages without timing requirements should not create a task.  Instead,
>they should initialize their required events at init-time, and provide a
>function which allows the application to designate the event queue to be
>used.  For example, the nimble host exposes the following function:
>
>    void ble_hs_evq_set(struct os_eventq *evq);
>
>It is a bit of a burden to require the application to set each package's
>event queue.  To mitigate this burden, the following function has been
>added to the OS:
>
>    void os_eventq_dflt_set(struct os_eventq *evq);
>
>This function designates a default event queue.  If a package is not
>explicitly told which event queue to use, it will automatically use the
>default.
>
>I hope that mostly makes sense.  Please ask if you have any questions or
>if something doesn't seem right.
>
>Thanks,
>Chris

Reply via email to