Serenity Operating System
at master 199 lines 5.5 kB view raw
1/* 2 * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Assertions.h> 8#include <LibMain/Main.h> 9#include <LibThreading/Thread.h> 10#include <errno.h> 11#include <pthread.h> 12#include <semaphore.h> 13#include <unistd.h> 14 15static ErrorOr<void> test_once() 16{ 17 constexpr size_t threads_count = 10; 18 19 static Vector<int> v; 20 v.clear(); 21 pthread_once_t once = PTHREAD_ONCE_INIT; 22 Vector<NonnullRefPtr<Threading::Thread>, threads_count> threads; 23 24 for (size_t i = 0; i < threads_count; i++) { 25 threads.unchecked_append(TRY(Threading::Thread::try_create([&] { 26 return pthread_once(&once, [] { 27 v.append(35); 28 sleep(1); 29 }); 30 }))); 31 threads.last()->start(); 32 } 33 // clang-format off 34 // It wants to put [[maybe_unused]] on its own line, for some reason. 35 for (auto& thread : threads) 36 [[maybe_unused]] auto res = thread->join(); 37 // clang-format on 38 39 VERIFY(v.size() == 1); 40 41 return {}; 42} 43 44static ErrorOr<void> test_mutex() 45{ 46 constexpr size_t threads_count = 10; 47 constexpr size_t num_times = 100; 48 49 Vector<int> v; 50 Vector<NonnullRefPtr<Threading::Thread>, threads_count> threads; 51 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 52 53 for (size_t i = 0; i < threads_count; i++) { 54 threads.unchecked_append(TRY(Threading::Thread::try_create([&] { 55 for (size_t j = 0; j < num_times; j++) { 56 pthread_mutex_lock(&mutex); 57 v.append(35); 58 sched_yield(); 59 pthread_mutex_unlock(&mutex); 60 sched_yield(); 61 } 62 return 0; 63 }))); 64 threads.last()->start(); 65 } 66 // clang-format off 67 // It wants to put [[maybe_unused]] on its own line, for some reason. 68 for (auto& thread : threads) 69 [[maybe_unused]] auto res = thread->join(); 70 // clang-format on 71 72 VERIFY(v.size() == threads_count * num_times); 73 VERIFY(pthread_mutex_trylock(&mutex) == 0); 74 VERIFY(pthread_mutex_trylock(&mutex) == EBUSY); 75 76 return {}; 77} 78 79static ErrorOr<void> test_semaphore_as_lock() 80{ 81 constexpr size_t threads_count = 10; 82 constexpr size_t num_times = 100; 83 84 Vector<int> v; 85 Vector<NonnullRefPtr<Threading::Thread>, threads_count> threads; 86 sem_t semaphore; 87 sem_init(&semaphore, 0, 1); 88 89 for (size_t i = 0; i < threads_count; i++) { 90 threads.unchecked_append(TRY(Threading::Thread::try_create([&] { 91 for (size_t j = 0; j < num_times; j++) { 92 sem_wait(&semaphore); 93 v.append(35); 94 sched_yield(); 95 sem_post(&semaphore); 96 sched_yield(); 97 } 98 return 0; 99 }))); 100 threads.last()->start(); 101 } 102 // clang-format off 103 // It wants to put [[maybe_unused]] on its own line, for some reason. 104 for (auto& thread : threads) 105 [[maybe_unused]] auto res = thread->join(); 106 // clang-format on 107 108 VERIFY(v.size() == threads_count * num_times); 109 VERIFY(sem_trywait(&semaphore) == 0); 110 VERIFY(sem_trywait(&semaphore) == EAGAIN); 111 112 return {}; 113} 114 115static ErrorOr<void> test_semaphore_as_event() 116{ 117 Vector<int> v; 118 sem_t semaphore; 119 sem_init(&semaphore, 0, 0); 120 121 auto reader = TRY(Threading::Thread::try_create([&] { 122 sem_wait(&semaphore); 123 VERIFY(v.size() == 1); 124 return 0; 125 })); 126 reader->start(); 127 128 auto writer = TRY(Threading::Thread::try_create([&] { 129 sched_yield(); 130 v.append(35); 131 sem_post(&semaphore); 132 return 0; 133 })); 134 writer->start(); 135 136 [[maybe_unused]] auto r1 = reader->join(); 137 [[maybe_unused]] auto r2 = writer->join(); 138 139 VERIFY(sem_trywait(&semaphore) == EAGAIN); 140 141 return {}; 142} 143 144static ErrorOr<void> test_semaphore_nonbinary() 145{ 146 constexpr size_t num = 5; 147 constexpr size_t threads_count = 10; 148 constexpr size_t num_times = 100; 149 150 Vector<NonnullRefPtr<Threading::Thread>, threads_count> threads; 151 sem_t semaphore; 152 sem_init(&semaphore, 0, num); 153 154 Atomic<u32, AK::memory_order_relaxed> value = 0; 155 Atomic<bool, AK::memory_order_relaxed> seen_more_than_two = false; 156 157 for (size_t i = 0; i < threads_count; i++) { 158 threads.unchecked_append(TRY(Threading::Thread::try_create([&] { 159 for (size_t j = 0; j < num_times; j++) { 160 sem_wait(&semaphore); 161 u32 v = 1 + value.fetch_add(1); 162 VERIFY(v <= num); 163 if (v > 2) 164 seen_more_than_two.store(true); 165 sched_yield(); 166 value.fetch_sub(1); 167 sem_post(&semaphore); 168 } 169 return 0; 170 }))); 171 threads.last()->start(); 172 } 173 // clang-format off 174 // It wants to put [[maybe_unused]] on its own line, for some reason. 175 for (auto& thread : threads) 176 [[maybe_unused]] auto res = thread->join(); 177 // clang-format on 178 179 VERIFY(value.load() == 0); 180 VERIFY(seen_more_than_two.load()); 181 for (size_t i = 0; i < num; i++) { 182 VERIFY(sem_trywait(&semaphore) == 0); 183 } 184 VERIFY(sem_trywait(&semaphore) == EAGAIN); 185 186 return {}; 187} 188 189ErrorOr<int> serenity_main(Main::Arguments) 190{ 191 TRY(test_once()); 192 TRY(test_mutex()); 193 194 TRY(test_semaphore_as_lock()); 195 TRY(test_semaphore_as_event()); 196 TRY(test_semaphore_nonbinary()); 197 198 return 0; 199}