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