1 /* Test of glthread_rwlock_rdlock function. 2 Copyright (C) 2017-2021 Free Software Foundation, Inc. 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 3 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 16 17 /* Written by Bruno Haible <bruno@clisp.org>, 2005. 18 Inspired by 19 https://github.com/linux-test-project/ltp/blob/master/testcases/open_posix_testsuite/conformance/interfaces/pthread_rwlock_rdlock/2-2.c 20 by Intel Corporation. */ 21 22 #include <config.h> 23 24 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS 25 26 #include "glthread/lock.h" 27 28 #include <errno.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 33 #include "glthread/thread.h" 34 35 /* Verify that in a situation where 36 - an rwlock is taken by a reader and has a writer waiting, 37 - an additional reader requests the lock, 38 - the waiting writer and the requesting reader threads have the same 39 priority, 40 the requesting reader thread gets blocked, so that at some point the 41 waiting writer can acquire the lock. 42 Without such a guarantee, when there a N readers and each of the readers 43 spends more than 1/Nth of the time with the lock held, there is a high 44 probability that the waiting writer will not get the lock in a given finite 45 time, a phenomenon called "writer starvation". 46 Without such a guarantee, applications have a hard time avoiding writer 47 starvation. 48 49 POSIX:2008 makes this requirement only for implementations that support TPS 50 (Thread Priority Scheduling) and only for the scheduling policies SCHED_FIFO 51 and SCHED_RR, see 52 https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html 53 but test verifies the guarantee regardless of TPS and regardless of 54 scheduling policy. */ 55 56 #define SUCCEED() exit (0) 57 #define FAILURE() exit (1) 58 #define UNEXPECTED(n) (fprintf (stderr, "Unexpected outcome %d\n", n), abort ()) 59 60 /* The main thread creates the waiting writer and the requesting reader threads 61 in the default way; this guarantees that they have the same priority. 62 We can reuse the main thread as first reader thread. */ 63 64 static gl_rwlock_t lock; 65 static gl_thread_t reader1; 66 static gl_thread_t writer; 67 static gl_thread_t reader2; 68 static gl_thread_t timer; 69 /* Used to pass control from writer to reader2 and from reader2 to timer, 70 as in a relay race. 71 Passing control from one running thread to another running thread 72 is most likely faster than to create the second thread. */ 73 static gl_lock_t baton; 74 75 static void * 76 timer_func (void *ignored) /* */ 77 { 78 /* Step 13 (can be before or after step 12): 79 The timer thread takes the baton, then waits a moment to make sure 80 it can tell whether the second reader thread is blocked at step 12. */ 81 if (glthread_lock_lock (&baton)) 82 UNEXPECTED (13); 83 usleep (100000); 84 /* By the time we get here, it's clear that the second reader thread is 85 blocked at step 12. This is the desired behaviour. */ 86 SUCCEED (); 87 } 88 89 static void * 90 reader2_func (void *ignored) /* */ 91 { 92 int err; 93 94 /* Step 8 (can be before or after step 7): 95 The second reader thread takes the baton, then waits a moment to make sure 96 the writer thread has reached step 7. */ 97 if (glthread_lock_lock (&baton)) 98 UNEXPECTED (8); 99 usleep (100000); 100 /* Step 9 omitted. */ 101 /* Step 10: Launch a timer, to test whether the next call blocks. */ 102 if (glthread_create (&timer, timer_func, NULL)) 103 UNEXPECTED (10); 104 /* Step 11: Release the baton. */ 105 if (glthread_lock_unlock (&baton)) 106 UNEXPECTED (11); 107 /* Step 12: The second reader thread requests the lock. */ 108 err = glthread_rwlock_rdlock (&lock); 109 if (err == 0) 110 FAILURE (); 111 else 112 UNEXPECTED (12); 113 } 114 115 static void * 116 writer_func (void *ignored) /* */ 117 { 118 /* Step 4: Take the baton, so that the second reader thread does not go ahead 119 too early. */ 120 if (glthread_lock_lock (&baton)) 121 UNEXPECTED (4); 122 /* Step 5: Create the second reader thread. */ 123 if (glthread_create (&reader2, reader2_func, NULL)) 124 UNEXPECTED (5); 125 /* Step 6: Release the baton. */ 126 if (glthread_lock_unlock (&baton)) 127 UNEXPECTED (6); 128 /* Step 7: The writer thread requests the lock. */ 129 if (glthread_rwlock_wrlock (&lock)) 130 UNEXPECTED (7); 131 return NULL; 132 } 133 134 int 135 main () /* */ 136 { 137 reader1 = gl_thread_self (); 138 139 /* Step 1: The main thread initializes the lock and the baton. */ 140 if (glthread_rwlock_init (&lock)) 141 UNEXPECTED (1); 142 if (glthread_lock_init (&baton)) 143 UNEXPECTED (1); 144 /* Step 2: The main thread acquires the lock as a reader. */ 145 if (glthread_rwlock_rdlock (&lock)) 146 UNEXPECTED (2); 147 /* Step 3: Create the writer thread. */ 148 if (glthread_create (&writer, writer_func, NULL)) 149 UNEXPECTED (3); 150 /* Job done. Go to sleep. */ 151 for (;;) 152 { 153 sleep (1); 154 } 155 } 156 157 #else 158 159 /* No multithreading available. */ 160 161 #include <stdio.h> 162 163 int 164 main () /* */ 165 { 166 fputs ("Skipping test: multithreading not enabled\n", stderr); 167 return 77; 168 } 169 170 #endif