README.md
1@page page_component_posix POSIX Interface
2
3# Introduction to Pthreads
4
5POSIX Threads is abbreviated as Pthreads. POSIX is the abbreviation of "Portable Operating System Interface". POSIX is a set of standards established by IEEE Computer Society to improve the compatibility of different operating systems and the portability of applications. Pthreads is a threaded POSIX standard defined in the POSIX.1c, Threads extensions (IEEE Std1003.1c-1995) standard, which defines a set of C programming language types, functions, and constants. Defined in the `pthread.h` header file and a thread library, there are about 100 APIs, all of which have a "`pthread_`" prefix and can be divided into 4 categories:
6
7- **Thread management**: Thread creating, detaching, joining, and setting and querying thread attributes.
8
9- **Mutex**: Abbreviation for "mutual exclusion", which restricts thread access to shared data and protects the integrity of shared data. This includes creating, destroying, locking, and unlocking mutex and some functions for setting or modifying mutex properties.
10
11- **Condition variable**: Communication between threads used to share a mutex. It includes functions such as creation, destruction, waiting condition variables, and sending signal .
12
13- **Read/write locks and barriers**: including the creation, destruction, wait, and related property settings of read-write locks and barriers.
14
15POSIX semaphores are used with Pthreads, but are not part of the Pthreads standard definition and are defined in POSIX.1b, Real-time extensions (IEEE Std1003.1b-1993). Therefore the prefix of the semaphore correlation function is "`sem_`" instead of "`pthread_`".
16
17Message queues, like semaphores, are used with Pthreads and are not part of the Pthreads standard definition and are defined in the IEEE Std 1003.1-2001 standard. The prefix of the message queue related function is "`mq_`".
18
19| Function Prefix | Function Group |
20|----------------------|----------------------|
21| `pthread_ ` | Thread itself and various related functions |
22| `pthread_attr_` | Thread attribute object |
23| `Pthread_mutex_` | Mutex |
24| `pthread_mutexattr_` | Mutex attribute object |
25| `pthread_cond_ ` | Conditional variable |
26| `pthread_condattr_` | Condition variable attribute object |
27| `pthread_rwlock_` | Read-write lock |
28| `pthread_rwlockattr_` | Read-write lock attribute object |
29| `pthread_spin_` | Spin lock |
30| `pthread_barrier_ ` | Barrier |
31| `pthread_barrierattr_` | Barrier attribute object |
32| `sem_` | Semaphore |
33| `mq_ ` | Message queue |
34
35Most Pthreads functions return a value of 0 if they succeed, and an error code contained in the `errno.h` header file if unsuccessful. Many operating systems support Pthreads, such as Linux, MacOSX, Android, and Solaris, so applications written using Pthreads functions are very portable and can be compiled and run directly on many platforms that support Pthreads.
36
37## Use POSIX in RT-Thread
38
39Using the POSIX API interface in RT-Thread includes several parts: libc (for example, newlib), filesystem, pthread, and so on. Need to open the relevant options in rtconfig.h:
40
41``` c
42#define RT_USING_LIBC
43#define RT_USING_DFS
44#define RT_USING_DFS_DEVFS
45#define RT_USING_PTHREADS
46```
47
48RT-Thread implements most of the functions and constants of Pthreads, defined in the pthread.h, mqueue.h, semaphore.h, and sched.h header files according to the POSIX standard. Pthreads is a sublibrary of libc, and Pthreads in RT-Thread are based on the encapsulation of RT-Thread kernel functions, making them POSIX compliant. The Pthreads functions and related functions implemented in RT-Thread are described in detail in the following sections.
49
50# Thread
51
52## Thread Handle
53
54``` c
55typedef rt_thread_t pthread_t;
56```
57
58`Pthread_t` is a redefinition of the `rt_thread_t` type, defined in the `pthread.h` header file. rt_thread_t is the thread handle (or thread identifier) of the RT-Thread and is a pointer to the thread control block. You need to define a variable of type pthread_t before creating a thread. Each thread corresponds to its own thread control block, which is a data structure used by the operating system to control threads. It stores some information about the thread, such as priority, thread name, and thread stack address. Thread control blocks and thread specific information are described in detail in the @ref page_thread_management chapter.
59
60## Create Thread
61
62``` c
63int pthread_create (pthread_t *tid,
64 const pthread_attr_t *attr,
65 void *(*start) (void *), void *arg);
66```
67
68| **Parameter** | **Description** |
69|----------|------------------------------------------------------|
70| tid | Pointer to thread handle (thread identifier), cannot be NULL |
71| attr | Pointer to the thread property, if NULL is used, the default thread property is used |
72| start | Thread entry function address |
73| arg | The argument passed to the thread entry function |
74|**return**| —— |
75| 0 | succeeded |
76| EINVAL | Invalid parameter |
77| ENOMEM | Dynamic allocation of memory failed |
78
79This function creates a pthread thread. This function dynamically allocates the POSIX thread data block and the RT-Thread thread control block, and saves the start address (thread ID) of the thread control block in the memory pointed to by the parameter tid, which can be used to operate in other threads. This thread; and the thread attribute pointed to by attr, the thread entry function pointed to by start, and the entry function parameter arg are stored in the thread data block and the thread control block. If the thread is created successfully, the thread immediately enters the ready state and participates in the scheduling of the system. If the thread creation fails, the resources occupied by the thread are released.
80
81Thread properties and related functions are described in detail in the *Thread Advanced Programming* chapter. In general, the default properties can be used.
82
83> After the pthread thread is created, if the thread needs to be created repeatedly, you need to set the pthread thread to detach mode, or use pthread_join to wait for the created pthread thread to finish.
84
85### Example Code for Creating Thread
86
87The following program initializes two threads, which have a common entry function, but their entry parameters are not the same. Others, they have the same priority and are scheduled for rotation in time slices.
88
89``` c
90#include <pthread.h>
91#include <unistd.h>
92#include <stdio.h>
93
94/* Thread control block */
95static pthread_t tid1;
96static pthread_t tid2;
97
98/* Function return value check */
99static void check_result(char* str,int result)
100{
101 if (0 == result)
102 {
103 printf("%s successfully!\n",str);
104 }
105 else
106 {
107 printf("%s failed! error code is %d\n",str,result);
108 }
109}
110
111/* Thread entry function */
112static void* thread_entry(void* parameter)
113{
114 int count = 0;
115 int no = (int) parameter; /* Obtain the thread's entry parameters */
116
117 while (1)
118 {
119 /* Printout thread count value */
120 printf("thread%d count: %d\n", no, count ++);
121
122 sleep(2); /* Sleep for 2 seconds */
123 }
124}
125
126/* User application portal */
127int rt_application_init()
128{
129 int result;
130
131 /* Create thread 1, the property is the default value, the entry function is thread_entry, and the entry function parameter is 1 */
132 result = pthread_create(&tid1,NULL,thread_entry,(void*)1);
133 check_result("thread1 created", result);
134
135 /* Create thread 2, the property is the default value, the entry function is thread_entry, and the entry function parameter is 2 */
136 result = pthread_create(&tid2,NULL,thread_entry,(void*)2);
137 check_result("thread2 created", result);
138
139 return 0;
140}
141```
142
143## Detach Thread
144
145``` c
146int pthread_detach (pthread_t thread);
147```
148
149| Parameter | Description |
150|------|----------------------|
151| thread | Thread handle (thread identifier) |
152|**return**| —— |
153| 0 | succeeded |
154
155Call this function, If the pthread does not finish running, set the detach state of the thread thread property to detached; when the thread thread has finished, the system will reclaim the resources occupied by the pthread thread.
156
157Usage: The child thread calls `pthread_detach(pthread_self())` (*pthread_self()* returns the thread handle of the currently calling thread), or another thread calls `pthread_detach(thread_id)`. The separation state of the thread attributes will be described in detail later.
158
159> Once the detach state of the thread property is set to detached, the thread cannot be waited by the pthread_join() function or re-set to detached.
160
161### Example Code for Detaching Thread
162
163The following program initializes 2 threads, which have the same priority and are scheduled according to the time slice. Both threads will be set to the detached state. The 2 threads will automatically exit after printing 3 times of information. After exiting, the system will automatically reclaim its resources.
164
165``` c
166#include <pthread.h>
167#include <unistd.h>
168#include <stdio.h>
169
170/* Thread control block */
171static pthread_t tid1;
172static pthread_t tid2;
173
174/* Function return value check */
175static void check_result(char* str,int result)
176{
177 if (0 == result)
178 {
179 printf("%s successfully!\n",str);
180 }
181 else
182 {
183 printf("%s failed! error code is %d\n",str,result);
184 }
185}
186
187/* Thread 1 entry function */
188static void* thread1_entry(void* parameter)
189{
190 int i;
191
192 printf("i'm thread1 and i will detach myself!\n");
193 pthread_detach(pthread_self()); /* Thread 1 detach itself */
194
195 for (i = 0;i < 3;i++) /* Cycle print 3 times */
196 {
197 printf("thread1 run count: %d\n",i);
198 sleep(2); /* Sleep 2 seconds */
199 }
200
201 printf("thread1 exited!\n");
202 return NULL;
203}
204
205/* Thread 2 entry function */
206static void* thread2_entry(void* parameter)
207{
208 int i;
209
210 for (i = 0;i < 3;i++) /* Cycle print 3 times */
211 {
212 printf("thread2 run count: %d\n",i);
213 sleep(2); /* Sleep 2 seconds */
214 }
215
216 printf("thread2 exited!\n");
217 return NULL;
218}
219/* User application portal */
220int rt_application_init()
221{
222 int result;
223
224 /* Create thread 1, property is the default value, separation state is the default value joinable,
225 * The entry function is thread1_entry and the entry function parameter is NULL */
226 result = pthread_create(&tid1,NULL,thread1_entry,NULL);
227 check_result("thread1 created",result);
228
229 /* Create thread 2, the property is the default value, the separation state is the default value joinable,
230 * The entry function is thread2_entry and the entry function parameter is NULL */
231 result = pthread_create(&tid2,NULL,thread2_entry,NULL);
232 check_result("thread2 created",result);
233
234 pthread_detach(tid2); /* detach thread 2 */
235
236 return 0;
237}
238```
239
240## Waiting for Thread to End
241
242``` c
243int pthread_join (pthread_t thread, void**value_ptr);
244```
245
246| Parameter | **Description** |
247|----------|----------------------|
248| thread | Thread handle (thread identifier) |
249| value_ptr | User-defined pointer to store the return value of the waiting thread, which can be obtained by the function pthread_join() |
250|**Return**| —— |
251| 0 | succeeded |
252| EDEADLK | Thread join itself |
253| EINVAL | Join a thread with a detached state |
254| ESRCH | Could not find the thread |
255
256The thread calling this function blocks and waits for the thread with the joinable property to finish running and gets the return value of the thread. The address of the returned value is stored in `value_ptr` and frees the resources held by thread.
257
258The pthread_join() and pthread_detach() functions are similar in that they are used to reclaim the resources occupied by threads after the thread running ends. A thread cannot wait for itself to end. The detached state of the thread thread must be `joinable`, and one thread only corresponds to the `pthread_join()` call. A thread with a split state of joinable will only release the resources it occupies when other threads execute `pthread_join()` on it. So in order to avoid memory leaks, all threads that will end up running, either detached or set to detached, or use pthread_join() to reclaim the resources they consume.
259
260### Example Code for Waiting for the Thread to End
261
262The following program code initializes 2 threads, they have the same priority, and the threads of the same priority are scheduled according to the time slice. The separation status of the 2 thread attributes is the default value joinable, and thread 1 starts running first, and ends after printing 3 times of information. Thread 2 calls pthread_join() to block waiting for thread 1 to end, and reclaims the resources occupied by thread 1, and thread 2 prints the message every 2 seconds.
263
264``` c
265#include <pthread.h>
266#include <unistd.h>
267#include <stdio.h>
268
269/* Thread control block */
270static pthread_t tid1;
271static pthread_t tid2;
272
273/* Function return value check */
274static void check_result(char* str,int result)
275{
276 if (0 == result)
277 {
278 printf("%s successfully!\n",str);
279 }
280 else
281 {
282 printf("%s failed! error code is %d\n",str,result);
283 }
284}
285
286/* Thread 1 entry function */
287static void* thread1_entry(void* parameter)
288{
289 int i;
290
291 for (int i = 0;i < 3;i++) /* Cycle print 3 times */
292 {
293 printf("thread1 run count: %d\n",i);
294 sleep(2); /* Sleep 2 seconds */
295 }
296
297 printf("thread1 exited!\n");
298 return NULL;
299}
300
301/* Thread 2 entry function */
302static void* thread2_entry(void* parameter)
303{
304 int count = 0;
305 void* thread1_return_value;
306
307 /* Blocking waiting thread 1 running end */
308 pthread_join(tid1, NULL);
309
310 /* Thread 2 print information to start output */
311 while(1)
312 {
313 /* Print thread count value output */
314 printf("thread2 run count: %d\n",count ++);
315 sleep(2); /* Sleep 2 seconds */
316 }
317
318 return NULL;
319}
320
321/* User application portal */
322int rt_application_init()
323{
324 int result;
325 /* Create thread 1, property is the default value, separation state is the default value joinable,
326 * The entry function is thread1_entry and the entry function parameter is NULL */
327 result = pthread_create(&tid1,NULL,thread1_entry,NULL);
328 check_result("thread1 created",result);
329
330 /* Create thread 2, the property is the default value, the separation state is the default value joinable,
331 * The entry function is thread2_entry and the entry function parameter is NULL */
332 result = pthread_create(&tid2,NULL,thread2_entry,NULL);
333 check_result("thread2 created",result);
334
335 return 0;
336}
337```
338
339## Exit Thread
340
341``` c
342void pthread_exit(void *value_ptr);
343```
344
345| *Parameter* | **Description** |
346|----------|---------------------------|
347| value_ptr | User-defined pointer to store the return value of the waiting thread, which can be obtained by the function pthread_join() |
348
349Calling this function by the pthread thread terminates execution, just as the process calls the exit() function and returns a pointer to the value returned by the thread. The thread exit is initiated by the thread itself.
350
351> If the split state of the thread is joinable, the resources occupied by the thread will not be released after the thread exits. The pthread_join() function must be called to release the resources occupied by the thread.
352
353### Example Code for Exiting Thread
354
355This program initializes 2 threads, they have the same priority, and the threads of the same priority are scheduled according to the time slice. The separation state of the two thread attributes is the default value joinable, and thread 1 starts running first, sleeps for 2 seconds after printing the information once, and then prints the exit information and then ends the operation. Thread 2 calls pthread_join() to block waiting for thread 1 to end, and reclaims the resources occupied by thread 1, and thread 2 prints the message every 2 seconds.
356
357``` c
358#include <pthread.h>
359#include <unistd.h>
360#include <stdio.h>
361
362/* Thread control block */
363static pthread_t tid1;
364static pthread_t tid2;
365
366/* Function return value check function */
367static void check_result(char* str,int result)
368{
369 if (0 == result)
370 {
371 printf("%s successfully!\n",str);
372 }
373 else
374 {
375 printf("%s failed! error code is %d\n",str,result);
376 }
377}
378
379/* Thread 1 entry function */
380static void* thread1_entry(void* parameter)
381{
382 int count = 0;
383 while(1)
384 {
385 /* Print thread count value output */
386 printf("thread1 run count: %d\n",count ++);
387 sleep(2); /* Sleep 2 seconds */
388 printf("thread1 will exit!\n");
389
390 pthread_exit(0); /* Thread 1 voluntarily quits */
391 }
392}
393
394/* Thread 2 entry function */
395static void* thread2_entry(void* parameter)
396{
397 int count = 0;
398
399 /* The block waits for thread 1 to finish running */
400 pthread_join(tid1,NULL);
401 /* Thread 2 starts outputting print information */
402 while(1)
403 {
404 /* Print thread count value output */
405 printf("thread2 run count: %d\n",count ++);
406 sleep(2); /* Sleep 2 seconds */
407 }
408}
409
410/* User application portal */
411int rt_application_init()
412{
413 int result;
414
415 /* Create thread 1, property is the default value, separation state is the default value joinable,
416 * The entry function is thread1_entry and the entry function parameter is NULL */
417 result = pthread_create(&tid1,NULL,thread1_entry,NULL);
418 check_result("thread1 created",result);
419
420 /* Create thread 2, the property is the default value, the separation state is the default value joinable,
421 * The entry function is thread2_entry and the entry function parameter is NULL */
422 result = pthread_create(&tid2,NULL,thread2_entry,NULL);
423 check_result("thread2 created",result);
424
425 return 0;
426}
427```
428
429# Mutex
430
431Mutexes, also known as mutually exclusive semaphores, are a special binary semaphore. Mutexes are used to ensure the integrity of shared resources. Only one thread can access the shared resource at any time. To access shared resources, the thread must first obtain the mutex. After the access is complete, the mutex must be released. Embedded shared resources include memory, IO, SCI, SPI, etc. If two threads access shared resources at the same time, there may be problems because one thread may use the resource while another thread modifies the shared resource and consider sharing.
432
433There are only two kinds of operations of mutex, locking or unlocking, and only one thread holds a mutex at a time. When a thread holds it, the mutex is latched and its ownership is obtained by this thread. Conversely, when this thread releases it, it unlocks the mutex and loses its ownership. When a thread holds a mutex, other threads will not be able to unlock it or hold it.
434
435The main APIs of the mutex include: calling `pthread_mutex_init()` to initialize a mutex, `pthread_mutex_destroy()` to destroy the mutex, pthread_mutex_lock() to lock the mutex, and `pthread_mutex_unlock()` to unlock the mutex.
436
437The rt-thread operating system implements a priority inheritance algorithm to prevent priority inversion.Priority inheritance is the practice of raising the priority of a low-priority thread that occupies a resource to the same level as the highest-priority thread of all the threads waiting for the resource, then executing, and then returning to the initial setting when the low-priority thread releases the resource.Thus, threads that inherit priority prevent system resources from being preempted by any intermediate priority thread.
438
439For a detailed introduction to priority reversal, please refer to the @ref page_thread_sync Mutex section.
440
441## Mutex Lock Control Block
442
443Each mutex corresponds to a mutex control block that contains some information about the control of the mutex. Before creating a mutex, you must first define a variable of type `pthread_mutex_t`. pthread_mutex_t is a redefinition of pthread_mutex. The pthread_mutex data structure is defined in the pthread.h header file. The data structure is as follows:
444
445``` c
446struct pthread_mutex
447{
448 pthread_mutexattr_t attr; /* Mutex attribute */
449 struct rt_mutex lock; /* RT-Thread Mutex lock control block */
450};
451typedef struct pthread_mutex pthread_mutex_t;
452
453//rt_mutex is a data structure defined in the RT-Thread kernel, defined in the rtdef.h header file. The data structure is as follows:
454
455struct rt_mutex
456{
457 struct rt_ipc_object parent; /* Inherited from the ipc_object class */
458 rt_uint16_t value; /* Mutex value */
459 rt_uint8_t original_priority; /* thread's original priority */
460 rt_uint8_t hold; /* Mutex lock holding count */
461 struct rt_thread *owner; /* Thread that currently has a mutex */
462};
463typedef struct rt_mutex* rt_mutex_t; /* Rt_mutext_t is a pointer to the mutex structure */
464```
465
466## Initialize the Mutex
467
468``` c
469int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
470```
471
472| **Parameter** | **Description** |
473|----------|------------------------------------------------------|
474| mutex | Mutex lock handle, cannot be NULL |
475| attr | Pointer to the mutex attribute, if the pointer is NULL, the default attribute is used. |
476|**return**| —— |
477| 0 | succeeded |
478| EINVAL | Invalid parameter |
479
480This function initializes the mutex `mutex` and sets the mutex property according to the mutex attribute object pointed to by `attr`. After successful initialization, the mutex is unlocked and the thread can obtain it. This function encapsulates the rt_mutex_init() function.
481
482In addition to calling the pthread_mutex_init() function to create a mutex, you can also statically initialize the mutex with the macro PTHREAD_MUTEX_INITIALIZER by: `pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER` (structure constant), which is equivalent to specifying attr to NULL when calling pthread_mutex_init().
483
484The mutex lock properties and related functions are described in detail in the *thread advanced programming* chapter. In general, the default properties can be used.
485
486## Destroy Mutex
487
488``` c
489int pthread_mutex_destroy(pthread_mutex_t *mutex);
490```
491
492| **Parameter** | **Description** |
493|----------|----------------------|
494| mutex | Mutex lock handle, cannot be NULL |
495|**return**| —— |
496| 0 | Succeeded |
497| EINVAL | Mutex is empty or mutex has been destroyed |
498| EBUSY | Mutex is being used |
499
500This function destroys the mutex `mutex`. Mutex is mutable in an uninitialized state after destruction. After destroying the mutex's properties and control block parameters will not be valid, but you can call pthread_mutex_init() to reinitialize the destroyed mutex. However, there is no need to destroy the mutex that is statically initialized with the macro PTHREAD_MUTEX_INITIALIZER.
501
502The mutex can be destroyed when it is determined that the mutex is not locked and no thread is blocked on the mutex.
503
504## Blocking Mode Locks the Mutex
505
506``` c
507int pthread_mutex_lock(pthread_mutex_t *mutex);
508```
509
510| **Parameter** | **Description** |
511|----------|----------------------|
512| mutex | Mutex lock handle, cannot be NULL |
513|**return**| —— |
514| 0 | Succeeded |
515| EINVAL | Invalid parameter |
516| EDEADLK | Mutexes mutex do not call this function repeatedly for a thread with a nested lock |
517
518This function locks the mutex `mutex`, which is a wrapper of the rt_mutex_take() function. If the mutex has not been locked yet, the thread applying for the mutex will successfully lock the mutex. If the mutex has been locked by the current thread and the mutex type is a nested lock, the mutex's holding count is incremented by one, and the current thread will not suspend waiting (deadlock), but the thread must corresponds to the same number of unlocks. If the mutex is held by another thread, the current thread will be blocked until the other thread unlocks the mutex, and the thread waiting for the mutex will acquire the mutex according to the *first in first out* principle. .
519
520## Non-blocking Mode Locks the Mutex
521
522``` c
523int pthread_mutex_trylock(pthread_mutex_t *mutex);
524```
525
526| **Parameter** | **Description** |
527|----------|----------------------|
528| mutex | Mutex lock handle, cannot be NULL |
529|**return**| —— |
530| 0 | succeeded |
531| EINVAL | Invalid parameter |
532| EDEADLK | Mutexes are not nested locks, but threads call this function repeatedly |
533| EBUSY | Mutexes mutex has been locked by other threads |
534
535This function is a non-blocking version of the pthread_mutex_lock() function. The difference is that if the mutex has been locked, the thread will not be blocked, but the error code will be returned immediately.
536
537## Unlock the Mutex
538
539``` c
540int pthread_mutex_unlock(pthread_mutex_t *mutex);
541```
542
543| Parameter | **Description** |
544|----------|----------------------|
545| mutex | Mutex lock handle, cannot be NULL |
546|**return**| —— |
547| 0 | Succeeded |
548| EINVAL | Invalid parameter |
549| EPERM | This function is called repeatedly by a thread when the mutex is not a nested lock |
550| EBUSY | Unlock the mutex held by other threads with the type of error detection lock |
551
552Calling this function to unlock the mutex. This function is a wrapper of the rt_mutex_release() function. When the thread completes the access of the shared resource, it should release the possessed mutex as soon as possible, so that other threads can acquire the mutex in time. Only a thread that already has a mutex can release it, and its holding count is decremented by one each time the mutex is released. When the mutex's holding count is zero (ie, the holding thread has released all holding operations), the mutex becomes available, and the thread waiting on the mutex is placed in a first-in-first-out manner. If the thread's run priority is promoted by the mutex lock, then when the mutex is released, the thread reverts to the priority before holding the mutex.
553
554## Example Code for Mutex Lock
555
556This program will initialize 2 threads, they have the same priority, 2 threads will call the same printer() function to output their own string, the printer() function will output only one character at a time, then sleep for 1 second, call printer The thread of the () function also sleeps. If you do not use a mutex, thread 1 prints a character, and after hibernation, thread 2 is executed, and thread 2 prints a character, so that the thread 1 and thread 2 strings cannot be completely printed, and the printed string is confusing. If a mutex is used to protect the print function printer() shared by 2 threads, thread 1 takes the mutex and executes the printer() print function to print a character, then sleeps for 1 second, which is switched to thread 2 because The nick lock has been locked by thread 1, and thread 2 will block until thread 1 of thread 1 is fully released and the thread 2 is woken up after the mutex is actively released.
557
558``` c
559#include <pthread.h>
560#include <unistd.h>
561#include <stdio.h>
562
563/* Thread control block */
564static pthread_t tid1;
565static pthread_t tid2;
566/* Mutex lock control block */
567static pthread_mutex_t mutex;
568/* Thread-sharing print function */
569static void printer(char* str)
570{
571 while(*str != 0)
572 {
573 putchar(*str); /* Output one character */
574 str++;
575 sleep(1); /* Sleep 1 second */
576 }
577 printf("\n");
578}
579/* Function return value check */
580static void check_result(char* str,int result)
581{
582 if (0 == result)
583 {
584 printf("%s successfully!\n",str);
585 }
586 else
587 {
588 printf("%s failed! error code is %d\n",str,result);
589 }
590}
591/* Thread entry */
592static void* thread1_entry(void* parameter)
593{
594 char* str = "thread1 hello RT-Thread";
595 while (1)
596 {
597 pthread_mutex_lock(&mutex); /* Mutex lock */
598
599 printer(str); /* Access shared print function */
600
601 pthread_mutex_unlock(&mutex); /* Unlock after access is complete */
602
603 sleep(2); /* Sleep 2 seconds */
604 }
605}
606static void* thread2_entry(void* parameter)
607{
608 char* str = "thread2 hi world";
609 while (1)
610 {
611 pthread_mutex_lock(&mutex); /* The mutex locks */
612
613 printer(str); /* Access shared print function */
614
615 pthread_mutex_unlock(&mutex); /* Unlock after access is complete */
616
617 sleep(2); /* Sleep 2 seconds */
618 }
619}
620/* User application portal */
621int rt_application_init()
622{
623 int result;
624 /* Initialize a mutex */
625 pthread_mutex_init(&mutex,NULL);
626
627 /* Create thread 1, the thread entry is thread1_entry, the attribute parameter is NULL, the default value is selected, and the entry parameter is NULL.*/
628 result = pthread_create(&tid1,NULL,thread1_entry,NULL);
629 check_result("thread1 created",result);
630
631 /* Create thread 2, thread entry is thread2_entry, property parameter is NULL select default value, entry parameter is NULL*/
632 result = pthread_create(&tid2,NULL,thread2_entry,NULL);
633 check_result("thread2 created",result);
634
635 return 0;
636}
637```
638
639# Conditional Variable
640
641A condition variable is actually a semaphore used for synchronization between threads. A condition variable is used to block a thread. When a condition is met, a condition is sent to the blocked thread. The blocking thread is woken up. The condition variable needs to be used with the mutex. The mutex is used to protect the shared data.
642
643Condition variables can be used to inform shared data status. For example, if a thread that processes a shared resource queue finds that the queue is empty, then the thread can only wait until one node is added to the queue. After adding, a conditional variable signal is sent to activate the waiting thread.
644
645The main operations of the condition variable include: calling `pthread_cond_init()` to initialize the condition variable, calling `pthread_cond_destroy()` to destroy a condition variable, calling `pthread_cond_wait()` to wait for a condition variable, and calling `pthread_cond_signal()` to send a condition variable.
646
647## Condition Variable Control Block
648
649Each condition variable corresponds to a condition variable control block, including some information about the operation of the condition variable. A `pthread_cond_t` condition variable control block needs to be defined before initializing a condition variable. `pthread_cond_t` is a redefinition of the `pthread_cond` structure type, defined in the pthread.h header file.
650
651``` c
652struct pthread_cond
653{
654 pthread_condattr_t attr; /* Condition variable attribute */
655 struct rt_semaphore sem; /* RT-Thread semaphore control block */
656};
657typedef struct pthread_cond pthread_cond_t;
658
659Rt_semaphore is a data structure defined in the RT-Thread kernel. It is a semaphore control block defined in the rtdef.h header file.
660
661struct rt_semaphore
662{
663 struct rt_ipc_object parent; /* Inherited from the ipc_object class */
664 rt_uint16_t value; /* Semaphore value */
665};
666```
667
668## Initialization Condition Variable
669
670``` c
671int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
672```
673
674| **Parameter** | **Description** |
675|----|------------------------------------------------|
676| cond | Conditional variable handle, cannot be NULL |
677| attr | Pointer to the condition variable property, if NULL then use the default property value |
678|**return**| —— |
679| 0 | succeeded |
680| EINVAL | Invalid parameter |
681
682This function initializes the `cond` condition variable and sets its properties according to the condition variable property pointed to by `attr` , which is a wrapper of the `rt_sem_init()` function, based on semaphore implementation. The condition variable is not available after successful initialization.
683
684You can also statically initialize a condition variable with the macro PTHREAD_COND_INITIALIZER by: `pthread_cond_t cond = PTHREAD_COND_INITIALIZER` (structural constant), which is equivalent to specifying NULL when calling `pthread_cond_init()`.
685
686Attr General setting NULL use the default value, as described in the thread advanced programming chapter.
687
688## Destroy Condition Variables
689
690``` c
691int pthread_cond_destroy(pthread_cond_t *cond);
692```
693
694| **Parameter** | **Description** |
695|----|------------------------|
696| cond | Conditional variable handle, cannot be NULL |
697|**return**| —— |
698| 0 | Succeeded |
699| EINVAL | Invalid parameter |
700| EPERM | Mutexes are not nested locks, but threads call this function repeatedly |
701| EBUSY | Condition variables are being used |
702
703This function destroys the `cond` condition variable, and the `cond` is uninitialized after destruction. The attribute and control block parameters of the condition variable will not be valid after destruction, but can be reinitialized by calling `pthread_cond_init()` or statically.
704
705Before destroying a condition variable, you need to make sure that no threads are blocked on the condition variable and will not wait to acquire, signal, or broadcast.
706
707## Blocking Mode to Obtain Condition Variables
708
709``` c
710int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
711```
712
713| **Parameter** | **Description** |
714|----------|----------------------------------|
715| cond | Conditional variable handle, cannot be NULL |
716| mutex | Pointer to the mutex control block, cannot be NULL |
717|**return**| —— |
718| 0 | Succeeded |
719| EINVAL | Invalid parameter |
720
721This function gets the `cond` condition variable in blocking mode. The thread needs to lock the mutex before waiting for the condition variable. This function first determines whether the condition variable is available. If it is not available, initializes a condition variable, then unlocks the mutex and then tries to acquire a semaphore when the semaphore's value is greater than zero, it indicates that the semaphore is available, the thread will get the semaphore, and the condition variable will be obtained, and the corresponding semaphore value will be decremented by 1. If the value of the semaphore is equal to zero, indicating that the semaphore is not available, the thread will block until the semaphore is available, after which the mutex will be locked again.
722
723## Specify Blocking Time to Obtain Condition Variables
724
725``` c
726int pthread_cond_timedwait(pthread_cond_t *cond,
727 pthread_mutex_t *mutex,
728 const struct timespec *abstime);
729```
730
731| **Parameter** | **Description** |
732|-------|-------------------------------------------------|
733| cond | Conditional variable handle, cannot be NULL |
734| mutex | Pointer to the mutex control block, cannot be NULL |
735| abstime | The specified wait time in operating system clock tick (OS Tick) |
736|**return**| —— |
737| 0 | Succeeded |
738| EINVAL | Invalid parameter |
739| EPERM | Mutexes are not nested locks, but threads call this function repeatedly |
740| ETIMEDOUT | time out |
741
742The only difference between this function and the `pthread_cond_wait()` function is that if the condition variable is not available, the thread will be blocked for the `abstime` duration. After the timeout, the function will directly return the ETIMEDOUT error code and the thread will be woken up to the ready state.
743
744## Send a Conditional Semaphore
745
746``` c
747int pthread_cond_signal(pthread_cond_t *cond);
748```
749
750| **Parameter** | **Description** |
751|----|------------------------|
752| cond | Conditional variable handle, cannot be NULL |
753|**return**| —— |
754| 0 | Succeeded |
755
756This function sends a signal and wakes up only one thread waiting for the `cond` condition variable, which encapsulates the rt_sem_release() function, which is to send a semaphore. When the value of the semaphore is equal to zero, and a thread waits for this semaphore, it will wake up the first thread waiting in the queue of the semaphore to get the semaphore. Otherwise the value of the semaphore will be increased by 1.
757
758## Broadcast
759
760``` c
761int pthread_cond_broadcast(pthread_cond_t *cond);
762```
763
764| **Parameter** | **Description** |
765|----|------------------------|
766| cond | Conditional variable handle, cannot be NULL |
767|**return**| —— |
768| 0 | Succeeded |
769| EINVAL | Invalid parameter |
770
771Calling this function will wake up all threads waiting for the `cond` condition variable.
772
773## Example Code for Condition Variable
774
775This example is a producer consumer model with a producer thread and a consumer thread that have the same priority. The producer will produce a number every 2 seconds, put it in the list pointed to by the `head`, and then call pthread_cond_signal() to send signal to the consumer thread to inform the consumer that there is data in the thread list. The consumer thread calls pthread_cond_wait() to wait for the producer thread to send a signal.
776
777``` c
778#include <pthread.h>
779#include <unistd.h>
780#include <stdio.h>
781#include <stdlib.h>
782
783/* Statically initialize a mutex and a condition variable */
784static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
785static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
786
787/* Pointer to the thread control block */
788static pthread_t tid1;
789static pthread_t tid2;
790
791/* Function return value check */
792static void check_result(char* str,int result)
793{
794 if (0 == result)
795 {
796 printf("%s successfully!\n",str);
797 }
798 else
799 {
800 printf("%s failed! error code is %d\n",str,result);
801 }
802}
803
804/* The structure data produced by the producer is stored in the linked list. */
805struct node
806{
807 int n_number;
808 struct node* n_next;
809};
810struct node* head = NULL; /* Link header, is a shared resource */
811
812/* Consumer thread entry function */
813static void* consumer(void* parameter)
814{
815 struct node* p_node = NULL;
816
817 pthread_mutex_lock(&mutex); /* Lock on mutex */
818
819 while (1)
820 {
821 while (head == NULL) /* Determine if there are elements in the list */
822 {
823 pthread_cond_wait(&cond,&mutex); /* Try to get a condition variable */
824 }
825 /*
826 Pthread_cond_wait() will unlock the mutex first, then block in the wait queue until the fetch condition variable is awakened. After being woken up, the thread will lock the mutex again and successfully enter the critical section.
827 */
828
829 p_node = head; /* Obtain resources */
830 head = head->n_next; /* Header pointing to the next resource */
831 /* Printout */
832 printf("consume %d\n",p_node->n_number);
833
834 free(p_node); /* Release the memory occupied by the node after obtaining the resource */
835 }
836 pthread_mutex_unlock(&mutex); /* Release the mutex */
837 return 0;
838}
839/* Producer thread entry function */
840static void* product(void* patameter)
841{
842 int count = 0;
843 struct node *p_node;
844
845 while(1)
846 {
847 /* Dynamically allocate a block of structured memory */
848 p_node = (struct node*)malloc(sizeof(struct node));
849 if (p_node != NULL)
850 {
851 p_node->n_number = count++;
852 pthread_mutex_lock(&mutex); /* To operate on the critical resource head, lock it first */
853
854 p_node->n_next = head;
855 head = p_node; /* Insert data into the list header */
856
857 pthread_mutex_unlock(&mutex); /* Unlock */
858 printf("produce %d\n",p_node->n_number);
859
860 pthread_cond_signal(&cond); /* send a Signal to wake up a thread */
861
862 sleep(2); /* Sleep 2 seconds */
863 }
864 else
865 {
866 printf("product malloc node failed!\n");
867 break;
868 }
869 }
870}
871
872int rt_application_init()
873{
874 int result;
875
876 /* Create a producer thread, the property is the default value, the entry function is product, and the entry function parameter is NULL*/
877 result = pthread_create(&tid1,NULL,product,NULL);
878 check_result("product thread created",result);
879
880 /* Create a consumer thread, the property is the default value, the entry function is consumer, and the entry function parameter is NULL */
881 result = pthread_create(&tid2,NULL,consumer,NULL);
882 check_result("consumer thread created",result);
883
884 return 0;
885}
886```
887
888# Read-write Lock
889
890Read-write locks are also known as multi-reader single-writer locks. The read-write lock divides the visitors of the shared resource into readers and writers. The reader only reads and accesses the shared resources, and the writer needs to write the shared resources. Only one thread can occupy the read-write lock of the write mode at the same time, but there can be multiple threads simultaneously occupying the read-write lock of the read mode. Read-write locks are suitable for reading data structures much more often than writes because read patterns can be shared when locked, and write mode locks are exclusive.
891
892Read-write locks are usually implemented based on mutex locks and condition variables. A thread can lock a read-write lock several times, and it must also have the corresponding number of unlocks.
893
894The main operations of the read-write lock include: calling `pthread_rwlock_init()` to initialize a read-write lock, the write thread calling `pthread_rwlock_wrlock()` to lock the read-write lock, and the read thread calling `pthread_rwlock_rdlock()` to lock the read-write lock , when this read-write lock is not required, calling `pthread_rwlock_destroy()` to destroys the read-write lock.
895
896## Read-write Lock Control Block
897
898Each read-write lock corresponds to a read-write lock control block, including some information about the operation of the read-write lock. `pthread_rwlock_t` is a redefinition of the `pthread_rwlock` data structure, defined in the `pthread.h` header file. Before creating a read-write lock, you need to define a data structure of type `pthread_rwlock_t`.
899
900``` c
901struct pthread_rwlock
902{
903 pthread_rwlockattr_t attr; /* Read-write lock attribute */
904 pthread_mutex_t rw_mutex; /* Mutex lock */
905 pthread_cond_t rw_condreaders; /* Conditional variables for the reader thread to use */
906 pthread_cond_t rw_condwriters; /* Conditional variable for the writer thread to use */
907 int rw_nwaitreaders; /* Reader thread waiting to count */
908 int rw_nwaitwriters; /* Writer thread waiting to count */
909 /* Read-write lock value, value 0: unlocked, value -1: is locked by the writer thread, greater than 0 value: locked by the reader thread */
910 int rw_refcount;
911};
912typedef struct pthread_rwlock pthread_rwlock_t; /* Type redefinition */
913```
914
915## Initialize Read-write Lock
916
917``` c
918int pthread_rwlock_init (pthread_rwlock_t *rwlock,
919 const pthread_rwlockattr_t *attr);
920```
921
922| **Parameter** | **Description** |
923|------|-------------------------------------------|
924| rwlock | Read-write lock handle, cannot be NULL |
925| attr | Pointer to the read-write lock property, RT-Thread does not use this variable |
926|**return**| —— |
927| 0 | Succeeded |
928| EINVAL | Invalid parameter |
929
930This function initializes an `rwlock` read-write lock. This function initializes the semaphore and condition variables of the read-write lock control block with default values, and the associated count parameter is initially 0. The read-write lock after initialization is in an unlocked state.
931
932You can also use the macro PTHREAD_RWLOCK_INITIALIZER to statically initialize the read-write lock by: `pthread_rwlock_t mutex = PTHREAD_RWLOCK_INITIALIZER` (structural constant), which is equivalent to specifying `attr` a NULL value when calling pthread_rwlock_init().
933
934`attr` generally sets NULL to the default value, as described in the chapter on advanced threading.
935
936## Destroy Read-write Lock
937
938``` c
939int pthread_rwlock_destroy (pthread_rwlock_t *rwlock);
940```
941
942| **Parameter** | **Description** |
943|------|----------------------|
944| rwlock | Read-write lock handle, cannot be NULL |
945|**return**| —— |
946| 0 | Succeeded |
947| EINVAL | Invalid parameter |
948| EBUSY | The read-write lock is currently being used or has a thread waiting for the read-write lock |
949| EDEADLK | Deadlock |
950
951This function destroys a `rwlock` read-write lock, which destroys the mutex and condition variables in the read-write lock. After the destruction, the properties of the read-write lock and the control block parameters will not be valid, but you can call pthread_rwlock_init() or re-initialize the read-write lock in static mode.
952
953## Read-Lock of Read-Write Lock
954
955### Blocking mode Read-lock the read-write locks
956
957``` c
958int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock);
959```
960
961| **Parameter** | **Description** |
962|------|----------------------|
963| rwlock | Read-write lock handle, cannot be NULL |
964|**return**| —— |
965| 0 | Succeeded |
966| EINVAL | Invalid parameter |
967| EDEADLK | Deadlock |
968
969The reader thread can call this function to read-lock the `rwlock` read-write lock. If the read-write lock is not write-locked and no writer thread is blocked on the read-write lock, the read-write thread will successfully acquire the read-write lock. If the read-write lock has been write-locked, the reader thread will block until the thread that executes the write-lock unlocks the read-write lock.
970
971### Non-blocking Mode Read-lock Read-write Locks
972
973``` c
974int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock);
975```
976
977| **Parameter** | **Description** |
978|------|----------------------|
979| rwlock | Read-write lock handle, cannot be NULL |
980|**return**| —— |
981| 0 | Succeeded |
982| EINVAL | Invalid parameter |
983| EBUSY | The read-write lock is currently being used or has a thread waiting for the read-write lock |
984| EDEADLK | Deadlock |
985
986This function differs from the pthread_rwlock_rdlock() function in that if the read-write lock is already write-locked, the reader thread is not blocked, but instead returns an error code EBUSY.
987
988### Specify Blocking Time for the Read-write Lock to be Read-Locked
989
990``` c
991int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,
992 const struct timespec *abstime);
993```
994
995| **Parameter** | **Description** |
996|-------|-------------------------------------------------|
997| rwlock | Read-write lock handle, cannot be NULL |
998| abstime | The specified wait time in operating system clock tick (OS Tick) |
999|**return**| —— |
1000| 0 | Succeeded |
1001| EINVAL | Invalid parameter |
1002| ETIMEDOUT | Time out |
1003| EDEADLK | Deadlock |
1004
1005The difference between this function and the pthread_rwlock_rdlock() function is that if the read-write lock has been write-locked, the reader thread will block the specified abstime duration. After the timeout, the function will return the error code ETIMEDOUT and the thread will be woken up to the ready state.
1006
1007## Write-Lock of Read-Write Lock
1008
1009### Blocking Mode Write-Locks a Read-write Lock
1010
1011``` c
1012int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock);
1013```
1014
1015| **Parameter** | **Description** |
1016|------|----------------------|
1017| rwlock | Read-write lock handle, cannot be NULL |
1018|**return**| —— |
1019| 0 | Succeeded |
1020| EINVAL | Invalid parameter |
1021| EDEADLK | Deadlock |
1022
1023The writer thread calls this function to write-lock the `rwlock` read-write lock. A write-lock read-write lock is similar to a mutex, and only one thread can write-lock a read-write lock at a time. If no thread locks the read-write lock, that is, the read-write lock value is 0, the writer thread that calls this function will write-lock the read-write lock, and other threads cannot acquire the read-write lock at this time. If there is already a thread locked the read-write lock, ie the read/write lock value is not 0, then the writer thread will be blocked until the read-write lock is unlocked.
1024
1025### Non-blocking Mode Write-Lock a Read-write Lock
1026
1027``` c
1028int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock);
1029```
1030
1031| **Parameter** | **Description** |
1032|------|----------------------|
1033| rwlock | Read-write lock handle, cannot be NULL |
1034|**return**| —— |
1035| 0 | Succeeded |
1036| EINVAL | Invalid parameter |
1037| EBUSY | The read-write lock is currently Write-Locked or there are reader threads blocked on the read-write lock |
1038| EDEADLK | Deadlock |
1039
1040The only difference between this function and the pthread_rwlock_wrlock() function is that if a thread has locked the read-write lock, ie the read-write lock value is not 0, the writer thread that called the function will directly return an error code, and the thread will not be Blocked.
1041
1042### Specify Blocking Time for the Read-write Lock to be Write-Lock
1043
1044``` c
1045int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,
1046 const struct timespec *abstime);
1047```
1048
1049| **Parameter** | **Description** |
1050|--------------|---------------------|
1051| rwlock abstime | Read-write lock handle, cannot specify the wait time for NULL, the unit is the operating system clock beat (OS Tick) |
1052|**return**| —— |
1053| 0 | Succeeded |
1054| EINVAL | Invalid parameter |
1055| ETIMEDOUT | Time out |
1056| EDEADLK | Deadlock |
1057
1058The only difference between this function and the pthread_rwlock_wrlock() function is that if a thread locks the read-write lock, that is, the read-write lock value is not 0, the calling thread blocks the specified `abstime` duration. After the timeout, the function returns the error code ETIMEDOUT, and the thread will be woken up to the ready state.
1059
1060## Unlock the Read-write Lock
1061
1062``` c
1063int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);
1064```
1065
1066| **Parameter** | **Description** |
1067|------|----------------------|
1068| rwlock | Read-write lock handle, cannot be NULL |
1069|**return**| —— |
1070| 0 | Succeeded |
1071| EINVAL | Invalid parameter |
1072| EDEADLK | Deadlock |
1073
1074This function unlocks the `rwlock` read-write lock. A thread locks the same read-write lock multiple times and must have the same number of unlocks. If multiple threads wait for the read-write lock to lock after unlocking, the system will activate the waiting thread according to the first-in-first-out rule.
1075
1076## Example Code for Read-write Lock
1077
1078This program has two reader threads, one reader thread. The two reader threads read-lock the read-write lock first, then sleep for 2 seconds. This time the other reader threads can read-lock the read-write lock, and then read the shared data.
1079
1080``` c
1081#include <pthread.h>
1082#include <sched.h>
1083#include <stdio.h>
1084
1085/* Thread control block */
1086static pthread_t reader1;
1087static pthread_t reader2;
1088static pthread_t writer1;
1089/* Shared data book */
1090static int book = 0;
1091/* Read-write lock */
1092static pthread_rwlock_t rwlock;
1093/* Function result check */
1094static void check_result(char* str,int result)
1095{
1096 if (0 == result)
1097 {
1098 printf("%s successfully!\n",str);
1099 }
1100 else
1101 {
1102 printf("%s failed! error code is %d\n",str,result);
1103 }
1104}
1105/* Thread entry */
1106static void* reader1_entry(void* parameter)
1107{
1108 while (1)
1109 {
1110
1111 pthread_rwlock_rdlock(&rwlock); /* Try to read-lock the read-write lock */
1112
1113 printf("reader1 read book value is %d\n",book);
1114 sleep(2); /* The thread sleeps for 2 seconds, switching to another thread to run */
1115
1116 pthread_rwlock_unlock(&rwlock); /* Unlock the read-write lock after the thread runs */
1117 }
1118}
1119static void* reader2_entry(void* parameter)
1120{
1121 while (1)
1122 {
1123 pthread_rwlock_rdlock(&rwlock); /* Try to read-lock the read-write lock */
1124
1125 printf("reader2 read book value is %d\n",book);
1126 sleep(2); /* The thread sleeps for 2 seconds, switching to another thread to run */
1127
1128 pthread_rwlock_unlock(&rwlock); /* Unlock the read-write lock after the thread runs */
1129 }
1130}
1131static void* writer1_entry(void* parameter)
1132{
1133 while (1)
1134 {
1135 pthread_rwlock_wrlock(&rwlock); /* Try to write-lock the read-write lock */
1136
1137 book++;
1138 printf("writer1 write book value is %d\n",book);
1139
1140 pthread_rwlock_unlock(&rwlock); /* Unlock the read-write lock */
1141
1142 sleep(2); /* The thread sleeps for 2 seconds, switching to another thread to run */
1143 }
1144}
1145/* User application portal */
1146int rt_application_init()
1147{
1148 int result;
1149 /* Default property initializes read-write lock */
1150 pthread_rwlock_init(&rwlock,NULL);
1151
1152 /* Create a reader1 thread, the thread entry is reader1_entry, the thread attribute is the default value, and the entry parameter is NULL*/
1153 result = pthread_create(&reader1,NULL,reader1_entry,NULL);
1154 check_result("reader1 created",result);
1155
1156 /* Create a reader2 thread, the thread entry is reader2_entry, the thread attribute is the default value, and the entry parameter is NULL*/
1157 result = pthread_create(&reader2,NULL,reader2_entry,NULL);
1158 check_result("reader2 created",result);
1159
1160 /* Create a writer1 thread, the thread entry is writer1_entry, the thread attribute is, and the entry parameter is NULL*/
1161 result = pthread_create(&writer1,NULL,writer1_entry,NULL);
1162 check_result("writer1 created",result);
1163
1164 return 0;
1165}
1166```
1167
1168# Barrier
1169
1170Barriers are a way to synchronize multithreading. Barrier means a barrier or railing that blocks multiple threads that arrive in the same railing until all threads arrived, then remove the railings and let them go at the same time. The thread that arrives first will block, and when all the threads that call the pthread_barrier_wait() function (the number equal to the count specified by the barrier initialization) arrive, the threads will enter the ready state from the blocked state and participate in the system scheduling again.
1171
1172Barriers are implemented based on condition variables and mutex locks. The main operations include: calling `pthread_barrier_init()` to initialize a barrier, and other threads calling `pthread_barrier_wait()`. After all threads arrived, the thread wakes up to the ready state. Destroy a barrier by calling pthread_barrier_destroy() when the barrier will not be used.
1173
1174## Barrier Control Block
1175
1176Before creating a barrier, you need to define a `pthread_barrier_t` barrier control block. `pthread_barrier_t` is a redefinition of the `pthread_barrier` structure type, defined in the pthread.h header file.
1177
1178``` c
1179struct pthread_barrier
1180{
1181 int count; /* The number of waiting threads specified */
1182 pthread_cond_t cond; /* Conditional variable */
1183 pthread_mutex_t mutex; /* Mutex lock */
1184};
1185typedef struct pthread_barrier pthread_barrier_t;
1186```
1187
1188## Create a Barrier
1189
1190``` c
1191int pthread_barrier_init(pthread_barrier_t *barrier,
1192 const pthread_barrierattr_t *attr,
1193 unsigned count);
1194```
1195
1196| **Parameter** | **Description** |
1197|-------|-------------------------------|
1198| attr | Pointer to the barrier property, if passing NULL, use the default value. PTHREAD_PROCESS_PRIVATE must be used as a non-NULL value. |
1199| barrier | Barrier handle |
1200| count | The number of waiting threads specified |
1201|**return**| —— |
1202| 0 | Succeeded |
1203| EINVAL | Invalid parameter |
1204
1205This function creates a `barrier` barrier and initializes the conditional variables and mutex locks of the barrier control block according to the default parameters. The number of waiting threads specified after initialization is `count`, and pthread_barrier_wait() must be called for `count` threads.
1206
1207attr generally sets NULL to the default value, as described in the chapter on *thread advanced programming*.
1208
1209## Destruction of Barrier
1210
1211``` c
1212int pthread_barrier_destroy(pthread_barrier_t *barrier);
1213```
1214
1215| **Parameter** | **Description** |
1216|-------|--------|
1217| barrier | Barrier handle |
1218|**return**| —— |
1219| 0 | Succeeded |
1220| EINVAL | Invalid parameter |
1221
1222This function destroys a barrier. The barrier's properties and control block parameters will not be valid after destruction, but can be reinitialized by calling pthread_barrier_init().
1223
1224## Wait for Barrier
1225
1226``` c
1227int pthread_barrier_wait(pthread_barrier_t *barrier);
1228```
1229
1230| **Parameter** | **Description** |
1231|-------|--------|
1232| barrier | Barrier handle |
1233|**return**| —— |
1234| 0 | Succeeded |
1235| EINVAL | Invalid parameter |
1236
1237This function synchronizes the threads waiting in front of the barrier and called by each thread. If the number of queue waiting threads is not 0, count will be decremented by 1. If the count is 0, indicating that all threads have reached the railing. All arriving threads will be woken up and re-entered into the ready state to participate in system scheduling. If count is not 0 after the decrease, it indicates that there is still threads that do not reach the barrier, and the calling thread will block until all threads reach the barrier.
1238
1239## Example Code for Barrier
1240
1241This program will create 3 threads, initialize a barrier, and the barrier waits for 3 threads. 3 threads will call pthread_barrier_wait() to wait in front of the barrier. When all 3 threads are arrived, 3 threads will enter the ready state. The output count information is printed every 2 seconds.
1242
1243``` c
1244#include <pthread.h>
1245#include <unistd.h>
1246#include <stdio.h>
1247
1248/* Thread control block */
1249static pthread_t tid1;
1250static pthread_t tid2;
1251static pthread_t tid3;
1252/* Barrier control block */
1253static pthread_barrier_t barrier;
1254/* Function return value check function */
1255static void check_result(char* str,int result)
1256{
1257 if (0 == result)
1258 {
1259 printf("%s successfully!\n",str);
1260 }
1261 else
1262 {
1263 printf("%s failed! error code is %d\n",str,result);
1264 }
1265}
1266/* Thread 1 entry function */
1267static void* thread1_entry(void* parameter)
1268{
1269 int count = 0;
1270
1271 printf("thread1 have arrived the barrier!\n");
1272 pthread_barrier_wait(&barrier); /* Reach the barrier and wait for other threads to arrive */
1273
1274 while (1)
1275 {
1276 /* Print thread count value output */
1277 printf("thread1 count: %d\n",count ++);
1278
1279 /* Sleep 2 seconds */
1280 sleep(2);
1281 }
1282}
1283/* Thread 2 entry function */
1284static void* thread2_entry(void* parameter)
1285{
1286 int count = 0;
1287
1288 printf("thread2 have arrived the barrier!\n");
1289 pthread_barrier_wait(&barrier);
1290
1291 while (1)
1292 {
1293 /* Print thread count value */
1294 printf("thread2 count: %d\n",count ++);
1295
1296 /* Sleep 2 seconds */
1297 sleep(2);
1298 }
1299}
1300/* Thread 3 entry function */
1301static void* thread3_entry(void* parameter)
1302{
1303 int count = 0;
1304
1305 printf("thread3 have arrived the barrier!\n");
1306 pthread_barrier_wait(&barrier);
1307
1308 while (1)
1309 {
1310 /* Print thread count value */
1311 printf("thread3 count: %d\n",count ++);
1312
1313 /* Sleep 2 seconds */
1314 sleep(2);
1315 }
1316}
1317/* User application portal */
1318int rt_application_init()
1319{
1320 int result;
1321 pthread_barrier_init(&barrier,NULL,3);
1322
1323 /* Create thread 1, thread entry is thread1_entry, property parameter is set to NULL, select default value, entry parameter is NULL*/
1324 result = pthread_create(&tid1,NULL,thread1_entry,NULL);
1325 check_result("thread1 created",result);
1326
1327 /* Create thread 2, thread entry is thread2_entry, property parameter is set to NULL, select default value, entry parameter is NULL*/
1328 result = pthread_create(&tid2,NULL,thread2_entry,NULL);
1329 check_result("thread2 created",result);
1330
1331 /* Create thread 3, thread entry is thread3_entry, property parameter is set to NULL Select default value, entry parameter is NULL*/
1332 result = pthread_create(&tid3,NULL,thread3_entry,NULL);
1333 check_result("thread3 created",result);
1334
1335}
1336```
1337
1338# Semaphore
1339
1340Semaphores can be used for communication between processes and processes, or between in-process threads. Each semaphore has a semaphore value that is not less than 0, corresponding to the available amount of semaphore. Call sem_init() or sem_open() to assign an initial value to the semaphore . Call sem_post() to increment the semaphore value by 1. Call sem_wait() to decrement the semaphore value by 1. If the current semaphore is 0, call sem_wait(), the thread will suspended on the wait queue for this semaphore until the semaphore value is greater than 0 and is available.
1341
1342Depending on the value of the semaphore (representing the number of available resources), POSIX semaphores can be divided into:
1343
1344- **Binary semaphore**: The value of the semaphore is only 0 and 1, and the initial value is specified as 1. This is the same as a mutex. If the resource is locked, the semaphore value is 0. If the resource is available, the semaphore value is 1. Equivalent to only one key, after the thread gets the key, after completing the access to the shared resource, you need to unlock it, put the key back, and use it for other threads that need this key. The method is the same as the mutex lock. The wait semaphore function must be used in pairs with the send semaphore function. It cannot be used alone.
1345
1346- **Count semaphore**: The value of the semaphore ranges from 0 to a limit greater than 1 (POSIX indicates that the system's maximum limit is at least 32767). This count indicates the number of available semaphores. At this point, the send semaphore function can be called separately to send the semaphore, which is equivalent to having more than one key, the thread takes a key and consumes one, and the used key does not have to be put back.
1347
1348POSIX semaphores are also divided into named semaphores and unnamed semaphores:
1349
1350- A named semaphore: its value is stored in a file and is generally used for inter-process synchronization or mutual exclusion.
1351
1352- Unnamed semaphore: Its value is stored in memory and is generally used for inter-thread synchronization or mutual exclusion.
1353
1354The POSIX semaphore of the RT-Thread operating system is mainly based on a package of RT-Thread kernel semaphores, mainly used for communication between threads in the system. It is used in the same way as the semaphore of the RT-Thread kernel.
1355
1356## Semaphore Control Block
1357
1358Each semaphore corresponds to a semaphore control block. Before creating a semaphore, you need to define a sem_t semaphore control block. Sem_t is a redefinition of the posix_sem structure type, defined in the semaphore.h header file.
1359
1360``` c
1361struct posix_sem
1362{
1363 rt_uint16_t refcount;
1364 rt_uint8_t unlinked;
1365 rt_uint8_t unamed;
1366 rt_sem_t sem; /* RT-Thread semaphore */
1367 struct posix_sem* next; /* Point to the next semaphore control block */
1368};
1369typedef struct posix_sem sem_t;
1370
1371Rt_sem_t is the RT-Thread semaphore control block, defined in the rtdef.h header file.
1372
1373struct rt_semaphore
1374{
1375 struct rt_ipc_object parent;/* Inherited from the ipc_object class */
1376 rt_uint16_t value; /* Semaphore value */
1377};
1378/* rt_sem_t is a pointer type to the semaphore structure */
1379typedef struct rt_semaphore* rt_sem_t;
1380
1381```
1382
1383## Unnamed semaphore
1384
1385The value of an unnamed semaphore is stored in memory and is generally used for inter-thread synchronization or mutual exclusion. Before using it, you must first call sem_init() to initialize it.
1386
1387### Initialize the unnamed semaphore
1388
1389``` c
1390int sem_init(sem_t *sem, int pshared, unsigned int value);
1391```
1392
1393| **Parameter** | **Description** |
1394|-------|--------------------------------------|
1395| sem | Semaphore handle |
1396| value | The initial value of the semaphore, indicating the available amount of semaphore resources |
1397| pshared | RT-Thread unimplemented parameters |
1398|**return**| —— |
1399| 0 | Succeeded |
1400| -1 | Failed |
1401
1402This function initializes an unnamed semaphore sem, initializes the semaphore related data structure according to the given or default parameters, and puts the semaphore into the semaphore list. The semaphore value after initialization is the given initial value. This function is a wrapper of the rt_sem_create() function.
1403
1404### Destroy Unnamed Semaphore
1405
1406``` c
1407int sem_destroy(sem_t *sem);
1408```
1409
1410| **Parameter** | **Description** |
1411|----|----------|
1412| sem | Semaphore handle |
1413|**return**| —— |
1414| 0 | Succeeded |
1415| -1 | Failed |
1416
1417This function destroys an unnamed semaphore sem and releases the resources occupied by the semaphore.
1418
1419## Named Semaphore
1420
1421A named semaphore whose value is stored in a file and is generally used for inter-process synchronization or mutual exclusion. Two processes can operate on named semaphores of the same name. The well-known semaphore implementation in the RT-Thread operating system is similar to the unnamed semaphore. It is designed for communication between threads and is similar in usage.
1422
1423### Create or Open a Named Semaphore
1424
1425``` c
1426sem_t *sem_open(const char *name, int oflag, ...);
1427```
1428
1429| **Parameter** | **Description** |
1430|----------|----------------|
1431| name | Semaphore name |
1432| oflag | The way the semaphore is opened |
1433|**return**| —— |
1434| Semaphore handle | Succeeded |
1435| NULL | Failed |
1436
1437This function creates a new semaphore based on the semaphore name or opens an existing semaphore. The optional values for Oflag are `0`, `O_CREAT` or `O_CREAT|O_EXCL`. If Oflag is set to `O_CREAT` , a new semaphore is created. If Oflag sets to `O_CREAT|O_EXCL`, it returns NULL if the semaphore already exists, and creates a new semaphore if it does not exist. If Oflag is set to 0, a semaphore does not exist and NULL is returned.
1438
1439### Detach the Named Semaphore
1440
1441``` c
1442int sem_unlink(const char *name);
1443```
1444
1445| **Parameter** | **Description** |
1446|----|----------|
1447| name | Semaphore name |
1448|**return**| —— |
1449| 0 | Succeeded |
1450| -1 | Failed, semaphore does not exist |
1451
1452This function looks up the semaphore based on the semaphore name, and marks the semaphore as a detached state if the semaphore is present. Then check the reference count. If the value is 0, the semaphore is deleted immediately. If the value is not 0, it will not be deleted until all threads holding the semaphore close the semaphore.
1453
1454### Close the Named Semaphore
1455
1456``` c
1457int sem_close(sem_t *sem);
1458```
1459
1460| **Parameter** | **Description** |
1461|----|----------|
1462| sem | Semaphore handle |
1463|**return**| —— |
1464| 0 | Succeeded |
1465| -1 | Failed |
1466
1467When a thread terminates,it closes the semaphore it occupies. Whether the thread terminates voluntarily or involuntarily, this closing operation is performed. This is equivalent to a reduction of 1 in the number of semaphores held. If the holding count is zero after subtracting 1 and the semaphore is in separated state, the `sem` semaphore will be deleted and the resources it occupies will be released.
1468
1469## Obtain Semaphore Value
1470
1471``` c
1472int sem_getvalue(sem_t *sem, int *sval);
1473```
1474
1475| **Parameter** | **Description** |
1476|----|---------------------------------|
1477| sem | Semaphore handle, cannot be NULL |
1478| sval | Save the obtained semaphore value address, cannot be NULL |
1479|**return**| —— |
1480| 0 | Succeeded |
1481| -1 | Failed |
1482
1483This function obtains the value of the semaphore and saves it in the memory pointed to by `sval` to know the amount of semaphore resources.
1484
1485## Blocking Mode to Wait Semaphore
1486
1487``` c
1488int sem_wait(sem_t *sem);
1489```
1490
1491| **Parameter** | **Description** |
1492|----|----------------------|
1493| sem | Semaphore handle, cannot be NULL |
1494|**return**| —— |
1495| 0 | Succeeded |
1496| -1 | Failed |
1497
1498The thread calls this function to get the semaphore, which is a wrapper of the `rt_sem_take(sem,RT_WAITING_FOREVER)` function. If the semaphore value is greater than zero, the semaphore is available, the thread gets the semaphore, and the semaphore value is decremented by one. If the semaphore value is equal to 0, indicating that the semaphore is not available, the thread is blocked and entering the suspended state and queued in a first-in, first-out manner until the semaphore is available.
1499
1500## Non-blocking Mode to Wait Semaphore
1501
1502``` c
1503int sem_trywait(sem_t *sem);
1504```
1505
1506| **Parameter** | **Description** |
1507|----|----------------------|
1508| sem | Semaphore handle, cannot be NULL |
1509|**return**| —— |
1510| 0 | Succeeded |
1511| -1 | Failed |
1512
1513This function is a non-blocking version of the sem_wait() function and is a wrapper of the `rt_sem_take(sem,0)` function. When the semaphore is not available, the thread does not block, but returns directly.
1514
1515## Specify the Blocking Time Waiting for the Semaphore
1516
1517``` c
1518int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
1519```
1520
1521| **Parameter** | **Description** |
1522|------------|-------------------------------------------------|
1523| sem | Semaphore handle, cannot be NULL |
1524| abs_timeout | The specified wait time in operating system clock tick (OS Tick) |
1525|**return**| —— |
1526| 0 | Succeeded |
1527| -1 | Failed |
1528
1529The difference between this function and `the sem_wait()` function is that if the semaphore is not available, the thread will block the duration of `abs_timeout`. After the timeout, the function returns -1, and the thread will be awakened from the blocking state to the ready state.
1530
1531## Send Semaphore
1532
1533``` c
1534int sem_post(sem_t *sem);
1535```
1536
1537| **Parameter** | **Description** |
1538|----|----------------------|
1539| sem | Semaphore handle, cannot be NULL |
1540|**return**| —— |
1541| 0 | Succeeded |
1542| -1 | Failed |
1543
1544This function will release a sem semaphore, which is a wrapper of the rt_sem_release() function. If the thread queue waiting for the semaphore is not empty, indicating that there are threads waiting for the semaphore, the first thread waiting for the semaphore will switch from the suspended state to the ready state, waiting for system scheduling. If no thread is waiting for the semaphore, the semaphore value will be incremented by one.
1545
1546## Example Code for Unnamed Semaphore
1547
1548A typical case of semaphore usage is the producer consumer model. A producer thread and a consumer thread operate on the same block of memory, the producer fills the shared memory, and the consumer reads the data from the shared memory.
1549
1550This program creates 2 threads, 2 semaphores, one semaphore indicates that the shared data is empty, one semaphore indicates that the shared data is not empty, and a mutex is used to protect the shared resource. After the producer thread produces the data, it will send a `full_sem` semaphore to the consumer, informing the consumer that the thread has data available, and waiting for the `empty_sem` semaphore sent by the consumer thread after 2 seconds of sleep. The consumer thread processes the shared data after the `full_sem` sent by the producer, and sends an `empty_sem` semaphore to the producer thread after processing. The program will continue to loop like this.
1551
1552``` c
1553#include <pthread.h>
1554#include <unistd.h>
1555#include <stdio.h>
1556#include <stdlib.h>
1557#include <semaphore.h>
1558
1559/* Statically initialize a mutex to protect shared resources */
1560static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1561/* 2 semaphore control blocks, one for resource empty signals and one for resource full signals */
1562static sem_t empty_sem,full_sem;
1563
1564/* Pointer to the thread control block */
1565static pthread_t tid1;
1566static pthread_t tid2;
1567
1568/* Function return value check */
1569static void check_result(char* str,int result)
1570{
1571 if (0 == result)
1572 {
1573 printf("%s successfully!\n",str);
1574 }
1575 else
1576 {
1577 printf("%s failed! error code is %d\n",str,result);
1578 }
1579}
1580
1581/* The structure data produced by the producer is stored in the linked list. */
1582struct node
1583{
1584 int n_number;
1585 struct node* n_next;
1586};
1587struct node* head = NULL; /* Link header, a shared resource */
1588
1589/* Consumer thread entry function */
1590static void* consumer(void* parameter)
1591{
1592 struct node* p_node = NULL;
1593
1594 while (1)
1595 {
1596 sem_wait(&full_sem);
1597 pthread_mutex_lock(&mutex); /* Lock mutex */
1598
1599 while (head != NULL) /* Determine if there are elements in the list */
1600 {
1601 p_node = head; /* Obtain resources */
1602 head = head->n_next; /* Header pointing to the next resource */
1603 /* Print */
1604 printf("consume %d\n",p_node->n_number);
1605
1606 free(p_node); /* Release the memory occupied by the node after getting the resource */
1607 }
1608
1609 pthread_mutex_unlock(&mutex); /* The critical section data operation is completed, and the mutex is released. */
1610
1611 sem_post(&empty_sem); /* Send a null semaphore to the producer */
1612 }
1613}
1614/* Producer thread entry function */
1615static void* product(void* patameter)
1616{
1617 int count = 0;
1618 struct node *p_node;
1619
1620 while(1)
1621 {
1622 /* Dynamically allocate a block of structured memory */
1623 p_node = (struct node*)malloc(sizeof(struct node));
1624 if (p_node != NULL)
1625 {
1626 p_node->n_number = count++;
1627 pthread_mutex_lock(&mutex); /* To operate on the critical resource head, lock it first */
1628
1629 p_node->n_next = head;
1630 head = p_node; /* Insert data into the list header */
1631
1632 pthread_mutex_unlock(&mutex); /* Unlock */
1633 printf("produce %d\n",p_node->n_number);
1634
1635 sem_post(&full_sem); /* Send a full semaphore to the consumer */
1636 }
1637 else
1638 {
1639 printf("product malloc node failed!\n");
1640 break;
1641 }
1642 sleep(2); /* Sleep 2 seconds */
1643 sem_wait(&empty_sem); /* Wait for consumers to send empty semapho */
1644 }
1645}
1646
1647int rt_application_init()
1648{
1649 int result;
1650
1651 sem_init(&empty_sem,NULL,0);
1652 sem_init(&full_sem,NULL,0);
1653 /* Create a producer thread, the property is the default value, the entry function is product, and the entry function parameter is NULL*/
1654 result = pthread_create(&tid1,NULL,product,NULL);
1655 check_result("product thread created",result);
1656
1657 /* Create a consumer thread, the property is the default value, the entry function is consumer, and the entry function parameter is NULL */
1658 result = pthread_create(&tid2,NULL,consumer,NULL);
1659 check_result("consumer thread created",result);
1660
1661 return 0;
1662}
1663```
1664
1665# Message Queue
1666
1667Message Queuing is another commonly used inter-thread communication method that accepts messages of unfixed length from threads or interrupt service routines and caches the messages in their own memory space. Other threads can also read the corresponding message from the message queue, and when the message queue is empty, the reader thread can be suspended. When a new message arrives, the suspended thread will be woken up to receive and process the message.
1668
1669The main operations of the message queue include: creating or opening by the function `mq_open()`, calling `mq_send()` to send a message to the message queue, calling `mq_receive()` to get a message from the message queue, and when the message queue is not in use, you can call `mq_unlink()` to delete message queue.
1670
1671POSIX message queue is mainly used for inter-process communication. The POSIX message queue of RT-Thread operating system is mainly based on a package of RT-Thread kernel message queue, mainly used for communication between threads in the system. It is used in the same way as the message queue of the RT-Thread kernel.
1672
1673## Message Queue Control Block
1674
1675Each message queue corresponds to a message queue control block. Before creating a message queue, you need to define a message queue control block. The message queue control block is defined in the mqueue.h header file.
1676
1677``` c
1678struct mqdes
1679{
1680 rt_uint16_t refcount; /* Reference count */
1681 rt_uint16_t unlinked; /* Separation status of the message queue, a value of 1 indicates that the message queue has been separated */
1682 rt_mq_t mq; /* RT-Thread message queue control block */
1683 struct mqdes* next; /* Point to the next message queue control block */
1684};
1685typedef struct mqdes* mqd_t; /* Message queue control block pointer type redefinition */
1686```
1687
1688## Create or Open a Message Queue
1689
1690``` c
1691mqd_t mq_open(const char *name, int oflag, ...);
1692```
1693
1694| **Parameter** | **Description** |
1695|----------|----------------|
1696| name | Message queue name |
1697| oflag | Message queue open mode |
1698|**return**| —— |
1699| Message queue handle | Succeeded |
1700| NULL | Failed |
1701
1702This function creates a new message queue or opens an existing message queue based on the name of the message queue. The optional values for Oflag are `0`, `O_CREAT` or `O_CREAT\|O_EXCL`. If Oflag is set to `O_CREAT` then a new message queue is created. If Oflag sets `O_CREAT\|O_EXCL`, it returns NULL if the message queue already exists, and creates a new message queue if it does not exist. If Oflag is set to `0`, the message queue does not exist and returns NULL.
1703
1704## Detach Message Queue
1705
1706``` c
1707int mq_unlink(const char *name);
1708```
1709
1710| **Parameter** | **Description** |
1711|----|------------|
1712| name | Message queue name |
1713|**return**| —— |
1714| 0 | Succeeded |
1715| -1 | Failed |
1716
1717This function finds the message queue based on the message queue name name. If found, it sets the message queue to a detached state. If the hold count is 0, the message queue is deleted and the resources occupied by the message queue are released.
1718
1719## Close the Message Queue
1720
1721``` c
1722int mq_close(mqd_t mqdes);
1723```
1724
1725| **Parameter** | **Description** |
1726|----------|------------|
1727| mqdes | Message queue handle |
1728|**return**| —— |
1729| 0 | Succeeded |
1730| -1 | Failed |
1731
1732When a thread terminates,it closes the message queue it occupies. Whether the thread terminates voluntarily or involuntarily, this closure is performed. This is equivalent to the message queue holding count minus 1. If the holding count is 0 after the minus 1 and the message queue is in the separated state, the `mqdes` message queue will be deleted and released the resources it occupies.
1733
1734## Block Mode to Send a Message
1735
1736``` c
1737int mq_send(mqd_t mqdes,
1738 const char *msg_ptr,
1739 size_t msg_len,
1740 unsigned msg_prio);
1741```
1742
1743| **Parameter** | **Description** |
1744|---------|----------------------------------|
1745| mqdes | Message queue handle, cannot be NULL |
1746| sg_ptr | Pointer to the message to be sent, cannot be NULL |
1747| msg_len | The length of the message sent |
1748| msg_prio | RT-Thread unimplemented this parameter |
1749|**return**| —— |
1750| 0 | Succeeded |
1751| -1 | Failed |
1752
1753This function is used to send a message to the `mqdes` message queue, which is a wrapper of the rt_mq_send() function. This function adds the message pointed to by `msg_ptr` to the `mqdes` message queue, and the length of the message sent `msg_len` must be less than or equal to the maximum message length set when the message queue is created.
1754
1755If the message queue is full, that is, the number of messages in the message queue is equal to the maximum number of messages, the thread that sent the message or the interrupt program will receive an error code (-RT_EFULL).
1756
1757## Specify Blocking Time to Send a Message
1758
1759``` c
1760int mq_timedsend(mqd_t mqdes,
1761 const char *msg_ptr,
1762 size_t msg_len,
1763 unsigned msg_prio,
1764 const struct timespec *abs_timeout);
1765```
1766
1767| **Parameter** | **Description** |
1768|------------|-------------------------------------------------|
1769| mqdes | Message queue handle, cannot be NULL |
1770| msg_ptr | Pointer to the message to be sent, cannot be NULL |
1771| msg_len | The length of the message sent |
1772| msg_prio | RT-Thread unimplemented parameters |
1773| abs_timeout | The specified wait time in operating system clock tick (OS Tick) |
1774|**Parameter**| —— |
1775| 0 | Succeeded |
1776| -1 | Failed |
1777
1778Currently RT-Thread does not support sending messages with the specified blocking time, but the function interface has been implemented, which is equivalent to calling mq_send().
1779
1780## Blocking Mode to Receive Message
1781
1782``` c
1783ssize_t mq_receive(mqd_t mqdes,
1784 char *msg_ptr,
1785 size_t msg_len,
1786 unsigned *msg_prio);
1787```
1788
1789| **Parameter** | **Description** |
1790|---------|----------------------------------|
1791| mqdes | Message queue handle, cannot be NULL |
1792| msg_ptr | Pointer to the message to be sent, cannot be NULL |
1793| msg_len | The length of the message sent |
1794| msg_prio | RT-Thread unimplemented parameters |
1795|**return**| —— |
1796| Message length | Succeeded |
1797| -1 | Failed |
1798
1799This function removes the oldest message from the `mqdes` message queue and puts the message in the memory pointed to by `msg_ptr`. If the message queue is empty, the thread that called the mq_receive() function will block until the message in the message queue is available.
1800
1801## Specify Blocking Time to Receive Message
1802
1803``` c
1804ssize_t mq_timedreceive(mqd_t mqdes,
1805 char *msg_ptr,
1806 size_t msg_len,
1807 unsigned *msg_prio,
1808 const struct timespec *abs_timeout);
1809```
1810
1811| **Parameter** | **Description** |
1812|------------|-------------------------------------------------|
1813| mqdes | Message queue handle, cannot be NULL |
1814| msg_ptr | Pointer to the message to be sent, cannot be NULL |
1815| msg_len | The length of the message sent |
1816| msg_prio | RT-Thread unimplemented parameters |
1817| abs_timeout | The specified wait time in operating system clock tick (OS Tick) |
1818|**return**| —— |
1819| Message length | Succeeded |
1820| -1 | Failed |
1821
1822The difference between this function and the mq_receive() function is that if the message queue is empty, the thread will block the `abs_timeout` duration. After the timeout, the function will return `-1`, and the thread will be awakened from the blocking state to the ready state.
1823
1824## Example Code for Message Queue
1825
1826This program creates 3 threads, thread2 accepts messages from the message queue, and thread2 and thread3 send messages to the message queue.
1827
1828``` c
1829#include <mqueue.h>
1830#include <stdio.h>
1831
1832/* Thread control block */
1833static pthread_t tid1;
1834static pthread_t tid2;
1835static pthread_t tid3;
1836/* Message queue handle */
1837static mqd_t mqueue;
1838
1839/* Function return value check function */
1840static void check_result(char* str,int result)
1841{
1842 if (0 == result)
1843 {
1844 printf("%s successfully!\n",str);
1845 }
1846 else
1847 {
1848 printf("%s failed! error code is %d\n",str,result);
1849 }
1850}
1851/* Thread 1 entry function */
1852static void* thread1_entry(void* parameter)
1853{
1854 char buf[128];
1855 int result;
1856
1857 while (1)
1858 {
1859 /* Receive messages from the message queue */
1860 result = mq_receive(mqueue, &buf[0], sizeof(buf), 0);
1861 if (result != -1)
1862 {
1863 /* Output content */
1864 printf("thread1 recv [%s]\n", buf);
1865 }
1866
1867 /* Sleep 1 second */
1868 // sleep(1);
1869 }
1870}
1871/* Thread 2 entry function */
1872static void* thread2_entry(void* parameter)
1873{
1874 int i, result;
1875 char buf[] = "message2 No.x";
1876
1877 while (1)
1878 {
1879 for (i = 0; i < 10; i++)
1880 {
1881 buf[sizeof(buf) - 2] = '0' + i;
1882
1883 printf("thread2 send [%s]\n", buf);
1884 /* Send a message to the message queue */
1885 result = mq_send(mqueue, &buf[0], sizeof(buf), 0);
1886 if (result == -1)
1887 {
1888 /* Message queue full, delayed 1s */
1889 printf("thread2:message queue is full, delay 1s\n");
1890 sleep(1);
1891 }
1892 }
1893
1894 /* Sleep 2 seconds */
1895 sleep(2);
1896 }
1897}
1898/* Thread 3 entry function */
1899static void* thread3_entry(void* parameter)
1900{
1901 int i, result;
1902 char buf[] = "message3 No.x";
1903
1904 while (1)
1905 {
1906 for (i = 0; i < 10; i++)
1907 {
1908 buf[sizeof(buf) - 2] = '0' + i;
1909
1910 printf("thread3 send [%s]\n", buf);
1911 /* Send messages to the message queue */
1912 result = mq_send(mqueue, &buf[0], sizeof(buf), 0);
1913 if (result == -1)
1914 {
1915 /* Message queue full, delayed 1s */
1916 printf("thread3:message queue is full, delay 1s\n");
1917 sleep(1);
1918 }
1919 }
1920
1921 /* Sleep 2 seconds */
1922 sleep(2);
1923 }
1924}
1925/* User application portal */
1926int rt_application_init()
1927{
1928 int result;
1929 struct mq_attr mqstat;
1930 int oflag = O_CREAT|O_RDWR;
1931#define MSG_SIZE 128
1932#define MAX_MSG 128
1933 memset(&mqstat, 0, sizeof(mqstat));
1934 mqstat.mq_maxmsg = MAX_MSG;
1935 mqstat.mq_msgsize = MSG_SIZE;
1936 mqstat.mq_flags = 0;
1937 mqueue = mq_open("mqueue1",O_CREAT,0777,&mqstat);
1938
1939 /* Create thread 1, thread entry is thread1_entry, property parameter is set to NULL, select default value, entry parameter is NULL*/
1940 result = pthread_create(&tid1,NULL,thread1_entry,NULL);
1941 check_result("thread1 created",result);
1942
1943 /* Create thread 2, thread entry is thread2_entry, property parameter is set to NULL, select default value, entry parameter is NULL*/
1944 result = pthread_create(&tid2,NULL,thread2_entry,NULL);
1945 check_result("thread2 created",result);
1946
1947 /* Create thread 3, thread entry is thread3_entry, property parameter is set to NULL Select default value, entry parameter is NULL*/
1948 result = pthread_create(&tid3,NULL,thread3_entry,NULL);
1949 check_result("thread3 created",result);
1950
1951
1952 return 0;
1953}
1954```
1955
1956# Thread Advanced Programming
1957
1958This section provides a detailed introduction to some of the rarely used property objects and related functions.
1959
1960The thread attributes implemented by RT-Thread include thread stack size, thread priority, thread separation status, and thread scheduling policy. `pthread_create()` must initialize the property object before using the property object. APIs such as setting thread properties should be called before the thread is created. Changes of thread attributes do not affect the threads that have been created.
1961
1962The thread attribute structure `pthread_attr_t` is defined in the pthread.h header file. The thread attribute structure is as follows:
1963
1964``` c
1965/* pthread_attr_t Type redefinition */
1966typedef struct pthread_attr pthread_attr_t;
1967/* Thread attribute structure */
1968struct pthread_attr
1969{
1970 void* stack_base; /* Thread stack address */
1971 rt_uint32_t stack_size; /* Thread stack size */
1972 rt_uint8_t priority; /* Thread priority */
1973 rt_uint8_t detachstate; /* Thread detached state */
1974 rt_uint8_t policy; /* Thread scheduling policy */
1975 rt_uint8_t inheritsched; /* Thread inheritance */
1976};
1977```
1978
1979## Thread Property Initialization and Deinitialization
1980
1981The thread property initialization and deinitialization functions are as follows:
1982
1983``` c
1984int pthread_attr_init(pthread_attr_t *attr);
1985int pthread_attr_destroy(pthread_attr_t *attr);
1986```
1987
1988| **Parameter** | **Description** |
1989|----|------------------|
1990| attr | Pointer to the thread property |
1991|**return**| —— |
1992| 0 | Succeeded |
1993
1994Using the pthread_attr_init() function initializes the thread attribute structure `attr` with the default value, which is equivalent to setting the parameter to NULL when calling the thread initialization function. You need to define a `pthread_attr_t` attribute object before use. This function must be called before the pthread_create() function.
1995
1996The pthread_attr_destroy() function deinitializes the property pointed to by `attr` and can then reinitialize this property object by calling the pthread_attr_init() function again.
1997
1998## Thread Detached State
1999
2000Setting or getting the separation state of a thread is as follows. By default, the thread is non-separated.
2001
2002``` c
2003int pthread_attr_setdetachstate(pthread_attr_t *attr, int state);
2004int pthread_attr_getdetachstate(pthread_attr_t const *attr, int *state);
2005```
2006
2007| **Parameter** | **Description** |
2008|----------|-------------------|
2009| attr | Pointer to the thread property |
2010| state | Thread detached state |
2011|**return**| —— |
2012| 0 | Succeeded |
2013
2014The thread separation state property value state can be `PTHREAD_CREATE_JOINABL` (non-detached) and `THREAD_CREATE_DETACHED` (detached).
2015
2016The detached state of a thread determines how a thread reclaims the resources it occupies after the end of its run. There are two types of thread separation: joinable or detached. When the thread is created, you should call pthread_join() or pthread_detach() to reclaim the resources occupied by the thread after it finishes running. If the thread's detached state is joinable, other threads can call the pthread_join() function to wait for the thread to finish and get the thread return value, and then reclaim the resources occupied by the thread. A thread with a detached state cannot be joined by another thread. Immediately after the end of its operation, the system resources are released.
2017
2018## Thread Scheduling Policy
2019
2020Setting \ Obtaining thread scheduling policy function is as follows:
2021
2022``` c
2023int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
2024int pthread_attr_getschedpolicy(pthread_attr_t const *attr, int *policy);
2025```
2026
2027Only the function interface is implemented. The default different priorities are based on priority scheduling, and the same priority time slice polling scheduling
2028
2029## Thread Scheduling Parameter
2030
2031Set / Obtain the thread's priority function as follows:
2032
2033``` c
2034int pthread_attr_setschedparam(pthread_attr_t *attr,
2035 struct sched_param const *param);
2036int pthread_attr_getschedparam(pthread_attr_t const *attr,
2037 struct sched_param *param);
2038```
2039
2040| **Parameter** | **Description** |
2041|----------|------------------|
2042| attr | Pointer to the thread property |
2043| param | Pointer to the dispatch parameter |
2044|**return**| —— |
2045| 0 | Succeeded |
2046
2047The `pthread_attr_setschedparam()` function sets the priority of the thread. Use `param` to set the thread priority.
2048
2049**Parameter** : The `struct sched_param` is defined in sched.h and has the following structure:
2050
2051``` c
2052struct sched_param
2053{
2054 int sched_priority; /* Thread priority */
2055};
2056```
2057
2058The member `sched_paraority` of the `sched_param` controls the priority value of the thread.
2059
2060## Thread Stack Size
2061
2062Set / Obtain the stack size of a thread is as follows:
2063
2064``` c
2065int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size);
2066int pthread_attr_getstacksize(pthread_attr_t const *attr, size_t *stack_size);
2067```
2068
2069| **Parameter** | **Description** |
2070|-----------|------------------|
2071| attr | Pointer to the thread property |
2072| stack_size | Thread stack size |
2073|**return**| —— |
2074| 0 | Succeeded |
2075
2076The `pthread_attr_setstacksize()` function sets the stack size in bytes. Stack space address alignment is required on most systems (for example, the ARM architecture needs to be aligned to a 4-byte address).
2077
2078## Thread Stack Size and Address
2079
2080Set / Obtain the stack address and stack size of a thread is as follows:
2081
2082``` c
2083int pthread_attr_setstack(pthread_attr_t *attr,
2084 void *stack_base,
2085 size_t stack_size);
2086int pthread_attr_getstack(pthread_attr_t const *attr,
2087 void**stack_base,
2088 size_t *stack_size);
2089```
2090
2091| **Parameter** | **Description** |
2092|-----------|------------------|
2093| attr | Pointer to the thread property |
2094| stack_size | Thread stack size |
2095| stack_base | Thread stack address |
2096|**return**| —— |
2097| 0 | Succeeded |
2098
2099## Thread Attribute Related Function
2100
2101The function that sets / obtains the scope of the thread is as follows:
2102
2103``` c
2104int pthread_attr_setscope(pthread_attr_t *attr, int scope);
2105int pthread_attr_getscope(pthread_attr_t const *attr);
2106```
2107
2108| **Parameter** | **Description** |
2109|-----------|------------------|
2110| attr | Pointer to the thread property |
2111| scope | Thread scope |
2112|**return**| —— |
2113| 0 | scope is PTHREAD_SCOPE_SYSTEM |
2114| EOPNOTSUPP | scope is PTHREAD_SCOPE_PROCESS |
2115| EINVAL | scope is PTHREAD_SCOPE_SYSTEM |
2116
2117## Example Code for Thread Property
2118
2119This program will initialize 2 threads, they have a common entry function, but their entry parameters are not the same. The first thread created will use the provided `attr` thread attribute, and the other thread will use the system default attribute. Thread priority is a very important parameter, so this program will modify the first created thread to have a priority of 8, and the system default priority of 24.
2120
2121``` c
2122#include <pthread.h>
2123#include <unistd.h>
2124#include <stdio.h>
2125#include <sched.h>
2126
2127/* Thread control block */
2128static pthread_t tid1;
2129static pthread_t tid2;
2130
2131/* Function return value check */
2132static void check_result(char* str,int result)
2133{
2134 if (0 == result)
2135 {
2136 printf("%s successfully!\n",str);
2137 }
2138 else
2139 {
2140 printf("%s failed! error code is %d\n",str,result);
2141 }
2142}
2143/* Thread entry function */
2144static void* thread_entry(void* parameter)
2145{
2146 int count = 0;
2147 int no = (int) parameter; /* Obtain the thread's entry parameters */
2148
2149 while (1)
2150 {
2151 /* Printout thread count value */
2152 printf("thread%d count: %d\n", no, count ++);
2153
2154 sleep(2); /* Sleep 2 seconds */
2155 }
2156}
2157
2158/* User application portal */
2159int rt_application_init()
2160{
2161 int result;
2162 pthread_attr_t attr; /* Thread attribute */
2163 struct sched_param prio; /* Thread priority */
2164
2165 prio.sched_priority = 8; /* Priority is set to 8 */
2166 pthread_attr_init(&attr); /* Initialize the property with default values first */
2167 pthread_attr_setschedparam(&attr,&prio); /* Modify the priority corresponding to the attribute */
2168
2169 /* Create thread 1, attribute is attr, entry function is thread_entry, and the entry function parameter is 1 */
2170 result = pthread_create(&tid1,&attr,thread_entry,(void*)1);
2171 check_result("thread1 created",result);
2172
2173 /* Create thread 2, the property is the default value, the entry function is thread_entry, and the entry function parameter is 2 */
2174 result = pthread_create(&tid2,NULL,thread_entry,(void*)2);
2175 check_result("thread2 created",result);
2176
2177 return 0;
2178}
2179```
2180
2181## Thread Cancellation
2182
2183Cancellation is a mechanism that allows one thread to end other threads. A thread can send a cancel request to another thread. Depending on the settings, the target thread may ignore it and may end immediately or postpone it until the next cancellation point.
2184
2185### Send Cancellation Request
2186
2187The cancellation request can be sent using the following function:
2188
2189``` c
2190int pthread_cancel(pthread_t thread);
2191```
2192
2193| **Parameter** | **Description** |
2194|------|--------|
2195| thread | Thread handle |
2196|**return**| —— |
2197| 0 | Succeeded |
2198
2199This function sends a cancel request to the `thread` thread. Whether the thread will respond to the cancellation request and when it responds depends on the state and type of thread cancellation.
2200
2201### Set Cancel Status
2202
2203The cancellation request can be set using the following function:
2204
2205``` c
2206int pthread_setcancelstate(int state, int *oldstate);
2207```
2208
2209| **Parameter** | **Description** |
2210|--------|-------------------------------|
2211| state | There are two values: <br />`PTHREAD_CANCEL_ENABLE`: Cancel enable. <br />`PTHREAD_CANCEL_DISABLE`: Cancel disabled (default value when thread is created). |
2212| oldstate | Save the original cancellation status |
2213|**return**| —— |
2214| 0 | Succeeded |
2215| EINVAL | state is not PTHREAD_CANCEL_ENABLE or PTHREAD_CANCEL_DISABLE |
2216
2217This function sets the cancel state and is called by the thread itself. Canceling the enabled thread will react to the cancel request, and canceling the disabled thread will not react to the cancel request.
2218
2219### Set Cancellation Type
2220
2221You can use the following function to set the cancellation type, which is called by the thread itself:
2222
2223``` c
2224int pthread_setcanceltype(int type, int *oldtype);
2225```
2226
2227| **Parameter** | **Description** |
2228|-------|---------------------------------|
2229| type | There are 2 values: <br />`PTHREAD_CANCEL_DEFFERED`: After the thread receives the cancellation request, it will continue to run to the next cancellation point and then end. (Default value when thread is created) .<br />`PTHREAD_CANCEL_ASYNCHRONOUS`: The thread ends immediately. |
2230| oldtype | Save the original cancellation type |
2231|**return**| —— |
2232| 0 | Succeeded |
2233| EINVAL | state is neither PTHREAD_CANCEL_DEFFERED nor PTHREAD_CANCEL_ASYNCHRONOUS |
2234
2235### Set Cancellation Point
2236
2237The cancellation point can be set using the following function:
2238
2239``` c
2240void pthread_testcancel(void);
2241```
2242
2243This function creates a cancellation point where the thread is called. Called primarily by a thread that does not contain a cancellation point, it can respond to a cancellation request. This function does not work if pthread_testcancel() is called while the cancel state is disabled.
2244
2245### Cancellation Point
2246
2247The cancellation point is where the thread ends when it accepts the cancellation request. According to the POSIX standard, system calls that cause blocking, such as pthread_join(), pthread_testcancel(), pthread_cond_wait(), pthread_cond_timedwait(), and sem_wait(), are cancellation points.
2248
2249All cancellation points included in RT-Thread are as follows:
2250
2251- mq_receive()
2252
2253- mq_send()
2254
2255- mq_timedreceive()
2256
2257- mq_timedsend()
2258
2259- msgrcv()
2260
2261- msgsnd()
2262
2263- msync()
2264
2265- pthread_cond_timedwait()
2266
2267- pthread_cond_wait()
2268
2269- pthread_join()
2270
2271- pthread_testcancel()
2272
2273- sem_timedwait()
2274
2275- sem_wait()
2276
2277- pthread_rwlock_rdlock()
2278
2279- pthread_rwlock_timedrdlock()
2280
2281- pthread_rwlock_timedwrlock()
2282
2283- pthread_rwlock_wrlock()
2284
2285### Example Code for Thread Cancel
2286
2287This program creates 2 threads. After thread2 starts running, it sleeps for 8 seconds. Thread1 sets its own cancel state and type, and then prints the run count information in an infinite loop. After thread2 wakes up, it sends a cancel request to thread1, and thread1 ends the run immediately after receiving the cancel request.
2288
2289``` c
2290#include <pthread.h>
2291#include <unistd.h>
2292#include <stdio.h>
2293
2294/* Thread control block */
2295static pthread_t tid1;
2296static pthread_t tid2;
2297
2298/* Function return value check */
2299static void check_result(char* str,int result)
2300{
2301 if (0 == result)
2302 {
2303 printf("%s successfully!\n",str);
2304 }
2305 else
2306 {
2307 printf("%s failed! error code is %d\n",str,result);
2308 }
2309}
2310/* Thread 1 entry function */
2311static void* thread1_entry(void* parameter)
2312{
2313 int count = 0;
2314 /* Set the cancel state of thread 1 to be enabled. The cancel type is terminated immediately after the thread receives the cancel point. */
2315 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
2316 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2317
2318 while(1)
2319 {
2320 /* Print thread count value output */
2321 printf("thread1 run count: %d\n",count ++);
2322 sleep(2); /* Sleep 2 seconds */
2323 }
2324}
2325/* Thread 2 entry function */
2326static void* thread2_entry(void* parameter)
2327{
2328 int count = 0;
2329 sleep(8);
2330 /* Send a cancel request to thread 1 */
2331 pthread_cancel(tid1);
2332 /* Waiting for thread 1 to finish in blocking mode */
2333 pthread_join(tid1,NULL);
2334 printf("thread1 exited!\n");
2335 /* Thread 2 print information to start output */
2336 while(1)
2337 {
2338 /* Print thread count value output */
2339 printf("thread2 run count: %d\n",count ++);
2340 sleep(2); /* Sleep 2 seconds */
2341 }
2342}
2343/* User application portal */
2344int rt_application_init()
2345{
2346 int result;
2347 /* Create thread 1, the property is the default value, the separation state is the default value joinable, the entry function is thread1_entry, and the entry function parameter is NULL */
2348 result = pthread_create(&tid1,NULL,thread1_entry,NULL);
2349 check_result("thread1 created",result);
2350
2351 /* Create thread 2, the property is the default value, the separation state is the default value joinable, the entry function is thread2_entry, and the entry function parameter is NULL */
2352 result = pthread_create(&tid2,NULL,thread2_entry,NULL);
2353 check_result("thread2 created",result);
2354
2355 return 0;
2356}
2357```
2358
2359## One-time Initialization
2360
2361It can be initialized once using the following function:
2362
2363``` c
2364int pthread_once(pthread_once_t * once_control, void (*init_routine) (void));
2365```
2366
2367| **Parameter** | **Description** |
2368|-------------|--------|
2369| once_control | Control variable |
2370| init_routine | Execute function |
2371|**return**| —— |
2372| 0 | Succeeded |
2373
2374Sometimes we need to initialize some variables only once. If we do multiple initialization procedures, it will get an error. In traditional sequential programming, one-time initialization is often managed by using Boolean variables. The control variable is statically initialized to 0, and any code that relies on initialization can test the variable. If the variable value is still 0, it can be initialized and then set the variable to 1. Codes that are checked later will skip initialization.
2375
2376## Clean up after the Thread Ends
2377
2378The thread cleanup function interface:
2379
2380``` c
2381void pthread_cleanup_pop(int execute);
2382void pthread_cleanup_push(void (*routine)(void*), void *arg);
2383```
2384
2385| **Parameter** | **Description** |
2386|-------|-----------------------------|
2387| execute | 0 or 1, determin whether to execute the cleanup function |
2388| routine | Pointer to the cleanup function |
2389| arg | The parameter passed to the cleanup function |
2390
2391pthread_cleanup_push() puts the specified cleanup `routine` into the thread's cleanup function list. pthread_cleanup_pop() takes the first function from the header of the cleanup function list. If `execute` is a non-zero value, then this function is executed.
2392
2393## Other Thread Related Functions
2394
2395### Determine if two Threads are Equal
2396
2397``` c
2398int pthread_equal (pthread_t t1, pthread_t t2);
2399```
2400
2401| **Parameter** | **Description** |
2402|----------|--------|
2403| pthread_t | Thread handle |
2404|**return**| —— |
2405| 0 | Not equal |
2406| 1 | Equal |
2407
2408### Obtain Thread Handle
2409
2410``` c
2411pthread_t pthread_self (void);
2412```
2413pthread_self() returns the handle of the calling thread.
2414
2415### Get the Maximum and Minimum Priority
2416
2417``` c
2418int sched_get_priority_min(int policy);
2419int sched_get_priority_max(int policy);
2420```
2421
2422| **Parameter** | **Description** |
2423|------|---------------------------------|
2424| policy | 2 values are optional: SCHED_FIFO, SCHED_RR |
2425
2426sched_get_priority_min() returns a value of 0, with the highest priority in RT-Thread and sched_get_priority_max() with the lowest priority.
2427
2428## Mutex Attribute
2429
2430The mutex properties implemented by RT-Thread include the mutex type and the mutex scope.
2431
2432### Mutex Lock Attribute Initialization and Deinitialization
2433
2434``` c
2435int pthread_mutexattr_init(pthread_mutexattr_t *attr);
2436int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
2437```
2438
2439| **Parameter** | **Description** |
2440|----|------------------------|
2441| attr | Pointer to the mutex attribute object |
2442|**return**| —— |
2443| 0 | Succeeded |
2444| EINVAL | Invalid parameter |
2445
2446The pthread_mutexattr_init() function initializes the property object pointed to by `attr` with the default value, which is equivalent to setting the property parameter to NULL when calling the pthread_mutex_init() function.
2447
2448The pthread_mutexattr_destroy() function will initialize the property object pointed to by `attr` and can be reinitialized by calling the pthread_mutexattr_init() function.
2449
2450### Mutex Lock Scope
2451
2452``` c
2453int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
2454int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared);
2455```
2456
2457| **Parameter** | **Description** |
2458|-------|--------------------|
2459| type | Mutex type |
2460| pshared | There are 2 optional values: <br />`PTHREAD_PROCESS_PRIVATE`: The default value, used to synchronize only threads in the process. `PTHREAD_PROCESS_SHARED`: Used to synchronize threads in this process and other processes. |
2461|**return**| —— |
2462| 0 | Succeeded |
2463| EINVAL | Invalid parameter |
2464
2465### Mutex Type
2466
2467``` c
2468int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
2469int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type);
2470```
2471
2472| **Parameter** | **Description** |
2473|----|------------------------|
2474| type | Mutex type |
2475| attr | Pointer to the mutex attribute object |
2476|**return**| —— |
2477| 0 | Succeeded |
2478| EINVAL | Invalid parameter |
2479
2480The type of mutex determines how a thread behaves when it acquires a mutex. RT-Thread implements three mutex types:
2481
2482- **PTHREAD_MUTEX_NORMAL**: Normal lock. When a thread is locked, the remaining threads requesting the lock will form a wait queue, and after unlocking, the lock will be acquired in the first-in first-out manner. If a thread attempts to regain the mutex without first releasing the mutex, it does not generate a deadlock, but instead returns an error code, just like the error checking lock.
2483
2484- **PTHREAD_MUTEX_RECURSIVE**: Nested locks that allow a thread to successfully acquire the same lock multiple times, requiring the same number of unlocks to release the mutex.
2485
2486- **PTHREAD_MUTEX_ERRORCHECK**: Error checking lock, if a thread tries to regain the mutex without first releasing the mutex, an error is returned. This ensures that deadlocks do not occur when multiple locks are not allowed.
2487
2488## Condition Variable Attribute
2489
2490Use the default value PTHREAD_PROCESS_PRIVATE to initialize the condition variable attribute attr to use the following function:
2491
2492``` c
2493int pthread_condattr_init(pthread_condattr_t *attr);
2494```
2495
2496| **Parameter** | **Description** |
2497|----|--------------------------|
2498| attr | Pointer to a condition variable property object |
2499|**return**| —— |
2500| 0 | Succeeded |
2501| EINVAL | Invalid parameter |
2502
2503### Obtain Condition Variable Scope
2504
2505``` c
2506int pthread_mutexattr_getpshared(pthread_mutexattr_t *attr, int *pshared);
2507```
2508
2509| **Parameter** | **Description** |
2510|----|--------------------------|
2511| attr | Pointer to a condition variable property object |
2512|**return**| —— |
2513| 0 | Succeeded |
2514| EINVAL | Invalid parameter |
2515
2516## Read-write Lock Attribute
2517
2518### Initialize Property
2519
2520``` c
2521int pthread_rwlockattr_init (pthread_rwlockattr_t *attr);
2522```
2523
2524| **Parameter** | **Description** |
2525|----|--------------------|
2526| attr | Pointer to the read-write lock property |
2527|**return**| —— |
2528| 0 | Succeeded |
2529|-1 | Invalid parameter |
2530
2531This function initializes the read-write lock attribute `attr` with the default value PTHREAD_PROCESS_PRIVATE.
2532
2533### Obtain Scope
2534
2535``` c
2536int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared);
2537```
2538
2539| **Parameter** | **Description** |
2540|-------|--------------------------|
2541| attr | Pointer to the read-write lock property |
2542| pshared | Pointer to the scope of the read-write lock |
2543|**return**| —— |
2544| 0 | Succeeded |
2545|-1 | Invalid parameter |
2546
2547The memory pointed to by pshared is saved as PTHREAD_PROCESS_PRIVATE.
2548
2549## Barrier Attribute
2550
2551### Initialize Property
2552
2553``` c
2554int pthread_barrierattr_init(pthread_barrierattr_t *attr);
2555```
2556
2557| **Parameter** | **Description** |
2558|----|------------------|
2559| attr | Pointer to the barrier property |
2560|**return**| —— |
2561| 0 | Succeeded |
2562|-1 | Invalid parameter |
2563
2564The modified function initializes the barrier attribute `attr` with the default value PTHREAD_PROCESS_PRIVATE.
2565
2566### Obtain Scope
2567
2568``` c
2569int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, int *pshared);
2570```
2571
2572| **Parameter** | **Description** |
2573|-------|-----------------------------|
2574| attr | Pointer to the barrier property |
2575| pshared | Pointer to save barrier scope data |
2576|**return**| —— |
2577| 0 | Succeeded |
2578|-1 | Invalid parameter |
2579
2580## Message Queue Property
2581
2582The message queue attribute control block is as follows:
2583
2584``` c
2585struct mq_attr
2586{
2587 long mq_flags; /* Message queue flag to indicate whether to block */
2588 long mq_maxmsg; /* Message queue maximum number of messages */
2589 long mq_msgsize; /* The maximum number of bytes per message in the message queue */
2590 long mq_curmsgs; /* Message queue current message number */
2591};
2592```
2593### Obtain Attribute
2594``` c
2595int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);
2596```
2597
2598| **Parameter** | **Description** |
2599|------|------------------------|
2600| mqdes | Pointer to the message queue control block |
2601| mqstat | Pointer to save the get data |
2602|**return**| —— |
2603| 0 | Succeeded |
2604|-1 | Invalid parameter |
2605