diff --git a/TESTS/events/queue/main.cpp b/TESTS/events/queue/main.cpp index 738fdd0..75d724f 100644 --- a/TESTS/events/queue/main.cpp +++ b/TESTS/events/queue/main.cpp @@ -250,6 +250,46 @@ TEST_ASSERT_EQUAL(counter, 60); } +int timeleft_events[2]; + +void check_time_left(EventQueue* queue, int index, int expected) { + const int event_id = timeleft_events[index]; + TEST_ASSERT_INT_WITHIN(2, expected, queue->time_left(event_id)); + touched = true; +} + +void time_left(EventQueue* queue, int index) { + const int event_id = timeleft_events[index]; + TEST_ASSERT_EQUAL(0, queue->time_left(event_id)); +} + +void time_left_test() { + EventQueue queue(TEST_EQUEUE_SIZE); + + // Enque check events + TEST_ASSERT(queue.call_in(50, check_time_left, &queue, 0, 100-50)); + TEST_ASSERT(queue.call_in(200, check_time_left, &queue, 1, 200-200)); + + // Enque events to be checked + timeleft_events[0] = queue.call_in(100, time_left, &queue, 0); + timeleft_events[1] = queue.call_in(200, time_left, &queue, 1); + TEST_ASSERT(timeleft_events[0]); + TEST_ASSERT(timeleft_events[1]); + + queue.dispatch(300); + + // Ensure check was called + TEST_ASSERT(touched); + touched = false; + + int id = queue.call(func0); + TEST_ASSERT(id); + TEST_ASSERT_EQUAL(0, queue.time_left(id)); + queue.dispatch(10); + + // Test invalid event id + TEST_ASSERT_EQUAL(-1, queue.time_left(0)); +} // Test setup utest::v1::status_t test_setup(const size_t number_of_cases) { @@ -274,6 +314,8 @@ Case("Testing the event class", event_class_test), Case("Testing the event class helpers", event_class_helper_test), Case("Testing the event inference", event_inference_test), + + Case("Testing time_left", time_left_test), }; Specification specification(test_setup, cases); diff --git a/events/EventQueue.cpp b/events/EventQueue.cpp index 420f31b..3abe8d9 100644 --- a/events/EventQueue.cpp +++ b/events/EventQueue.cpp @@ -47,6 +47,10 @@ return equeue_cancel(&_equeue, id); } +int EventQueue::time_left(int id) { + return equeue_timeleft(&_equeue, id); +} + void EventQueue::background(Callback update) { _update = update; diff --git a/events/EventQueue.h b/events/EventQueue.h index 17ccfa4..01ab02a 100644 --- a/events/EventQueue.h +++ b/events/EventQueue.h @@ -114,6 +114,8 @@ * one of the call functions. It is safe to call cancel after an event * has already been dispatched. * + * id must be valid i.e. event must have not finished executing. + * * The cancel function is irq safe. * * If called while the event queue's dispatch loop is active, the cancel @@ -124,6 +126,25 @@ */ void cancel(int id); + /** Query how much time is left for delayed event + * + * If the event is delayed, this function can be used to query how much time + * is left until the event is due to be dispatched. + * + * id must be valid i.e. event must have not finished executing. + * + * This function is irq safe. + * + * @param id Unique id of the event + * + * @return Remaining time in milliseconds or + * 0 if event is already due to be dispatched or + * is currently executing. + * Undefined if id is invalid. + * + */ + int time_left(int id); + /** Background an event queue onto a single-shot timer-interrupt * * When updated, the event queue will call the provided update function @@ -171,6 +192,8 @@ * @return A unique id that represents the posted event and can * be passed to cancel, or an id of 0 if there is not * enough memory to allocate the event. + * Returned id will remain valid until event has finished + * executing. */ template int call(F f) { diff --git a/events/equeue/equeue.c b/events/equeue/equeue.c index ad0117f..4fc29fc 100644 --- a/events/equeue/equeue.c +++ b/events/equeue/equeue.c @@ -364,6 +364,25 @@ } } +int equeue_timeleft(equeue_t *q, int id) { + int ret = -1; + + if (!id) { + return -1; + } + + // decode event from unique id and check that the local id matches + struct equeue_event *e = (struct equeue_event *) + &q->buffer[id & ((1 << q->npw2)-1)]; + + equeue_mutex_lock(&q->queuelock); + if (e->id == id >> q->npw2) { + ret = equeue_clampdiff(e->target, equeue_tick()); + } + equeue_mutex_unlock(&q->queuelock); + return ret; +} + void equeue_break(equeue_t *q) { equeue_mutex_lock(&q->queuelock); q->break_requested = true; diff --git a/events/equeue/equeue.h b/events/equeue/equeue.h index 4939d7e..f0d8d2a 100644 --- a/events/equeue/equeue.h +++ b/events/equeue/equeue.h @@ -187,6 +187,15 @@ // the event may have already begun executing. void equeue_cancel(equeue_t *queue, int id); +// Query how much time is left for delayed event +// +// If event is delayed, this function can be used to query how much time +// is left until the event is due to be dispatched. +// +// This function is irq safe. +// +int equeue_timeleft(equeue_t *q, int id); + // Background an event queue onto a single-shot timer // // The provided update function will be called to indicate when the queue