Example

Dependencies:   FXAS21002 FXOS8700Q

Revision:
0:11cc2b7889af
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/simple-mbed-cloud-client/mbed-cloud-client/mbed-client/mbed-client-classic/source/m2mtimerpimpl.cpp	Tue Nov 19 09:49:38 2019 +0000
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2015-2016 ARM Limited. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ * Licensed under the Apache License, Version 2.0 (the License); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mbed-client-classic/m2mtimerpimpl.h"
+#include "mbed-client/m2mtimerobserver.h"
+
+#include "eventOS_event_timer.h"
+#include "eventOS_scheduler.h"
+
+#include <assert.h>
+#include <string.h>
+
+
+#define MBED_CLIENT_TIMER_TASKLET_INIT_EVENT 0 // Tasklet init occurs always when generating a tasklet
+#define MBED_CLIENT_TIMER_EVENT 10
+
+// This is set to _status on constructor, which forces the lazy second phase initialization
+// to happen once in initialize_tasklet(). Whole scheme is there to avoid overhead or
+// unwanted serialization on event OS scheduler mutex, as the whole tasklet needs to be initialized
+// just once for the whole lifecycle of cloud client.
+#define STATUS_INIT_NOT_DONE_YET 3
+
+
+int8_t M2MTimerPimpl::_tasklet_id = -1;
+
+extern "C" void tasklet_func(arm_event_s *event)
+{
+    // skip the init event as there will be a timer event after
+    if (event->event_type == MBED_CLIENT_TIMER_EVENT) {
+
+        M2MTimerPimpl* timer = (M2MTimerPimpl*)event->data_ptr;
+        assert(timer);
+        timer->handle_timer_event(*event);
+    }
+}
+
+void M2MTimerPimpl::handle_timer_event(const arm_event_s &event)
+{
+    // Clear the reference to timer event which is now received and handled.
+    // This avoids the useless work from canceling a event if the timer is restarted
+    // and also lets the assertions verify the object state correctly.
+    _timer_event = NULL;
+
+    if (get_still_left_time() > 0) {
+        start_still_left_timer();
+    } else {
+        timer_expired();
+    }
+}
+
+M2MTimerPimpl::M2MTimerPimpl(M2MTimerObserver& observer)
+: _observer(observer),
+  _interval(0),
+  _intermediate_interval(0),
+  _total_interval(0),
+  _still_left(0),
+  _timer_event(NULL),
+  _type(M2MTimerObserver::Notdefined),
+  _status(STATUS_INIT_NOT_DONE_YET),
+  _dtls_type(false),
+  _single_shot(true)
+{
+}
+
+M2MTimerPimpl::~M2MTimerPimpl()
+{
+    // cancel the timer request, if any is pending
+    cancel();
+
+    // there is no turning back, event os does not have eventOS_event_handler_delete() or similar,
+    // so the tasklet is lost forever.
+}
+
+void M2MTimerPimpl::initialize_tasklet()
+{
+    // A micro-optimization to avoid operations on mutex on every time the timer is started.
+    // After all, the tasklet needs to be created just once for the lifecyle of whole client.
+    if (_status == STATUS_INIT_NOT_DONE_YET) {
+
+        eventOS_scheduler_mutex_wait();
+
+        if (_tasklet_id < 0) {
+            _tasklet_id = eventOS_event_handler_create(tasklet_func, MBED_CLIENT_TIMER_TASKLET_INIT_EVENT);
+            assert(_tasklet_id >= 0);
+        }
+
+        _status = 0;
+
+        eventOS_scheduler_mutex_release();
+    }
+}
+
+void M2MTimerPimpl::start_timer(uint64_t interval,
+                                M2MTimerObserver::Type type,
+                                bool single_shot)
+{
+    initialize_tasklet();
+
+    _dtls_type = false;
+    _intermediate_interval = 0;
+    _total_interval = 0;
+    _status = 0;
+    _single_shot = single_shot;
+    _interval = interval;
+    _type = type;
+    _still_left = 0;
+    start();
+}
+
+void M2MTimerPimpl::start_dtls_timer(uint64_t intermediate_interval, uint64_t total_interval, M2MTimerObserver::Type type)
+{
+    initialize_tasklet();
+
+    _dtls_type = true;
+    _intermediate_interval = intermediate_interval;
+    _total_interval = total_interval;
+    _interval = _intermediate_interval;
+    _status = 0;
+    _single_shot = false;
+    _type = type;
+    start();
+}
+
+void M2MTimerPimpl::start()
+{
+    // Cancel ongoing events before creating a new one.
+    // Otherwise it can happen that there are multiple events running at the same time.
+    cancel();
+
+    int32_t wait_time;
+
+    if (_interval > INT32_MAX) {
+        _still_left = _interval - INT32_MAX;
+        wait_time = INT32_MAX;
+    } else {
+        wait_time = _interval;
+    }
+
+    request_event_in(wait_time);
+}
+
+void M2MTimerPimpl::request_event_in(int32_t delay_ms)
+{
+    // init struct to zero to avoid hassle when new fields are added to it
+    arm_event_t event = { 0 };
+
+    event.receiver = _tasklet_id;
+    event.sender = _tasklet_id;
+    event.event_type = MBED_CLIENT_TIMER_EVENT;
+    event.data_ptr = this;
+    event.priority = ARM_LIB_MED_PRIORITY_EVENT;
+
+    // check first, that there is no timer event still pending
+    assert(_timer_event == NULL);
+
+    const uint32_t delay_ticks = eventOS_event_timer_ms_to_ticks(delay_ms);
+
+    _timer_event = eventOS_event_timer_request_in(&event, delay_ticks);
+
+    // The timer request may fail only if the system is out of pre-allocated
+    // timers and it can not allocate more.
+    assert(_timer_event != NULL);
+}
+
+void M2MTimerPimpl::cancel()
+{
+    // NULL event is ok to cancel
+    eventOS_cancel(_timer_event);
+
+    _timer_event = NULL;
+}
+
+void M2MTimerPimpl::stop_timer()
+{
+    _interval = 0;
+    _single_shot = true;
+    _still_left = 0;
+    cancel();
+}
+
+void M2MTimerPimpl::timer_expired()
+{
+    _status++;
+
+    // The code is  expecting that the expiration has happened 0, 1 or more times,
+    // and we also need to check for overflow as the _status is stored in 2 bits slot.
+    if (_status > 2) {
+        _status = 2;
+    }
+
+    _observer.timer_expired(_type);
+
+    if ((!_dtls_type) && (!_single_shot)) {
+        // start next round of periodic timer
+        start();
+    } else if ((_dtls_type) && (!is_total_interval_passed())) {
+        // if only the intermediate time has passed, we need still wait up to total time
+        _interval = _total_interval - _intermediate_interval;
+        start();
+    }
+}
+
+bool M2MTimerPimpl::is_intermediate_interval_passed() const
+{
+    if (_status > 0) {
+        return true;
+    }
+    return false;
+}
+
+bool M2MTimerPimpl::is_total_interval_passed() const
+{
+    if (_status > 1) {
+        return true;
+    }
+    return false;
+}
+
+uint64_t M2MTimerPimpl::get_still_left_time() const
+{
+   return _still_left;
+}
+
+void M2MTimerPimpl::start_still_left_timer()
+{
+    if (_still_left > 0) {
+
+        int32_t wait_time;
+
+        if (_still_left > INT32_MAX) {
+            _still_left = _still_left - INT32_MAX;
+            wait_time = INT32_MAX;
+        } else {
+            wait_time = _still_left;
+            _still_left = 0;
+        }
+
+        request_event_in(wait_time);
+
+    } else {
+        _observer.timer_expired(_type);
+        if (!_single_shot) {
+            start_timer(_interval, _type, _single_shot);
+        }
+    }
+}