# RT-Thread IPC Semaphore
- 為一個值,代表同時可用的個數
- 不等於 0 時可用,取用時將值減 1
- 當不可用時,將 thread 掛在等待的鏈上
## 結構
:::success
**File:** rtdef.h
:::
```c=591
#ifdef RT_USING_SEMAPHORE
/**
* Semaphore structure
*/
struct rt_semaphore
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< value of semaphore. */
};
typedef struct rt_semaphore *rt_sem_t;
#endif
```
---
### flags
```c=569
/**
* IPC flags and control command definitions
*/
#define RT_IPC_FLAG_FIFO 0x00 /**< FIFOed IPC. @ref IPC. */
#define RT_IPC_FLAG_PRIO 0x01 /**< PRIOed IPC. @ref IPC. */
#define RT_IPC_CMD_UNKNOWN 0x00 /**< unknown IPC command */
#define RT_IPC_CMD_RESET 0x01 /**< reset IPC object */
#define RT_WAITING_FOREVER -1 /**< Block forever until get resource. */
#define RT_WAITING_NO 0 /**< Non-block. */
```
---
:::success
**File:** ipc.c
:::
## 建立 semaphore
### 動態記憶體管理
#### `rt_sem_create`
| 功能 | 回傳值 |
| --- | ------ |
| 建立 semaphore | semaphore |
| `*name` | `value` | `flag` |
| ------- | ------- | ------ |
| 名字 | semaphore 值,即最大可同時使用人數 | FIFO / PRIO |
```c=247
/**
* This function will create a semaphore from system resource
*
* @param name the name of semaphore
* @param value the init value of semaphore
* @param flag the flag of semaphore
*
* @return the created semaphore, RT_NULL on error happen
*
* @see rt_sem_init
*/
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)
{
rt_sem_t sem;
RT_DEBUG_NOT_IN_INTERRUPT;
/* allocate object */
sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name);
if (sem == RT_NULL)
return sem;
/* init ipc object */
rt_ipc_object_init(&(sem->parent));
/* set init value */
sem->value = value;
/* set parent */
sem->parent.parent.flag = flag;
return sem;
}
RTM_EXPORT(rt_sem_create);
```
- 首先需要一塊 semaphore 的大小,初始化 ipc 物件,再依序寫入初始值及 flag
---
### 靜態記憶體管理
#### `rt_sem_init`
| 功能 | 回傳值 |
| --- | ------ |
| 初始化 semaphore | `RT_EOK` |
| `sem` | `*name` | `value` | `flag` |
| ----- | ------- | ------- | ------ |
| semaphore 本體 | 名字 | semaphore 值,即最大可同時使用人數 | FIFO / PRIO |
```c=186
/**
* This function will initialize a semaphore and put it under control of
* resource management.
*
* @param sem the semaphore object
* @param name the name of semaphore
* @param value the init value of semaphore
* @param flag the flag of semaphore
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_sem_init(rt_sem_t sem,
const char *name,
rt_uint32_t value,
rt_uint8_t flag)
{
RT_ASSERT(sem != RT_NULL);
/* init object */
rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name);
/* init ipc object */
rt_ipc_object_init(&(sem->parent));
/* set init value */
sem->value = value;
/* set parent */
sem->parent.parent.flag = flag;
return RT_EOK;
}
RTM_EXPORT(rt_sem_init);
```
- 由於使用靜態記憶體,這裡就不需要再 allocate。
---
## 刪除 semaphore
### 動態記憶體管理
#### `rt_sem_delete`
| 功能 | 回傳值 |
| --- | ------ |
| 刪除 semaphore | `RT_EOK` |
| `sem` |
| ----- |
| 欲刪除的 semaphore |
```c=282
/**
* This function will delete a semaphore object and release the memory
*
* @param sem the semaphore object
*
* @return the error code
*
* @see rt_sem_detach
*/
rt_err_t rt_sem_delete(rt_sem_t sem)
{
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent) == RT_FALSE);
/* wakeup all suspend threads */
rt_ipc_list_resume_all(&(sem->parent.suspend_thread));
/* delete semaphore object */
rt_object_delete(&(sem->parent.parent));
return RT_EOK;
}
RTM_EXPORT(rt_sem_delete);
```
- 首先需把所有正在等待此 semaphore 的 thread 叫醒
- 接著呼叫 `rt_object_delete` 清除此物件(semaphore)
---
### 靜態記憶體管理
#### `rt_sem_detach`
| 功能 | 回傳值 |
| --- | ------ |
| 刪除 semaphore | `RT_EOK` |
| `sem` |
| ----- |
| 欲刪除的 semaphore |
```c=220
/**
* This function will detach a semaphore from resource management
*
* @param sem the semaphore object
*
* @return the operation status, RT_EOK on successful
*
* @see rt_sem_delete
*/
rt_err_t rt_sem_detach(rt_sem_t sem)
{
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent));
/* wakeup all suspend threads */
rt_ipc_list_resume_all(&(sem->parent.suspend_thread));
/* detach semaphore object */
rt_object_detach(&(sem->parent.parent));
return RT_EOK;
}
RTM_EXPORT(rt_sem_detach);
```
- 這裡則透過 `rt_object_detach` 清除
---
## 使用 semaphore
- 呼叫 `rt_sem_take` 來取得 semaphore,傳入的 time 是等待時間
#### `rt_sem_take`
| 功能 | 回傳值 |
| --- | ------ |
| 要求 semaphore | `RT_EOK` |
| `sem` | `time` |
| ----- | ------ |
| 欲要求的 semaphore | 等待時間(如果需要)|
```c=311
/**
* This function will take a semaphore, if the semaphore is unavailable, the
* thread shall wait for a specified time.
*
* @param sem the semaphore object
* @param time the waiting time
*
* @return the error code
*/
rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
{
register rt_base_t temp;
struct rt_thread *thread;
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent)));
/* disable interrupt */
temp = rt_hw_interrupt_disable();
```
- 由於待會會修改 semaphore 的值,這裡先將中斷關閉
```c=+
RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s take sem:%s, which value is: %d\n",
rt_thread_self()->name,
((struct rt_object *)sem)->name,
sem->value));
if (sem->value > 0)
{
/* semaphore is available */
sem->value --;
/* enable interrupt */
rt_hw_interrupt_enable(temp);
}
```
- 如過 `sem->value` 值大於 0 代表可用,接著減一,並開啟中斷
```c=+
else
{
/* no waiting, return with timeout */
if (time == 0)
{
rt_hw_interrupt_enable(temp);
return -RT_ETIMEOUT;
}
```
- 如果 semaphore 不可用時:
- 且 time 為 0,表示不等待,直接開啟中斷並 return
```c=+
else
{
/* current context checking */
RT_DEBUG_IN_THREAD_CONTEXT;
/* semaphore is unavailable, push to suspend list */
/* get current thread */
thread = rt_thread_self();
/* reset thread error number */
thread->error = RT_EOK;
RT_DEBUG_LOG(RT_DEBUG_IPC, ("sem take: suspend thread - %s\n",
thread->name));
/* suspend thread */
rt_ipc_list_suspend(&(sem->parent.suspend_thread),
thread,
sem->parent.parent.flag);
```
- 如果要等待,則將 thread 插入 suspend list
```c=+
/* has waiting time, start thread timer */
if (time > 0)
{
RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n",
thread->name));
/* reset the timeout of thread timer and start it */
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&time);
rt_timer_start(&(thread->thread_timer));
}
```
- 且如果等待時間大於 0,則啟動一個 timeout 為 time 的 timer
```c=+
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* do schedule */
rt_schedule();
if (thread->error != RT_EOK)
{
return thread->error;
}
}
}
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent)));
return RT_EOK;
}
RTM_EXPORT(rt_sem_take);
```
- 最後開啟中斷,做一次調度
---
- 若是不想等待,可以呼叫 `rt_sem_trytake`
- 即呼叫 `rt_sem_take` 及傳入 `time` 為 0
### `rt_sem_trytake`
| 功能 | 回傳值 |
| --- | ------ |
| 要求 semaphore(不等待) | `RT_EOK` |
| `sem` |
| ----- |
| 欲要求的 semaphore |
```c=408
/**
* This function will try to take a semaphore and immediately return
*
* @param sem the semaphore object
*
* @return the error code
*/
rt_err_t rt_sem_trytake(rt_sem_t sem)
{
return rt_sem_take(sem, 0);
}
RTM_EXPORT(rt_sem_trytake);
```
---
- 還 semaphore 則使用 `rt_sem_release`
#### `rt_sem_release`
| 功能 | 回傳值 |
| --- | ------ |
| 釋放 semaphore | `RT_EOK` |
| `sem` |
| ----- |
| 欲要求的 semaphore |
```c=421
/**
* This function will release a semaphore, if there are threads suspended on
* semaphore, it will be waked up.
*
* @param sem the semaphore object
*
* @return the error code
*/
rt_err_t rt_sem_release(rt_sem_t sem)
{
register rt_base_t temp;
register rt_bool_t need_schedule;
/* parameter check */
RT_ASSERT(sem != RT_NULL);
RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(sem->parent.parent)));
need_schedule = RT_FALSE;
/* disable interrupt */
temp = rt_hw_interrupt_disable();
```
- 首先將待會會遇到的 flag(`need_schedule`)設為 false
- 因為待會也會修改 semaphore 的值,這裡需要關閉中斷
```c=+
RT_DEBUG_LOG(RT_DEBUG_IPC, ("thread %s releases sem:%s, which value is: %d\n",
rt_thread_self()->name,
((struct rt_object *)sem)->name,
sem->value));
if (!rt_list_isempty(&sem->parent.suspend_thread))
{
/* resume the suspended thread */
rt_ipc_list_resume(&(sem->parent.suspend_thread));
need_schedule = RT_TRUE;
}
```
- 如果有人在等此 semaphore,先恢復他,並修改 `need_schedule` 為 true
```c=+
else
sem->value ++; /* increase value */
```
- 如果沒有人在等待,則加一
```c=+
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* resume a thread, re-schedule */
if (need_schedule == RT_TRUE)
rt_schedule();
return RT_EOK;
}
RTM_EXPORT(rt_sem_release);
```
- 最後開啟中斷,並根據 `need_schedule` 來決定需不需要執行一次調度
###### tags: `RT-Thread` `kernel` `ipc` `semaphore`