# [Legato] Timer
> https://docs.legato.io/latest/c_timer.html
https://docs.legato.io/latest/le__timer_8h.html
## Legato Timer 基本使用流程
1. 用 [`le_timer_Create`](https://docs.legato.io/latest/le__timer_8h.html#aee41169a210378b369f440cf99146522) 來建立每個 timer,並且取得該 timer 的 reference [`le_timer_Ref_t`](https://docs.legato.io/latest/le__timer_8h.html#a763fa6992488cdce3b5a820817094838)
2. 在 timer 被啟動前,設定 timer 的 attributes:
- [`le_timer_SetHandler`](https://docs.legato.io/latest/le__timer_8h.html#a763fa6992488cdce3b5a820817094838)
- 設定當 timer 到期後,要執行哪個 timer expiry handler function
- timer expiry handler function 的 prototype:[`typedef void (*le_timer_ExpiryHandler_t) ( le_timer_Ref_t timerRef )`](https://docs.legato.io/latest/le__timer_8h.html#a81ec5f8df2fbde873566e55489d8cedd)
- [`le_timer_SetInterval()`](https://docs.legato.io/latest/le__timer_8h.html#a0a103d5cef5e83fc9088859d527bbd43) (or [`le_timer_SetMsInterval()`](https://docs.legato.io/latest/le__timer_8h.html#a292b0a7d6dc0796a36a54fd04c6a7eeb))
- 設定 timer 要倒數多久
- [`le_timer_SetRepeat()`](https://docs.legato.io/latest/le__timer_8h.html#a292b0a7d6dc0796a36a54fd04c6a7eeb)
- 設定 timer 要 repeat 多少次,預設值是 1,若設定為 0 則會不停 repeat
- [`le_timer_SetContextPtr()`](https://docs.legato.io/latest/le__timer_8h.html#af6900bdb4653ff95f7f7be918b9e482d)
- 若要傳 context 到 handler function 內,則可用此 API,然後在 handler function 內使用 [`le_timer_GetContextPtr()`](https://docs.legato.io/latest/le__timer_8h.html#aa0432dbabb32b546c0c0e6ced5ba9d3d) 來取得 context
- 此 API 並不會複製一份 context 物件傳到 handler function 內,而是直接 reference 到原本的該 context 物件,所以在 handler function 內對 context 物件做的修改,會影響到該 context 物件
- [`le_timer_SetWakeup()`](https://docs.legato.io/latest/le__timer_8h.html#a1fa3f608038fabf828677beb66cdd6ef)
- 若 timer 到期時,系統是處在 suspend 狀態,那系統也會被 timer 叫醒,若不希望 suspend 的系統會被 timer 叫醒,則可用此 API 將此功能設置為 false
3. 使用 [`le_timer_Start()`](https://docs.legato.io/latest/le__timer_8h.html#ada2ce7f8cb1e76ed959e323ae94bbfc0) 啟動 timer
4. 若一個 timer 已經啟動,但需要它重新倒數計時,可以使用 [`le_timer_Restart()`](https://docs.legato.io/latest/le__timer_8h.html#ab6b83d6302095a46b6046160c0a479bb)
若要停止一個 timer,則可使用 [`le_timer_Stop()`](https://docs.legato.io/latest/le__timer_8h.html#af310daa378bd6ca39373a47e073f2243)
5. timer 不再使用後,必須用 [`le_timer_Delete()`](https://docs.legato.io/latest/le__timer_8h.html#ae103f6736bf855e77e5e59bbad1e27a7) 將其刪除
- 若 timer 的 context pointer 有動態分配記憶體,則在刪除 timer 前記得要先釋放記憶體,可使用以下 function:
```cpp
static void DeleteTimerAndFreePtr(le_timer_Ref_t t)
{
le_timer_Stop( t );
free( le_timer_GetContextPtr( t ) );
le_timer_Delete( t ); // timer ref is now invalid
}
```
來源:https://docs.legato.io/latest/c_timer.html
## 注意事項
> 以下內容節錄自 https://docs.legato.io/latest/c_timer.html
- ++A timer should only be used by the thread that created it.++
It's not safe for a thread to use or manipulate a timer that belongs to another thread.
- ++The timer expiry handler is called by the **event loop** of **the thread that starts the timer**.++
- ++因此,當 timer 的時間到達了,會把這個 event 放到啟動該 timer 的 thread 的 Event Queue,待其到達 Event Queue 的 front,才會執行該 timer expiry handler++
- ++啟動 timer 的 thread 若是由 `le_thread_Create()` 的 worker thread,則必須執行 `le_event_RunLoop()` 以啟動其 Event Loop、這樣才會執行 timer expiry handler++
- ++The call to the timer expiry handler **may not occur immediately after the timer expires**, depending on which other functions are called from the **event loop**.++
++The amount of delay is entirely dependent on other work done in the **event loop**.++
- 由於 timer 時間到達後,timer expiry handler 會被放到 Event Loop,因此並不保證時間到達後就會馬上執行該 timer expiry handler,因為若 Event Loop 前面還有其他會花很多時間的 event,就會 delay 到 timer expiry handler 的執行。
- For a repeating timer, if this delay is longer than the timer period, one or more timer expiries may be dropped. To reduce the likelihood of dropped expiries, the combined execution time of all handlers called from the event loop should ideally be less than the timer period.
## Example 1
以下簡單的例子示範一個 worker thread 建立一個 timer,interval 是 3 秒,重複 5 次,並且 worker thread 會啟動此 timer。
### Project Structure
```
timer
├── timerApp.adef
└── timerComp
├── Component.cdef
└── timer.c
```
### `timerApp.adef`
```adef
executables:
{
timerExec = ( timerComp )
}
processes:
{
run:
{
timerProc = ( timerExec )
}
}
```
### `Component.cdef`
```cdef
sources:
{
timer.c
}
```
### `timer.c`
```cpp
#include "legato.h"
#include "interfaces.h"
#define REPEAT_TIME (5)
#define INTERVAL_SEC (3)
#define INTERVAL_USEC (0)
#define HANDLER_NAME_BYTE_SIZE (50)
#define MSG_BYTE_SIZE (100)
static le_thread_Ref_t workerThreadRef = NULL;
typedef struct
{
int32_t called_time;
char *handler_name;
char *msg;
} MyHandlerContext_t;
void expiryHandlerFunc(le_timer_Ref_t timerRef)
{
MyHandlerContext_t *handlerContextPtr = (MyHandlerContext_t *) le_timer_GetContextPtr(timerRef);
handlerContextPtr->called_time += 1;
snprintf(handlerContextPtr->msg, (sizeof(char) * 100), "Timer expired times: %d", handlerContextPtr->called_time);
LE_INFO("[%s] inside %s", le_thread_GetMyName(), handlerContextPtr->handler_name);
LE_INFO("Address pointed by handlerContextPtr: %p", (void *) handlerContextPtr);
LE_INFO("%s", handlerContextPtr->msg);
if (handlerContextPtr->called_time == REPEAT_TIME)
{
le_timer_Stop( timerRef );
free(handlerContextPtr->handler_name);
free(handlerContextPtr->msg);
le_timer_Delete( timerRef ); // timer ref is now invalid
LE_INFO("[%s] timer deleted", le_thread_GetMyName());
le_thread_Exit(NULL);
}
}
void workerThreadDestructor(void *context)
{
LE_INFO("[%s] inside worker thread destructor", le_thread_GetMyName());
}
void *workerThreadFunc(void *context)
{
// create a timer
le_timer_Ref_t timerRef = le_timer_Create("myTimer");
// set attributes of the timer
// set the timer interval
le_clk_Time_t timerInterval = {.sec = INTERVAL_SEC, .usec = INTERVAL_USEC};
le_timer_SetInterval(timerRef, timerInterval);
// set the repeat time of the timer
le_timer_SetRepeat(timerRef, REPEAT_TIME);
// set the timer expiry handler function for the timer
le_timer_SetHandler(timerRef, expiryHandlerFunc);
// set the context for the timer expiry handler function
MyHandlerContext_t myTimerHandlerContext = {.called_time = 0, .handler_name = NULL, .msg = NULL};
myTimerHandlerContext.handler_name = (char *) malloc(sizeof(char) * HANDLER_NAME_BYTE_SIZE);
strcpy(myTimerHandlerContext.handler_name, "MyTimerExpiryHandlerFunc");
myTimerHandlerContext.msg = (char *) malloc(sizeof(char) * MSG_BYTE_SIZE);
strcpy(myTimerHandlerContext.msg, "message of MyTimerExpiryHandlerFunc");
le_timer_SetContextPtr(timerRef, (void *) &myTimerHandlerContext);
// start the timer
le_timer_Start(timerRef);
le_thread_AddDestructor(workerThreadDestructor, NULL);
LE_INFO("Address of myTimerHandlerContext: %p", (void *) &myTimerHandlerContext);
// start to run the Event Loop
le_event_RunLoop();
return NULL;
}
COMPONENT_INIT
{
// create thread and set its thread function
workerThreadRef = le_thread_Create("WorkerThread", workerThreadFunc, NULL);
// start threads
le_thread_Start(workerThreadRef);
}
```
### Result

## Example 2
以下例子主要是為了測試 timer 的機制是不是也是使用 Event Loop。
此例子,在 main thread 裡面用 `le_event_QueueFunctionToThread` 將會睡 3 秒的 deferred function 丟到 worker thread 的 Event Queue,然後 worker thread 也啟動一個會重複 4 次,每次都倒數 3 秒的 timer。
### `timer.c`
```cpp
#include "legato.h"
#include "interfaces.h"
#define REPEAT_TIME (4)
#define INTERVAL_SEC (3)
#define INTERVAL_USEC (0)
#define SLEEP_TIME (3)
static le_thread_Ref_t workerThreadRef = NULL;
static le_sem_Ref_t semaphore = NULL;
void expiryHandlerFunc(le_timer_Ref_t timerRef)
{
int32_t *called_time = (int32_t *) le_timer_GetContextPtr(timerRef);
*called_time += 1;
LE_INFO("[%s] inside timer expiry handler function (%d)", le_thread_GetMyName(), *called_time);
le_sem_Post(semaphore);
if (*called_time == REPEAT_TIME)
{
le_timer_Stop(timerRef);
le_timer_Delete(timerRef);
LE_INFO("[%s] timer deleted", le_thread_GetMyName());
le_thread_Exit(NULL);
}
}
void workerThreadDestructor(void *context)
{
LE_INFO("[%s] inside worker thread destructor", le_thread_GetMyName());
}
void *workerThreadFunc(void *context)
{
// create a timer
le_timer_Ref_t timerRef = le_timer_Create("myTimer");
// set attributes of the timer
// set the timer interval
le_clk_Time_t timerInterval = {.sec = INTERVAL_SEC, .usec = INTERVAL_USEC};
le_timer_SetInterval(timerRef, timerInterval);
// set the repeat time of the timer
le_timer_SetRepeat(timerRef, REPEAT_TIME);
// set the timer expiry handler function for the timer
le_timer_SetHandler(timerRef, expiryHandlerFunc);
int32_t called_time = 0;
LE_INFO("Address of local variable of worker thread: %p", (void *) &called_time);
le_timer_SetContextPtr(timerRef, (void *) &called_time);
// start the timer
le_timer_Start(timerRef);
le_thread_AddDestructor(workerThreadDestructor, NULL);
// start to run the Event Loop
le_event_RunLoop();
return NULL;
}
void deferredFunc(void *param1Ptr, void *param2Ptr)
{
*( (int32_t *) param1Ptr ) += 1;
LE_INFO("[%s] inside deferredFunc (%d), ready to sleep for %d sec", le_thread_GetMyName(), *( (int32_t *) param1Ptr ), SLEEP_TIME);
LE_INFO("Address of param1Ptr: %p", param1Ptr);
sleep(SLEEP_TIME);
}
COMPONENT_INIT
{
semaphore = le_sem_Create("MySemaphore", 0);
// create thread and set its thread function
workerThreadRef = le_thread_Create("WorkerThread", workerThreadFunc, NULL);
int32_t deferredFunc_called_time = 0;
LE_INFO("Address of deferredFunc_called_time: %p", (void *) &deferredFunc_called_time);
le_event_QueueFunctionToThread(workerThreadRef, deferredFunc, (void *) &deferredFunc_called_time, NULL);
// start threads
le_thread_Start(workerThreadRef);
le_sem_Wait(semaphore);
le_event_QueueFunctionToThread(workerThreadRef, deferredFunc, (void *) &deferredFunc_called_time, NULL);
le_event_QueueFunctionToThread(workerThreadRef, deferredFunc, (void *) &deferredFunc_called_time, NULL);
le_event_QueueFunctionToThread(workerThreadRef, deferredFunc, (void *) &deferredFunc_called_time, NULL);
LE_INFO("COMPONENT_INIT finished");
}
```
### Result

從結果可以看到,第一個 timer expiry handler 被執行後,main thread 就丟了三個 deferred function 到 worker thread 的 Event Queue。
而當 worker thread 在執行這三個 deferred function 的時候,第二至第四次的 timer 到期事件也發生了,這些 event 也都被丟到 Event Queue。
待三個 deferred function 都執行完畢後,就輪到第二至第四次的 timer 到期事件到達 Event Queue 的 front,所以可以看到 worker thread 在 09:59:23 這一秒內就執行了三個 timer expiry function。