Roy Want / Mbed OS beaconCompileReadyFork
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers EventQueueClassic.h Source File

EventQueueClassic.h

00001 /*
00002  * Copyright (c) 2016, ARM Limited, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 #ifndef BLE_API_SOURCE_MBEDCLASSICEVENTQUEUE_H_
00018 #define BLE_API_SOURCE_MBEDCLASSICEVENTQUEUE_H_
00019 
00020 #include <cmsis.h>
00021 #include "PriorityQueue.h"
00022 #include "Ticker.h"
00023 #include "Timer.h"
00024 #include <stdio.h>
00025 #include "Thunk.h"
00026 #include "MakeThunk.h"
00027 #include "EventQueue.h"
00028 
00029 #ifdef TARGET_NORDIC
00030 #include "util/NordicCriticalSectionLock.h"
00031 typedef ::util::NordicCriticalSectionLock CriticalSection;
00032 #else
00033 #include <util/CriticalSectionLock.h>
00034 typedef ::mbed::util::CriticalSectionLock CriticalSection;
00035 #endif
00036 
00037 namespace eq {
00038 
00039 template<std::size_t EventCount>
00040 class EventQueueClassic: public EventQueue {
00041 
00042     /// Describe an event.
00043     /// An event is composed of a function f to execute after a a time t.
00044     /// Optionnaly, the event can be periodic and in this case the function f
00045     /// is executed after each period p.
00046     struct Event {
00047         /// construct an event
00048         /// @param f The function to execute when this event occur
00049         /// @param ms_remaining_time remaining time before this event occurence
00050         /// @param ms_repeat_period If the event is periodic, this parameter is the
00051         /// period between to occurence of this event.
00052         Event(const function_t& f, ms_time_t ms_remaining_time, ms_time_t ms_repeat_period = 0) :
00053             _f(f),
00054             _ms_remaining_time(ms_remaining_time),
00055             _ms_repeat_period(ms_repeat_period) {
00056         }
00057 
00058         /// call the inner function within an event
00059         void operator()() {
00060             _f();
00061         }
00062 
00063         /// return a reference to the inner function
00064         const function_t& get_function() const {
00065             return _f;
00066         }
00067 
00068         /// comparison operator used by the priority queue.
00069         /// comaprare remaining time between two events
00070         friend bool operator<(const Event& lhs, const Event& rhs) {
00071             return lhs._ms_remaining_time < rhs._ms_remaining_time;
00072         }
00073 
00074         /// return the time remaining when this event was inserted into the priority queue.
00075         ms_time_t get_ms_remaining_time() const {
00076             return _ms_remaining_time;
00077         }
00078 
00079         /// update the remaining time for this event
00080         void set_ms_remaining_time(ms_time_t new_remaining_time) {
00081             _ms_remaining_time = new_remaining_time;
00082         }
00083 
00084         /// If an event is periodic, return the time between two occurence
00085         ms_time_t get_ms_repeat_period() const {
00086             return _ms_repeat_period;
00087         }
00088 
00089     private:
00090         function_t _f;
00091         ms_time_t _ms_remaining_time;
00092         const ms_time_t _ms_repeat_period;
00093     };
00094 
00095     /// type of the internal queue
00096     typedef PriorityQueue<Event, EventCount> priority_queue_t;
00097 
00098     /// iterator for the queue type
00099     typedef typename priority_queue_t::iterator q_iterator_t;
00100 
00101     /// node type in the queue
00102     typedef typename priority_queue_t::Node q_node_t;
00103 
00104 public:
00105     /// Construct an empty event queue
00106     EventQueueClassic() :
00107         _events_queue(), _ticker(), _timer(), _timed_event_pending(false) {
00108     }
00109 
00110     virtual ~EventQueueClassic() { }
00111 
00112     virtual bool cancel(event_handle_t event_handle) {
00113         CriticalSection critical_section;
00114         bool success = _events_queue.erase(static_cast<q_node_t*>(event_handle));
00115         if (success) {
00116             // update the timers and events remaining time
00117             updateTime();
00118         }
00119         return success;
00120     }
00121 
00122     void dispatch() {
00123         while(true) {
00124             function_t f;
00125             // pick a task from the queue/ or leave
00126             {
00127                 CriticalSection cs;
00128                 q_iterator_t event_it = _events_queue.begin();
00129                 if(event_it != _events_queue.end() && event_it->get_ms_remaining_time() == 0) {
00130                     f = event_it->get_function();
00131                     // if the event_it should be repeated, reschedule it
00132                     if (event_it->get_ms_repeat_period()) {
00133                         reschedule_event(event_it);
00134                     } else {
00135                         _events_queue.pop();
00136                     }
00137                 } else {
00138                     break;
00139                 }
00140             }
00141             f();
00142         }
00143     }
00144 
00145 private:
00146 
00147     void update_ticker(ms_time_t ms_delay) {
00148         _timed_event_pending = true;
00149         _ticker.detach();
00150         _ticker.attach(this, &EventQueueClassic::updateTime, ((float) ms_delay / 1000));
00151     }
00152 
00153     void update_ticker(q_node_t* ref, ms_time_t ms_delay) {
00154         // look if the node inserted is the first node with a delay
00155         for (q_iterator_t it = _events_queue.begin(); it != _events_queue.end(); ++it) {
00156             if(it->get_ms_remaining_time()) {
00157                 if (it.get_node() == ref) {
00158                     // update the ticker to ms_delay if the first event
00159                     // with a delay is the one inserted
00160                     update_ticker(ms_delay);
00161                 }
00162                 break;
00163             }
00164         }
00165     }
00166 
00167     void update_events_remaining_time(ms_time_t elapsed_time) {
00168         bool ticker_updated = false;
00169 
00170         for (q_iterator_t it = _events_queue.begin();
00171              it != _events_queue.end(); ++it) {
00172             ms_time_t remaining_time = it->get_ms_remaining_time();
00173             if(remaining_time) {
00174                 if(remaining_time <= elapsed_time) {
00175                     it->set_ms_remaining_time(0);
00176                 } else {
00177                     it->set_ms_remaining_time(remaining_time - elapsed_time);
00178                     if (!ticker_updated) {
00179                         update_ticker(it->get_ms_remaining_time());
00180                         _timer.start();
00181                         ticker_updated = true;
00182                     }
00183                 }
00184             }
00185         }
00186     }
00187 
00188     void updateTime() {
00189         CriticalSection critical_section;
00190         ms_time_t elapsed_time = _timer.read_ms();
00191         _timed_event_pending = false;
00192         _timer.stop();
00193         _timer.reset();
00194         _ticker.detach();
00195         update_events_remaining_time(elapsed_time);
00196     }
00197 
00198     void reschedule_event(q_iterator_t& event_it) {
00199         ms_time_t ms_period = event_it->get_ms_repeat_period();
00200 
00201         if (_timed_event_pending ==  false) {
00202             update_ticker(ms_period);
00203             _timer.start();
00204             event_it->set_ms_remaining_time(ms_period);
00205             _events_queue.update(event_it);
00206         } else {
00207             int elapsed_time = _timer.read_ms();
00208             event_it->set_ms_remaining_time(elapsed_time + ms_period);
00209             _events_queue.update(event_it);
00210             update_ticker(event_it.get_node(), ms_period);
00211         }
00212     }
00213 
00214     virtual event_handle_t do_post(const function_t& fn, ms_time_t ms_delay = 0, bool repeat = false) {
00215         if(repeat && (ms_delay == 0)) {
00216             return NULL;
00217         }
00218 
00219         Event event(fn, ms_delay, repeat ? ms_delay : 0);
00220 
00221         CriticalSection critical_section;
00222         if (_events_queue.full()) {
00223             return NULL;
00224         }
00225 
00226         // there is no need to update timings if ms_delay == 0
00227         if (!ms_delay) {
00228             return _events_queue.push(event).get_node();
00229         }
00230 
00231         // if there is no pending timed event, just add this one and start timers
00232         if (_timed_event_pending ==  false) {
00233             update_ticker(ms_delay);
00234             _timer.start();
00235             return _events_queue.push(event).get_node();
00236         }
00237 
00238         int elapsed_time = _timer.read_ms();
00239 
00240         // update remaining time and post the event
00241         event.set_ms_remaining_time(ms_delay + elapsed_time);
00242         event_handle_t handle = _events_queue.push(event).get_node();
00243         update_ticker(static_cast<q_node_t*>(handle), ms_delay);
00244 
00245         return handle;
00246     }
00247 
00248     priority_queue_t _events_queue;
00249     mbed::Ticker _ticker;
00250     mbed::Timer _timer;
00251     bool _timed_event_pending;
00252 };
00253 
00254 } // namespace eq
00255 
00256 #endif /* BLE_API_SOURCE_MBEDCLASSICEVENTQUEUE_H_ */