Home | Projects | Notes > Multi-Threading (POSIX Threads) > Thread Synchronization - Read/Write Lock
A read/write lock (RW lock) allows concurrent access for read-only operations, whereas write operations require exclusive access. This improves performance compared to using regular mutexes.
Consider three threads in a program; T1 (read-only), T2 (read-only), T3 (write).
xxxxxxxxxx
141/* T1 (read-only) - computes the sum of all elements of the array */
2int sum(int *arr, int size)
3{
4 int i; sum = 0;
5 prhtread_mutex_lock(&mutex);
6
7 for (i = 0; i < size; i++)
8 {
9 sum += arr[i];
10 }
11
12 pthread_mutex_unlock(&mutex);
13 return sum;
14}
xxxxxxxxxx
141/* T2 (read-only) - computes the product of all elements of the array */
2int sum(int *arr, int size)
3{
4 int i; mul = 0;
5 prhtread_mutex_lock(&mutex);
6
7 for (i = 0; i < size; i++)
8 {
9 sum *= arr[i];
10 }
11
12 pthread_mutex_unlock(&mutex);
13 return sum;
14}
xxxxxxxxxx
141/* T3 (write) - square each element of the array */
2int sum(int *arr, int size)
3{
4 int i;
5 prhtread_mutex_lock(&mutex);
6
7 for (i = 0; i < size; i++)
8 {
9 arr[i] *= arr[i];
10 }
11
12 pthread_mutex_unlock(&mutex);
13 return sum;
14}
T1 and T2 perform read operations (read-read are non-conflicting operations), hence, should be allowed without blocking each other. However, a regular mutex would not allow this. This program can be improved by using an RW lock instead. It allows multiple reader threads (e.g., T1, T2) to enter the critical section simultaneously:
xxxxxxxxxx
141/* T1 (read-only) - computes the sum of all elements of the array */
2int sum(int *arr, int size)
3{
4 int i; sum = 0;
5 rdlock(&rwlock);
6
7 for (i = 0; i < size; i++)
8 {
9 sum += arr[i];
10 }
11
12 unlock(&rwlock);
13 return sum;
14}
xxxxxxxxxx
141/* T2 (read-only) - computes the product of all elements of the array */
2int sum(int *arr, int size)
3{
4 int i; mul = 0;
5 rdlock(&rwlock);
6
7 for (i = 0; i < size; i++)
8 {
9 sum *= arr[i];
10 }
11
12 unlock(&rwlock);
13 return sum;
14}
xxxxxxxxxx
141/* T3 (write) - square each element of the array */
2int sum(int *arr, int size)
3{
4 int i;
5 wrlock(&rwlock);
6
7 for (i = 0; i < size; i++)
8 {
9 arr[i] *= arr[i];
10 }
11
12 unlock(&rwlock);
13 return sum;
14}
Using an RW lock, T1 and T2 can execute in parallel on different CPUs without getting blocked. Great performance improvement!
A thread need to specify the type of lock it wants to obtain; read lock (rdlock()
), or write lock (wrlock()
) on the lock variable (rwlock
).
Read-read (non-conflicting operations)
Write-read, write-write (conflicting operations)
A test program to demonstrate the POSIX read/write locks.
xxxxxxxxxx
1181/*
2 * File Name : rwlock_std_main.c
3 * Description : A test driver for the POSIX standard read/write locks
4 * Author : Modified by Kyungjae Lee (Original: Abhishek Sagar - Juniper Networks)
5 * Date Created : 01/14/2023
6 */
7
8
9
10
11
12
13static pthread_rwlock_t rw_lock;
14static int n_r = 0; /* counts the number of reader threads executing in the CS */
15static int n_w = 0; /* counts the number of writer threads executing in the CS */
16pthread_mutex_t state_check_mutex; /* mutex for operations on the shared variable n_r */
17
18static void cs_status_check()
19{
20 pthread_mutex_lock(&state_check_mutex);
21 assert(n_r >= 0 && n_w >= 0); /* Cannot be negative */
22
23 if (n_r == 0 && n_w == 0)
24 {
25 // valid condition
26 }
27 else if (n_r > 0 && n_w == 0)
28 {
29 // valid condition
30 }
31 else if (n_r == 0 && n_w == 1)
32 {
33 // valid condition
34 }
35 else
36 {
37 assert(0);
38 }
39
40 /* due to the nature of the printf() - using buffer - it also needs to be protected
41 using the mutex (without protection, it will printout meaningless values */
42 printf ("n_r = %d, n_w = %d\n", n_r, n_w); /* # of reader & writer threads in the CS */
43 pthread_mutex_unlock(&state_check_mutex);
44}
45
46static void execute_cs()
47{
48 cs_status_check();
49}
50
51static void reader_enter_cs()
52{
53 pthread_mutex_lock(&state_check_mutex);
54 n_r++; /* mutex required since multiple reader threads are allowed to be in the CS */
55 pthread_mutex_unlock(&state_check_mutex);
56}
57
58static void reader_leave_cs()
59{
60 pthread_mutex_lock(&state_check_mutex);
61 n_r--; /* mutex required since multiple reader threads are allowed to be in the CS */
62 pthread_mutex_unlock(&state_check_mutex);
63}
64
65static void writer_enter_cs()
66{
67 n_w++; /* does not need a mutex since a writer thread will always be alone in the CS */
68}
69
70static void writer_leave_cs()
71{
72 n_w--; /* does not need a mutex since a writer thread will always be alone in the CS */
73}
74
75void* read_thread_fn (void *arg)
76{
77 while(1)
78 {
79 pthread_rwlock_rdlock(&rw_lock);
80 reader_enter_cs(); /* to keep track of the # or threads executing in the CS */
81
82 execute_cs();
83
84 reader_leave_cs(); /* to keep track of the # or threads executing in the CS */
85 pthread_rwlock_unlock(&rw_lock);
86 }
87}
88
89void* write_thread_fn (void *arg)
90{
91 while(1)
92 {
93 pthread_rwlock_wrlock(&rw_lock);
94 writer_enter_cs();
95
96 execute_cs();
97
98 writer_leave_cs();
99 pthread_rwlock_unlock(&rw_lock);
100 }
101}
102
103/* any reader threads can execute in the critical section concurrently, but if
104 a writer thread is in the critical section, no other threads are allowed to get in */
105int main(int argc, char *argv[])
106{
107 static pthread_t th1, th2, th3, th4, th5, th6; /* 3 reader, 3 writer threads */
108 pthread_rwlock_init(&rw_lock, NULL);
109 pthread_mutex_init (&state_check_mutex, NULL);
110 pthread_create(&th1, NULL, read_thread_fn, NULL);
111 pthread_create(&th2, NULL, read_thread_fn, NULL);
112 pthread_create(&th3, NULL, read_thread_fn, NULL);
113 pthread_create(&th4, NULL, write_thread_fn, NULL);
114 pthread_create(&th5, NULL, write_thread_fn, NULL);
115 pthread_create(&th6, NULL, write_thread_fn, NULL);
116 pthread_exit(0);
117 return 0;
118}
xxxxxxxxxx
131n_r = 1, n_w = 0
2n_r = 1, n_w = 0
3n_r = 1, n_w = 0
4...
5n_r = 3, n_w = 0
6n_r = 3, n_w = 0
7n_r = 1, n_w = 0
8n_r = 2, n_w = 0
9n_r = 2, n_w = 0
10...
11n_r = 3, n_w = 0
12n_r = 3, n_w = 0
13...
The point of this program is to show that no two writer threads can be in the critical section simultaneously. To make the output analysis easier:
xxxxxxxxxx
191./rwlock_std_main > log
2
3$ cat log | grep "n_r = 1, n_w = 0" | wc -l
4785254
5
6$ cat log | grep "n_r = 2, n_w = 0" | wc -l
73119865
8
9$ cat log | grep "n_r = 3, n_w = 0" | wc -l
103256646
11
12$ cat log | grep "n_r = 0, n_w = 3" | wc -l
130
14
15$ cat log | grep "n_r = 0, n_w = 2" | wc -l
160
17
18$ cat log | grep "n_r = 0, n_w = 1" | wc -l
19452
Notice that the case including
n_w = 2
orn_w = 3
does not exist in the output!
Properties of POSIX read/write locks:
When a reader thread is executing in a critical section, other reader threads are still allowed to enter the critical section.
When a writer thread is executing in a critical section, no other threads are allowed to enter the critical section at all.
Let the RW lock support the property of recursiveness, that is, a thread can lock the RW lock multiple times if necessary.
When the RW lock is unlocked, let the OS scheduling policy decide which waiting thread is to enter the critical section next in such a way that no thread suffers from starvation.
assert()
if a thread in the system tries to unlock the already unlocked (or available) RW lock, or unlock the RW lock that is locked by another thread. In such cases, the program must terminate.
Insert as many assert()
s as possible to ensure that the lock is always in the valid state.
Remember! Condition variables and mutexes are the basic building blocks of any thread synchronization data structures.
Interface for Read/Write Locks
xxxxxxxxxx
431/*
2 * File Name : rwlock.h
3 * Description : Interface for read/write locks
4 * Author : Modified by Kyungjae Lee (Original: Abhishek Sagar - Juniper Networks)
5 * Date Created : 01/14/2023
6 */
7
8
9
10
11
12
13
14
15typedef struct rwlock_
16{
17 /* a mutex to manipulate/inspect the state of rwlock in a mutually exclusive way */
18 pthread_mutex_t state_mutex;
19 /* a CV to block the the threads when the RW lock is not available */
20 pthread_cond_t cv;
21 /* count of number of concurrent threads executing inside CS
22 - for writer threads, this value will always be 1 */
23 uint16_t n_locks;
24 /* no of reader threads waiting for the RW lock grant */
25 uint16_t n_reader_waiting;
26 /* no of writer threads waiting for the RW lock grant */
27 uint16_t n_writer_waiting;
28 /* is RW lock currently occupied by reader threads */
29 bool is_locked_by_reader;
30 /* is RW lock currently occupied by a writer thread */
31 bool is_locked_by_writer;
32 /* thread handle of the writer thread currently holding the RW lock
33 (it is 0 if the RW lock is not being held by any writer threads */
34 pthread_t writer_thread; /* if the RW is locked by a writer thread, which thread is it? */
35} rw_lock_t;
36
37void rw_lock_init (rw_lock_t *rw_lock);
38void rw_lock_rd_lock (rw_lock_t *rw_lock);
39void rw_lock_wr_lock (rw_lock_t *rw_lock);
40void rw_lock_unlock (rw_lock_t *rw_lock) ;
41void rw_lock_destroy(rw_lock_t *rw_lock);
42
43/* RWLOCK_H */
Implementation of Read/Write Locks
xxxxxxxxxx
2561/*
2 * File Name : rwlock.c
3 * Description : Implementation of read/write locks
4 * Author : Modified by Kyungjae Lee (Original: Abhishek Sagar - Juniper Networks)
5 * Date Created : 01/14/2023
6 */
7
8
9
10
11void rw_lock_init (rw_lock_t *rw_lock)
12{
13 pthread_mutex_init(&rw_lock->state_mutex, NULL);
14 pthread_cond_init(&rw_lock->cv, NULL);
15 rw_lock->n_reader_waiting = 0;
16 rw_lock->n_writer_waiting = 0;
17 rw_lock->is_locked_by_reader = false;
18 rw_lock->is_locked_by_writer = false;
19 rw_lock->writer_thread = 0;
20}
21
22void rw_lock_rd_lock (rw_lock_t *rw_lock)
23{
24 pthread_mutex_lock(&rw_lock->state_mutex);
25
26 /* case 1 : rw_lock is available (in unlocked state) */
27 if (rw_lock->is_locked_by_reader == false && rw_lock->is_locked_by_writer == false)
28 {
29 /* double-check the state of rW_lock */
30 assert(rw_lock->n_locks == 0);
31 assert(!rw_lock->is_locked_by_reader);
32 assert(!rw_lock->is_locked_by_writer);
33 assert(!rw_lock->writer_thread);
34
35 /* update the state of rw_lock (CS protected by state_mutex) */
36 rw_lock->n_locks++;
37 rw_lock->is_locked_by_reader = true;
38
39 pthread_mutex_unlock(&rw_lock->state_mutex);
40 return;
41 }
42
43 /* case 2 : rw_lock is locked by reader thread(s) already (recursive lock allowed) */
44 if (rw_lock->is_locked_by_reader)
45 {
46 /* double-check the state of rw_lock */
47 assert(rw_lock->is_locked_by_writer == false);
48 assert(rw_lock->n_locks);
49 assert(!rw_lock->writer_thread);
50
51 /* update the state of rw_lock (CS protected by state_mutex) */
52 rw_lock->n_locks++;
53
54 pthread_mutex_unlock(&rw_lock->state_mutex);
55 return;
56 }
57
58 /* case 3 : rw_lock is locked by a writer thread */
59 while (rw_lock->is_locked_by_writer)
60 {
61 /* double-check the state of rw_lock */
62 assert(rw_lock->n_locks); /* a writer thread can lock rw_lock recursively */
63 assert(rw_lock->is_locked_by_reader == false);
64 assert(rw_lock->writer_thread);
65
66 rw_lock->n_reader_waiting++; /* increment waiting threads counter */
67 pthread_cond_wait(&rw_lock->cv, &rw_lock->state_mutex); /* block until available */
68 /* when the lock becomes available (upon receiving a signal on CV) */
69 rw_lock->n_reader_waiting--; /* wakeup and decrement waiting thread counter */
70 }
71
72 /* will reach here only when the previous while loop condition becomes false,
73 meaning that rw_lock is at least not locked by any writer thread (rw_lock
74 may still be locked by one or more reader threads) */
75 if (rw_lock->n_locks == 0)
76 {
77 /* first reader enters CS */
78 rw_lock->is_locked_by_reader = true;
79 }
80 rw_lock->n_locks++;
81 assert (rw_lock->is_locked_by_writer == false);
82 assert(!rw_lock->writer_thread);
83 pthread_mutex_unlock(&rw_lock->state_mutex);
84}
85
86void rw_lock_unlock (rw_lock_t *rw_lock)
87{
88 pthread_mutex_lock(&rw_lock->state_mutex);
89
90 /* case 1 : rw_lock is already in unlocked state */
91 assert(rw_lock->n_locks); /* do not allow unlocking an already unlocked rw_lock */
92
93 /* case 2 : a writer thread trying to unlock rw_lock which is locked by a writer thread */
94 if (rw_lock->is_locked_by_writer)
95 {
96 /* only the owner of the rw_lock must attempt to unlock rw_lock */
97 assert(pthread_self() == rw_lock->writer_thread);
98 assert(rw_lock->is_locked_by_reader == false);
99
100 rw_lock->n_locks--; /* unlock rw_lock */
101
102 if (rw_lock->n_locks)
103 {
104 /* the current writer thread still owns rw_lock (more unlocks required to
105 completely release rw_lock) */
106 pthread_mutex_unlock(&rw_lock->state_mutex);
107 return;
108 }
109
110 /* at this point, the current writer thread has no more to unlock */
111 if (rw_lock->n_reader_waiting || rw_lock->n_writer_waiting)
112 {
113 /* broadcast instead of signal in case there are multiple 'reader' threads
114 waiting to enter the critical section */
115 pthread_cond_broadcast(&rw_lock->cv);
116 /* either all waiting reader threads will get in the critical section,
117 or exactly one of the waiting writer thread(s) will get in */
118 }
119
120 rw_lock->is_locked_by_writer = false;
121 rw_lock->writer_thread = 0;
122 pthread_mutex_unlock(&rw_lock->state_mutex);
123 return;
124 }
125
126 /* case 3 : reader thread trying to unlock the rw_lock which is locked by a reader thread
127 - case 3 cannot be moved before case 2 (leave it to you for reasoning) */
128
129 /* There is minor design flaw in our implementation. Not actually flaw, but
130 a room to mis-use the rw_locks which is a trade off for simplicity. Here Simplicity is
131 that rw_lock implementation do not keep track of "who" all reader threads have
132 obtain the read lock on rw_lock. Here is an example, how rw_lock implementation
133 could be erroneously used by the developer, yet rw_lock implementation would not
134 report a bug ( assert () ).
135 Suppose, threads T1, T2 and T3 owns read locks on rw_lock. and Now, some
136 Thread T4 which do not own any type of lock on rw_lock invoke rw_lock_unlock( )
137 API. The API would still honor the unlock request, since our rw_lock do not keep
138 track of who all "reader" threads owns the rw_lock and treat T4 as some reader thread
139 trying to unlock the rw_lock */
140
141 else if (rw_lock->is_locked_by_reader)
142 {
143 assert(rw_lock->is_locked_by_writer == false);
144 assert(rw_lock->writer_thread == 0);
145
146 rw_lock->n_locks--;
147
148 if (rw_lock->n_locks)
149 {
150 /* if rw_lock is not completely unlocked yet */
151 pthread_mutex_unlock(&rw_lock->state_mutex);
152 return;
153 }
154
155 /* at this point, the current read thread has no more to unlock */
156 if (rw_lock->n_reader_waiting || rw_lock->n_writer_waiting)
157 {
158 pthread_cond_broadcast(&rw_lock->cv);
159 }
160
161 rw_lock->is_locked_by_reader = false;
162 pthread_mutex_unlock(&rw_lock->state_mutex);
163 return;
164 }
165
166 assert(0);
167}
168
169void rw_lock_wr_lock (rw_lock_t *rw_lock)
170{
171 pthread_mutex_lock(&rw_lock->state_mutex);
172
173 /* case 1 : rw_lock is available (in unlocked state) */
174 if (rw_lock->is_locked_by_reader == false && rw_lock->is_locked_by_writer == false)
175 {
176 /* double-check the state of rw_lock */
177 assert(rw_lock->n_locks == 0);
178 assert(!rw_lock->is_locked_by_reader);
179 assert(!rw_lock->is_locked_by_writer);
180 assert(!rw_lock->writer_thread);
181
182 /* update the state of rw_lock (CS protected by state_mutex) */
183 rw_lock->n_locks++;
184 rw_lock->is_locked_by_writer = true;
185 rw_lock->writer_thread = pthread_self();
186
187 pthread_mutex_unlock(&rw_lock->state_mutex);
188 return;
189 }
190
191 /* case 2 : rw_lock is locked by a writer thread (itself) already
192 (recursive lock) */
193 if (rw_lock->is_locked_by_writer && rw_lock->writer_thread == pthread_self())
194 {
195 /* double-check the state of rw_lock */
196 assert(rw_lock->is_locked_by_reader == false);
197 assert(rw_lock->n_locks);
198
199 /* update the state of rw_lock (CS protected by state_mutex) */
200 rw_lock->n_locks++; /* implements the recursive lock by the same thread */
201 pthread_mutex_unlock(&rw_lock->state_mutex);
202 return;
203 }
204
205 /* case 3 : rw_lock is already locked by any other thread(s) than the current thread */
206 while (rw_lock->is_locked_by_reader || rw_lock->is_locked_by_writer)
207 {
208 /* sanity checks */
209 if (rw_lock->is_locked_by_reader)
210 {
211 assert(rw_lock->is_locked_by_writer == false);
212 assert(rw_lock->n_locks);
213 assert(rw_lock->writer_thread == 0);
214 }
215 else if (rw_lock->is_locked_by_writer)
216 {
217 assert(rw_lock->is_locked_by_reader == false);
218 assert(rw_lock->n_locks);
219 assert(rw_lock->writer_thread);
220 }
221
222 /* at this point, the current thread is trying to obtain the rw_lock which is not
223 available */
224 rw_lock->n_writer_waiting++;
225 pthread_cond_wait(&rw_lock->cv, &rw_lock->state_mutex); /* block until available */
226 rw_lock->n_writer_waiting--;
227 } /* while loop instead of if statement to prevent spurious wakeup */
228
229 /* now rw_lock is available and the current writer thread owns (locks) rw_lock */
230 assert(rw_lock->is_locked_by_reader == false);
231 assert(rw_lock->is_locked_by_writer == false);
232 assert(rw_lock->n_locks == 0);
233 assert(!rw_lock->writer_thread);
234
235 rw_lock->is_locked_by_writer = true;
236 rw_lock->n_locks++;
237 rw_lock->writer_thread = pthread_self();
238 pthread_mutex_unlock(&rw_lock->state_mutex);
239}
240
241/* it is the developer's responsibility to invoke this API on the RW lock object only when
242 it is 100% ensured that it is not being used by any threads in the system */
243void rw_lock_destroy(rw_lock_t *rw_lock)
244{
245 /* safety-check before destroy */
246 assert(rw_lock->n_locks == 0); /* no threads should be executing in the CS */
247 assert(rw_lock->n_reader_waiting == 0); /* no threads should be blocked on the RW lock */
248 assert(rw_lock->n_writer_waiting == 0);
249 assert(rw_lock->is_locked_by_writer == false); /* RW lock is not locked by any threads */
250 assert(rw_lock->is_locked_by_reader == false);
251 assert(!rw_lock->writer_thread);
252
253 /* destroy */
254 pthread_mutex_destroy(&rw_lock->state_mutex); /* destroy mutex */
255 pthread_cond_destroy(&rw_lock->cv); /* condition variable */
256}
Test Driver for Read/Write Locks
xxxxxxxxxx
1181/*
2 * File Name : rwlock_main.c
3 * Description : A test driver for the user defined read/write locks
4 * Author : Modified by Kyungjae Lee (Original: Abhishek Sagar - Juniper Networks)
5 * Date Created : 01/14/2023
6 */
7
8
9
10
11
12
13static pthread_rwlock_t rw_lock;
14static int n_r = 0; /* counts the number of reader threads executing in the CS */
15static int n_w = 0; /* counts the number of writer threads executing in the CS */
16pthread_mutex_t state_check_mutex; /* mutex for operations on the shared variable n_r */
17
18static void cs_status_check()
19{
20 pthread_mutex_lock(&state_check_mutex);
21 assert(n_r >= 0 && n_w >= 0); /* Cannot be negative */
22
23 if (n_r == 0 && n_w == 0)
24 {
25 // valid condition
26 }
27 else if (n_r > 0 && n_w == 0)
28 {
29 // valid condition
30 }
31 else if (n_r == 0 && n_w == 1)
32 {
33 // valid condition
34 }
35 else
36 {
37 assert(0);
38 }
39
40 /* due to the nature of the printf() - using buffer - it also needs to be protected
41 using the mutex (without protection, it will printout meaningless values */
42 printf ("n_r = %d, n_w = %d\n", n_r, n_w); /* # of reader & writer threads in the CS */
43 pthread_mutex_unlock(&state_check_mutex);
44}
45
46static void execute_cs()
47{
48 cs_status_check();
49}
50
51static void reader_enter_cs()
52{
53 pthread_mutex_lock(&state_check_mutex);
54 n_r++; /* mutex required since multiple reader threads are allowed to be in the CS */
55 pthread_mutex_unlock(&state_check_mutex);
56}
57
58static void reader_leave_cs()
59{
60 pthread_mutex_lock(&state_check_mutex);
61 n_r--; /* mutex required since multiple reader threads are allowed to be in the CS */
62 pthread_mutex_unlock(&state_check_mutex);
63}
64
65static void writer_enter_cs()
66{
67 n_w++; /* does not need a mutex since a writer thread will always be alone in the CS */
68}
69
70static void writer_leave_cs()
71{
72 n_w--; /* does not need a mutex since a writer thread will always be alone in the CS */
73}
74
75void* read_thread_fn (void *arg)
76{
77 while(1)
78 {
79 pthread_rwlock_rdlock(&rw_lock);
80 reader_enter_cs(); /* to keep track of the # or threads executing in the CS */
81
82 execute_cs();
83
84 reader_leave_cs(); /* to keep track of the # or threads executing in the CS */
85 pthread_rwlock_unlock(&rw_lock);
86 }
87}
88
89void* write_thread_fn (void *arg)
90{
91 while(1)
92 {
93 pthread_rwlock_wrlock(&rw_lock);
94 writer_enter_cs();
95
96 execute_cs();
97
98 writer_leave_cs();
99 pthread_rwlock_unlock(&rw_lock);
100 }
101}
102
103/* any reader threads can execute in the critical section concurrently, but if
104 a writer thread is in the critical section, no other threads are allowed to get in */
105int main(int argc, char *argv[])
106{
107 static pthread_t th1, th2, th3, th4, th5, th6; /* 3 reader, 3 writer threads */
108 pthread_rwlock_init(&rw_lock, NULL);
109 pthread_mutex_init (&state_check_mutex, NULL);
110 pthread_create(&th1, NULL, read_thread_fn, NULL);
111 pthread_create(&th2, NULL, read_thread_fn, NULL);
112 pthread_create(&th3, NULL, read_thread_fn, NULL);
113 pthread_create(&th4, NULL, write_thread_fn, NULL);
114 pthread_create(&th5, NULL, write_thread_fn, NULL);
115 pthread_create(&th6, NULL, write_thread_fn, NULL);
116 pthread_exit(0);
117 return 0;
118}
If the program gets terminated by one of those assert()
s, it means the program is buggy. Also, check if any programmer is using the APIs the wrong way!
Sagar, A. (2022). Part A - Multithreading & Thread Synchronization - Pthreads [Video file]. Retrieved from https://www.udemy.com/course/multithreading_parta/