# 作業系統工程-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![](https://i.imgur.com/fr3PAWB.png) ## 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 ![](https://i.imgur.com/dlnQM5a.png) ## 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; } ```