EventFramework library allows the creation of an event-driven infrastructure in which small "threads" can handle events in a multithreaded execution context. The EventFramework can be configured to act as a cooperative or a fully-preemptive kernel with fixed-priority scheduling. Furthermore, this kernel matches run-to-completion semantics, and hence a single-stack configuration is enough to keep running this multithreaded execution environment. As running threads shares global stack, a huge quantity of RAM is saved in contrast with traditional RTOSes.

Dependents:   sensors_KL46Z_xmn

EventFramework.h

Committer:
raulMrello
Date:
2012-10-03
Revision:
1:ec12f2e32faf
Parent:
0:9d09acc8f9d9

File content as of revision 1:ec12f2e32faf:

/* mbed EventFramework Library 
 * Copyright (c) 2012 raulMrello
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef _EVENT_FRAMEWORK_H_
#define _EVENT_FRAMEWORK_H_

#include "../Types/Types.h"
#include "../Event/Event.h"
#include "../EventHandler/EventHandler.h"
#include "../List/List.h"

/** EventFramework class
 *
 * EventFramework class allows the creation of an event-driven infrastructure in which small "threads" (EventHandler 
 *  instances) can handle events in a multithreaded execution context. As other traditional RTOSes, the EventFramework
 * can be configured to act as a cooperative or a fully-preemptive kernel with fixed-priority scheduling. 
 * Furthermore, this kernel matches run-to-completion semantics, and hence a single-stack configuration 
 * is enough to keep running this multithreaded execution environment. As all running tasks shares processor's
 * stack, a huge quantity of RAM is saved in contrast with traditional RTOSes.
 * This Framework has been designed to be extremely simple, but still powerful. Let's try it. Here comes an
 * example of use:
 *
 * @code
 * // Configuring and starting an EventFramework-based application formed by two threads who interchanges two events.
 * #include "mbed.h"
 * #include "EventFramework.h"
 * 
 * // framework creation with fully-preemptive scheduling mechanism.
 * EventFramework kernel(EventFramework::SCHED_VALUE_PREEMPTIVE);
 *
 * // Events creation with priorities 0(max) and 65535(min).
 * Event ev1(65535);
 * Event ev2(0);
 *
 * // EventHandlers creation with priorities 0(max) and 65535(min)
 * EventHandler eh1(0); 
 * EventHandler eh2(65535);
 *
 * // declaration of event dispatching functions that will be attached to previous EventHandlers
 * EventDispatchingRoutine ev1_handle_func;
 * EventDispatchingRoutine ev2_handle_func;
 * 
 * int main() {
 *     // events must be registered into the framework
 *     kernel.AddEvent(&ev1);
 *     kernel.AddEvent(&ev1);
 *    
 *     // handle [eh1] will process [ev1] events through [ev1_handle_func] routine. So it must attach that routine
 *     // handle [eh2] will process [ev2] events through [ev2_handle_func] routine. So it must attach that routine
 *     eh1.Attach(&ev1_handle_func);
 *     eh2.Attach(&ev2_handle_func);
 *
 *     // handlers are registered into the kernel to listen to specific events. [eh1] listens to [ev1], [eh2] listens to [ev2].
 *     kernel.AddEventListener(&ev1, &eh1);
 *     kernel.AddEventListener(&ev1, &eh2);
 *
 *     // application starts. In this case task [eh1] is executed through the interface EventHandler::Execute.
 *     eh1.Execute();
 *
 *     // the event-driven kernel starts its operation.
 *     while(1) {
 *         kernel.Schedule();
 *     }
 * }
 *
 * // user's implementation of the event handlers
 * uint32_t ev1_handle_func(void* me, void* args){
 *     // add code here to process ev1 events and (optionally) publish ev2 events.
 *     // howto: publishing an ev2 event and attaching the value of a variable [time].
 *     uint32_t time = 1000;
 *     kernel.PublishEvent(&ev2, (void*)time); 
 * }
 *
 * uint32_t ev2_handle_func(void* me, void* args){
 *     // add code here to process ev2 events and (optionally) publish ev1 events.
 *     // howto: extracting the value of [time] variable attached to [ev1] event
 *     uint32_t time = (uint32_t)args;
 *
 *     // howto: publishing an ev1 event without data attached.
 *     kernel.PublishEvent(&ev1, 0); 
 * }
 * @endcode
 *
 */

class EventFramework{
    public:    
    /** Creates an EventFramework instance. This constructor accepts up to four arguments:
     *
     * @param schpol is the selected scheduling policy: EventFramework::SCHED_VALUE_COOPERATIVE or EventFramework::SCHED_VALUE_PREEMPTIVE
     * @param cbEI is a (void (*)(void)) callback to enable all the interrupts of the processor.
     * @param lock is a (void (*)(void)) callback to enter into a critial region.
     * @param unlock is a (void (*)(void)) callback to exit from a critial region.
     */
    EventFramework(uint32_t schpol, void (*cbEI)(void)=NULL, void (*lock)(void)=NULL, void (*unlock)(void)=NULL);

     /** Default destructor
     *
     * Deallocates reserved memory for an EventFramework instance.
     */
    ~EventFramework();    

    /** Registers a new Event into the framework.
     *
     * @param ev Event to be registered into the framework
     */
    void AddEvent(Event* ev);

    /** Unregisters an existing Event from the framework.
     *
     * @param ev Event to be unregistered from the framework
     */
    void RemoveEvent(Event* ev);

    /** Registers a new EventHandler to listen Event notifications. It can accept up to three arguments:
     *
     * @param ev Event instance to listen to.
     * @param hnd EventHandler who will listen to [ev] events.
     * @param func Event dispatching function to attach to [hnd] for [ev] events processing.
     */
    void AddEventListener(Event* ev, EventHandler* hnd, EventDispatchingRoutine* func=NULL);

    /** Unregister an existing EventHandler listener from the framework.
     *
     * @param ev Event instance to which [hnd] is listening to.
     * @param hnd EventHandler to remove from the [ev] listener list.
     */
    void RemoveEventListener(Event* ev, EventHandler* hnd);

    /** Publish an Event with (optionally) attached data. 
     *
     * @param evt Event published to be processed.
     * @param args (optional) attached data reference. If not used then set 0.
     */
    void PublishEvent(Event* evt, void* args);

    /** Configure specific features of the EventFramework.
     *
     * @param key feature key to update
     * @param value feature value to setup
     */
    void Configure(uint32_t key, uint32_t value);

    /** Gets a reference of the Event in process.
     *
     * @returns Event reference of the Event in process.
     */
    Event* GetEvent(void);

    /** Saves actual context on ISR entry. It allows ISR nesting in fully-preemptive scheduling.
     * Must be the first instruction on ISR entry.
     * Example:
     * @code
     * void ISR_Handler(void){
     *      kernel.SaveContext();
     *      // add here your isr code.
     *      ....
     *      kernel.RestoreContext();
     * }
     * @endcode
     *
     */
    void SaveContext(void);

    /** Restores previous context on ISR exit. It must be the last instruction before ISR exit.
     * Example:
     * @code
     * void ISR_Handler(void){
     *      kernel.SaveContext();
     *      // add here your isr code.
     *      ....
     *      kernel.RestoreContext();
     * }
     * @endcode
     *
     */
    void RestoreContext(void);

    /** Executes the EventFramework according with the selected scheduling policy.
     *
     */
    void Schedule(void);

    /// Feature key to select the SCHEDULING execution model
    const static uint32_t SCHED_KEY = 0x00000001;
    /// Feature value to setup the SCHEDULING model as COOPERATIVE
    const static uint32_t SCHED_VALUE_COOPERATIVE = 0;
    /// Feature value to setup the SCHEDULING model as PREEMPTIVE
    const static uint32_t SCHED_VALUE_PREEMPTIVE = 1;
    
    private:
    bool IsPreemptive(void);
    void AddEventToList(Event* ev, List* list);
    void AddPendingEvent(Event* ev);
    void RemovePendingEvent(Event* ev);
    uint8_t nesting;
    uint16_t currPrio;
    void (*cbEnableInterrupts)(void);
    void (*cbLock)(void);
    void (*cbUnlock)(void);
    List* list;
    List* queue;
    Event* event;
    uint32_t ctflags;
};

#endif