# [Legato] Thread
> https://docs.legato.io/latest/c_threading.html
https://docs.legato.io/latest/le__thread_8h.html
## Legato Thraed 基本使用流程

Legato Thread 基本使用流程如上圖所示。
一個 worker thread 有三種可能的原因結束:
1. 從 thread main function return。
2. Worker thread 自己呼叫 `le_thread_Exit()`。
3. 被其他 thread 呼叫 `le_thread_Cancel()` 而結束。
最後,若有其他 thread 呼叫 `le_thread_Join()` 在等該 worker thread 的結果,也要記得把結果傳給該 thread。
## 注意事項
- 和 POSIX `pthread_create` 不一樣的地方在於,當我們執行 `le_thread_Create` 建立一個 Legato thread 後,Legato thread 並不會馬上執行,而是處在 ++suspend state++。
- 若有需要可以趁這時候設定 Legato thread 的一些 attributes,然後再用 `le_thread_Start` 來啟動該 Legato thread。
- 當 parentThread 建立了另一個 thread - childThread,則只有 parentThread 可以設定 childThread 的 attributes 以及啟動該 childThread,其他 thread 不可以設定該 childThread 的 attributes 或啟動它。
- 我們可以為同一個 thread 註冊多個 destructors,這些 destructor 會以註冊時的反向順序被呼叫(也就是最後註冊的 destructor 會最先被呼叫)。
## Example
以下例子,示範怎麼傳多個變數到 worker thread,並且將結果回傳給呼叫 `le_thread_Join()` 的 thread 以取得結果。
此外,也為 worker thread 註冊三個 destructor,以及傳送 `context` 至 destructor,並在 destructor 內取得 `context`。
最後,亦觀察 destructor 的呼叫順序是否會依照註冊 destructor 的反向順序呼叫。
### Project Structure
```
thread
├── threadApp.adef
└── threadComp
├── Component.cdef
└── thread.c
```
### `threadApp.adef`
```adef
executables:
{
threadExec = ( threadComp )
}
processes:
{
run:
{
threadProc = ( threadExec )
}
}
```
### `Component.cdef`
```cdef
sources:
{
thread.c
}
```
### `thread.c`
```cpp
#include "legato.h"
#include "interfaces.h"
#define DESTRUCTOR_NUM (3)
#define MSG_BYTE_SIZE ( sizeof(char) * 50 )
static le_thread_Ref_t workerThreadRef = NULL;
typedef struct
{
int32_t n1;
int32_t n2;
char *str;
} MyParameter_t;
typedef struct
{
int32_t sum;
char *str;
} MyReturnValue_t;
void threadDestructor1(void *context)
{
LE_INFO("[%s] %s", le_thread_GetMyName(), (char *) context);
free(context);
}
void threadDestructor2(void *context)
{
LE_INFO("[%s] %s", le_thread_GetMyName(), (char *) context);
free(context);
}
void threadDestructor3(void *context)
{
LE_INFO("[%s] %s", le_thread_GetMyName(), (char *) context);
free(context);
}
void (*threadDestructorFuncs[]) (void *) = { threadDestructor1, threadDestructor2, threadDestructor3 };
void *workerThreadFunc(void *context)
{
char *destructorMsg[DESTRUCTOR_NUM] = {NULL};
for (int i = 0; i < DESTRUCTOR_NUM; ++i)
{
destructorMsg[i] = (char *) malloc(MSG_BYTE_SIZE);
snprintf(destructorMsg[i], MSG_BYTE_SIZE, "inside worker thread DESTRUCTOR %d", (i + 1));
le_thread_AddDestructor(threadDestructorFuncs[i], destructorMsg[i]);
}
MyParameter_t *paramPtr = (MyParameter_t *) context;
paramPtr->str = "WorkerThread says Hello!";
MyReturnValue_t *retPtr = (MyReturnValue_t *) malloc(sizeof(MyReturnValue_t));
retPtr->sum = (paramPtr->n1 + paramPtr->n2);
retPtr->str = paramPtr->str;
LE_INFO("[%s] retPtr->sum: %d, retPtr->str: %s", le_thread_GetMyName(), retPtr->sum, retPtr->str);
return (void *) retPtr;
}
COMPONENT_INIT
{
MyParameter_t my_param = {.n1 = 100, .n2 = 200, .str = NULL};
LE_INFO("[%s] my_param - n1: %d, n2: %d, str: %s", le_thread_GetMyName(), my_param.n1, my_param.n2, ( (my_param.str == NULL) ? "NULL" : my_param.str) );
workerThreadRef = le_thread_Create("MyWorkerThread", workerThreadFunc, (void *) &my_param);
le_thread_SetJoinable(workerThreadRef);
le_thread_Start(workerThreadRef);
MyReturnValue_t *res = NULL;
le_thread_Join(workerThreadRef, (void **) &res);
LE_INFO("[%s] result - sum: %d, str: %s", le_thread_GetMyName(), res->sum, res->str);
free(res);
}
```
### Result

以上的程式碼,讓 worker thread 先註冊了 destructor1,之後才註冊 destructor2,最後註冊 destructor3。
從執行結果可看到,當該 worker thread 要結束時,確實先呼叫了 destructor3,然後 destructor2,最後才呼叫 destructor1。