• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Mutex
2
3
4## Basic Concepts
5
6A mutual exclusion (mutex) is a special binary semaphore used for exclusive access to shared resources. When a task holds the mutex, the task obtains the ownership of the mutex. When the task releases the mutex, the task will lose the ownership of the mutex. When a task holds a mutex, other tasks cannot hold the mutex. In an environment where multiple tasks compete for shared resources, the mutex ensures exclusive access to the shared resources.
7
8A mutex has three attributes: protocol attribute, priority upper limit attribute, and type attribute. The protocol attribute is used to handle a mutex requested by tasks of different priorities. The protocol attribute can be any of the following:
9
10- LOS_MUX_PRIO_NONE
11
12  Do not inherit or protect the priority of the task requesting the mutex.
13
14- LOS_MUX_PRIO_INHERIT
15
16  Inherits the priority of the task that requests the mutex. This is the default protocol attribute. When the mutex protocol attribute is set to this value: If a task with a higher priority is blocked because the mutex is already held by a task, the priority of the task holding the mutex will be backed up to the priority bitmap of the task control block, and then set to be the same as that of the task of a higher priority. When the task holding the mutex releases the mutex, its task priority is restored to its original value.
17
18- LOS_MUX_PRIO_PROTECT
19
20  Protects the priority of the task that requests the mutex. When the mutex protocol attribute is set to this value: If the priority of the task that requests the mutex is lower than the upper limit of the mutex priority, the task priority will be backed up to the priority bitmap of the task control block, and then set to the upper limit value of the mutex priority. When the mutex is released, the task priority is restored to its original value.
21
22  The type attribute of a mutex specifies whether to check for deadlocks and whether to support recursive holding of the mutex. The type attribute can be any of the following:
23
24- LOS_MUX_NORMAL
25
26  Common mutex, which does not check for deadlocks. If a task repeatedly attempts to hold a mutex, the thread will be deadlocked. If the mutex type attribute is set to this value, a task cannot release a mutex held by another task or repeatedly release a mutex. Otherwise, unexpected results will be caused.
27
28- LOS_MUX_RECURSIVE
29
30  Recursive mutex, which is the default attribute. If the type attribute of a mutex is set to this value, a task can hold the mutex for multiple times. Another task can hold this mutex only when the number of lock holding times is the same as the number of lock release times. However, any attempt to hold a mutex held by another task or attempt to release a mutex that has been released will return an error code.
31
32- LOS_MUX_ERRORCHECK
33
34  Mutex for error checks. When a mutex is set to this type, an error code will be returned if a task attempts to repeatedly hold the mutex, attempts to release the mutex held by another task, or attempts to release the mutex that has been released.
35
36
37## Working Principles
38
39In a multi-task environment, multiple tasks may access the same shared resources. However, certain shared resources are not shared, and can only be accessed exclusively by tasks. A mutex can be used to address this issue.
40
41When non-shared resources are accessed by a task, the mutex is locked. Other tasks will be blocked until the mutex is released by the task. The mutex allows only one task to access the shared resources at a time, ensuring integrity of operations on the shared resources.
42
43**Figure 1** Mutex working mechanism for the small system
44
45![](figures/mutex-working-mechanism-for-small-systems.png "mutex-working-mechanism-for-small-systems")
46
47
48## Development Guidelines
49
50
51### Available APIs
52
53  **Table 1** APIs of the mutex module
54
55| Category| API Description |
56| -------- | -------- |
57| Initializing or destroying a mutex| - **LOS_MuxInit**: initializes a mutex.<br>- **LOS_MuxDestroy**: destroys a mutex.|
58| Requesting or releasing a mutex| - **LOS_MuxLock**: requests a mutex.<br>- **LOS_MuxTrylock**: requests a mutex without blocking.<br>- **LOS_MuxUnlock**: releases a mutex.|
59| Verifying a mutex| - **LOS_MuxIsValid**: checks whether the mutex release is valid.<br>- **LOS_MuxAttrDestroy**: destroys the specified mutex attribute.|
60| Setting and obtaining mutex attributes| - **LOS_MuxAttrGetType**: obtains the type attribute of a mutex.<br>- **LOS_MuxAttrSetType**: sets the type attribute for a mutex.<br>- **LOS_MuxAttrGetProtocol**: obtains the protocol attribute of a mutex.<br>- **LOS_MuxAttrSetProtocol**: sets the protocol attribute for a mutex.<br>- **LOS_MuxAttrGetPrioceiling**: obtains the priority upper limit attribute of a mutex.<br>- **LOS_MuxAttrSetPrioceiling**: sets the priority upper limit attribute for a mutex.<br>- **LOS_MuxGetPrioceiling**: obtains the priority upper limit of this mutex.<br>- **LOS_MuxSetPrioceiling**: sets the priority upper limit for this mutex. |
61
62
63### How to Develop
64
65The typical mutex development process is as follows:
66
671. Call **LOS_MuxInit** to initialize a mutex.
68
692. Call **LOS_MuxLock** to request a mutex.
70
71The following modes are available:
72
73- Non-block mode: A task acquires the mutex if the requested mutex is not held by any task or the task holding the mutex is the same as the task requesting the mutex.
74
75- Permanent block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with a highest priority in the ready queue will be executed. The blocked task can be unlocked and executed only when the mutex is released.
76
77- Scheduled block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with the highest priority in the ready queue will be executed. The blocked task can be executed only when the mutex is released within the specified timeout period or when the specified timeout period expires.
78
793. Call **LOS_MuxUnlock** to release a mutex.
80
81- If tasks are blocked by the specified mutex, the task with a higher priority will be unblocked when the mutex is released. The unblocked task changes to the Ready state and is scheduled.
82
83- If no task is blocked by the specified mutex, the mutex is released successfully.
84
854. Call **LOS_MuxDestroy** to destroy a mutex.
86
87> **NOTE**<br>
88> - Two tasks cannot lock the same mutex. If a task attempts to lock a mutex held by another task, the task will be blocked until the mutex is unclocked.
89>
90> - Mutexes cannot be used in the interrupt service program.
91>
92> - The system using the LiteOS-A kernel must ensure real-time task scheduling and avoid long-time task blocking. Therefore, a mutex must be released as soon as possible after use.
93
94
95### Development Example
96
97#### Example Description
98
99This example implements the following:
100
1011. Create the **Example_TaskEntry** task. In this task, create a mutex to lock task scheduling, and create two tasks **Example_MutexTask1** (with a lower priority) and **Example_MutexTask2** (with a higher priority) to unlock task scheduling.
102
1032. When being scheduled, **Example_MutexTask2** requests a mutex in permanent block mode. After acquiring the mutex, **Example_MutexTask2** enters the sleep mode for 100 ticks. **Example_MutexTask2** is suspended, and **Example_MutexTask1** is woken up.
104
1053. **Example_MutexTask1** requests a mutex in scheduled block mode, and waits for 10 ticks. Because the mutex is still held by **Example_MutexTask2**, **Example_MutexTask1** is suspended. After 10 ticks, **Example_MutexTask1** is woken up and attempts to request a mutex in permanent block mode. **Example_MutexTask1** is suspended because the mutex is still held by **Example_MutexTask2**.
106
1074. After 100 ticks, **Example_MutexTask2** is woken up and releases the mutex, and then **Example_MutexTask1** is woken up. **Example_MutexTask1** acquires the mutex and then releases the mutex. At last, the mutex is deleted.
108
109#### Sample Code
110
111The sample code can be compiled and verified in **./kernel/liteos_a/testsuites/kernel/src/osTest.c**. The **Example_MutexEntry** function is called in **TestTaskEntry**.
112
113The sample code is as follows:
114
115```
116#include <string.h>
117#include "los_mux.h"
118
119/* Mutex */
120LosMux g_testMutex;
121/* Task ID*/
122UINT32 g_testTaskId01;
123UINT32 g_testTaskId02;
124
125VOID Example_MutexTask1(VOID)
126{
127    UINT32 ret;
128    LOS_TaskDelay(50);
129
130    dprintf("task1 try to get mutex, wait 10 ticks.\n");
131    /* Request a mutex. */
132    ret = LOS_MuxLock(&g_testMutex, 10);
133
134    if (ret == LOS_OK) {
135        dprintf("task1 get mutex g_testMux.\n");
136        /* Release the mutex. */
137        LOS_MuxUnlock(&g_testMutex);
138        return;
139    }
140    if (ret == LOS_ETIMEDOUT) {
141        dprintf("task1 timeout and try to get mutex, wait forever.\n");
142        /* Request a mutex. */
143        ret = LOS_MuxLock(&g_testMutex, LOS_WAIT_FOREVER);
144        if (ret == LOS_OK) {
145            dprintf("task1 wait forever, get mutex g_testMux.\n");
146            /* Release the mutex. */
147            LOS_MuxUnlock(&g_testMutex);
148            /* Delete the mutex. */
149            LOS_MuxDestroy(&g_testMutex);
150            dprintf("task1 post and delete mutex g_testMux.\n");
151            return;
152        }
153    }
154    return;
155}
156
157VOID Example_MutexTask2(VOID)
158{
159    dprintf("task2 try to get mutex, wait forever.\n");
160    /* Request a mutex. */
161    (VOID)LOS_MuxLock(&g_testMutex, LOS_WAIT_FOREVER);
162
163    dprintf("task2 get mutex g_testMux and suspend 100 ticks.\n");
164
165    /* Enable the task to enter sleep mode for 100 ticks. */
166    LOS_TaskDelay(100);
167
168    dprintf("task2 resumed and post the g_testMux\n");
169    /* Release the mutex. */
170    LOS_MuxUnlock(&g_testMutex);
171    return;
172}
173
174UINT32 Example_MutexEntry(VOID)
175{
176    UINT32 ret;
177    TSK_INIT_PARAM_S task1;
178    TSK_INIT_PARAM_S task2;
179
180    /* Initialize the mutex. */
181    LOS_MuxInit(&g_testMutex, NULL);
182
183    /* Lock task scheduling. */
184    LOS_TaskLock();
185
186    /* Create task 1. */
187    memset(&task1, 0, sizeof(TSK_INIT_PARAM_S));
188    task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1;
189    task1.pcName       = "MutexTsk1";
190    task1.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
191    task1.usTaskPrio   = 5;
192    ret = LOS_TaskCreate(&g_testTaskId01, &task1);
193    if (ret != LOS_OK) {
194        dprintf("task1 create failed.\n");
195        return LOS_NOK;
196    }
197
198    /* Create task 2. */
199    memset(&task2, 0, sizeof(TSK_INIT_PARAM_S));
200    task2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2;
201    task2.pcName       = "MutexTsk2";
202    task2.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
203    task2.usTaskPrio   = 4;
204    ret = LOS_TaskCreate(&g_testTaskId02, &task2);
205    if (ret != LOS_OK) {
206        dprintf("task2 create failed.\n");
207        return LOS_NOK;
208    }
209
210    /* Unlock task scheduling. */
211    LOS_TaskUnlock();
212
213    return LOS_OK;
214}
215```
216
217**Verification**
218
219The development is successful if the return result is as follows:
220
221
222```
223task2 try to get mutex, wait forever.
224task2 get mutex g_testMux and suspend 100 ticks.
225task1 try to get mutex, wait 10 ticks.
226task1 timeout and try to get mutex, wait forever.
227task2 resumed and post the g_testMux
228task1 wait forever, get mutex g_testMux.
229task1 post and delete mutex g_testMux.
230```
231