# 作業系統工程-Synchonization
## Table Of Content
###### tags: `作業系統工程 Operating System Programming note`, `110-1`, `2021`
[TOC]
---
# ipc 共同的 function
## inition
```c=
rt_inline rt_err_t _ipc_object_init(struct rt_ipc_object *ipc)
{
rt_list_init(&(ipc->suspend_thread));/* initialize ipc object */
return RT_EOK;
}
```
## resume
```c=
rt_inline rt_err_t _ipc_list_resume(rt_list_t *list)
{
struct rt_thread *thread;
thread = rt_list_entry(list->next, struct rt_thread, tlist);/* get thread entry */
RT_DEBUG_LOG(RT_DEBUG_IPC, ("resume thread:%s\n", thread->name));
rt_thread_resume(thread);/* resume it */
return RT_EOK;
}
```
## resume all
- = 用迴圈呼叫上方 function
```c=
rt_inline rt_err_t _ipc_list_resume_all(rt_list_t *list)
{
struct rt_thread *thread;
register rt_ubase_t temp;
/* wakeup all suspended threads */
while (!rt_list_isempty(list))
{
temp = rt_hw_interrupt_disable();/* disable interrupt */
thread = rt_list_entry(list->next, struct rt_thread, tlist);/* get next suspended thread */
thread->error = -RT_ERROR;/* set error code to RT_ERROR */
/*
* resume thread
* In rt_thread_resume function, it will remove current thread from
* suspended list
*/
rt_thread_resume(thread);
rt_hw_interrupt_enable(temp);/* enable interrupt */
}
return RT_EOK;
}
```
# Semaphone
## Concept
### Semaphone operation
## Semaphone 在 rtdef 中的定義
```c=
#ifdef RT_USING_SEMAPHORE
struct rt_semaphore
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< value of semaphore. */
rt_uint16_t reserved; /**< reserved field */
};
typedef struct rt_semaphore *rt_sem_t;
#endif
```
## Semaphone operation 實作
### Inition
```c=
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);
RT_ASSERT(value < 0x10000U);
RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
/* initialize object */
rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name);
/* initialize ipc object */
_ipc_object_init(&(sem->parent));
sem->value = (rt_uint16_t)value;/* set initial value */
sem->parent.parent.flag = flag;/* set parent */
return RT_EOK;
}
```
### Create
```c=
rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)
{
rt_sem_t sem;
RT_ASSERT(value < 0x10000U);
RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
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;
_ipc_object_init(&(sem->parent));/* initialize ipc object */
sem->value = value;/* set initial value */
sem->parent.parent.flag = flag;/* set parent */
return sem;
}
```
### Take
```c=
rt_err_t rt_sem_trytake(rt_sem_t sem)
{
return rt_sem_take(sem, RT_WAITING_NO);
}
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)));
temp = rt_hw_interrupt_disable();/* disable interrupt */
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 --;
//拿走一個 sem (做完)就打開中斷
rt_hw_interrupt_enable(temp);/* enable interrupt */
}
else/* semaphore is NOT available */
{
/* no waiting, return with timeout */
if (time == 0)
{
rt_hw_interrupt_enable(temp);
return -RT_ETIMEOUT;
}
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 */
_ipc_list_suspend(&(sem->parent.suspend_thread), thread, sem->parent.parent.flag);
/* 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));
}
rt_hw_interrupt_enable(temp);/* enable interrupt */
rt_schedule();/* do schedule */
if (thread->error != RT_EOK)
return thread->error;
}
}
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent)));
return RT_EOK;
}
```
### Release
```c=
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;
temp = rt_hw_interrupt_disable(); /* disable interrupt */
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))
{
_ipc_list_resume(&(sem->parent.suspend_thread));/* resume the suspended thread */
need_schedule = RT_TRUE;
}
else
{
if(sem->value < RT_SEM_VALUE_MAX)
sem->value ++; /* increase value */
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
}
rt_hw_interrupt_enable(temp);/* enable interrupt */
if (need_schedule == RT_TRUE)/* resume a thread, re-schedule */
rt_schedule();
return RT_EOK;
}
```
### Detach
```c=
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));
//先叫醒
_ipc_list_resume_all(&(sem->parent.suspend_thread));/* wakeup all suspended threads */
//再移除
rt_object_detach(&(sem->parent.parent));/* detach semaphore object */
return RT_EOK;
}
```
### Delete
```c=
rt_err_t rt_sem_delete(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) == RT_FALSE);
RT_DEBUG_NOT_IN_INTERRUPT;
//先叫醒
_ipc_list_resume_all(&(sem->parent.suspend_thread));/* wakeup all suspended threads */
//再刪除
rt_object_delete(&(sem->parent.parent));/* delete semaphore object */
return RT_EOK;
}
```
# Mutex
## Concept
### Mutex operation

## Mutex 在 rtdef 中的定義
```c=
#ifdef RT_USING_MUTEX
struct rt_mutex
{
struct rt_ipc_object parent; /**< inherit from ipc_object */
rt_uint16_t value; /**< value of mutex */
rt_uint8_t original_priority;/**< priority of last thread hold the mutex */
rt_uint8_t hold; /**< numbers of thread hold the mutex */
struct rt_thread *owner; /**< current owner of mutex */
//掛著現在拿住此 mutex 的 thread
};
typedef struct rt_mutex *rt_mutex_t;
#endif
```
## Mutex operation 實作
### Create
```c=
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
{
struct rt_mutex *mutex;
/* flag parameter has been obsoleted */
RT_UNUSED(flag);
RT_DEBUG_NOT_IN_INTERRUPT;
/* allocate object */
mutex = (rt_mutex_t)rt_object_allocate(RT_Object_Class_Mutex, name);
if (mutex == RT_NULL)
return mutex;
/* initialize ipc object */
_ipc_object_init(&(mutex->parent));
mutex->value = 1;
mutex->owner = RT_NULL;
mutex->original_priority = 0xFF;
mutex->hold = 0;
/* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */
mutex->parent.parent.flag = RT_IPC_FLAG_PRIO;
return mutex;
}
```
### Init
```c=
rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
{
/* flag parameter has been obsoleted */
RT_UNUSED(flag);
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
/* initialize object */
rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name);
/* initialize ipc object */
_ipc_object_init(&(mutex->parent));
mutex->value = 1;
mutex->owner = RT_NULL;
mutex->original_priority = 0xFF;
mutex->hold = 0;
/* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */
mutex->parent.parent.flag = RT_IPC_FLAG_PRIO;
return RT_EOK;
}
```
### Take
```c=
rt_err_t rt_mutex_trytake(rt_mutex_t mutex)
{
return rt_mutex_take(mutex, RT_WAITING_NO);
}
rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
{
register rt_base_t temp;
struct rt_thread *thread;
/* this function must not be used in interrupt even if time = 0 */
RT_DEBUG_IN_THREAD_CONTEXT;
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
/* get current thread */
thread = rt_thread_self();
/* disable interrupt */
temp = rt_hw_interrupt_disable();
RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent)));
RT_DEBUG_LOG(RT_DEBUG_IPC,
("mutex_take: current thread %s, mutex value: %d, hold: %d\n",
thread->name, mutex->value, mutex->hold));
/* reset thread error */
thread->error = RT_EOK;
if (mutex->owner == thread)
{
if(mutex->hold < RT_MUTEX_HOLD_MAX)
{
/* it's the same thread */
mutex->hold ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
}
else
{
#ifdef RT_USING_SIGNALS
__again:
#endif /* RT_USING_SIGNALS */
/* The value of mutex is 1 in initial status. Therefore, if the
* value is great than 0, it indicates the mutex is avaible.
*/
if (mutex->value > 0)
{
/* mutex is available */
mutex->value --;
/* set mutex owner and original priority */
mutex->owner = thread;
mutex->original_priority = thread->current_priority;
if(mutex->hold < RT_MUTEX_HOLD_MAX)
{
mutex->hold ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
}
else
{
/* no waiting, return with timeout */
if (time == 0)
{
/* set error as timeout */
thread->error = -RT_ETIMEOUT;
/* enable interrupt */
rt_hw_interrupt_enable(temp);
return -RT_ETIMEOUT;
}
else
{
/* mutex is unavailable, push to suspend list */
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: suspend thread: %s\n",
thread->name));
/* change the owner thread priority of mutex */
if (thread->current_priority < mutex->owner->current_priority)
{
/* change the owner thread priority */
rt_thread_control(mutex->owner,
RT_THREAD_CTRL_CHANGE_PRIORITY,
&thread->current_priority);
}
/* suspend current thread */
_ipc_list_suspend(&(mutex->parent.suspend_thread),
thread,
mutex->parent.parent.flag);
/* has waiting time, start thread timer */
if (time > 0)
{
RT_DEBUG_LOG(RT_DEBUG_IPC,
("mutex_take: start the timer of thread:%s\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));
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* do schedule */
rt_schedule();
if (thread->error != RT_EOK)
{
#ifdef RT_USING_SIGNALS
/* interrupt by signal, try it again */
if (thread->error == -RT_EINTR) goto __again;
#endif /* RT_USING_SIGNALS */
/* return error */
return thread->error;
}
else
{
/* the mutex is taken successfully. */
/* disable interrupt */
temp = rt_hw_interrupt_disable();
}
}
}
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent)));
return RT_EOK;
}
```
### Release
```c=
rt_err_t rt_mutex_release(rt_mutex_t mutex)
{
register rt_base_t temp;
struct rt_thread *thread;
rt_bool_t need_schedule;
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
need_schedule = RT_FALSE;
/* only thread could release mutex because we need test the ownership */
RT_DEBUG_IN_THREAD_CONTEXT;
/* get current thread */
thread = rt_thread_self();
/* disable interrupt */
temp = rt_hw_interrupt_disable();
RT_DEBUG_LOG(RT_DEBUG_IPC,
("mutex_release:current thread %s, mutex value: %d, hold: %d\n",
thread->name, mutex->value, mutex->hold));
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent)));
/* mutex only can be released by owner */
if (thread != mutex->owner)
{
thread->error = -RT_ERROR;
/* enable interrupt */
rt_hw_interrupt_enable(temp);
return -RT_ERROR;
}
/* decrease hold */
mutex->hold --;
/* if no hold */
if (mutex->hold == 0)
{
/* change the owner thread to original priority */
if (mutex->original_priority != mutex->owner->current_priority)
{
rt_thread_control(mutex->owner,
RT_THREAD_CTRL_CHANGE_PRIORITY,
&(mutex->original_priority));
}
/* wakeup suspended thread */
if (!rt_list_isempty(&mutex->parent.suspend_thread))
{
/* get suspended thread */
thread = rt_list_entry(mutex->parent.suspend_thread.next,
struct rt_thread,
tlist);
RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release: resume thread: %s\n",
thread->name));
/* set new owner and priority */
mutex->owner = thread;
mutex->original_priority = thread->current_priority;
if(mutex->hold < RT_MUTEX_HOLD_MAX)
{
mutex->hold ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
/* resume thread */
_ipc_list_resume(&(mutex->parent.suspend_thread));
need_schedule = RT_TRUE;
}
else
{
if(mutex->value < RT_MUTEX_VALUE_MAX)
{
/* increase value */
mutex->value ++;
}
else
{
rt_hw_interrupt_enable(temp); /* enable interrupt */
return -RT_EFULL; /* value overflowed */
}
/* clear owner */
mutex->owner = RT_NULL;
mutex->original_priority = 0xff;
}
}
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* perform a schedule */
if (need_schedule == RT_TRUE)
rt_schedule();
return RT_EOK;
}
```
### Delete
```c=
rt_err_t rt_mutex_delete(rt_mutex_t mutex)
{
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent) == RT_FALSE);
RT_DEBUG_NOT_IN_INTERRUPT;
/* wakeup all suspended threads */
_ipc_list_resume_all(&(mutex->parent.suspend_thread));
/* delete mutex object */
rt_object_delete(&(mutex->parent.parent));
return RT_EOK;
}
```
### Detach
```c=
rt_err_t rt_mutex_detach(rt_mutex_t mutex)
{
/* parameter check */
RT_ASSERT(mutex != RT_NULL);
RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent));
/* wakeup all suspended threads */
_ipc_list_resume_all(&(mutex->parent.suspend_thread));
/* detach mutex object */
rt_object_detach(&(mutex->parent.parent));
return RT_EOK;
}
```