1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2006-03-14     Bernard      the first version
9  * 2006-04-25     Bernard      implement semaphore
10  * 2006-05-03     Bernard      add RT_IPC_DEBUG
11  *                             modify the type of IPC waiting time to rt_int32_t
12  * 2006-05-10     Bernard      fix the semaphore take bug and add IPC object
13  * 2006-05-12     Bernard      implement mailbox and message queue
14  * 2006-05-20     Bernard      implement mutex
15  * 2006-05-23     Bernard      implement fast event
16  * 2006-05-24     Bernard      implement event
17  * 2006-06-03     Bernard      fix the thread timer init bug
18  * 2006-06-05     Bernard      fix the mutex release bug
19  * 2006-06-07     Bernard      fix the message queue send bug
20  * 2006-08-04     Bernard      add hook support
21  * 2009-05-21     Yi.qiu       fix the sem release bug
22  * 2009-07-18     Bernard      fix the event clear bug
23  * 2009-09-09     Bernard      remove fast event and fix ipc release bug
24  * 2009-10-10     Bernard      change semaphore and mutex value to unsigned value
25  * 2009-10-25     Bernard      change the mb/mq receive timeout to 0 if the
26  *                             re-calculated delta tick is a negative number.
27  * 2009-12-16     Bernard      fix the rt_ipc_object_suspend issue when IPC flag
28  *                             is RT_IPC_FLAG_PRIO
29  * 2010-01-20     mbbill       remove rt_ipc_object_decrease function.
30  * 2010-04-20     Bernard      move memcpy outside interrupt disable in mq
31  * 2010-10-26     yi.qiu       add module support in rt_mp_delete and rt_mq_delete
32  * 2010-11-10     Bernard      add IPC reset command implementation.
33  * 2011-12-18     Bernard      add more parameter checking in message queue
34  * 2013-09-14     Grissiom     add an option check in rt_event_recv
35  * 2018-10-02     Bernard      add 64bit support for mailbox
36  * 2019-09-16     tyx          add send wait support for message queue
37  * 2020-07-29     Meco Man     fix thread->event_set/event_info when received an
38  *                             event without pending
39  * 2020-10-11     Meco Man     add value overflow-check code
40  * 2021-01-03     Meco Man     implement rt_mb_urgent()
41  * 2021-05-30     Meco Man     implement rt_mutex_trytake()
42  * 2022-01-07     Gabriel      Moving __on_rt_xxxxx_hook to ipc.c
43  * 2022-01-24     THEWON       let rt_mutex_take return thread->error when using signal
44  * 2022-04-08     Stanley      Correct descriptions
45  * 2022-10-15     Bernard      add nested mutex feature
46  * 2022-10-16     Bernard      add prioceiling feature in mutex
47  * 2023-04-16     Xin-zheqi    redesigen queue recv and send function return real message size
48  * 2023-09-15     xqyjlj       perf rt_hw_interrupt_disable/enable
49  */
50 
51 #include <rtthread.h>
52 #include <rthw.h>
53 
54 #define DBG_TAG           "kernel.ipc"
55 #define DBG_LVL           DBG_INFO
56 #include <rtdbg.h>
57 
58 #define GET_MESSAGEBYTE_ADDR(msg)               ((struct rt_mq_message *) msg + 1)
59 #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
60 extern void (*rt_object_trytake_hook)(struct rt_object *object);
61 extern void (*rt_object_take_hook)(struct rt_object *object);
62 extern void (*rt_object_put_hook)(struct rt_object *object);
63 #endif /* RT_USING_HOOK */
64 
65 /**
66  * @addtogroup group_thread_comm
67  * @{
68  */
69 
70 /**
71  * @brief    This function will initialize an IPC object, such as semaphore, mutex, messagequeue and mailbox.
72  *
73  * @note     Executing this function will complete an initialization of the suspend thread list of the ipc object.
74  *
75  * @param    ipc is a pointer to the IPC object.
76  *
77  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
78  *           When the return value is any other values, it means the initialization failed.
79  *
80  * @warning  This function can be called from all IPC initialization and creation.
81  */
_ipc_object_init(struct rt_ipc_object * ipc)82 rt_inline rt_err_t _ipc_object_init(struct rt_ipc_object *ipc)
83 {
84     /* initialize ipc object */
85     rt_list_init(&(ipc->suspend_thread));
86 
87     return RT_EOK;
88 }
89 
90 
91 /**
92  * @brief   Dequeue a thread from suspended list and set it to ready. The 2 are
93  *          taken as an atomic operation, so if a thread is returned, it's
94  *          resumed by us, not any other threads or async events. This is useful
95  *          if a consumer may be resumed by timeout, signals... besides its
96  *          producer.
97  *
98  * @param   susp_list the list thread dequeued from. RT_NULL if no list.
99  * @param   thread_error thread error number of the resuming thread.
100  *          A negative value in this set will be discarded, and thread error
101  *          will not be changed.
102  *
103  * @return  struct rt_thread * RT_NULL if failed, otherwise the thread resumed
104  */
rt_susp_list_dequeue(rt_list_t * susp_list,rt_err_t thread_error)105 struct rt_thread *rt_susp_list_dequeue(rt_list_t *susp_list, rt_err_t thread_error)
106 {
107     rt_sched_lock_level_t slvl;
108     rt_thread_t thread;
109     rt_err_t error;
110 
111     RT_SCHED_DEBUG_IS_UNLOCKED;
112     RT_ASSERT(susp_list != RT_NULL);
113 
114     rt_sched_lock(&slvl);
115     if (!rt_list_isempty(susp_list))
116     {
117         thread = RT_THREAD_LIST_NODE_ENTRY(susp_list->next);
118         error = rt_sched_thread_ready(thread);
119 
120         if (error)
121         {
122             LOG_D("%s [error:%d] failed to resume thread:%p from suspended list",
123                   __func__, error, thread);
124 
125             thread = RT_NULL;
126         }
127         else
128         {
129             /* thread error should not be a negative value */
130             if (thread_error >= 0)
131             {
132                 /* set thread error code to notified resuming thread */
133                 thread->error = thread_error;
134             }
135         }
136     }
137     else
138     {
139         thread = RT_NULL;
140     }
141     rt_sched_unlock(slvl);
142 
143     LOG_D("resume thread:%s\n", thread->parent.name);
144 
145     return thread;
146 }
147 
148 
149 /**
150  * @brief   This function will resume all suspended threads in the IPC object list,
151  *          including the suspended list of IPC object, and private list of mailbox etc.
152  *
153  * @note    This function will resume all threads in the IPC object list.
154  *          By contrast, the rt_ipc_list_resume() function will resume a suspended thread in the list of a IPC object.
155  *
156  * @param   susp_list is a pointer to a suspended thread list of the IPC object.
157  * @param   thread_error thread error number of the resuming thread.
158  *          A negative value in this set will be discarded, and thread error
159  *          will not be changed.
160  *
161  * @return  Return the operation status. When the return value is RT_EOK, the function is successfully executed.
162  *          When the return value is any other values, it means this operation failed.
163  *
164  */
rt_susp_list_resume_all(rt_list_t * susp_list,rt_err_t thread_error)165 rt_err_t rt_susp_list_resume_all(rt_list_t *susp_list, rt_err_t thread_error)
166 {
167     struct rt_thread *thread;
168 
169     RT_SCHED_DEBUG_IS_UNLOCKED;
170 
171     /* wakeup all suspended threads */
172     thread = rt_susp_list_dequeue(susp_list, thread_error);
173     while (thread)
174     {
175         /*
176          * resume NEXT thread
177          * In rt_thread_resume function, it will remove current thread from
178          * suspended list
179          */
180         thread = rt_susp_list_dequeue(susp_list, thread_error);
181     }
182 
183     return RT_EOK;
184 }
185 
186 /**
187  * @brief   This function will resume all suspended threads in the IPC object list,
188  *          including the suspended list of IPC object, and private list of mailbox etc.
189  *          A lock is passing and hold while operating.
190  *
191  * @note    This function will resume all threads in the IPC object list.
192  *          By contrast, the rt_ipc_list_resume() function will resume a suspended thread in the list of a IPC object.
193  *
194  * @param   susp_list is a pointer to a suspended thread list of the IPC object.
195  * @param   thread_error thread error number of the resuming thread.
196  *          A negative value in this set will be discarded, and thread error
197  *          will not be changed.
198  * @param   lock the lock to be held while operating susp_list
199  *
200  * @return  Return the operation status. When the return value is RT_EOK, the function is successfully executed.
201  *          When the return value is any other values, it means this operation failed.
202  *
203  */
rt_susp_list_resume_all_irq(rt_list_t * susp_list,rt_err_t thread_error,struct rt_spinlock * lock)204 rt_err_t rt_susp_list_resume_all_irq(rt_list_t *susp_list,
205                                      rt_err_t thread_error,
206                                      struct rt_spinlock *lock)
207 {
208     struct rt_thread *thread;
209     rt_base_t level;
210 
211     RT_SCHED_DEBUG_IS_UNLOCKED;
212 
213     do
214     {
215         level = rt_spin_lock_irqsave(lock);
216 
217         /*
218          * resume NEXT thread
219          * In rt_thread_resume function, it will remove current thread from
220          * suspended list
221          */
222         thread = rt_susp_list_dequeue(susp_list, thread_error);
223 
224         rt_spin_unlock_irqrestore(lock, level);
225     }
226     while (thread);
227 
228     return RT_EOK;
229 }
230 
231 /**
232  * @brief   Add a thread to the suspend list
233  *
234  * @note    Caller must hold the scheduler lock
235  *
236  * @param   susp_list the list thread enqueued to
237  * @param   thread the suspended thread
238  * @param   ipc_flags the pattern of suspend list
239  * @return  RT_EOK on succeed, otherwise a failure
240  */
rt_susp_list_enqueue(rt_list_t * susp_list,rt_thread_t thread,int ipc_flags)241 rt_err_t rt_susp_list_enqueue(rt_list_t *susp_list, rt_thread_t thread, int ipc_flags)
242 {
243     RT_SCHED_DEBUG_IS_LOCKED;
244 
245     switch (ipc_flags)
246     {
247     case RT_IPC_FLAG_FIFO:
248         rt_list_insert_before(susp_list, &RT_THREAD_LIST_NODE(thread));
249         break; /* RT_IPC_FLAG_FIFO */
250 
251     case RT_IPC_FLAG_PRIO:
252         {
253             struct rt_list_node *n;
254             struct rt_thread *sthread;
255 
256             /* find a suitable position */
257             for (n = susp_list->next; n != susp_list; n = n->next)
258             {
259                 sthread = RT_THREAD_LIST_NODE_ENTRY(n);
260 
261                 /* find out */
262                 if (rt_sched_thread_get_curr_prio(thread) < rt_sched_thread_get_curr_prio(sthread))
263                 {
264                     /* insert this thread before the sthread */
265                     rt_list_insert_before(&RT_THREAD_LIST_NODE(sthread), &RT_THREAD_LIST_NODE(thread));
266                     break;
267                 }
268             }
269 
270             /*
271              * not found a suitable position,
272              * append to the end of suspend_thread list
273              */
274             if (n == susp_list)
275                 rt_list_insert_before(susp_list, &RT_THREAD_LIST_NODE(thread));
276         }
277         break;/* RT_IPC_FLAG_PRIO */
278 
279     default:
280         RT_ASSERT(0);
281         break;
282     }
283 
284     return RT_EOK;
285 }
286 
287 /**
288  * @brief   Print thread on suspend list to system console
289  */
rt_susp_list_print(rt_list_t * list)290 void rt_susp_list_print(rt_list_t *list)
291 {
292 #ifdef RT_USING_CONSOLE
293     rt_sched_lock_level_t slvl;
294     struct rt_thread *thread;
295     struct rt_list_node *node;
296 
297     rt_sched_lock(&slvl);
298 
299     for (node = list->next; node != list; node = node->next)
300     {
301         thread = RT_THREAD_LIST_NODE_ENTRY(node);
302         rt_kprintf("%.*s", RT_NAME_MAX, thread->parent.name);
303 
304         if (node->next != list)
305             rt_kprintf("/");
306     }
307 
308     rt_sched_unlock(slvl);
309 #else
310     (void)list;
311 #endif
312 }
313 
314 
315 #ifdef RT_USING_SEMAPHORE
316 /**
317  * @addtogroup group_semaphore Semaphore
318  * @{
319  */
320 
_sem_object_init(rt_sem_t sem,rt_uint16_t value,rt_uint8_t flag,rt_uint16_t max_value)321 static void _sem_object_init(rt_sem_t       sem,
322                              rt_uint16_t    value,
323                              rt_uint8_t     flag,
324                              rt_uint16_t    max_value)
325 {
326     /* initialize ipc object */
327     _ipc_object_init(&(sem->parent));
328 
329     sem->max_value = max_value;
330     /* set initial value */
331     sem->value = value;
332 
333     /* set parent */
334     sem->parent.parent.flag = flag;
335     rt_spin_lock_init(&(sem->spinlock));
336 }
337 
338 /**
339  * @brief    This function will initialize a static semaphore object.
340  *
341  * @note     For the static semaphore object, its memory space is allocated by the compiler during compiling,
342  *           and shall placed on the read-write data segment or on the uninitialized data segment.
343  *           By contrast, the rt_sem_create() function will allocate memory space automatically and initialize
344  *           the semaphore.
345  *
346  * @see      rt_sem_create()
347  *
348  * @param    sem is a pointer to the semaphore to initialize. It is assumed that storage for the semaphore will be
349  *           allocated in your application.
350  *
351  * @param    name is a pointer to the name you would like to give the semaphore.
352  *
353  * @param    value is the initial value for the semaphore.
354  *           If used to share resources, you should initialize the value as the number of available resources.
355  *           If used to signal the occurrence of an event, you should initialize the value as 0.
356  *
357  * @param    flag is the semaphore flag, which determines the queuing way of how multiple threads wait
358  *           when the semaphore is not available.
359  *           The semaphore flag can be ONE of the following values:
360  *
361  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
362  *
363  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
364  *                                         (also known as first-come-first-served (FCFS) scheduling strategy).
365  *
366  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
367  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
368  *               the first-in-first-out principle, and you clearly understand that all threads involved in
369  *               this semaphore will become non-real-time threads.
370  *
371  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
372  *           If the return value is any other values, it represents the initialization failed.
373  *
374  * @warning  This function can ONLY be called from threads.
375  */
rt_sem_init(rt_sem_t sem,const char * name,rt_uint32_t value,rt_uint8_t flag)376 rt_err_t rt_sem_init(rt_sem_t    sem,
377                      const char *name,
378                      rt_uint32_t value,
379                      rt_uint8_t  flag)
380 {
381     RT_ASSERT(sem != RT_NULL);
382     RT_ASSERT(value < 0x10000U);
383     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
384 
385     /* initialize object */
386     rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name);
387 
388     _sem_object_init(sem, value, flag, RT_SEM_VALUE_MAX);
389 
390     return RT_EOK;
391 }
392 RTM_EXPORT(rt_sem_init);
393 
394 
395 /**
396  * @brief    This function will detach a static semaphore object.
397  *
398  * @note     This function is used to detach a static semaphore object which is initialized by rt_sem_init() function.
399  *           By contrast, the rt_sem_delete() function will delete a semaphore object.
400  *           When the semaphore is successfully detached, it will resume all suspended threads in the semaphore list.
401  *
402  * @see      rt_sem_delete()
403  *
404  * @param    sem is a pointer to a semaphore object to be detached.
405  *
406  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
407  *           If the return value is any other values, it means that the semaphore detach failed.
408  *
409  * @warning  This function can ONLY detach a static semaphore initialized by the rt_sem_init() function.
410  *           If the semaphore is created by the rt_sem_create() function, you MUST NOT USE this function to detach it,
411  *           ONLY USE the rt_sem_delete() function to complete the deletion.
412  */
rt_sem_detach(rt_sem_t sem)413 rt_err_t rt_sem_detach(rt_sem_t sem)
414 {
415     rt_base_t level;
416 
417     /* parameter check */
418     RT_ASSERT(sem != RT_NULL);
419     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
420     RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent));
421 
422     level = rt_spin_lock_irqsave(&(sem->spinlock));
423     /* wakeup all suspended threads */
424     rt_susp_list_resume_all(&(sem->parent.suspend_thread), RT_ERROR);
425     rt_spin_unlock_irqrestore(&(sem->spinlock), level);
426 
427     /* detach semaphore object */
428     rt_object_detach(&(sem->parent.parent));
429 
430     return RT_EOK;
431 }
432 RTM_EXPORT(rt_sem_detach);
433 
434 #ifdef RT_USING_HEAP
435 /**
436  * @brief    Creating a semaphore object.
437  *
438  * @note     For the semaphore object, its memory space is allocated automatically.
439  *           By contrast, the rt_sem_init() function will initialize a static semaphore object.
440  *
441  * @see      rt_sem_init()
442  *
443  * @param    name is a pointer to the name you would like to give the semaphore.
444  *
445  * @param    value is the initial value for the semaphore.
446  *           If used to share resources, you should initialize the value as the number of available resources.
447  *           If used to signal the occurrence of an event, you should initialize the value as 0.
448  *
449  * @param    flag is the semaphore flag, which determines the queuing way of how multiple threads wait
450  *           when the semaphore is not available.
451  *           The semaphore flag can be ONE of the following values:
452  *
453  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
454  *
455  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
456  *                                         (also known as first-come-first-served (FCFS) scheduling strategy).
457  *
458  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
459  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
460  *               the first-in-first-out principle, and you clearly understand that all threads involved in
461  *               this semaphore will become non-real-time threads.
462  *
463  * @return   Return a pointer to the semaphore object. When the return value is RT_NULL, it means the creation failed.
464  *
465  * @warning  This function can NOT be called in interrupt context. You can use macor RT_DEBUG_NOT_IN_INTERRUPT to check it.
466  */
rt_sem_create(const char * name,rt_uint32_t value,rt_uint8_t flag)467 rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag)
468 {
469     rt_sem_t sem;
470 
471     RT_ASSERT(value < 0x10000U);
472     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
473 
474     RT_DEBUG_NOT_IN_INTERRUPT;
475 
476     /* allocate object */
477     sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name);
478     if (sem == RT_NULL)
479         return sem;
480 
481     _sem_object_init(sem, value, flag, RT_SEM_VALUE_MAX);
482 
483     return sem;
484 }
485 RTM_EXPORT(rt_sem_create);
486 
487 
488 /**
489  * @brief    This function will delete a semaphore object and release the memory space.
490  *
491  * @note     This function is used to delete a semaphore object which is created by the rt_sem_create() function.
492  *           By contrast, the rt_sem_detach() function will detach a static semaphore object.
493  *           When the semaphore is successfully deleted, it will resume all suspended threads in the semaphore list.
494  *
495  * @see      rt_sem_detach()
496  *
497  * @param    sem is a pointer to a semaphore object to be deleted.
498  *
499  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
500  *           If the return value is any other values, it means that the semaphore detach failed.
501  *
502  * @warning  This function can ONLY delete a semaphore initialized by the rt_sem_create() function.
503  *           If the semaphore is initialized by the rt_sem_init() function, you MUST NOT USE this function to delete it,
504  *           ONLY USE the rt_sem_detach() function to complete the detachment.
505  */
rt_sem_delete(rt_sem_t sem)506 rt_err_t rt_sem_delete(rt_sem_t sem)
507 {
508     rt_ubase_t level;
509 
510     /* parameter check */
511     RT_ASSERT(sem != RT_NULL);
512     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
513     RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent) == RT_FALSE);
514 
515     RT_DEBUG_NOT_IN_INTERRUPT;
516 
517     level = rt_spin_lock_irqsave(&(sem->spinlock));
518     /* wakeup all suspended threads */
519     rt_susp_list_resume_all(&(sem->parent.suspend_thread), RT_ERROR);
520     rt_spin_unlock_irqrestore(&(sem->spinlock), level);
521 
522     /* delete semaphore object */
523     rt_object_delete(&(sem->parent.parent));
524 
525     return RT_EOK;
526 }
527 RTM_EXPORT(rt_sem_delete);
528 #endif /* RT_USING_HEAP */
529 
530 
531 /**
532  * @brief    This function will take a semaphore, if the semaphore is unavailable, the thread shall wait for
533  *           the semaphore up to a specified time.
534  *
535  * @note     When this function is called, the count value of the sem->value will decrease 1 until it is equal to 0.
536  *           When the sem->value is 0, it means that the semaphore is unavailable. At this time, it will suspend the
537  *           thread preparing to take the semaphore.
538  *           On the contrary, the rt_sem_release() function will increase the count value of sem->value by 1 each time.
539  *
540  * @see      rt_sem_trytake()
541  *
542  * @param    sem is a pointer to a semaphore object.
543  *
544  * @param    timeout is a timeout period (unit: an OS tick). If the semaphore is unavailable, the thread will wait for
545  *           the semaphore up to the amount of time specified by this parameter.
546  *
547  *           NOTE:
548  *           If use Macro RT_WAITING_FOREVER to set this parameter, which means that when the
549  *           message is unavailable in the queue, the thread will be waiting forever.
550  *           If use macro RT_WAITING_NO to set this parameter, which means that this
551  *           function is non-blocking and will return immediately.
552  *
553  * @return   Return the operation status. ONLY When the return value is RT_EOK, the operation is successful.
554  *           If the return value is any other values, it means that the semaphore take failed.
555  *
556  * @warning  This function can ONLY be called in the thread context. It MUST NOT BE called in interrupt context.
557  */
_rt_sem_take(rt_sem_t sem,rt_int32_t timeout,int suspend_flag)558 static rt_err_t _rt_sem_take(rt_sem_t sem, rt_int32_t timeout, int suspend_flag)
559 {
560     rt_base_t level;
561     struct rt_thread *thread;
562     rt_err_t ret;
563 
564     /* parameter check */
565     RT_ASSERT(sem != RT_NULL);
566     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
567 
568     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent)));
569 
570     /* current context checking */
571     RT_DEBUG_SCHEDULER_AVAILABLE(1);
572 
573     level = rt_spin_lock_irqsave(&(sem->spinlock));
574 
575     LOG_D("thread %s take sem:%s, which value is: %d",
576           rt_thread_self()->parent.name,
577           sem->parent.parent.name,
578           sem->value);
579 
580     if (sem->value > 0)
581     {
582         /* semaphore is available */
583         sem->value --;
584         rt_spin_unlock_irqrestore(&(sem->spinlock), level);
585     }
586     else
587     {
588         /* no waiting, return with timeout */
589         if (timeout == 0)
590         {
591             rt_spin_unlock_irqrestore(&(sem->spinlock), level);
592             return -RT_ETIMEOUT;
593         }
594         else
595         {
596             /* semaphore is unavailable, push to suspend list */
597             /* get current thread */
598             thread = rt_thread_self();
599 
600             /* reset thread error number */
601             thread->error = RT_EINTR;
602 
603             LOG_D("sem take: suspend thread - %s", thread->parent.name);
604 
605             /* suspend thread */
606             ret = rt_thread_suspend_to_list(thread, &(sem->parent.suspend_thread),
607                                             sem->parent.parent.flag, suspend_flag);
608             if (ret != RT_EOK)
609             {
610                 rt_spin_unlock_irqrestore(&(sem->spinlock), level);
611                 return ret;
612             }
613 
614             /* has waiting time, start thread timer */
615             if (timeout > 0)
616             {
617                 LOG_D("set thread:%s to timer list", thread->parent.name);
618 
619                 /* reset the timeout of thread timer and start it */
620                 rt_timer_control(&(thread->thread_timer),
621                                  RT_TIMER_CTRL_SET_TIME,
622                                  &timeout);
623                 rt_timer_start(&(thread->thread_timer));
624             }
625 
626             /* enable interrupt */
627             rt_spin_unlock_irqrestore(&(sem->spinlock), level);
628 
629             /* do schedule */
630             rt_schedule();
631 
632             if (thread->error != RT_EOK)
633             {
634                 return thread->error > 0 ? -thread->error : thread->error;
635             }
636         }
637     }
638 
639     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(sem->parent.parent)));
640 
641     return RT_EOK;
642 }
643 
rt_sem_take(rt_sem_t sem,rt_int32_t time)644 rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time)
645 {
646     return _rt_sem_take(sem, time, RT_UNINTERRUPTIBLE);
647 }
648 RTM_EXPORT(rt_sem_take);
649 
rt_sem_take_interruptible(rt_sem_t sem,rt_int32_t time)650 rt_err_t rt_sem_take_interruptible(rt_sem_t sem, rt_int32_t time)
651 {
652     return _rt_sem_take(sem, time, RT_INTERRUPTIBLE);
653 }
654 RTM_EXPORT(rt_sem_take_interruptible);
655 
rt_sem_take_killable(rt_sem_t sem,rt_int32_t time)656 rt_err_t rt_sem_take_killable(rt_sem_t sem, rt_int32_t time)
657 {
658     return _rt_sem_take(sem, time, RT_KILLABLE);
659 }
660 RTM_EXPORT(rt_sem_take_killable);
661 
662 /**
663  * @brief    This function will try to take a semaphore, if the semaphore is unavailable, the thread returns immediately.
664  *
665  * @note     This function is very similar to the rt_sem_take() function, when the semaphore is not available,
666  *           the rt_sem_trytake() function will return immediately without waiting for a timeout.
667  *           In other words, rt_sem_trytake(sem) has the same effect as rt_sem_take(sem, 0).
668  *
669  * @see      rt_sem_take()
670  *
671  * @param    sem is a pointer to a semaphore object.
672  *
673  * @return   Return the operation status. ONLY When the return value is RT_EOK, the operation is successful.
674  *           If the return value is any other values, it means that the semaphore take failed.
675  */
rt_sem_trytake(rt_sem_t sem)676 rt_err_t rt_sem_trytake(rt_sem_t sem)
677 {
678     return rt_sem_take(sem, RT_WAITING_NO);
679 }
680 RTM_EXPORT(rt_sem_trytake);
681 
682 
683 /**
684  * @brief    This function will release a semaphore. If there is thread suspended on the semaphore, it will get resumed.
685  *
686  * @note     If there are threads suspended on this semaphore, the first thread in the list of this semaphore object
687  *           will be resumed, and a thread scheduling (rt_schedule) will be executed.
688  *           If no threads are suspended on this semaphore, the count value sem->value of this semaphore will increase by 1.
689  *
690  * @param    sem is a pointer to a semaphore object.
691  *
692  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
693  *           If the return value is any other values, it means that the semaphore release failed.
694  */
rt_sem_release(rt_sem_t sem)695 rt_err_t rt_sem_release(rt_sem_t sem)
696 {
697     rt_base_t level;
698     rt_bool_t need_schedule;
699 
700     /* parameter check */
701     RT_ASSERT(sem != RT_NULL);
702     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
703 
704     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(sem->parent.parent)));
705 
706     need_schedule = RT_FALSE;
707 
708     level = rt_spin_lock_irqsave(&(sem->spinlock));
709 
710     LOG_D("thread %s releases sem:%s, which value is: %d",
711           rt_thread_self()->parent.name,
712           sem->parent.parent.name,
713           sem->value);
714 
715     if (!rt_list_isempty(&sem->parent.suspend_thread))
716     {
717         /* resume the suspended thread */
718         rt_susp_list_dequeue(&(sem->parent.suspend_thread), RT_EOK);
719         need_schedule = RT_TRUE;
720     }
721     else
722     {
723         if(sem->value < sem->max_value)
724         {
725             sem->value ++; /* increase value */
726         }
727         else
728         {
729             rt_spin_unlock_irqrestore(&(sem->spinlock), level);
730             return -RT_EFULL; /* value overflowed */
731         }
732     }
733 
734     rt_spin_unlock_irqrestore(&(sem->spinlock), level);
735 
736     /* resume a thread, re-schedule */
737     if (need_schedule == RT_TRUE)
738         rt_schedule();
739 
740     return RT_EOK;
741 }
742 RTM_EXPORT(rt_sem_release);
743 
744 
745 /**
746  * @brief    This function will set some extra attributions of a semaphore object.
747  *
748  * @note     Currently this function only supports the RT_IPC_CMD_RESET command to reset the semaphore.
749  *
750  * @param    sem is a pointer to a semaphore object.
751  *
752  * @param    cmd is a command word used to configure some attributions of the semaphore.
753  *
754  * @param    arg is the argument of the function to execute the command.
755  *
756  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
757  *           If the return value is any other values, it means that this function failed to execute.
758  */
rt_sem_control(rt_sem_t sem,int cmd,void * arg)759 rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg)
760 {
761     rt_base_t level;
762 
763     /* parameter check */
764     RT_ASSERT(sem != RT_NULL);
765     RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore);
766 
767     if (cmd == RT_IPC_CMD_RESET)
768     {
769         rt_ubase_t value;
770 
771         /* get value */
772         value = (rt_uintptr_t)arg;
773         level = rt_spin_lock_irqsave(&(sem->spinlock));
774 
775         /* resume all waiting thread */
776         rt_susp_list_resume_all(&sem->parent.suspend_thread, RT_ERROR);
777 
778         /* set new value */
779         sem->value = (rt_uint16_t)value;
780         rt_spin_unlock_irqrestore(&(sem->spinlock), level);
781         rt_schedule();
782 
783         return RT_EOK;
784     }
785     else if (cmd == RT_IPC_CMD_SET_VLIMIT)
786     {
787         rt_ubase_t max_value;
788         rt_bool_t need_schedule = RT_FALSE;
789 
790         max_value = (rt_uint16_t)((rt_uintptr_t)arg);
791         if (max_value > RT_SEM_VALUE_MAX || max_value < 1)
792         {
793             return -RT_EINVAL;
794         }
795 
796         level = rt_spin_lock_irqsave(&(sem->spinlock));
797         if (max_value < sem->value)
798         {
799             if (!rt_list_isempty(&sem->parent.suspend_thread))
800             {
801                 /* resume all waiting thread */
802                 rt_susp_list_resume_all(&sem->parent.suspend_thread, RT_ERROR);
803                 need_schedule = RT_TRUE;
804             }
805         }
806         /* set new value */
807         sem->max_value = max_value;
808         rt_spin_unlock_irqrestore(&(sem->spinlock), level);
809 
810         if (need_schedule)
811         {
812             rt_schedule();
813         }
814 
815         return RT_EOK;
816     }
817 
818     return -RT_ERROR;
819 }
820 RTM_EXPORT(rt_sem_control);
821 
822 /**@}*/
823 #endif /* RT_USING_SEMAPHORE */
824 
825 #ifdef RT_USING_MUTEX
826 /* iterate over each suspended thread to update highest priority in pending threads */
_mutex_update_priority(struct rt_mutex * mutex)827 rt_inline rt_uint8_t _mutex_update_priority(struct rt_mutex *mutex)
828 {
829     struct rt_thread *thread;
830 
831     if (!rt_list_isempty(&mutex->parent.suspend_thread))
832     {
833         thread = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next);
834         mutex->priority = rt_sched_thread_get_curr_prio(thread);
835     }
836     else
837     {
838         mutex->priority = 0xff;
839     }
840 
841     return mutex->priority;
842 }
843 
844 /* get highest priority inside its taken object and its init priority */
_thread_get_mutex_priority(struct rt_thread * thread)845 rt_inline rt_uint8_t _thread_get_mutex_priority(struct rt_thread* thread)
846 {
847     rt_list_t *node = RT_NULL;
848     struct rt_mutex *mutex = RT_NULL;
849     rt_uint8_t priority = rt_sched_thread_get_init_prio(thread);
850 
851     rt_list_for_each(node, &(thread->taken_object_list))
852     {
853         mutex = rt_list_entry(node, struct rt_mutex, taken_list);
854         rt_uint8_t mutex_prio = mutex->priority;
855         /* prio at least be priority ceiling */
856         mutex_prio = mutex_prio < mutex->ceiling_priority ? mutex_prio : mutex->ceiling_priority;
857 
858         if (priority > mutex_prio)
859         {
860             priority = mutex_prio;
861         }
862     }
863 
864     return priority;
865 }
866 
867 /* update priority of target thread and the thread suspended it if any */
_thread_update_priority(struct rt_thread * thread,rt_uint8_t priority,int suspend_flag)868 rt_inline void _thread_update_priority(struct rt_thread *thread, rt_uint8_t priority, int suspend_flag)
869 {
870     rt_err_t ret = -RT_ERROR;
871     struct rt_object* pending_obj = RT_NULL;
872 
873     LOG_D("thread:%s priority -> %d", thread->parent.name, priority);
874 
875     /* change priority of the thread */
876     ret = rt_sched_thread_change_priority(thread, priority);
877 
878     while ((ret == RT_EOK) && rt_sched_thread_is_suspended(thread))
879     {
880         /* whether change the priority of taken mutex */
881         pending_obj = thread->pending_object;
882 
883         if (pending_obj && rt_object_get_type(pending_obj) == RT_Object_Class_Mutex)
884         {
885             rt_uint8_t mutex_priority = 0xff;
886             struct rt_mutex* pending_mutex = (struct rt_mutex *)pending_obj;
887 
888             /* re-insert thread to suspended thread list to resort priority list */
889             rt_list_remove(&RT_THREAD_LIST_NODE(thread));
890 
891             ret = rt_susp_list_enqueue(
892                 &(pending_mutex->parent.suspend_thread), thread,
893                 pending_mutex->parent.parent.flag);
894             if (ret == RT_EOK)
895             {
896                 /* update priority */
897                 _mutex_update_priority(pending_mutex);
898                 /* change the priority of mutex owner thread */
899                 LOG_D("mutex: %s priority -> %d", pending_mutex->parent.parent.name,
900                         pending_mutex->priority);
901 
902                 mutex_priority = _thread_get_mutex_priority(pending_mutex->owner);
903                 if (mutex_priority != rt_sched_thread_get_curr_prio(pending_mutex->owner))
904                 {
905                     thread = pending_mutex->owner;
906 
907                     ret = rt_sched_thread_change_priority(thread, mutex_priority);
908                 }
909                 else
910                 {
911                     ret = -RT_ERROR;
912                 }
913             }
914         }
915         else
916         {
917             ret = -RT_ERROR;
918         }
919     }
920 }
921 
_check_and_update_prio(rt_thread_t thread,rt_mutex_t mutex)922 static rt_bool_t _check_and_update_prio(rt_thread_t thread, rt_mutex_t mutex)
923 {
924     RT_SCHED_DEBUG_IS_LOCKED;
925     rt_bool_t do_sched = RT_FALSE;
926 
927     if ((mutex->ceiling_priority != 0xFF) || (rt_sched_thread_get_curr_prio(thread) == mutex->priority))
928     {
929         rt_uint8_t priority = 0xff;
930 
931         /* get the highest priority in the taken list of thread */
932         priority = _thread_get_mutex_priority(thread);
933 
934         rt_sched_thread_change_priority(thread, priority);
935 
936         /**
937          * notify a pending reschedule. Since scheduler is locked, we will not
938          * really do a re-schedule at this point
939          */
940         do_sched = RT_TRUE;
941     }
942     return do_sched;
943 }
944 
_mutex_before_delete_detach(rt_mutex_t mutex)945 static void _mutex_before_delete_detach(rt_mutex_t mutex)
946 {
947     rt_sched_lock_level_t slvl;
948     rt_bool_t need_schedule = RT_FALSE;
949 
950     rt_spin_lock(&(mutex->spinlock));
951     /* wakeup all suspended threads */
952     rt_susp_list_resume_all(&(mutex->parent.suspend_thread), RT_ERROR);
953 
954     rt_sched_lock(&slvl);
955 
956     /* remove mutex from thread's taken list */
957     rt_list_remove(&mutex->taken_list);
958 
959     /* whether change the thread priority */
960     if (mutex->owner)
961     {
962         need_schedule = _check_and_update_prio(mutex->owner, mutex);
963     }
964 
965     if (need_schedule)
966     {
967         rt_sched_unlock_n_resched(slvl);
968     }
969     else
970     {
971         rt_sched_unlock(slvl);
972     }
973 
974     /* unlock and do necessary reschedule if required */
975     rt_spin_unlock(&(mutex->spinlock));
976 }
977 
978 /**
979  * @addtogroup group_mutex Mutex
980  * @{
981  */
982 
983 /**
984  * @brief    Initialize a static mutex object.
985  *
986  * @note     For the static mutex object, its memory space is allocated by the compiler during compiling,
987  *           and shall placed on the read-write data segment or on the uninitialized data segment.
988  *           By contrast, the rt_mutex_create() function will automatically allocate memory space
989  *           and initialize the mutex.
990  *
991  * @see      rt_mutex_create()
992  *
993  * @param    mutex is a pointer to the mutex to initialize. It is assumed that storage for the mutex will be
994  *           allocated in your application.
995  *
996  * @param    name is a pointer to the name that given to the mutex.
997  *
998  * @param    flag is the mutex flag, which determines the queuing way of how multiple threads wait
999  *           when the mutex is not available.
1000  *           NOTE: This parameter has been obsoleted. It can be RT_IPC_FLAG_PRIO, RT_IPC_FLAG_FIFO or RT_NULL.
1001  *
1002  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
1003  *           If the return value is any other values, it represents the initialization failed.
1004  *
1005  * @warning  This function can ONLY be called from threads.
1006  */
rt_mutex_init(rt_mutex_t mutex,const char * name,rt_uint8_t flag)1007 rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
1008 {
1009     /* flag parameter has been obsoleted */
1010     RT_UNUSED(flag);
1011 
1012     /* parameter check */
1013     RT_ASSERT(mutex != RT_NULL);
1014 
1015     /* initialize object */
1016     rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name);
1017 
1018     /* initialize ipc object */
1019     _ipc_object_init(&(mutex->parent));
1020 
1021     mutex->owner    = RT_NULL;
1022     mutex->priority = 0xFF;
1023     mutex->hold     = 0;
1024     mutex->ceiling_priority = 0xFF;
1025     rt_list_init(&(mutex->taken_list));
1026 
1027     /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */
1028     mutex->parent.parent.flag = RT_IPC_FLAG_PRIO;
1029     rt_spin_lock_init(&(mutex->spinlock));
1030 
1031     return RT_EOK;
1032 }
1033 RTM_EXPORT(rt_mutex_init);
1034 
1035 
1036 /**
1037  * @brief    This function will detach a static mutex object.
1038  *
1039  * @note     This function is used to detach a static mutex object which is initialized by rt_mutex_init() function.
1040  *           By contrast, the rt_mutex_delete() function will delete a mutex object.
1041  *           When the mutex is successfully detached, it will resume all suspended threads in the mutex list.
1042  *
1043  * @see      rt_mutex_delete()
1044  *
1045  * @param    mutex is a pointer to a mutex object to be detached.
1046  *
1047  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
1048  *           If the return value is any other values, it means that the mutex detach failed.
1049  *
1050  * @warning  This function can ONLY detach a static mutex initialized by the rt_mutex_init() function.
1051  *           If the mutex is created by the rt_mutex_create() function, you MUST NOT USE this function to detach it,
1052  *           ONLY USE the rt_mutex_delete() function to complete the deletion.
1053  */
rt_mutex_detach(rt_mutex_t mutex)1054 rt_err_t rt_mutex_detach(rt_mutex_t mutex)
1055 {
1056     /* parameter check */
1057     RT_ASSERT(mutex != RT_NULL);
1058     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
1059     RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent));
1060 
1061     _mutex_before_delete_detach(mutex);
1062 
1063     /* detach mutex object */
1064     rt_object_detach(&(mutex->parent.parent));
1065 
1066     return RT_EOK;
1067 }
1068 RTM_EXPORT(rt_mutex_detach);
1069 
1070 /* drop a thread from the suspend list of mutex */
1071 
1072 /**
1073  * @brief drop a thread from the suspend list of mutex
1074  *
1075  * @param mutex is a pointer to a mutex object.
1076  * @param thread is the thread should be dropped from mutex.
1077  */
rt_mutex_drop_thread(rt_mutex_t mutex,rt_thread_t thread)1078 void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread)
1079 {
1080     rt_uint8_t priority;
1081     rt_bool_t need_update = RT_FALSE;
1082     rt_sched_lock_level_t slvl;
1083 
1084     /* parameter check */
1085     RT_DEBUG_IN_THREAD_CONTEXT;
1086     RT_ASSERT(mutex != RT_NULL);
1087     RT_ASSERT(thread != RT_NULL);
1088 
1089     rt_spin_lock(&(mutex->spinlock));
1090 
1091     RT_ASSERT(thread->pending_object == &mutex->parent.parent);
1092 
1093     rt_sched_lock(&slvl);
1094 
1095     /* detach from suspended list */
1096     rt_list_remove(&RT_THREAD_LIST_NODE(thread));
1097 
1098     /**
1099      * Should change the priority of mutex owner thread
1100      * Note: After current thread is detached from mutex pending list, there is
1101      *       a chance that the mutex owner has been released the mutex. Which
1102      *       means mutex->owner can be NULL at this point. If that happened,
1103      *       it had already reset its priority. So it's okay to skip
1104      */
1105     if (mutex->owner && rt_sched_thread_get_curr_prio(mutex->owner) ==
1106                             rt_sched_thread_get_curr_prio(thread))
1107     {
1108         need_update = RT_TRUE;
1109     }
1110 
1111     /* update the priority of mutex */
1112     if (!rt_list_isempty(&mutex->parent.suspend_thread))
1113     {
1114         /* more thread suspended in the list */
1115         struct rt_thread *th;
1116 
1117         th = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next);
1118         /* update the priority of mutex */
1119         mutex->priority = rt_sched_thread_get_curr_prio(th);
1120     }
1121     else
1122     {
1123         /* set mutex priority to maximal priority */
1124         mutex->priority = 0xff;
1125     }
1126 
1127     /* try to change the priority of mutex owner thread */
1128     if (need_update)
1129     {
1130         /* get the maximal priority of mutex in thread */
1131         priority = _thread_get_mutex_priority(mutex->owner);
1132         if (priority != rt_sched_thread_get_curr_prio(mutex->owner))
1133         {
1134             _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE);
1135         }
1136     }
1137 
1138     rt_sched_unlock(slvl);
1139     rt_spin_unlock(&(mutex->spinlock));
1140 }
1141 
1142 
1143 /**
1144  * @brief set the prioceiling attribute of the mutex.
1145  *
1146  * @param mutex is a pointer to a mutex object.
1147  * @param priority is the priority should be set to mutex.
1148  *
1149  * @return return the old priority ceiling
1150  */
rt_mutex_setprioceiling(rt_mutex_t mutex,rt_uint8_t priority)1151 rt_uint8_t rt_mutex_setprioceiling(rt_mutex_t mutex, rt_uint8_t priority)
1152 {
1153     rt_uint8_t ret_priority = 0xFF;
1154     rt_uint8_t highest_prio;
1155     rt_sched_lock_level_t slvl;
1156 
1157     RT_DEBUG_IN_THREAD_CONTEXT;
1158 
1159     if ((mutex) && (priority < RT_THREAD_PRIORITY_MAX))
1160     {
1161         /* critical section here if multiple updates to one mutex happen */
1162         rt_spin_lock(&(mutex->spinlock));
1163         ret_priority = mutex->ceiling_priority;
1164         mutex->ceiling_priority = priority;
1165         if (mutex->owner)
1166         {
1167             rt_sched_lock(&slvl);
1168             highest_prio = _thread_get_mutex_priority(mutex->owner);
1169             if (highest_prio != rt_sched_thread_get_curr_prio(mutex->owner))
1170             {
1171                 _thread_update_priority(mutex->owner, highest_prio, RT_UNINTERRUPTIBLE);
1172             }
1173             rt_sched_unlock(slvl);
1174         }
1175         rt_spin_unlock(&(mutex->spinlock));
1176     }
1177     else
1178     {
1179         rt_set_errno(-RT_EINVAL);
1180     }
1181 
1182     return ret_priority;
1183 }
1184 RTM_EXPORT(rt_mutex_setprioceiling);
1185 
1186 
1187 /**
1188  * @brief set the prioceiling attribute of the mutex.
1189  *
1190  * @param mutex is a pointer to a mutex object.
1191  *
1192  * @return return the current priority ceiling of the mutex.
1193  */
rt_mutex_getprioceiling(rt_mutex_t mutex)1194 rt_uint8_t rt_mutex_getprioceiling(rt_mutex_t mutex)
1195 {
1196     rt_uint8_t prio = 0xFF;
1197 
1198     /* parameter check */
1199     RT_DEBUG_IN_THREAD_CONTEXT;
1200     RT_ASSERT(mutex != RT_NULL);
1201 
1202     if (mutex)
1203     {
1204         rt_spin_lock(&(mutex->spinlock));
1205         prio = mutex->ceiling_priority;
1206         rt_spin_unlock(&(mutex->spinlock));
1207     }
1208 
1209     return prio;
1210 }
1211 RTM_EXPORT(rt_mutex_getprioceiling);
1212 
1213 
1214 #ifdef RT_USING_HEAP
1215 /**
1216  * @brief    This function will create a mutex object.
1217  *
1218  * @note     For the mutex object, its memory space is automatically allocated.
1219  *           By contrast, the rt_mutex_init() function will initialize a static mutex object.
1220  *
1221  * @see      rt_mutex_init()
1222  *
1223  * @param    name is a pointer to the name that given to the mutex.
1224  *
1225  * @param    flag is the mutex flag, which determines the queuing way of how multiple threads wait
1226  *           when the mutex is not available.
1227  *           NOTE: This parameter has been obsoleted. It can be RT_IPC_FLAG_PRIO, RT_IPC_FLAG_FIFO or RT_NULL.
1228  *
1229  * @return   Return a pointer to the mutex object. When the return value is RT_NULL, it means the creation failed.
1230  *
1231  * @warning  This function can ONLY be called from threads.
1232  */
rt_mutex_create(const char * name,rt_uint8_t flag)1233 rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
1234 {
1235     struct rt_mutex *mutex;
1236 
1237     /* flag parameter has been obsoleted */
1238     RT_UNUSED(flag);
1239 
1240     RT_DEBUG_NOT_IN_INTERRUPT;
1241 
1242     /* allocate object */
1243     mutex = (rt_mutex_t)rt_object_allocate(RT_Object_Class_Mutex, name);
1244     if (mutex == RT_NULL)
1245         return mutex;
1246 
1247     /* initialize ipc object */
1248     _ipc_object_init(&(mutex->parent));
1249 
1250     mutex->owner    = RT_NULL;
1251     mutex->priority = 0xFF;
1252     mutex->hold     = 0;
1253     mutex->ceiling_priority = 0xFF;
1254     rt_list_init(&(mutex->taken_list));
1255 
1256     /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */
1257     mutex->parent.parent.flag = RT_IPC_FLAG_PRIO;
1258     rt_spin_lock_init(&(mutex->spinlock));
1259 
1260     return mutex;
1261 }
1262 RTM_EXPORT(rt_mutex_create);
1263 
1264 
1265 /**
1266  * @brief    This function will delete a mutex object and release this memory space.
1267  *
1268  * @note     This function is used to delete a mutex object which is created by the rt_mutex_create() function.
1269  *           By contrast, the rt_mutex_detach() function will detach a static mutex object.
1270  *           When the mutex is successfully deleted, it will resume all suspended threads in the mutex list.
1271  *
1272  * @see      rt_mutex_detach()
1273  *
1274  * @param    mutex is a pointer to a mutex object to be deleted.
1275  *
1276  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
1277  *           If the return value is any other values, it means that the mutex detach failed.
1278  *
1279  * @warning  This function can ONLY delete a mutex initialized by the rt_mutex_create() function.
1280  *           If the mutex is initialized by the rt_mutex_init() function, you MUST NOT USE this function to delete it,
1281  *           ONLY USE the rt_mutex_detach() function to complete the detachment.
1282  */
rt_mutex_delete(rt_mutex_t mutex)1283 rt_err_t rt_mutex_delete(rt_mutex_t mutex)
1284 {
1285     /* parameter check */
1286     RT_ASSERT(mutex != RT_NULL);
1287     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
1288     RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent) == RT_FALSE);
1289 
1290     RT_DEBUG_NOT_IN_INTERRUPT;
1291 
1292     _mutex_before_delete_detach(mutex);
1293 
1294     /* delete mutex object */
1295     rt_object_delete(&(mutex->parent.parent));
1296 
1297     return RT_EOK;
1298 }
1299 RTM_EXPORT(rt_mutex_delete);
1300 #endif /* RT_USING_HEAP */
1301 
1302 
1303 /**
1304  * @brief    This function will take a mutex, if the mutex is unavailable, the thread shall wait for
1305  *           the mutex up to a specified time.
1306  *
1307  * @note     When this function is called, the count value of the mutex->value will decrease 1 until it is equal to 0.
1308  *           When the mutex->value is 0, it means that the mutex is unavailable. At this time, it will suspend the
1309  *           thread preparing to take the mutex.
1310  *           On the contrary, the rt_mutex_release() function will increase the count value of mutex->value by 1 each time.
1311  *
1312  * @see      rt_mutex_trytake()
1313  *
1314  * @param    mutex is a pointer to a mutex object.
1315  *
1316  * @param    timeout is a timeout period (unit: an OS tick). If the mutex is unavailable, the thread will wait for
1317  *           the mutex up to the amount of time specified by the argument.
1318  *           NOTE: Generally, we set this parameter to RT_WAITING_FOREVER, which means that when the mutex is unavailable,
1319  *           the thread will be waitting forever.
1320  *
1321  * @return   Return the operation status. ONLY When the return value is RT_EOK, the operation is successful.
1322  *           If the return value is any other values, it means that the mutex take failed.
1323  *
1324  * @warning  This function can ONLY be called in the thread context. It MUST NOT BE called in interrupt context.
1325  */
_rt_mutex_take(rt_mutex_t mutex,rt_int32_t timeout,int suspend_flag)1326 static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend_flag)
1327 {
1328     struct rt_thread *thread;
1329     rt_err_t ret;
1330 
1331     /* this function must not be used in interrupt even if time = 0 */
1332     /* current context checking */
1333     RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
1334 
1335     /* parameter check */
1336     RT_ASSERT(mutex != RT_NULL);
1337     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
1338 
1339     /* get current thread */
1340     thread = rt_thread_self();
1341 
1342     rt_spin_lock(&(mutex->spinlock));
1343 
1344     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent)));
1345 
1346     LOG_D("mutex_take: current thread %s, hold: %d",
1347           thread->parent.name, mutex->hold);
1348 
1349     /* reset thread error */
1350     thread->error = RT_EOK;
1351 
1352     if (mutex->owner == thread)
1353     {
1354         if (mutex->hold < RT_MUTEX_HOLD_MAX)
1355         {
1356             /* it's the same thread */
1357             mutex->hold ++;
1358         }
1359         else
1360         {
1361             rt_spin_unlock(&(mutex->spinlock));
1362             return -RT_EFULL; /* value overflowed */
1363         }
1364     }
1365     else
1366     {
1367         /* whether the mutex has owner thread. */
1368         if (mutex->owner == RT_NULL)
1369         {
1370             /* set mutex owner and original priority */
1371             mutex->owner    = thread;
1372             mutex->priority = 0xff;
1373             mutex->hold     = 1;
1374 
1375             if (mutex->ceiling_priority != 0xFF)
1376             {
1377                 /* set the priority of thread to the ceiling priority */
1378                 if (mutex->ceiling_priority < rt_sched_thread_get_curr_prio(mutex->owner))
1379                     _thread_update_priority(mutex->owner, mutex->ceiling_priority, suspend_flag);
1380             }
1381 
1382             /* insert mutex to thread's taken object list */
1383             rt_list_insert_after(&thread->taken_object_list, &mutex->taken_list);
1384         }
1385         else
1386         {
1387             /* no waiting, return with timeout */
1388             if (timeout == 0)
1389             {
1390                 /* set error as timeout */
1391                 thread->error = RT_ETIMEOUT;
1392 
1393                 rt_spin_unlock(&(mutex->spinlock));
1394                 return -RT_ETIMEOUT;
1395             }
1396             else
1397             {
1398                 rt_sched_lock_level_t slvl;
1399                 rt_uint8_t priority;
1400 
1401                 /* mutex is unavailable, push to suspend list */
1402                 LOG_D("mutex_take: suspend thread: %s",
1403                       thread->parent.name);
1404 
1405                 /* suspend current thread */
1406                 ret = rt_thread_suspend_to_list(thread, &(mutex->parent.suspend_thread),
1407                                                 mutex->parent.parent.flag, suspend_flag);
1408                 if (ret != RT_EOK)
1409                 {
1410                     rt_spin_unlock(&(mutex->spinlock));
1411                     return ret;
1412                 }
1413 
1414                 /* set pending object in thread to this mutex */
1415                 thread->pending_object = &(mutex->parent.parent);
1416 
1417                 rt_sched_lock(&slvl);
1418 
1419                 priority = rt_sched_thread_get_curr_prio(thread);
1420 
1421                 /* update the priority level of mutex */
1422                 if (priority < mutex->priority)
1423                 {
1424                     mutex->priority = priority;
1425                     if (mutex->priority < rt_sched_thread_get_curr_prio(mutex->owner))
1426                     {
1427                         _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE); /* TODO */
1428                     }
1429                 }
1430 
1431                 rt_sched_unlock(slvl);
1432 
1433                 /* has waiting time, start thread timer */
1434                 if (timeout > 0)
1435                 {
1436                     LOG_D("mutex_take: start the timer of thread:%s",
1437                           thread->parent.name);
1438 
1439                     /* reset the timeout of thread timer and start it */
1440                     rt_timer_control(&(thread->thread_timer),
1441                                      RT_TIMER_CTRL_SET_TIME,
1442                                      &timeout);
1443                     rt_timer_start(&(thread->thread_timer));
1444                 }
1445 
1446                 rt_spin_unlock(&(mutex->spinlock));
1447 
1448                 /* do schedule */
1449                 rt_schedule();
1450 
1451                 rt_spin_lock(&(mutex->spinlock));
1452 
1453                 if (mutex->owner == thread)
1454                 {
1455                     /**
1456                      * get mutex successfully
1457                      * Note: assert to avoid an unexpected resume
1458                      */
1459                     RT_ASSERT(thread->error == RT_EOK);
1460                 }
1461                 else
1462                 {
1463                     /* the mutex has not been taken and thread has detach from the pending list. */
1464 
1465                     rt_bool_t need_update = RT_FALSE;
1466                     RT_ASSERT(mutex->owner != thread);
1467 
1468                     /* get value first before calling to other APIs */
1469                     ret = thread->error;
1470 
1471                     /* unexpected resume */
1472                     if (ret == RT_EOK)
1473                     {
1474                         ret = -RT_EINTR;
1475                     }
1476 
1477                     rt_sched_lock(&slvl);
1478 
1479                     /**
1480                      * Should change the priority of mutex owner thread
1481                      * Note: After current thread is detached from mutex pending list, there is
1482                      *       a chance that the mutex owner has been released the mutex. Which
1483                      *       means mutex->owner can be NULL at this point. If that happened,
1484                      *       it had already reset its priority. So it's okay to skip
1485                      */
1486                     if (mutex->owner && rt_sched_thread_get_curr_prio(mutex->owner) == rt_sched_thread_get_curr_prio(thread))
1487                         need_update = RT_TRUE;
1488 
1489                     /* update the priority of mutex */
1490                     if (!rt_list_isempty(&mutex->parent.suspend_thread))
1491                     {
1492                         /* more thread suspended in the list */
1493                         struct rt_thread *th;
1494 
1495                         th = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next);
1496                         /* update the priority of mutex */
1497                         mutex->priority = rt_sched_thread_get_curr_prio(th);
1498                     }
1499                     else
1500                     {
1501                         /* set mutex priority to maximal priority */
1502                         mutex->priority = 0xff;
1503                     }
1504 
1505                     /* try to change the priority of mutex owner thread */
1506                     if (need_update)
1507                     {
1508                         /* get the maximal priority of mutex in thread */
1509                         priority = _thread_get_mutex_priority(mutex->owner);
1510                         if (priority != rt_sched_thread_get_curr_prio(mutex->owner))
1511                         {
1512                             _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE);
1513                         }
1514                     }
1515 
1516                     rt_sched_unlock(slvl);
1517 
1518                     rt_spin_unlock(&(mutex->spinlock));
1519 
1520                     /* clear pending object before exit */
1521                     thread->pending_object = RT_NULL;
1522 
1523                     /* fix thread error number to negative value and return */
1524                     return ret > 0 ? -ret : ret;
1525                 }
1526             }
1527         }
1528     }
1529 
1530     rt_spin_unlock(&(mutex->spinlock));
1531 
1532     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent)));
1533 
1534     return RT_EOK;
1535 }
1536 
rt_mutex_take(rt_mutex_t mutex,rt_int32_t time)1537 rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time)
1538 {
1539     return _rt_mutex_take(mutex, time, RT_UNINTERRUPTIBLE);
1540 }
1541 RTM_EXPORT(rt_mutex_take);
1542 
rt_mutex_take_interruptible(rt_mutex_t mutex,rt_int32_t time)1543 rt_err_t rt_mutex_take_interruptible(rt_mutex_t mutex, rt_int32_t time)
1544 {
1545     return _rt_mutex_take(mutex, time, RT_INTERRUPTIBLE);
1546 }
1547 RTM_EXPORT(rt_mutex_take_interruptible);
1548 
rt_mutex_take_killable(rt_mutex_t mutex,rt_int32_t time)1549 rt_err_t rt_mutex_take_killable(rt_mutex_t mutex, rt_int32_t time)
1550 {
1551     return _rt_mutex_take(mutex, time, RT_KILLABLE);
1552 }
1553 RTM_EXPORT(rt_mutex_take_killable);
1554 
1555 /**
1556  * @brief    This function will try to take a mutex, if the mutex is unavailable, the thread returns immediately.
1557  *
1558  * @note     This function is very similar to the rt_mutex_take() function, when the mutex is not available,
1559  *           except that rt_mutex_trytake() will return immediately without waiting for a timeout
1560  *           when the mutex is not available.
1561  *           In other words, rt_mutex_trytake(mutex) has the same effect as rt_mutex_take(mutex, 0).
1562  *
1563  * @see      rt_mutex_take()
1564  *
1565  * @param    mutex is a pointer to a mutex object.
1566  *
1567  * @return   Return the operation status. ONLY When the return value is RT_EOK, the operation is successful.
1568  *           If the return value is any other values, it means that the mutex take failed.
1569  */
rt_mutex_trytake(rt_mutex_t mutex)1570 rt_err_t rt_mutex_trytake(rt_mutex_t mutex)
1571 {
1572     return rt_mutex_take(mutex, RT_WAITING_NO);
1573 }
1574 RTM_EXPORT(rt_mutex_trytake);
1575 
1576 
1577 /**
1578  * @brief    This function will release a mutex. If there is thread suspended on the mutex, the thread will be resumed.
1579  *
1580  * @note     If there are threads suspended on this mutex, the first thread in the list of this mutex object
1581  *           will be resumed, and a thread scheduling (rt_schedule) will be executed.
1582  *           If no threads are suspended on this mutex, the count value mutex->value of this mutex will increase by 1.
1583  *
1584  * @param    mutex is a pointer to a mutex object.
1585  *
1586  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
1587  *           If the return value is any other values, it means that the mutex release failed.
1588  */
rt_mutex_release(rt_mutex_t mutex)1589 rt_err_t rt_mutex_release(rt_mutex_t mutex)
1590 {
1591     rt_sched_lock_level_t slvl;
1592     struct rt_thread *thread;
1593     rt_bool_t need_schedule;
1594 
1595     /* parameter check */
1596     RT_ASSERT(mutex != RT_NULL);
1597     RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex);
1598 
1599     need_schedule = RT_FALSE;
1600 
1601     /* only thread could release mutex because we need test the ownership */
1602     RT_DEBUG_IN_THREAD_CONTEXT;
1603 
1604     /* get current thread */
1605     thread = rt_thread_self();
1606 
1607     rt_spin_lock(&(mutex->spinlock));
1608 
1609     LOG_D("mutex_release:current thread %s, hold: %d",
1610           thread->parent.name, mutex->hold);
1611 
1612     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent)));
1613 
1614     /* mutex only can be released by owner */
1615     if (thread != mutex->owner)
1616     {
1617         thread->error = -RT_ERROR;
1618         rt_spin_unlock(&(mutex->spinlock));
1619 
1620         return -RT_ERROR;
1621     }
1622 
1623     /* decrease hold */
1624     mutex->hold --;
1625     /* if no hold */
1626     if (mutex->hold == 0)
1627     {
1628         rt_sched_lock(&slvl);
1629 
1630         /* remove mutex from thread's taken list */
1631         rt_list_remove(&mutex->taken_list);
1632 
1633         /* whether change the thread priority */
1634         need_schedule = _check_and_update_prio(thread, mutex);
1635 
1636         /* wakeup suspended thread */
1637         if (!rt_list_isempty(&mutex->parent.suspend_thread))
1638         {
1639             struct rt_thread *next_thread;
1640             do
1641             {
1642                 /* get the first suspended thread */
1643                 next_thread = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next);
1644 
1645                 RT_ASSERT(rt_sched_thread_is_suspended(next_thread));
1646 
1647                 /* remove the thread from the suspended list of mutex */
1648                 rt_list_remove(&RT_THREAD_LIST_NODE(next_thread));
1649 
1650                 /* resume thread to ready queue */
1651                 if (rt_sched_thread_ready(next_thread) != RT_EOK)
1652                 {
1653                     /**
1654                      * a timeout timer had triggered while we try. So we skip
1655                      * this thread and try again.
1656                      */
1657                     next_thread = RT_NULL;
1658                 }
1659             } while (!next_thread && !rt_list_isempty(&mutex->parent.suspend_thread));
1660 
1661             if (next_thread)
1662             {
1663                 LOG_D("mutex_release: resume thread: %s",
1664                     next_thread->parent.name);
1665 
1666                 /* set new owner and put mutex into taken list of thread */
1667                 mutex->owner = next_thread;
1668                 mutex->hold  = 1;
1669                 rt_list_insert_after(&next_thread->taken_object_list, &mutex->taken_list);
1670 
1671                 /* cleanup pending object */
1672                 next_thread->pending_object = RT_NULL;
1673 
1674                 /* update mutex priority */
1675                 if (!rt_list_isempty(&(mutex->parent.suspend_thread)))
1676                 {
1677                     struct rt_thread *th;
1678 
1679                     th = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next);
1680                     mutex->priority = rt_sched_thread_get_curr_prio(th);
1681                 }
1682                 else
1683                 {
1684                     mutex->priority = 0xff;
1685                 }
1686 
1687                 need_schedule = RT_TRUE;
1688             }
1689             else
1690             {
1691                 /* no waiting thread is woke up, clear owner */
1692                 mutex->owner = RT_NULL;
1693                 mutex->priority = 0xff;
1694             }
1695 
1696             rt_sched_unlock(slvl);
1697         }
1698         else
1699         {
1700             rt_sched_unlock(slvl);
1701 
1702             /* clear owner */
1703             mutex->owner    = RT_NULL;
1704             mutex->priority = 0xff;
1705         }
1706     }
1707 
1708     rt_spin_unlock(&(mutex->spinlock));
1709 
1710     /* perform a schedule */
1711     if (need_schedule == RT_TRUE)
1712         rt_schedule();
1713 
1714     return RT_EOK;
1715 }
1716 RTM_EXPORT(rt_mutex_release);
1717 
1718 
1719 /**
1720  * @brief    This function will set some extra attributions of a mutex object.
1721  *
1722  * @note     Currently this function does not implement the control function.
1723  *
1724  * @param    mutex is a pointer to a mutex object.
1725  *
1726  * @param    cmd is a command word used to configure some attributions of the mutex.
1727  *
1728  * @param    arg is the argument of the function to execute the command.
1729  *
1730  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
1731  *           If the return value is any other values, it means that this function failed to execute.
1732  */
rt_mutex_control(rt_mutex_t mutex,int cmd,void * arg)1733 rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg)
1734 {
1735     RT_UNUSED(mutex);
1736     RT_UNUSED(cmd);
1737     RT_UNUSED(arg);
1738 
1739     return -RT_EINVAL;
1740 }
1741 RTM_EXPORT(rt_mutex_control);
1742 
1743 /**@}*/
1744 #endif /* RT_USING_MUTEX */
1745 
1746 #ifdef RT_USING_EVENT
1747 /**
1748  * @addtogroup group_event Event
1749  * @{
1750  */
1751 
1752 /**
1753  * @brief    The function will initialize a static event object.
1754  *
1755  * @note     For the static event object, its memory space is allocated by the compiler during compiling,
1756  *           and shall placed on the read-write data segment or on the uninitialized data segment.
1757  *           By contrast, the rt_event_create() function will allocate memory space automatically
1758  *           and initialize the event.
1759  *
1760  * @see      rt_event_create()
1761  *
1762  * @param    event is a pointer to the event to initialize. It is assumed that storage for the event
1763  *           will be allocated in your application.
1764  *
1765  * @param    name is a pointer to the name that given to the event.
1766  *
1767  * @param    flag is the event flag, which determines the queuing way of how multiple threads wait
1768  *           when the event is not available.
1769  *           The event flag can be ONE of the following values:
1770  *
1771  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
1772  *
1773  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
1774  *                                         (also known as first-come-first-served (FCFS) scheduling strategy).
1775  *
1776  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
1777  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
1778  *               the first-in-first-out principle, and you clearly understand that all threads involved in
1779  *               this event will become non-real-time threads.
1780  *
1781  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
1782  *           If the return value is any other values, it represents the initialization failed.
1783  *
1784  * @warning  This function can ONLY be called from threads.
1785  */
rt_event_init(rt_event_t event,const char * name,rt_uint8_t flag)1786 rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag)
1787 {
1788     /* parameter check */
1789     RT_ASSERT(event != RT_NULL);
1790     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
1791 
1792     /* initialize object */
1793     rt_object_init(&(event->parent.parent), RT_Object_Class_Event, name);
1794 
1795     /* set parent flag */
1796     event->parent.parent.flag = flag;
1797 
1798     /* initialize ipc object */
1799     _ipc_object_init(&(event->parent));
1800 
1801     /* initialize event */
1802     event->set = 0;
1803     rt_spin_lock_init(&(event->spinlock));
1804 
1805     return RT_EOK;
1806 }
1807 RTM_EXPORT(rt_event_init);
1808 
1809 
1810 /**
1811  * @brief    This function will detach a static event object.
1812  *
1813  * @note     This function is used to detach a static event object which is initialized by rt_event_init() function.
1814  *           By contrast, the rt_event_delete() function will delete an event object.
1815  *           When the event is successfully detached, it will resume all suspended threads in the event list.
1816  *
1817  * @see      rt_event_delete()
1818  *
1819  * @param    event is a pointer to an event object to be detached.
1820  *
1821  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
1822  *           If the return value is any other values, it means that the event detach failed.
1823  *
1824  * @warning  This function can ONLY detach a static event initialized by the rt_event_init() function.
1825  *           If the event is created by the rt_event_create() function, you MUST NOT USE this function to detach it,
1826  *           ONLY USE the rt_event_delete() function to complete the deletion.
1827  */
rt_event_detach(rt_event_t event)1828 rt_err_t rt_event_detach(rt_event_t event)
1829 {
1830     rt_base_t level;
1831 
1832     /* parameter check */
1833     RT_ASSERT(event != RT_NULL);
1834     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
1835     RT_ASSERT(rt_object_is_systemobject(&event->parent.parent));
1836 
1837     level = rt_spin_lock_irqsave(&(event->spinlock));
1838     /* resume all suspended thread */
1839     rt_susp_list_resume_all(&(event->parent.suspend_thread), RT_ERROR);
1840     rt_spin_unlock_irqrestore(&(event->spinlock), level);
1841 
1842     /* detach event object */
1843     rt_object_detach(&(event->parent.parent));
1844 
1845     return RT_EOK;
1846 }
1847 RTM_EXPORT(rt_event_detach);
1848 
1849 #ifdef RT_USING_HEAP
1850 /**
1851  * @brief    Creating an event object.
1852  *
1853  * @note     For the event object, its memory space is allocated automatically.
1854  *           By contrast, the rt_event_init() function will initialize a static event object.
1855  *
1856  * @see      rt_event_init()
1857  *
1858  * @param    name is a pointer to the name that given to the event.
1859  *
1860  * @param    flag is the event flag, which determines the queuing way of how multiple threads wait when the event
1861  *           is not available.
1862  *           The event flag can be ONE of the following values:
1863  *
1864  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
1865  *
1866  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
1867  *                                         (also known as first-come-first-served (FCFS) scheduling strategy).
1868  *
1869  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
1870  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
1871  *               the first-in-first-out principle, and you clearly understand that all threads involved in
1872  *               this event will become non-real-time threads.
1873  *
1874  * @return   Return a pointer to the event object. When the return value is RT_NULL, it means the creation failed.
1875  *
1876  * @warning  This function can ONLY be called from threads.
1877  */
rt_event_create(const char * name,rt_uint8_t flag)1878 rt_event_t rt_event_create(const char *name, rt_uint8_t flag)
1879 {
1880     rt_event_t event;
1881 
1882     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
1883 
1884     RT_DEBUG_NOT_IN_INTERRUPT;
1885 
1886     /* allocate object */
1887     event = (rt_event_t)rt_object_allocate(RT_Object_Class_Event, name);
1888     if (event == RT_NULL)
1889         return event;
1890 
1891     /* set parent */
1892     event->parent.parent.flag = flag;
1893 
1894     /* initialize ipc object */
1895     _ipc_object_init(&(event->parent));
1896 
1897     /* initialize event */
1898     event->set = 0;
1899     rt_spin_lock_init(&(event->spinlock));
1900 
1901     return event;
1902 }
1903 RTM_EXPORT(rt_event_create);
1904 
1905 
1906 /**
1907  * @brief    This function will delete an event object and release the memory space.
1908  *
1909  * @note     This function is used to delete an event object which is created by the rt_event_create() function.
1910  *           By contrast, the rt_event_detach() function will detach a static event object.
1911  *           When the event is successfully deleted, it will resume all suspended threads in the event list.
1912  *
1913  * @see      rt_event_detach()
1914  *
1915  * @param    event is a pointer to an event object to be deleted.
1916  *
1917  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
1918  *           If the return value is any other values, it means that the event detach failed.
1919  *
1920  * @warning  This function can ONLY delete an event initialized by the rt_event_create() function.
1921  *           If the event is initialized by the rt_event_init() function, you MUST NOT USE this function to delete it,
1922  *           ONLY USE the rt_event_detach() function to complete the detachment.
1923  */
rt_event_delete(rt_event_t event)1924 rt_err_t rt_event_delete(rt_event_t event)
1925 {
1926     /* parameter check */
1927     RT_ASSERT(event != RT_NULL);
1928     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
1929     RT_ASSERT(rt_object_is_systemobject(&event->parent.parent) == RT_FALSE);
1930 
1931     RT_DEBUG_NOT_IN_INTERRUPT;
1932 
1933     rt_spin_lock(&(event->spinlock));
1934     /* resume all suspended thread */
1935     rt_susp_list_resume_all(&(event->parent.suspend_thread), RT_ERROR);
1936     rt_spin_unlock(&(event->spinlock));
1937 
1938     /* delete event object */
1939     rt_object_delete(&(event->parent.parent));
1940 
1941     return RT_EOK;
1942 }
1943 RTM_EXPORT(rt_event_delete);
1944 #endif /* RT_USING_HEAP */
1945 
1946 
1947 /**
1948  * @brief    This function will send an event to the event object.
1949  *           If there is a thread suspended on the event, the thread will be resumed.
1950  *
1951  * @note     When using this function, you need to use the parameter (set) to specify the event flag of the event object,
1952  *           then the function will traverse the list of suspended threads waiting on the event object.
1953  *           If there is a thread suspended on the event, and the thread's event_info and the event flag of
1954  *           the current event object matches, the thread will be resumed.
1955  *
1956  * @param    event is a pointer to the event object to be sent.
1957  *
1958  * @param    set is a flag that you will set for this event's flag.
1959  *           You can set an event flag, or you can set multiple flags through OR logic operation.
1960  *
1961  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
1962  *           If the return value is any other values, it means that the event detach failed.
1963  */
rt_event_send(rt_event_t event,rt_uint32_t set)1964 rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
1965 {
1966     struct rt_list_node *n;
1967     struct rt_thread *thread;
1968     rt_sched_lock_level_t slvl;
1969     rt_base_t level;
1970     rt_base_t status;
1971     rt_bool_t need_schedule;
1972     rt_uint32_t need_clear_set = 0;
1973 
1974     /* parameter check */
1975     RT_ASSERT(event != RT_NULL);
1976     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
1977 
1978     if (set == 0)
1979         return -RT_ERROR;
1980 
1981     need_schedule = RT_FALSE;
1982 
1983     level = rt_spin_lock_irqsave(&(event->spinlock));
1984 
1985     /* set event */
1986     event->set |= set;
1987 
1988     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(event->parent.parent)));
1989 
1990     rt_sched_lock(&slvl);
1991     if (!rt_list_isempty(&event->parent.suspend_thread))
1992     {
1993         /* search thread list to resume thread */
1994         n = event->parent.suspend_thread.next;
1995         while (n != &(event->parent.suspend_thread))
1996         {
1997             /* get thread */
1998             thread = RT_THREAD_LIST_NODE_ENTRY(n);
1999 
2000             status = -RT_ERROR;
2001             if (thread->event_info & RT_EVENT_FLAG_AND)
2002             {
2003                 if ((thread->event_set & event->set) == thread->event_set)
2004                 {
2005                     /* received an AND event */
2006                     status = RT_EOK;
2007                 }
2008             }
2009             else if (thread->event_info & RT_EVENT_FLAG_OR)
2010             {
2011                 if (thread->event_set & event->set)
2012                 {
2013                     /* save the received event set */
2014                     thread->event_set = thread->event_set & event->set;
2015 
2016                     /* received an OR event */
2017                     status = RT_EOK;
2018                 }
2019             }
2020             else
2021             {
2022                 rt_sched_unlock(slvl);
2023                 rt_spin_unlock_irqrestore(&(event->spinlock), level);
2024 
2025                 return -RT_EINVAL;
2026             }
2027 
2028             /* move node to the next */
2029             n = n->next;
2030 
2031             /* condition is satisfied, resume thread */
2032             if (status == RT_EOK)
2033             {
2034                 /* clear event */
2035                 if (thread->event_info & RT_EVENT_FLAG_CLEAR)
2036                     need_clear_set |= thread->event_set;
2037 
2038                 /* resume thread, and thread list breaks out */
2039                 rt_sched_thread_ready(thread);
2040                 thread->error = RT_EOK;
2041 
2042                 /* need do a scheduling */
2043                 need_schedule = RT_TRUE;
2044             }
2045         }
2046         if (need_clear_set)
2047         {
2048             event->set &= ~need_clear_set;
2049         }
2050     }
2051 
2052     rt_sched_unlock(slvl);
2053     rt_spin_unlock_irqrestore(&(event->spinlock), level);
2054 
2055     /* do a schedule */
2056     if (need_schedule == RT_TRUE)
2057         rt_schedule();
2058 
2059     return RT_EOK;
2060 }
2061 RTM_EXPORT(rt_event_send);
2062 
2063 
2064 /**
2065  * @brief  This function will receive an event from event object. if the event is unavailable, the thread shall wait for
2066  *         the event up to a specified time.
2067  *
2068  * @note   If there are threads suspended on this semaphore, the first thread in the list of this semaphore object
2069  *         will be resumed, and a thread scheduling (rt_schedule) will be executed.
2070  *         If no threads are suspended on this semaphore, the count value sem->value of this semaphore will increase by 1.
2071  *
2072  * @param    event is a pointer to the event object to be received.
2073  *
2074  * @param    set is a flag that you will set for this event's flag.
2075  *           You can set an event flag, or you can set multiple flags through OR logic operation.
2076  *
2077  * @param    option is the option of this receiving event, it indicates how the receiving event is operated.
2078  *           The option can be one or more of the following values, When selecting multiple values,use logical OR to operate.
2079  *           (NOTE: RT_EVENT_FLAG_OR and RT_EVENT_FLAG_AND can only select one):
2080  *
2081  *
2082  *               RT_EVENT_FLAG_OR           The thread select to use logical OR to receive the event.
2083  *
2084  *               RT_EVENT_FLAG_AND          The thread select to use logical OR to receive the event.
2085  *
2086  *               RT_EVENT_FLAG_CLEAR        When the thread receives the corresponding event, the function
2087  *                                          determines whether to clear the event flag.
2088  *
2089  * @param    timeout is a timeout period (unit: an OS tick).
2090  *
2091  * @param    recved is a pointer to the received event. If you don't care about this value, you can use RT_NULL to set.
2092  *
2093  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
2094  *           If the return value is any other values, it means that the semaphore release failed.
2095  */
_rt_event_recv(rt_event_t event,rt_uint32_t set,rt_uint8_t option,rt_int32_t timeout,rt_uint32_t * recved,int suspend_flag)2096 static rt_err_t _rt_event_recv(rt_event_t   event,
2097                                rt_uint32_t  set,
2098                                rt_uint8_t   option,
2099                                rt_int32_t   timeout,
2100                                rt_uint32_t *recved,
2101                                int suspend_flag)
2102 {
2103     struct rt_thread *thread;
2104     rt_base_t level;
2105     rt_base_t status;
2106     rt_err_t ret;
2107 
2108     /* parameter check */
2109     RT_ASSERT(event != RT_NULL);
2110     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
2111 
2112     /* current context checking */
2113     RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
2114 
2115     if (set == 0)
2116         return -RT_ERROR;
2117 
2118     /* initialize status */
2119     status = -RT_ERROR;
2120     /* get current thread */
2121     thread = rt_thread_self();
2122     /* reset thread error */
2123     thread->error = -RT_EINTR;
2124 
2125     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(event->parent.parent)));
2126 
2127     level = rt_spin_lock_irqsave(&(event->spinlock));
2128 
2129     /* check event set */
2130     if (option & RT_EVENT_FLAG_AND)
2131     {
2132         if ((event->set & set) == set)
2133             status = RT_EOK;
2134     }
2135     else if (option & RT_EVENT_FLAG_OR)
2136     {
2137         if (event->set & set)
2138             status = RT_EOK;
2139     }
2140     else
2141     {
2142         /* either RT_EVENT_FLAG_AND or RT_EVENT_FLAG_OR should be set */
2143         RT_ASSERT(0);
2144     }
2145 
2146     if (status == RT_EOK)
2147     {
2148         thread->error = RT_EOK;
2149 
2150         /* set received event */
2151         if (recved)
2152             *recved = (event->set & set);
2153 
2154         /* fill thread event info */
2155         thread->event_set = (event->set & set);
2156         thread->event_info = option;
2157 
2158         /* received event */
2159         if (option & RT_EVENT_FLAG_CLEAR)
2160             event->set &= ~set;
2161     }
2162     else if (timeout == 0)
2163     {
2164         /* no waiting */
2165         thread->error = -RT_ETIMEOUT;
2166 
2167         rt_spin_unlock_irqrestore(&(event->spinlock), level);
2168 
2169         return -RT_ETIMEOUT;
2170     }
2171     else
2172     {
2173         /* fill thread event info */
2174         thread->event_set  = set;
2175         thread->event_info = option;
2176 
2177         /* put thread to suspended thread list */
2178         ret = rt_thread_suspend_to_list(thread, &(event->parent.suspend_thread),
2179                                         event->parent.parent.flag, suspend_flag);
2180         if (ret != RT_EOK)
2181         {
2182             rt_spin_unlock_irqrestore(&(event->spinlock), level);
2183             return ret;
2184         }
2185 
2186         /* if there is a waiting timeout, active thread timer */
2187         if (timeout > 0)
2188         {
2189             /* reset the timeout of thread timer and start it */
2190             rt_timer_control(&(thread->thread_timer),
2191                              RT_TIMER_CTRL_SET_TIME,
2192                              &timeout);
2193             rt_timer_start(&(thread->thread_timer));
2194         }
2195 
2196         rt_spin_unlock_irqrestore(&(event->spinlock), level);
2197 
2198         /* do a schedule */
2199         rt_schedule();
2200 
2201         if (thread->error != RT_EOK)
2202         {
2203             /* return error */
2204             return thread->error;
2205         }
2206 
2207         /* received an event, disable interrupt to protect */
2208         level = rt_spin_lock_irqsave(&(event->spinlock));
2209 
2210         /* set received event */
2211         if (recved)
2212             *recved = thread->event_set;
2213     }
2214 
2215     rt_spin_unlock_irqrestore(&(event->spinlock), level);
2216 
2217     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(event->parent.parent)));
2218 
2219     return thread->error;
2220 }
2221 
rt_event_recv(rt_event_t event,rt_uint32_t set,rt_uint8_t option,rt_int32_t timeout,rt_uint32_t * recved)2222 rt_err_t rt_event_recv(rt_event_t   event,
2223                        rt_uint32_t  set,
2224                        rt_uint8_t   option,
2225                        rt_int32_t   timeout,
2226                        rt_uint32_t *recved)
2227 {
2228     return _rt_event_recv(event, set, option, timeout, recved, RT_UNINTERRUPTIBLE);
2229 }
2230 RTM_EXPORT(rt_event_recv);
2231 
rt_event_recv_interruptible(rt_event_t event,rt_uint32_t set,rt_uint8_t option,rt_int32_t timeout,rt_uint32_t * recved)2232 rt_err_t rt_event_recv_interruptible(rt_event_t   event,
2233                        rt_uint32_t  set,
2234                        rt_uint8_t   option,
2235                        rt_int32_t   timeout,
2236                        rt_uint32_t *recved)
2237 {
2238     return _rt_event_recv(event, set, option, timeout, recved, RT_INTERRUPTIBLE);
2239 }
2240 RTM_EXPORT(rt_event_recv_interruptible);
2241 
rt_event_recv_killable(rt_event_t event,rt_uint32_t set,rt_uint8_t option,rt_int32_t timeout,rt_uint32_t * recved)2242 rt_err_t rt_event_recv_killable(rt_event_t   event,
2243                        rt_uint32_t  set,
2244                        rt_uint8_t   option,
2245                        rt_int32_t   timeout,
2246                        rt_uint32_t *recved)
2247 {
2248     return _rt_event_recv(event, set, option, timeout, recved, RT_KILLABLE);
2249 }
2250 RTM_EXPORT(rt_event_recv_killable);
2251 /**
2252  * @brief    This function will set some extra attributions of an event object.
2253  *
2254  * @note     Currently this function only supports the RT_IPC_CMD_RESET command to reset the event.
2255  *
2256  * @param    event is a pointer to an event object.
2257  *
2258  * @param    cmd is a command word used to configure some attributions of the event.
2259  *
2260  * @param    arg is the argument of the function to execute the command.
2261  *
2262  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
2263  *           If the return value is any other values, it means that this function failed to execute.
2264  */
rt_event_control(rt_event_t event,int cmd,void * arg)2265 rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg)
2266 {
2267     rt_base_t level;
2268 
2269     RT_UNUSED(arg);
2270 
2271     /* parameter check */
2272     RT_ASSERT(event != RT_NULL);
2273     RT_ASSERT(rt_object_get_type(&event->parent.parent) == RT_Object_Class_Event);
2274 
2275     if (cmd == RT_IPC_CMD_RESET)
2276     {
2277         level = rt_spin_lock_irqsave(&(event->spinlock));
2278 
2279         /* resume all waiting thread */
2280         rt_susp_list_resume_all(&event->parent.suspend_thread, RT_ERROR);
2281 
2282         /* initialize event set */
2283         event->set = 0;
2284 
2285         rt_spin_unlock_irqrestore(&(event->spinlock), level);
2286 
2287         rt_schedule();
2288 
2289         return RT_EOK;
2290     }
2291 
2292     return -RT_ERROR;
2293 }
2294 RTM_EXPORT(rt_event_control);
2295 
2296 /**@}*/
2297 #endif /* RT_USING_EVENT */
2298 
2299 #ifdef RT_USING_MAILBOX
2300 /**
2301  * @addtogroup group_mailbox MailBox
2302  * @{
2303  */
2304 
2305 /**
2306  * @brief    Initialize a static mailbox object.
2307  *
2308  * @note     For the static mailbox object, its memory space is allocated by the compiler during compiling,
2309  *           and shall placed on the read-write data segment or on the uninitialized data segment.
2310  *           By contrast, the rt_mb_create() function will allocate memory space automatically and initialize the mailbox.
2311  *
2312  * @see      rt_mb_create()
2313  *
2314  * @param    mb is a pointer to the mailbox to initialize.
2315  *           It is assumed that storage for the mailbox will be allocated in your application.
2316  *
2317  * @param    name is a pointer to the name that given to the mailbox.
2318  *
2319  * @param    msgpool the begin address of buffer to save received mail.
2320  *
2321  * @param    size is the maximum number of mails in the mailbox.
2322  *           For example, when the mailbox buffer capacity is N, size is N/4.
2323  *
2324  * @param    flag is the mailbox flag, which determines the queuing way of how multiple threads wait
2325  *           when the mailbox is not available.
2326  *           The mailbox flag can be ONE of the following values:
2327  *
2328  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
2329  *
2330  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
2331  *                                       (also known as first-come-first-served (FCFS) scheduling strategy).
2332  *
2333  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
2334  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
2335  *               the first-in-first-out principle, and you clearly understand that all threads involved in
2336  *               this mailbox will become non-real-time threads.
2337  *
2338  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
2339  *           If the return value is any other values, it represents the initialization failed.
2340  *
2341  * @warning  This function can ONLY be called from threads.
2342  */
rt_mb_init(rt_mailbox_t mb,const char * name,void * msgpool,rt_size_t size,rt_uint8_t flag)2343 rt_err_t rt_mb_init(rt_mailbox_t mb,
2344                     const char  *name,
2345                     void        *msgpool,
2346                     rt_size_t    size,
2347                     rt_uint8_t   flag)
2348 {
2349     RT_ASSERT(mb != RT_NULL);
2350     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
2351 
2352     /* initialize object */
2353     rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name);
2354 
2355     /* set parent flag */
2356     mb->parent.parent.flag = flag;
2357 
2358     /* initialize ipc object */
2359     _ipc_object_init(&(mb->parent));
2360 
2361     /* initialize mailbox */
2362     mb->msg_pool   = (rt_ubase_t *)msgpool;
2363     mb->size       = (rt_uint16_t)size;
2364     mb->entry      = 0;
2365     mb->in_offset  = 0;
2366     mb->out_offset = 0;
2367 
2368     /* initialize an additional list of sender suspend thread */
2369     rt_list_init(&(mb->suspend_sender_thread));
2370     rt_spin_lock_init(&(mb->spinlock));
2371 
2372     return RT_EOK;
2373 }
2374 RTM_EXPORT(rt_mb_init);
2375 
2376 
2377 /**
2378  * @brief    This function will detach a static mailbox object.
2379  *
2380  * @note     This function is used to detach a static mailbox object which is initialized by rt_mb_init() function.
2381  *           By contrast, the rt_mb_delete() function will delete a mailbox object.
2382  *           When the mailbox is successfully detached, it will resume all suspended threads in the mailbox list.
2383  *
2384  * @see      rt_mb_delete()
2385  *
2386  * @param    mb is a pointer to a mailbox object to be detached.
2387  *
2388  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
2389  *           If the return value is any other values, it means that the mailbox detach failed.
2390  *
2391  * @warning  This function can ONLY detach a static mailbox initialized by the rt_mb_init() function.
2392  *           If the mailbox is created by the rt_mb_create() function, you MUST NOT USE this function to detach it,
2393  *           ONLY USE the rt_mb_delete() function to complete the deletion.
2394  */
rt_mb_detach(rt_mailbox_t mb)2395 rt_err_t rt_mb_detach(rt_mailbox_t mb)
2396 {
2397     rt_base_t level;
2398 
2399     /* parameter check */
2400     RT_ASSERT(mb != RT_NULL);
2401     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
2402     RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent));
2403 
2404     level = rt_spin_lock_irqsave(&(mb->spinlock));
2405     /* resume all suspended thread */
2406     rt_susp_list_resume_all(&(mb->parent.suspend_thread), RT_ERROR);
2407     /* also resume all mailbox private suspended thread */
2408     rt_susp_list_resume_all(&(mb->suspend_sender_thread), RT_ERROR);
2409     rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2410 
2411     /* detach mailbox object */
2412     rt_object_detach(&(mb->parent.parent));
2413 
2414     return RT_EOK;
2415 }
2416 RTM_EXPORT(rt_mb_detach);
2417 
2418 #ifdef RT_USING_HEAP
2419 /**
2420  * @brief  Creating a mailbox object.
2421  *
2422  * @note   For the mailbox object, its memory space is allocated automatically.
2423  *         By contrast, the rt_mb_init() function will initialize a static mailbox object.
2424  *
2425  * @see    rt_mb_init()
2426  *
2427  * @param  name is a pointer that given to the mailbox.
2428  *
2429  * @param    size is the maximum number of mails in the mailbox.
2430  *           For example, when mailbox buffer capacity is N, size is N/4.
2431  *
2432  * @param    flag is the mailbox flag, which determines the queuing way of how multiple threads wait
2433  *           when the mailbox is not available.
2434  *           The mailbox flag can be ONE of the following values:
2435  *
2436  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
2437  *
2438  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
2439  *                                         (also known as first-come-first-served (FCFS) scheduling strategy).
2440  *
2441  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
2442  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
2443  *               the first-in-first-out principle, and you clearly understand that all threads involved in
2444  *               this mailbox will become non-real-time threads.
2445  *
2446  * @return   Return a pointer to the mailbox object. When the return value is RT_NULL, it means the creation failed.
2447  *
2448  * @warning  This function can ONLY be called from threads.
2449  */
rt_mb_create(const char * name,rt_size_t size,rt_uint8_t flag)2450 rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)
2451 {
2452     rt_mailbox_t mb;
2453 
2454     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
2455 
2456     RT_DEBUG_NOT_IN_INTERRUPT;
2457 
2458     /* allocate object */
2459     mb = (rt_mailbox_t)rt_object_allocate(RT_Object_Class_MailBox, name);
2460     if (mb == RT_NULL)
2461         return mb;
2462 
2463     /* set parent */
2464     mb->parent.parent.flag = flag;
2465 
2466     /* initialize ipc object */
2467     _ipc_object_init(&(mb->parent));
2468 
2469     /* initialize mailbox */
2470     mb->size     = (rt_uint16_t)size;
2471     mb->msg_pool = (rt_ubase_t *)RT_KERNEL_MALLOC(mb->size * sizeof(rt_ubase_t));
2472     if (mb->msg_pool == RT_NULL)
2473     {
2474         /* delete mailbox object */
2475         rt_object_delete(&(mb->parent.parent));
2476 
2477         return RT_NULL;
2478     }
2479     mb->entry      = 0;
2480     mb->in_offset  = 0;
2481     mb->out_offset = 0;
2482 
2483     /* initialize an additional list of sender suspend thread */
2484     rt_list_init(&(mb->suspend_sender_thread));
2485     rt_spin_lock_init(&(mb->spinlock));
2486 
2487     return mb;
2488 }
2489 RTM_EXPORT(rt_mb_create);
2490 
2491 
2492 /**
2493  * @brief    This function will delete a mailbox object and release the memory space.
2494  *
2495  * @note     This function is used to delete a mailbox object which is created by the rt_mb_create() function.
2496  *           By contrast, the rt_mb_detach() function will detach a static mailbox object.
2497  *           When the mailbox is successfully deleted, it will resume all suspended threads in the mailbox list.
2498  *
2499  * @see      rt_mb_detach()
2500  *
2501  * @param    mb is a pointer to a mailbox object to be deleted.
2502  *
2503  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
2504  *           If the return value is any other values, it means that the mailbox detach failed.
2505  *
2506  * @warning  This function can only delete mailbox created by the rt_mb_create() function.
2507  *           If the mailbox is initialized by the rt_mb_init() function, you MUST NOT USE this function to delete it,
2508  *           ONLY USE the rt_mb_detach() function to complete the detachment.
2509  */
rt_mb_delete(rt_mailbox_t mb)2510 rt_err_t rt_mb_delete(rt_mailbox_t mb)
2511 {
2512     /* parameter check */
2513     RT_ASSERT(mb != RT_NULL);
2514     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
2515     RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent) == RT_FALSE);
2516 
2517     RT_DEBUG_NOT_IN_INTERRUPT;
2518     rt_spin_lock(&(mb->spinlock));
2519 
2520     /* resume all suspended thread */
2521     rt_susp_list_resume_all(&(mb->parent.suspend_thread), RT_ERROR);
2522 
2523     /* also resume all mailbox private suspended thread */
2524     rt_susp_list_resume_all(&(mb->suspend_sender_thread), RT_ERROR);
2525 
2526     rt_spin_unlock(&(mb->spinlock));
2527 
2528     /* free mailbox pool */
2529     RT_KERNEL_FREE(mb->msg_pool);
2530 
2531     /* delete mailbox object */
2532     rt_object_delete(&(mb->parent.parent));
2533 
2534     return RT_EOK;
2535 }
2536 RTM_EXPORT(rt_mb_delete);
2537 #endif /* RT_USING_HEAP */
2538 
2539 
2540 /**
2541  * @brief    This function will send an mail to the mailbox object. If there is a thread suspended on the mailbox,
2542  *           the thread will be resumed.
2543  *
2544  * @note     When using this function to send a mail, if the mailbox if fully used, the current thread will
2545  *           wait for a timeout. If the set timeout time is reached and there is still no space available,
2546  *           the sending thread will be resumed and an error code will be returned.
2547  *           By contrast, the rt_mb_send() function will return an error code immediately without waiting time
2548  *           when the mailbox if fully used.
2549  *
2550  * @see      rt_mb_send()
2551  *
2552  * @param    mb is a pointer to the mailbox object to be sent.
2553  *
2554  * @param    value is a value to the content of the mail you want to send.
2555  *
2556  * @param    timeout is a timeout period (unit: an OS tick).
2557  *
2558  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
2559  *           If the return value is any other values, it means that the mailbox detach failed.
2560  *
2561  * @warning  This function can be called in interrupt context and thread context.
2562  */
_rt_mb_send_wait(rt_mailbox_t mb,rt_ubase_t value,rt_int32_t timeout,int suspend_flag)2563 static rt_err_t _rt_mb_send_wait(rt_mailbox_t mb,
2564                          rt_ubase_t   value,
2565                          rt_int32_t   timeout,
2566                          int suspend_flag)
2567 {
2568     struct rt_thread *thread;
2569     rt_base_t level;
2570     rt_uint32_t tick_delta;
2571     rt_err_t ret;
2572 
2573     /* parameter check */
2574     RT_ASSERT(mb != RT_NULL);
2575     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
2576 
2577     /* current context checking */
2578     RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
2579 
2580     /* initialize delta tick */
2581     tick_delta = 0;
2582     /* get current thread */
2583     thread = rt_thread_self();
2584 
2585     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent)));
2586 
2587     /* disable interrupt */
2588     level = rt_spin_lock_irqsave(&(mb->spinlock));
2589 
2590     /* for non-blocking call */
2591     if (mb->entry == mb->size && timeout == 0)
2592     {
2593         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2594         return -RT_EFULL;
2595     }
2596 
2597     /* mailbox is full */
2598     while (mb->entry == mb->size)
2599     {
2600         /* reset error number in thread */
2601         thread->error = -RT_EINTR;
2602 
2603         /* no waiting, return timeout */
2604         if (timeout == 0)
2605         {
2606             rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2607 
2608             return -RT_EFULL;
2609         }
2610 
2611         /* suspend current thread */
2612         ret = rt_thread_suspend_to_list(thread, &(mb->suspend_sender_thread),
2613                                         mb->parent.parent.flag, suspend_flag);
2614 
2615         if (ret != RT_EOK)
2616         {
2617             rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2618             return ret;
2619         }
2620 
2621         /* has waiting time, start thread timer */
2622         if (timeout > 0)
2623         {
2624             /* get the start tick of timer */
2625             tick_delta = rt_tick_get();
2626 
2627             LOG_D("mb_send_wait: start timer of thread:%s",
2628                   thread->parent.name);
2629 
2630             /* reset the timeout of thread timer and start it */
2631             rt_timer_control(&(thread->thread_timer),
2632                              RT_TIMER_CTRL_SET_TIME,
2633                              &timeout);
2634             rt_timer_start(&(thread->thread_timer));
2635         }
2636         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2637 
2638         /* re-schedule */
2639         rt_schedule();
2640 
2641         /* resume from suspend state */
2642         if (thread->error != RT_EOK)
2643         {
2644             /* return error */
2645             return thread->error;
2646         }
2647 
2648         level = rt_spin_lock_irqsave(&(mb->spinlock));
2649 
2650         /* if it's not waiting forever and then re-calculate timeout tick */
2651         if (timeout > 0)
2652         {
2653             tick_delta = rt_tick_get() - tick_delta;
2654             timeout -= tick_delta;
2655             if (timeout < 0)
2656                 timeout = 0;
2657         }
2658     }
2659 
2660     /* set ptr */
2661     mb->msg_pool[mb->in_offset] = value;
2662     /* increase input offset */
2663     ++ mb->in_offset;
2664     if (mb->in_offset >= mb->size)
2665         mb->in_offset = 0;
2666 
2667     if(mb->entry < RT_MB_ENTRY_MAX)
2668     {
2669         /* increase message entry */
2670         mb->entry ++;
2671     }
2672     else
2673     {
2674         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2675         return -RT_EFULL; /* value overflowed */
2676     }
2677 
2678     /* resume suspended thread */
2679     if (!rt_list_isempty(&mb->parent.suspend_thread))
2680     {
2681         rt_susp_list_dequeue(&(mb->parent.suspend_thread), RT_EOK);
2682 
2683         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2684 
2685         rt_schedule();
2686 
2687         return RT_EOK;
2688     }
2689     rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2690 
2691     return RT_EOK;
2692 }
2693 
rt_mb_send_wait(rt_mailbox_t mb,rt_ubase_t value,rt_int32_t timeout)2694 rt_err_t rt_mb_send_wait(rt_mailbox_t mb,
2695                          rt_ubase_t   value,
2696                          rt_int32_t   timeout)
2697 {
2698     return _rt_mb_send_wait(mb, value, timeout, RT_UNINTERRUPTIBLE);
2699 }
2700 RTM_EXPORT(rt_mb_send_wait);
2701 
rt_mb_send_wait_interruptible(rt_mailbox_t mb,rt_ubase_t value,rt_int32_t timeout)2702 rt_err_t rt_mb_send_wait_interruptible(rt_mailbox_t mb,
2703                          rt_ubase_t   value,
2704                          rt_int32_t   timeout)
2705 {
2706     return _rt_mb_send_wait(mb, value, timeout, RT_INTERRUPTIBLE);
2707 }
2708 RTM_EXPORT(rt_mb_send_wait_interruptible);
2709 
rt_mb_send_wait_killable(rt_mailbox_t mb,rt_ubase_t value,rt_int32_t timeout)2710 rt_err_t rt_mb_send_wait_killable(rt_mailbox_t mb,
2711                          rt_ubase_t   value,
2712                          rt_int32_t   timeout)
2713 {
2714     return _rt_mb_send_wait(mb, value, timeout, RT_KILLABLE);
2715 }
2716 RTM_EXPORT(rt_mb_send_wait_killable);
2717 /**
2718  * @brief    This function will send an mail to the mailbox object. If there is a thread suspended on the mailbox,
2719  *           the thread will be resumed.
2720  *
2721  * @note     When using this function to send a mail, if the mailbox is fully used, this function will return an error
2722  *           code immediately without waiting time.
2723  *           By contrast, the rt_mb_send_wait() function is set a timeout to wait for the mail to be sent.
2724  *
2725  * @see      rt_mb_send_wait()
2726  *
2727  * @param    mb is a pointer to the mailbox object to be sent.
2728  *
2729  * @param    value is a value to the content of the mail you want to send.
2730  *
2731  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
2732  *           If the return value is any other values, it means that the mailbox detach failed.
2733  */
rt_mb_send(rt_mailbox_t mb,rt_ubase_t value)2734 rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value)
2735 {
2736     return rt_mb_send_wait(mb, value, 0);
2737 }
2738 RTM_EXPORT(rt_mb_send);
2739 
rt_mb_send_interruptible(rt_mailbox_t mb,rt_ubase_t value)2740 rt_err_t rt_mb_send_interruptible(rt_mailbox_t mb, rt_ubase_t value)
2741 {
2742     return rt_mb_send_wait_interruptible(mb, value, 0);
2743 }
2744 RTM_EXPORT(rt_mb_send_interruptible);
2745 
rt_mb_send_killable(rt_mailbox_t mb,rt_ubase_t value)2746 rt_err_t rt_mb_send_killable(rt_mailbox_t mb, rt_ubase_t value)
2747 {
2748     return rt_mb_send_wait_killable(mb, value, 0);
2749 }
2750 RTM_EXPORT(rt_mb_send_killable);
2751 
2752 /**
2753  * @brief    This function will send an urgent mail to the mailbox object.
2754  *
2755  * @note     This function is almost the same as the rt_mb_send() function. The only difference is that
2756  *           when sending an urgent mail, the mail will be placed at the head of the mail queue so that
2757  *           the recipient can receive the urgent mail first.
2758  *
2759  * @see      rt_mb_send()
2760  *
2761  * @param    mb is a pointer to the mailbox object to be sent.
2762  *
2763  * @param    value is the content of the mail you want to send.
2764  *
2765  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
2766  *           If the return value is any other values, it means that the mailbox detach failed.
2767  */
rt_mb_urgent(rt_mailbox_t mb,rt_ubase_t value)2768 rt_err_t rt_mb_urgent(rt_mailbox_t mb, rt_ubase_t value)
2769 {
2770     rt_base_t level;
2771 
2772     /* parameter check */
2773     RT_ASSERT(mb != RT_NULL);
2774     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
2775 
2776     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent)));
2777 
2778     level = rt_spin_lock_irqsave(&(mb->spinlock));
2779 
2780     if (mb->entry == mb->size)
2781     {
2782         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2783         return -RT_EFULL;
2784     }
2785 
2786     /* rewind to the previous position */
2787     if (mb->out_offset > 0)
2788     {
2789         mb->out_offset --;
2790     }
2791     else
2792     {
2793         mb->out_offset = mb->size - 1;
2794     }
2795 
2796     /* set ptr */
2797     mb->msg_pool[mb->out_offset] = value;
2798 
2799     /* increase message entry */
2800     mb->entry ++;
2801 
2802     /* resume suspended thread */
2803     if (!rt_list_isempty(&mb->parent.suspend_thread))
2804     {
2805         rt_susp_list_dequeue(&(mb->parent.suspend_thread), RT_EOK);
2806 
2807         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2808 
2809         rt_schedule();
2810 
2811         return RT_EOK;
2812     }
2813     rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2814 
2815     return RT_EOK;
2816 }
2817 RTM_EXPORT(rt_mb_urgent);
2818 
2819 
2820 /**
2821  * @brief    This function will receive a mail from mailbox object, if there is no mail in mailbox object,
2822  *           the thread shall wait for a specified time.
2823  *
2824  * @note     Only when there is mail in the mailbox, the receiving thread can get the mail immediately and
2825  *           return RT_EOK, otherwise the receiving thread will be suspended until the set timeout. If the mail
2826  *           is still not received within the specified time, it will return-RT_ETIMEOUT.
2827  *
2828  * @param    mb is a pointer to the mailbox object to be received.
2829  *
2830  * @param    value is a flag that you will set for this mailbox's flag.
2831  *           You can set an mailbox flag, or you can set multiple flags through OR logic operations.
2832  *
2833  * @param    timeout is a timeout period (unit: an OS tick). If the mailbox object is not avaliable in the queue,
2834  *           the thread will wait for the object in the queue up to the amount of time specified by this parameter.
2835  *
2836  *           NOTE:
2837  *           If use Macro RT_WAITING_FOREVER to set this parameter, which means that when the
2838  *           mailbox object is unavailable in the queue, the thread will be waiting forever.
2839  *           If use macro RT_WAITING_NO to set this parameter, which means that this
2840  *           function is non-blocking and will return immediately.
2841  *
2842  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
2843  *           If the return value is any other values, it means that the mailbox release failed.
2844  */
_rt_mb_recv(rt_mailbox_t mb,rt_ubase_t * value,rt_int32_t timeout,int suspend_flag)2845 static rt_err_t _rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout, int suspend_flag)
2846 {
2847     struct rt_thread *thread;
2848     rt_base_t level;
2849     rt_uint32_t tick_delta;
2850     rt_err_t ret;
2851 
2852     /* parameter check */
2853     RT_ASSERT(mb != RT_NULL);
2854     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
2855 
2856     /* current context checking */
2857     RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
2858 
2859     /* initialize delta tick */
2860     tick_delta = 0;
2861     /* get current thread */
2862     thread = rt_thread_self();
2863 
2864     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mb->parent.parent)));
2865 
2866     level = rt_spin_lock_irqsave(&(mb->spinlock));
2867 
2868     /* for non-blocking call */
2869     if (mb->entry == 0 && timeout == 0)
2870     {
2871         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2872 
2873         return -RT_ETIMEOUT;
2874     }
2875 
2876     /* mailbox is empty */
2877     while (mb->entry == 0)
2878     {
2879         /* reset error number in thread */
2880         thread->error = -RT_EINTR;
2881 
2882         /* no waiting, return timeout */
2883         if (timeout == 0)
2884         {
2885             rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2886 
2887             thread->error = -RT_ETIMEOUT;
2888 
2889             return -RT_ETIMEOUT;
2890         }
2891 
2892         /* suspend current thread */
2893         ret = rt_thread_suspend_to_list(thread, &(mb->parent.suspend_thread),
2894                                         mb->parent.parent.flag, suspend_flag);
2895         if (ret != RT_EOK)
2896         {
2897             rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2898             return ret;
2899         }
2900 
2901         /* has waiting time, start thread timer */
2902         if (timeout > 0)
2903         {
2904             /* get the start tick of timer */
2905             tick_delta = rt_tick_get();
2906 
2907             LOG_D("mb_recv: start timer of thread:%s",
2908                   thread->parent.name);
2909 
2910             /* reset the timeout of thread timer and start it */
2911             rt_timer_control(&(thread->thread_timer),
2912                              RT_TIMER_CTRL_SET_TIME,
2913                              &timeout);
2914             rt_timer_start(&(thread->thread_timer));
2915         }
2916 
2917         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2918 
2919         /* re-schedule */
2920         rt_schedule();
2921 
2922         /* resume from suspend state */
2923         if (thread->error != RT_EOK)
2924         {
2925             /* return error */
2926             return thread->error;
2927         }
2928         level = rt_spin_lock_irqsave(&(mb->spinlock));
2929 
2930         /* if it's not waiting forever and then re-calculate timeout tick */
2931         if (timeout > 0)
2932         {
2933             tick_delta = rt_tick_get() - tick_delta;
2934             timeout -= tick_delta;
2935             if (timeout < 0)
2936                 timeout = 0;
2937         }
2938     }
2939 
2940     /* fill ptr */
2941     *value = mb->msg_pool[mb->out_offset];
2942 
2943     /* increase output offset */
2944     ++ mb->out_offset;
2945     if (mb->out_offset >= mb->size)
2946         mb->out_offset = 0;
2947 
2948     /* decrease message entry */
2949     if(mb->entry > 0)
2950     {
2951         mb->entry --;
2952     }
2953 
2954     /* resume suspended thread */
2955     if (!rt_list_isempty(&(mb->suspend_sender_thread)))
2956     {
2957         rt_susp_list_dequeue(&(mb->suspend_sender_thread), RT_EOK);
2958 
2959         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2960 
2961         RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent)));
2962 
2963         rt_schedule();
2964 
2965         return RT_EOK;
2966     }
2967     rt_spin_unlock_irqrestore(&(mb->spinlock), level);
2968 
2969     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent)));
2970 
2971     return RT_EOK;
2972 }
2973 
rt_mb_recv(rt_mailbox_t mb,rt_ubase_t * value,rt_int32_t timeout)2974 rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
2975 {
2976     return _rt_mb_recv(mb, value, timeout, RT_UNINTERRUPTIBLE);
2977 }
2978 RTM_EXPORT(rt_mb_recv);
2979 
rt_mb_recv_interruptible(rt_mailbox_t mb,rt_ubase_t * value,rt_int32_t timeout)2980 rt_err_t rt_mb_recv_interruptible(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
2981 {
2982     return _rt_mb_recv(mb, value, timeout, RT_INTERRUPTIBLE);
2983 }
2984 RTM_EXPORT(rt_mb_recv_interruptible);
2985 
rt_mb_recv_killable(rt_mailbox_t mb,rt_ubase_t * value,rt_int32_t timeout)2986 rt_err_t rt_mb_recv_killable(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
2987 {
2988     return _rt_mb_recv(mb, value, timeout, RT_KILLABLE);
2989 }
2990 RTM_EXPORT(rt_mb_recv_killable);
2991 
2992 /**
2993  * @brief    This function will set some extra attributions of a mailbox object.
2994  *
2995  * @note     Currently this function only supports the RT_IPC_CMD_RESET command to reset the mailbox.
2996  *
2997  * @param    mb is a pointer to a mailbox object.
2998  *
2999  * @param    cmd is a command used to configure some attributions of the mailbox.
3000  *
3001  * @param    arg is the argument of the function to execute the command.
3002  *
3003  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
3004  *           If the return value is any other values, it means that this function failed to execute.
3005  */
rt_mb_control(rt_mailbox_t mb,int cmd,void * arg)3006 rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg)
3007 {
3008     rt_base_t level;
3009 
3010     RT_UNUSED(arg);
3011 
3012     /* parameter check */
3013     RT_ASSERT(mb != RT_NULL);
3014     RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox);
3015 
3016     if (cmd == RT_IPC_CMD_RESET)
3017     {
3018         level = rt_spin_lock_irqsave(&(mb->spinlock));
3019 
3020         /* resume all waiting thread */
3021         rt_susp_list_resume_all(&(mb->parent.suspend_thread), RT_ERROR);
3022         /* also resume all mailbox private suspended thread */
3023         rt_susp_list_resume_all(&(mb->suspend_sender_thread), RT_ERROR);
3024 
3025         /* re-init mailbox */
3026         mb->entry      = 0;
3027         mb->in_offset  = 0;
3028         mb->out_offset = 0;
3029 
3030         rt_spin_unlock_irqrestore(&(mb->spinlock), level);
3031 
3032         rt_schedule();
3033 
3034         return RT_EOK;
3035     }
3036 
3037     return -RT_ERROR;
3038 }
3039 RTM_EXPORT(rt_mb_control);
3040 
3041 /**@}*/
3042 #endif /* RT_USING_MAILBOX */
3043 
3044 #ifdef RT_USING_MESSAGEQUEUE
3045 /**
3046  * @addtogroup group_messagequeue Message Queue
3047  * @{
3048  */
3049 
3050 /**
3051  * @brief    Initialize a static messagequeue object.
3052  *
3053  * @note     For the static messagequeue object, its memory space is allocated by the compiler during compiling,
3054  *           and shall placed on the read-write data segment or on the uninitialized data segment.
3055  *           By contrast, the rt_mq_create() function will allocate memory space automatically
3056  *           and initialize the messagequeue.
3057  *
3058  * @see      rt_mq_create()
3059  *
3060  * @param    mq is a pointer to the messagequeue to initialize. It is assumed that storage for
3061  *           the messagequeue will be allocated in your application.
3062  *
3063  * @param    name is a pointer to the name that given to the messagequeue.
3064  *
3065  * @param    msgpool is a pointer to the starting address of the memory space you allocated for
3066  *           the messagequeue in advance.
3067  *           In other words, msgpool is a pointer to the messagequeue buffer of the starting address.
3068  *
3069  * @param    msg_size is the maximum length of a message in the messagequeue (Unit: Byte).
3070  *
3071  * @param    pool_size is the size of the memory space allocated for the messagequeue in advance.
3072  *
3073  * @param    flag is the messagequeue flag, which determines the queuing way of how multiple threads wait
3074  *           when the messagequeue is not available.
3075  *           The messagequeue flag can be ONE of the following values:
3076  *
3077  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
3078  *
3079  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
3080  *                                         (also known as first-come-first-served (FCFS) scheduling strategy).
3081  *
3082  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
3083  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
3084  *               the first-in-first-out principle, and you clearly understand that all threads involved in
3085  *               this messagequeue will become non-real-time threads.
3086  *
3087  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
3088  *           If the return value is any other values, it represents the initialization failed.
3089  *
3090  * @warning  This function can ONLY be called from threads.
3091  */
rt_mq_init(rt_mq_t mq,const char * name,void * msgpool,rt_size_t msg_size,rt_size_t pool_size,rt_uint8_t flag)3092 rt_err_t rt_mq_init(rt_mq_t     mq,
3093                     const char *name,
3094                     void       *msgpool,
3095                     rt_size_t   msg_size,
3096                     rt_size_t   pool_size,
3097                     rt_uint8_t  flag)
3098 {
3099     struct rt_mq_message *head;
3100     rt_base_t temp;
3101     register rt_size_t msg_align_size;
3102 
3103     /* parameter check */
3104     RT_ASSERT(mq != RT_NULL);
3105     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
3106 
3107     /* initialize object */
3108     rt_object_init(&(mq->parent.parent), RT_Object_Class_MessageQueue, name);
3109 
3110     /* set parent flag */
3111     mq->parent.parent.flag = flag;
3112 
3113     /* initialize ipc object */
3114     _ipc_object_init(&(mq->parent));
3115 
3116     /* set message pool */
3117     mq->msg_pool = msgpool;
3118 
3119     /* get correct message size */
3120     msg_align_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE);
3121     mq->msg_size = msg_size;
3122     mq->max_msgs = pool_size / (msg_align_size + sizeof(struct rt_mq_message));
3123 
3124     if (0 == mq->max_msgs)
3125     {
3126         return -RT_EINVAL;
3127     }
3128 
3129     /* initialize message list */
3130     mq->msg_queue_head = RT_NULL;
3131     mq->msg_queue_tail = RT_NULL;
3132 
3133     /* initialize message empty list */
3134     mq->msg_queue_free = RT_NULL;
3135     for (temp = 0; temp < mq->max_msgs; temp ++)
3136     {
3137         head = (struct rt_mq_message *)((rt_uint8_t *)mq->msg_pool +
3138                                         temp * (msg_align_size + sizeof(struct rt_mq_message)));
3139         head->next = (struct rt_mq_message *)mq->msg_queue_free;
3140         mq->msg_queue_free = head;
3141     }
3142 
3143     /* the initial entry is zero */
3144     mq->entry = 0;
3145 
3146     /* initialize an additional list of sender suspend thread */
3147     rt_list_init(&(mq->suspend_sender_thread));
3148     rt_spin_lock_init(&(mq->spinlock));
3149 
3150     return RT_EOK;
3151 }
3152 RTM_EXPORT(rt_mq_init);
3153 
3154 
3155 /**
3156  * @brief    This function will detach a static messagequeue object.
3157  *
3158  * @note     This function is used to detach a static messagequeue object which is initialized by rt_mq_init() function.
3159  *           By contrast, the rt_mq_delete() function will delete a messagequeue object.
3160  *           When the messagequeue is successfully detached, it will resume all suspended threads in the messagequeue list.
3161  *
3162  * @see      rt_mq_delete()
3163  *
3164  * @param    mq is a pointer to a messagequeue object to be detached.
3165  *
3166  * @return   Return the operation status. When the return value is RT_EOK, the initialization is successful.
3167  *           If the return value is any other values, it means that the messagequeue detach failed.
3168  *
3169  * @warning  This function can ONLY detach a static messagequeue initialized by the rt_mq_init() function.
3170  *           If the messagequeue is created by the rt_mq_create() function, you MUST NOT USE this function to detach it,
3171  *           and ONLY USE the rt_mq_delete() function to complete the deletion.
3172  */
rt_mq_detach(rt_mq_t mq)3173 rt_err_t rt_mq_detach(rt_mq_t mq)
3174 {
3175     rt_base_t level;
3176 
3177     /* parameter check */
3178     RT_ASSERT(mq != RT_NULL);
3179     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
3180     RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent));
3181 
3182     level = rt_spin_lock_irqsave(&(mq->spinlock));
3183     /* resume all suspended thread */
3184     rt_susp_list_resume_all(&mq->parent.suspend_thread, RT_ERROR);
3185     /* also resume all message queue private suspended thread */
3186     rt_susp_list_resume_all(&(mq->suspend_sender_thread), RT_ERROR);
3187     rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3188 
3189     /* detach message queue object */
3190     rt_object_detach(&(mq->parent.parent));
3191 
3192     return RT_EOK;
3193 }
3194 RTM_EXPORT(rt_mq_detach);
3195 
3196 #ifdef RT_USING_HEAP
3197 /**
3198  * @brief    Creating a messagequeue object.
3199  *
3200  * @note     For the messagequeue object, its memory space is allocated automatically.
3201  *           By contrast, the rt_mq_init() function will initialize a static messagequeue object.
3202  *
3203  * @see      rt_mq_init()
3204  *
3205  * @param    name is a pointer that given to the messagequeue.
3206  *
3207  * @param    msg_size is the maximum length of a message in the messagequeue (Unit: Byte).
3208  *
3209  * @param    max_msgs is the maximum number of messages in the messagequeue.
3210  *
3211  * @param    flag is the messagequeue flag, which determines the queuing way of how multiple threads wait
3212  *           when the messagequeue is not available.
3213  *           The messagequeue flag can be ONE of the following values:
3214  *
3215  *               RT_IPC_FLAG_PRIO          The pending threads will queue in order of priority.
3216  *
3217  *               RT_IPC_FLAG_FIFO          The pending threads will queue in the first-in-first-out method
3218  *                                         (also known as first-come-first-served (FCFS) scheduling strategy).
3219  *
3220  *               NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to
3221  *               use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about
3222  *               the first-in-first-out principle, and you clearly understand that all threads involved in
3223  *               this messagequeue will become non-real-time threads.
3224  *
3225  * @return   Return a pointer to the messagequeue object. When the return value is RT_NULL, it means the creation failed.
3226  *
3227  * @warning  This function can NOT be called in interrupt context. You can use macor RT_DEBUG_NOT_IN_INTERRUPT to check it.
3228  */
rt_mq_create(const char * name,rt_size_t msg_size,rt_size_t max_msgs,rt_uint8_t flag)3229 rt_mq_t rt_mq_create(const char *name,
3230                      rt_size_t   msg_size,
3231                      rt_size_t   max_msgs,
3232                      rt_uint8_t  flag)
3233 {
3234     struct rt_messagequeue *mq;
3235     struct rt_mq_message *head;
3236     rt_base_t temp;
3237     register rt_size_t msg_align_size;
3238 
3239     RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO));
3240 
3241     RT_DEBUG_NOT_IN_INTERRUPT;
3242 
3243     /* allocate object */
3244     mq = (rt_mq_t)rt_object_allocate(RT_Object_Class_MessageQueue, name);
3245     if (mq == RT_NULL)
3246         return mq;
3247 
3248     /* set parent */
3249     mq->parent.parent.flag = flag;
3250 
3251     /* initialize ipc object */
3252     _ipc_object_init(&(mq->parent));
3253 
3254     /* initialize message queue */
3255 
3256     /* get correct message size */
3257     msg_align_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE);
3258     mq->msg_size = msg_size;
3259     mq->max_msgs = max_msgs;
3260 
3261     /* allocate message pool */
3262     mq->msg_pool = RT_KERNEL_MALLOC((msg_align_size + sizeof(struct rt_mq_message)) * mq->max_msgs);
3263     if (mq->msg_pool == RT_NULL)
3264     {
3265         rt_object_delete(&(mq->parent.parent));
3266 
3267         return RT_NULL;
3268     }
3269 
3270     /* initialize message list */
3271     mq->msg_queue_head = RT_NULL;
3272     mq->msg_queue_tail = RT_NULL;
3273 
3274     /* initialize message empty list */
3275     mq->msg_queue_free = RT_NULL;
3276     for (temp = 0; temp < mq->max_msgs; temp ++)
3277     {
3278         head = (struct rt_mq_message *)((rt_uint8_t *)mq->msg_pool +
3279                                         temp * (msg_align_size + sizeof(struct rt_mq_message)));
3280         head->next = (struct rt_mq_message *)mq->msg_queue_free;
3281         mq->msg_queue_free = head;
3282     }
3283 
3284     /* the initial entry is zero */
3285     mq->entry = 0;
3286 
3287     /* initialize an additional list of sender suspend thread */
3288     rt_list_init(&(mq->suspend_sender_thread));
3289     rt_spin_lock_init(&(mq->spinlock));
3290 
3291     return mq;
3292 }
3293 RTM_EXPORT(rt_mq_create);
3294 
3295 
3296 /**
3297  * @brief    This function will delete a messagequeue object and release the memory.
3298  *
3299  * @note     This function is used to delete a messagequeue object which is created by the rt_mq_create() function.
3300  *           By contrast, the rt_mq_detach() function will detach a static messagequeue object.
3301  *           When the messagequeue is successfully deleted, it will resume all suspended threads in the messagequeue list.
3302  *
3303  * @see      rt_mq_detach()
3304  *
3305  * @param    mq is a pointer to a messagequeue object to be deleted.
3306  *
3307  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
3308  *           If the return value is any other values, it means that the messagequeue detach failed.
3309  *
3310  * @warning  This function can ONLY delete a messagequeue initialized by the rt_mq_create() function.
3311  *           If the messagequeue is initialized by the rt_mq_init() function, you MUST NOT USE this function to delete it,
3312  *           ONLY USE the rt_mq_detach() function to complete the detachment.
3313  *           for example,the rt_mq_create() function, it cannot be called in interrupt context.
3314  */
rt_mq_delete(rt_mq_t mq)3315 rt_err_t rt_mq_delete(rt_mq_t mq)
3316 {
3317     /* parameter check */
3318     RT_ASSERT(mq != RT_NULL);
3319     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
3320     RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent) == RT_FALSE);
3321 
3322     RT_DEBUG_NOT_IN_INTERRUPT;
3323 
3324     rt_spin_lock(&(mq->spinlock));
3325     /* resume all suspended thread */
3326     rt_susp_list_resume_all(&(mq->parent.suspend_thread), RT_ERROR);
3327     /* also resume all message queue private suspended thread */
3328     rt_susp_list_resume_all(&(mq->suspend_sender_thread), RT_ERROR);
3329 
3330     rt_spin_unlock(&(mq->spinlock));
3331 
3332     /* free message queue pool */
3333     RT_KERNEL_FREE(mq->msg_pool);
3334 
3335     /* delete message queue object */
3336     rt_object_delete(&(mq->parent.parent));
3337 
3338     return RT_EOK;
3339 }
3340 RTM_EXPORT(rt_mq_delete);
3341 #endif /* RT_USING_HEAP */
3342 
3343 /**
3344  * @brief    This function will send a message to the messagequeue object. If
3345  *           there is a thread suspended on the messagequeue, the thread will be
3346  *           resumed.
3347  *
3348  * @note     When using this function to send a message, if the messagequeue is
3349  *           fully used, the current thread will wait for a timeout. If reaching
3350  *           the timeout and there is still no space available, the sending
3351  *           thread will be resumed and an error code will be returned. By
3352  *           contrast, the _rt_mq_send_wait() function will return an error code
3353  *           immediately without waiting when the messagequeue if fully used.
3354  *
3355  * @see      _rt_mq_send_wait()
3356  *
3357  * @param    mq is a pointer to the messagequeue object to be sent.
3358  *
3359  * @param    buffer is the content of the message.
3360  *
3361  * @param    size is the length of the message(Unit: Byte).
3362  *
3363  * @param    prio is message priority, A larger value indicates a higher priority
3364  *
3365  * @param    timeout is a timeout period (unit: an OS tick).
3366  *
3367  * @param    suspend_flag status flag of the thread to be suspended.
3368  *
3369  * @return   Return the operation status. When the return value is RT_EOK, the
3370  *           operation is successful. If the return value is any other values,
3371  *           it means that the messagequeue detach failed.
3372  *
3373  * @warning  This function can be called in interrupt context and thread
3374  * context.
3375  */
_rt_mq_send_wait(rt_mq_t mq,const void * buffer,rt_size_t size,rt_int32_t prio,rt_int32_t timeout,int suspend_flag)3376 static rt_err_t _rt_mq_send_wait(rt_mq_t mq,
3377                                  const void *buffer,
3378                                  rt_size_t size,
3379                                  rt_int32_t prio,
3380                                  rt_int32_t timeout,
3381                                  int suspend_flag)
3382 {
3383     rt_base_t level;
3384     struct rt_mq_message *msg;
3385     rt_uint32_t tick_delta;
3386     struct rt_thread *thread;
3387     rt_err_t ret;
3388 
3389     RT_UNUSED(prio);
3390 
3391     /* parameter check */
3392     RT_ASSERT(mq != RT_NULL);
3393     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
3394     RT_ASSERT(buffer != RT_NULL);
3395     RT_ASSERT(size != 0);
3396 
3397     /* current context checking */
3398     RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
3399 
3400     /* greater than one message size */
3401     if (size > mq->msg_size)
3402         return -RT_ERROR;
3403 
3404     /* initialize delta tick */
3405     tick_delta = 0;
3406     /* get current thread */
3407     thread = rt_thread_self();
3408 
3409     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mq->parent.parent)));
3410 
3411     level = rt_spin_lock_irqsave(&(mq->spinlock));
3412 
3413     /* get a free list, there must be an empty item */
3414     msg = (struct rt_mq_message *)mq->msg_queue_free;
3415     /* for non-blocking call */
3416     if (msg == RT_NULL && timeout == 0)
3417     {
3418         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3419 
3420         return -RT_EFULL;
3421     }
3422 
3423     /* message queue is full */
3424     while ((msg = (struct rt_mq_message *)mq->msg_queue_free) == RT_NULL)
3425     {
3426         /* reset error number in thread */
3427         thread->error = -RT_EINTR;
3428 
3429         /* no waiting, return timeout */
3430         if (timeout == 0)
3431         {
3432             rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3433 
3434             return -RT_EFULL;
3435         }
3436 
3437         /* suspend current thread */
3438         ret = rt_thread_suspend_to_list(thread, &(mq->suspend_sender_thread),
3439                                         mq->parent.parent.flag, suspend_flag);
3440         if (ret != RT_EOK)
3441         {
3442             rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3443             return ret;
3444         }
3445 
3446         /* has waiting time, start thread timer */
3447         if (timeout > 0)
3448         {
3449             /* get the start tick of timer */
3450             tick_delta = rt_tick_get();
3451 
3452             LOG_D("mq_send_wait: start timer of thread:%s",
3453                   thread->parent.name);
3454 
3455             /* reset the timeout of thread timer and start it */
3456             rt_timer_control(&(thread->thread_timer),
3457                              RT_TIMER_CTRL_SET_TIME,
3458                              &timeout);
3459             rt_timer_start(&(thread->thread_timer));
3460         }
3461 
3462         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3463 
3464         /* re-schedule */
3465         rt_schedule();
3466 
3467         /* resume from suspend state */
3468         if (thread->error != RT_EOK)
3469         {
3470             /* return error */
3471             return thread->error;
3472         }
3473         level = rt_spin_lock_irqsave(&(mq->spinlock));
3474 
3475         /* if it's not waiting forever and then re-calculate timeout tick */
3476         if (timeout > 0)
3477         {
3478             tick_delta = rt_tick_get() - tick_delta;
3479             timeout -= tick_delta;
3480             if (timeout < 0)
3481                 timeout = 0;
3482         }
3483     }
3484 
3485     /* move free list pointer */
3486     mq->msg_queue_free = msg->next;
3487 
3488     rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3489 
3490     /* the msg is the new tailer of list, the next shall be NULL */
3491     msg->next = RT_NULL;
3492 
3493     /* add the length */
3494     ((struct rt_mq_message *)msg)->length = size;
3495     /* copy buffer */
3496     rt_memcpy(GET_MESSAGEBYTE_ADDR(msg), buffer, size);
3497 
3498     /* disable interrupt */
3499     level = rt_spin_lock_irqsave(&(mq->spinlock));
3500 #ifdef RT_USING_MESSAGEQUEUE_PRIORITY
3501     msg->prio = prio;
3502     if (mq->msg_queue_head == RT_NULL)
3503         mq->msg_queue_head = msg;
3504 
3505     struct rt_mq_message *node, *prev_node = RT_NULL;
3506     for (node = mq->msg_queue_head; node != RT_NULL; node = node->next)
3507     {
3508         if (node->prio < msg->prio)
3509         {
3510             if (prev_node == RT_NULL)
3511                 mq->msg_queue_head = msg;
3512             else
3513                 prev_node->next = msg;
3514             msg->next = node;
3515             break;
3516         }
3517         if (node->next == RT_NULL)
3518         {
3519             if (node != msg)
3520                 node->next = msg;
3521             mq->msg_queue_tail = msg;
3522             break;
3523         }
3524         prev_node = node;
3525     }
3526 #else
3527     /* link msg to message queue */
3528     if (mq->msg_queue_tail != RT_NULL)
3529     {
3530         /* if the tail exists, */
3531         ((struct rt_mq_message *)mq->msg_queue_tail)->next = msg;
3532     }
3533 
3534     /* set new tail */
3535     mq->msg_queue_tail = msg;
3536     /* if the head is empty, set head */
3537     if (mq->msg_queue_head == RT_NULL)
3538         mq->msg_queue_head = msg;
3539 #endif
3540 
3541     if(mq->entry < RT_MQ_ENTRY_MAX)
3542     {
3543         /* increase message entry */
3544         mq->entry ++;
3545     }
3546     else
3547     {
3548         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3549         return -RT_EFULL; /* value overflowed */
3550     }
3551 
3552     /* resume suspended thread */
3553     if (!rt_list_isempty(&mq->parent.suspend_thread))
3554     {
3555         rt_susp_list_dequeue(&(mq->parent.suspend_thread), RT_EOK);
3556 
3557         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3558 
3559         rt_schedule();
3560 
3561         return RT_EOK;
3562     }
3563     rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3564 
3565     return RT_EOK;
3566 }
3567 
rt_mq_send_wait(rt_mq_t mq,const void * buffer,rt_size_t size,rt_int32_t timeout)3568 rt_err_t rt_mq_send_wait(rt_mq_t     mq,
3569                          const void *buffer,
3570                          rt_size_t   size,
3571                          rt_int32_t  timeout)
3572 {
3573     return _rt_mq_send_wait(mq, buffer, size, 0, timeout, RT_UNINTERRUPTIBLE);
3574 }
3575 RTM_EXPORT(rt_mq_send_wait);
3576 
rt_mq_send_wait_interruptible(rt_mq_t mq,const void * buffer,rt_size_t size,rt_int32_t timeout)3577 rt_err_t rt_mq_send_wait_interruptible(rt_mq_t     mq,
3578                          const void *buffer,
3579                          rt_size_t   size,
3580                          rt_int32_t  timeout)
3581 {
3582     return _rt_mq_send_wait(mq, buffer, size, 0, timeout, RT_INTERRUPTIBLE);
3583 }
3584 RTM_EXPORT(rt_mq_send_wait_interruptible);
3585 
rt_mq_send_wait_killable(rt_mq_t mq,const void * buffer,rt_size_t size,rt_int32_t timeout)3586 rt_err_t rt_mq_send_wait_killable(rt_mq_t     mq,
3587                          const void *buffer,
3588                          rt_size_t   size,
3589                          rt_int32_t  timeout)
3590 {
3591     return _rt_mq_send_wait(mq, buffer, size, 0, timeout, RT_KILLABLE);
3592 }
3593 RTM_EXPORT(rt_mq_send_wait_killable);
3594 /**
3595  * @brief    This function will send a message to the messagequeue object.
3596  *           If there is a thread suspended on the messagequeue, the thread will be resumed.
3597  *
3598  * @note     When using this function to send a message, if the messagequeue is fully used,
3599  *           the current thread will wait for a timeout.
3600  *           By contrast, when the messagequeue is fully used, the rt_mq_send_wait() function will
3601  *           return an error code immediately without waiting.
3602  *
3603  * @see      rt_mq_send_wait()
3604  *
3605  * @param    mq is a pointer to the messagequeue object to be sent.
3606  *
3607  * @param    buffer is the content of the message.
3608  *
3609  * @param    size is the length of the message(Unit: Byte).
3610  *
3611  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
3612  *           If the return value is any other values, it means that the messagequeue detach failed.
3613  *
3614  * @warning  This function can be called in interrupt context and thread context.
3615  */
rt_mq_send(rt_mq_t mq,const void * buffer,rt_size_t size)3616 rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size)
3617 {
3618     return rt_mq_send_wait(mq, buffer, size, 0);
3619 }
3620 RTM_EXPORT(rt_mq_send);
3621 
rt_mq_send_interruptible(rt_mq_t mq,const void * buffer,rt_size_t size)3622 rt_err_t rt_mq_send_interruptible(rt_mq_t mq, const void *buffer, rt_size_t size)
3623 {
3624     return rt_mq_send_wait_interruptible(mq, buffer, size, 0);
3625 }
3626 RTM_EXPORT(rt_mq_send_interruptible);
3627 
rt_mq_send_killable(rt_mq_t mq,const void * buffer,rt_size_t size)3628 rt_err_t rt_mq_send_killable(rt_mq_t mq, const void *buffer, rt_size_t size)
3629 {
3630     return rt_mq_send_wait_killable(mq, buffer, size, 0);
3631 }
3632 RTM_EXPORT(rt_mq_send_killable);
3633 /**
3634  * @brief    This function will send an urgent message to the messagequeue object.
3635  *
3636  * @note     This function is almost the same as the rt_mq_send() function. The only difference is that
3637  *           when sending an urgent message, the message is placed at the head of the messagequeue so that
3638  *           the recipient can receive the urgent message first.
3639  *
3640  * @see      rt_mq_send()
3641  *
3642  * @param    mq is a pointer to the messagequeue object to be sent.
3643  *
3644  * @param    buffer is the content of the message.
3645  *
3646  * @param    size is the length of the message(Unit: Byte).
3647  *
3648  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
3649  *           If the return value is any other values, it means that the mailbox detach failed.
3650  */
rt_mq_urgent(rt_mq_t mq,const void * buffer,rt_size_t size)3651 rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size)
3652 {
3653     rt_base_t level;
3654     struct rt_mq_message *msg;
3655 
3656     /* parameter check */
3657     RT_ASSERT(mq != RT_NULL);
3658     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
3659     RT_ASSERT(buffer != RT_NULL);
3660     RT_ASSERT(size != 0);
3661 
3662     /* greater than one message size */
3663     if (size > mq->msg_size)
3664         return -RT_ERROR;
3665 
3666     RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mq->parent.parent)));
3667 
3668     level = rt_spin_lock_irqsave(&(mq->spinlock));
3669 
3670     /* get a free list, there must be an empty item */
3671     msg = (struct rt_mq_message *)mq->msg_queue_free;
3672     /* message queue is full */
3673     if (msg == RT_NULL)
3674     {
3675         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3676 
3677         return -RT_EFULL;
3678     }
3679     /* move free list pointer */
3680     mq->msg_queue_free = msg->next;
3681 
3682     rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3683 
3684     /* add the length */
3685     ((struct rt_mq_message *)msg)->length = size;
3686     /* copy buffer */
3687     rt_memcpy(GET_MESSAGEBYTE_ADDR(msg), buffer, size);
3688 
3689     level = rt_spin_lock_irqsave(&(mq->spinlock));
3690 
3691     /* link msg to the beginning of message queue */
3692     msg->next = (struct rt_mq_message *)mq->msg_queue_head;
3693     mq->msg_queue_head = msg;
3694 
3695     /* if there is no tail */
3696     if (mq->msg_queue_tail == RT_NULL)
3697         mq->msg_queue_tail = msg;
3698 
3699     if(mq->entry < RT_MQ_ENTRY_MAX)
3700     {
3701         /* increase message entry */
3702         mq->entry ++;
3703     }
3704     else
3705     {
3706         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3707         return -RT_EFULL; /* value overflowed */
3708     }
3709 
3710     /* resume suspended thread */
3711     if (!rt_list_isempty(&mq->parent.suspend_thread))
3712     {
3713         rt_susp_list_dequeue(&(mq->parent.suspend_thread), RT_EOK);
3714 
3715         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3716 
3717         rt_schedule();
3718 
3719         return RT_EOK;
3720     }
3721 
3722     rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3723 
3724     return RT_EOK;
3725 }
3726 RTM_EXPORT(rt_mq_urgent);
3727 
3728 /**
3729  * @brief    This function will receive a message from message queue object,
3730  *           if there is no message in messagequeue object, the thread shall wait for a specified time.
3731  *
3732  * @note     Only when there is mail in the mailbox, the receiving thread can get the mail immediately and return RT_EOK,
3733  *           otherwise the receiving thread will be suspended until timeout.
3734  *           If the mail is not received within the specified time, it will return -RT_ETIMEOUT.
3735  *
3736  * @param    mq is a pointer to the messagequeue object to be received.
3737  *
3738  * @param    buffer is the content of the message.
3739  *
3740  * @param    prio is message priority, A larger value indicates a higher priority
3741  *
3742  * @param    size is the length of the message(Unit: Byte).
3743  *
3744  * @param    timeout is a timeout period (unit: an OS tick). If the message is unavailable, the thread will wait for
3745  *           the message in the queue up to the amount of time specified by this parameter.
3746  *
3747  * @param    suspend_flag status flag of the thread to be suspended.
3748  *
3749  *           NOTE:
3750  *           If use Macro RT_WAITING_FOREVER to set this parameter, which means that when the
3751  *           message is unavailable in the queue, the thread will be waiting forever.
3752  *           If use macro RT_WAITING_NO to set this parameter, which means that this
3753  *           function is non-blocking and will return immediately.
3754  *
3755  * @return   Return the real length of the message. When the return value is larger than zero, the operation is successful.
3756  *           If the return value is any other values, it means that the mailbox release failed.
3757  */
_rt_mq_recv(rt_mq_t mq,void * buffer,rt_size_t size,rt_int32_t * prio,rt_int32_t timeout,int suspend_flag)3758 static rt_ssize_t _rt_mq_recv(rt_mq_t mq,
3759                               void *buffer,
3760                               rt_size_t size,
3761                               rt_int32_t *prio,
3762                               rt_int32_t timeout,
3763                               int suspend_flag)
3764 {
3765     struct rt_thread *thread;
3766     rt_base_t level;
3767     struct rt_mq_message *msg;
3768     rt_uint32_t tick_delta;
3769     rt_err_t ret;
3770     rt_size_t len;
3771 
3772     RT_UNUSED(prio);
3773 
3774     /* parameter check */
3775     RT_ASSERT(mq != RT_NULL);
3776     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
3777     RT_ASSERT(buffer != RT_NULL);
3778     RT_ASSERT(size != 0);
3779 
3780     /* current context checking */
3781     RT_DEBUG_SCHEDULER_AVAILABLE(timeout != 0);
3782 
3783     /* initialize delta tick */
3784     tick_delta = 0;
3785     /* get current thread */
3786     thread = rt_thread_self();
3787     RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mq->parent.parent)));
3788 
3789     level = rt_spin_lock_irqsave(&(mq->spinlock));
3790 
3791     /* for non-blocking call */
3792     if (mq->entry == 0 && timeout == 0)
3793     {
3794         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3795 
3796         return -RT_ETIMEOUT;
3797     }
3798 
3799     /* message queue is empty */
3800     while (mq->entry == 0)
3801     {
3802         /* reset error number in thread */
3803         thread->error = -RT_EINTR;
3804 
3805         /* no waiting, return timeout */
3806         if (timeout == 0)
3807         {
3808             /* enable interrupt */
3809             rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3810 
3811             thread->error = -RT_ETIMEOUT;
3812 
3813             return -RT_ETIMEOUT;
3814         }
3815 
3816         /* suspend current thread */
3817         ret = rt_thread_suspend_to_list(thread, &(mq->parent.suspend_thread),
3818                                         mq->parent.parent.flag, suspend_flag);
3819         if (ret != RT_EOK)
3820         {
3821             rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3822             return ret;
3823         }
3824 
3825         /* has waiting time, start thread timer */
3826         if (timeout > 0)
3827         {
3828             /* get the start tick of timer */
3829             tick_delta = rt_tick_get();
3830 
3831             LOG_D("set thread:%s to timer list",
3832                   thread->parent.name);
3833 
3834             /* reset the timeout of thread timer and start it */
3835             rt_timer_control(&(thread->thread_timer),
3836                              RT_TIMER_CTRL_SET_TIME,
3837                              &timeout);
3838             rt_timer_start(&(thread->thread_timer));
3839         }
3840 
3841         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3842 
3843         /* re-schedule */
3844         rt_schedule();
3845 
3846         /* recv message */
3847         if (thread->error != RT_EOK)
3848         {
3849             /* return error */
3850             return thread->error;
3851         }
3852 
3853         level = rt_spin_lock_irqsave(&(mq->spinlock));
3854 
3855         /* if it's not waiting forever and then re-calculate timeout tick */
3856         if (timeout > 0)
3857         {
3858             tick_delta = rt_tick_get() - tick_delta;
3859             timeout -= tick_delta;
3860             if (timeout < 0)
3861                 timeout = 0;
3862         }
3863     }
3864 
3865     /* get message from queue */
3866     msg = (struct rt_mq_message *)mq->msg_queue_head;
3867 
3868     /* move message queue head */
3869     mq->msg_queue_head = msg->next;
3870     /* reach queue tail, set to NULL */
3871     if (mq->msg_queue_tail == msg)
3872         mq->msg_queue_tail = RT_NULL;
3873 
3874     /* decrease message entry */
3875     if(mq->entry > 0)
3876     {
3877         mq->entry --;
3878     }
3879 
3880     rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3881 
3882     /* get real message length */
3883     len = ((struct rt_mq_message *)msg)->length;
3884 
3885     if (len > size)
3886         len = size;
3887     /* copy message */
3888     rt_memcpy(buffer, GET_MESSAGEBYTE_ADDR(msg), len);
3889 
3890 #ifdef RT_USING_MESSAGEQUEUE_PRIORITY
3891     if (prio != RT_NULL)
3892         *prio = msg->prio;
3893 #endif
3894     level = rt_spin_lock_irqsave(&(mq->spinlock));
3895     /* put message to free list */
3896     msg->next = (struct rt_mq_message *)mq->msg_queue_free;
3897     mq->msg_queue_free = msg;
3898 
3899     /* resume suspended thread */
3900     if (!rt_list_isempty(&(mq->suspend_sender_thread)))
3901     {
3902         rt_susp_list_dequeue(&(mq->suspend_sender_thread), RT_EOK);
3903 
3904         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3905 
3906         RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mq->parent.parent)));
3907 
3908         rt_schedule();
3909 
3910         return len;
3911     }
3912 
3913     rt_spin_unlock_irqrestore(&(mq->spinlock), level);
3914 
3915     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mq->parent.parent)));
3916 
3917     return len;
3918 }
3919 
rt_mq_recv(rt_mq_t mq,void * buffer,rt_size_t size,rt_int32_t timeout)3920 rt_ssize_t rt_mq_recv(rt_mq_t    mq,
3921                     void      *buffer,
3922                     rt_size_t  size,
3923                     rt_int32_t timeout)
3924 {
3925     return _rt_mq_recv(mq, buffer, size, 0, timeout, RT_UNINTERRUPTIBLE);
3926 }
3927 RTM_EXPORT(rt_mq_recv);
3928 
rt_mq_recv_interruptible(rt_mq_t mq,void * buffer,rt_size_t size,rt_int32_t timeout)3929 rt_ssize_t rt_mq_recv_interruptible(rt_mq_t    mq,
3930                     void      *buffer,
3931                     rt_size_t  size,
3932                     rt_int32_t timeout)
3933 {
3934     return _rt_mq_recv(mq, buffer, size, 0, timeout, RT_INTERRUPTIBLE);
3935 }
3936 RTM_EXPORT(rt_mq_recv_interruptible);
3937 
rt_mq_recv_killable(rt_mq_t mq,void * buffer,rt_size_t size,rt_int32_t timeout)3938 rt_ssize_t rt_mq_recv_killable(rt_mq_t    mq,
3939                     void      *buffer,
3940                     rt_size_t  size,
3941                     rt_int32_t timeout)
3942 {
3943     return _rt_mq_recv(mq, buffer, size, 0, timeout, RT_KILLABLE);
3944 }
3945 #ifdef RT_USING_MESSAGEQUEUE_PRIORITY
rt_mq_send_wait_prio(rt_mq_t mq,const void * buffer,rt_size_t size,rt_int32_t prio,rt_int32_t timeout,int suspend_flag)3946 rt_err_t rt_mq_send_wait_prio(rt_mq_t mq,
3947                               const void *buffer,
3948                               rt_size_t size,
3949                               rt_int32_t prio,
3950                               rt_int32_t timeout,
3951                               int suspend_flag)
3952 {
3953     return _rt_mq_send_wait(mq, buffer, size, prio, timeout, suspend_flag);
3954 }
rt_mq_recv_prio(rt_mq_t mq,void * buffer,rt_size_t size,rt_int32_t * prio,rt_int32_t timeout,int suspend_flag)3955 rt_ssize_t rt_mq_recv_prio(rt_mq_t mq,
3956                            void *buffer,
3957                            rt_size_t size,
3958                            rt_int32_t *prio,
3959                            rt_int32_t timeout,
3960                            int suspend_flag)
3961 {
3962     return _rt_mq_recv(mq, buffer, size, prio, timeout, suspend_flag);
3963 }
3964 #endif
3965 RTM_EXPORT(rt_mq_recv_killable);
3966 /**
3967  * @brief    This function will set some extra attributions of a messagequeue object.
3968  *
3969  * @note     Currently this function only supports the RT_IPC_CMD_RESET command to reset the messagequeue.
3970  *
3971  * @param    mq is a pointer to a messagequeue object.
3972  *
3973  * @param    cmd is a command used to configure some attributions of the messagequeue.
3974  *
3975  * @param    arg is the argument of the function to execute the command.
3976  *
3977  * @return   Return the operation status. When the return value is RT_EOK, the operation is successful.
3978  *           If the return value is any other values, it means that this function failed to execute.
3979  */
rt_mq_control(rt_mq_t mq,int cmd,void * arg)3980 rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg)
3981 {
3982     rt_base_t level;
3983     struct rt_mq_message *msg;
3984 
3985     RT_UNUSED(arg);
3986 
3987     /* parameter check */
3988     RT_ASSERT(mq != RT_NULL);
3989     RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue);
3990 
3991     if (cmd == RT_IPC_CMD_RESET)
3992     {
3993         level = rt_spin_lock_irqsave(&(mq->spinlock));
3994 
3995         /* resume all waiting thread */
3996         rt_susp_list_resume_all(&mq->parent.suspend_thread, RT_ERROR);
3997         /* also resume all message queue private suspended thread */
3998         rt_susp_list_resume_all(&(mq->suspend_sender_thread), RT_ERROR);
3999 
4000         /* release all message in the queue */
4001         while (mq->msg_queue_head != RT_NULL)
4002         {
4003             /* get message from queue */
4004             msg = (struct rt_mq_message *)mq->msg_queue_head;
4005 
4006             /* move message queue head */
4007             mq->msg_queue_head = msg->next;
4008             /* reach queue tail, set to NULL */
4009             if (mq->msg_queue_tail == msg)
4010                 mq->msg_queue_tail = RT_NULL;
4011 
4012             /* put message to free list */
4013             msg->next = (struct rt_mq_message *)mq->msg_queue_free;
4014             mq->msg_queue_free = msg;
4015         }
4016 
4017         /* clean entry */
4018         mq->entry = 0;
4019 
4020         rt_spin_unlock_irqrestore(&(mq->spinlock), level);
4021 
4022         rt_schedule();
4023 
4024         return RT_EOK;
4025     }
4026 
4027     return -RT_ERROR;
4028 }
4029 RTM_EXPORT(rt_mq_control);
4030 
4031 /**@}*/
4032 #endif /* RT_USING_MESSAGEQUEUE */
4033 /**@}*/
4034