1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <inttypes.h> 6#include <stddef.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <threads.h> 11#include <unittest/unittest.h> 12#include <zircon/syscalls.h> 13#include <zircon/thread_annotations.h> 14#include <zircon/time.h> 15#include <zircon/types.h> 16 17static mtx_t g_mutex = MTX_INIT; 18 19static void xlog(const char* str) { 20 zx_time_t now = zx_clock_get(ZX_CLOCK_UTC); 21 unittest_printf("[%08" PRIu64 ".%08" PRIu64 "]: %s", 22 now / 1000000000, now % 1000000000, str); 23} 24 25static int mutex_thread_1(void* arg) { 26 xlog("thread 1 started\n"); 27 28 for (int times = 0; times < 300; times++) { 29 mtx_lock(&g_mutex); 30 zx_nanosleep(zx_deadline_after(ZX_USEC(1))); 31 mtx_unlock(&g_mutex); 32 } 33 34 xlog("thread 1 done\n"); 35 return 0; 36} 37 38static int mutex_thread_2(void* arg) { 39 xlog("thread 2 started\n"); 40 41 for (int times = 0; times < 150; times++) { 42 mtx_lock(&g_mutex); 43 zx_nanosleep(zx_deadline_after(ZX_USEC(2))); 44 mtx_unlock(&g_mutex); 45 } 46 47 xlog("thread 2 done\n"); 48 return 0; 49} 50 51static int mutex_thread_3(void* arg) { 52 xlog("thread 3 started\n"); 53 54 for (int times = 0; times < 100; times++) { 55 mtx_lock(&g_mutex); 56 zx_nanosleep(zx_deadline_after(ZX_USEC(3))); 57 mtx_unlock(&g_mutex); 58 } 59 60 xlog("thread 3 done\n"); 61 return 0; 62} 63 64static bool got_lock_1 = false; 65static bool got_lock_2 = false; 66static bool got_lock_3 = false; 67 68// These tests all conditionally acquire the lock, by design. The 69// thread safety analysis is not up to this, so disable it. 70static int mutex_try_thread_1(void* arg) TA_NO_THREAD_SAFETY_ANALYSIS { 71 xlog("thread 1 started\n"); 72 73 for (int times = 0; times < 300 || !got_lock_1; times++) { 74 int status = mtx_trylock(&g_mutex); 75 zx_nanosleep(zx_deadline_after(ZX_USEC(1))); 76 if (status == thrd_success) { 77 got_lock_1 = true; 78 mtx_unlock(&g_mutex); 79 } 80 } 81 82 xlog("thread 1 done\n"); 83 return 0; 84} 85 86static int mutex_try_thread_2(void* arg) TA_NO_THREAD_SAFETY_ANALYSIS { 87 xlog("thread 2 started\n"); 88 89 for (int times = 0; times < 150 || !got_lock_2; times++) { 90 int status = mtx_trylock(&g_mutex); 91 zx_nanosleep(zx_deadline_after(ZX_USEC(2))); 92 if (status == thrd_success) { 93 got_lock_2 = true; 94 mtx_unlock(&g_mutex); 95 } 96 } 97 98 xlog("thread 2 done\n"); 99 return 0; 100} 101 102static int mutex_try_thread_3(void* arg) TA_NO_THREAD_SAFETY_ANALYSIS { 103 xlog("thread 3 started\n"); 104 105 for (int times = 0; times < 100 || !got_lock_3; times++) { 106 int status = mtx_trylock(&g_mutex); 107 zx_nanosleep(zx_deadline_after(ZX_USEC(3))); 108 if (status == thrd_success) { 109 got_lock_3 = true; 110 mtx_unlock(&g_mutex); 111 } 112 } 113 114 xlog("thread 3 done\n"); 115 return 0; 116} 117 118static bool test_initializer(void) { 119 BEGIN_TEST; 120 121 int ret = mtx_init(&g_mutex, mtx_timed); 122 ASSERT_EQ(ret, thrd_success, "failed to initialize mtx_t"); 123 124 END_TEST; 125} 126 127static bool test_mutexes(void) { 128 BEGIN_TEST; 129 thrd_t thread1, thread2, thread3; 130 131 thrd_create_with_name(&thread1, mutex_thread_1, NULL, "thread 1"); 132 thrd_create_with_name(&thread2, mutex_thread_2, NULL, "thread 2"); 133 thrd_create_with_name(&thread3, mutex_thread_3, NULL, "thread 3"); 134 135 thrd_join(thread1, NULL); 136 thrd_join(thread2, NULL); 137 thrd_join(thread3, NULL); 138 139 END_TEST; 140} 141 142static bool test_try_mutexes(void) { 143 BEGIN_TEST; 144 thrd_t thread1, thread2, thread3; 145 146 thrd_create_with_name(&thread1, mutex_try_thread_1, NULL, "thread 1"); 147 thrd_create_with_name(&thread2, mutex_try_thread_2, NULL, "thread 2"); 148 thrd_create_with_name(&thread3, mutex_try_thread_3, NULL, "thread 3"); 149 150 thrd_join(thread1, NULL); 151 thrd_join(thread2, NULL); 152 thrd_join(thread3, NULL); 153 154 EXPECT_TRUE(got_lock_1, "failed to get lock 1"); 155 EXPECT_TRUE(got_lock_2, "failed to get lock 2"); 156 EXPECT_TRUE(got_lock_3, "failed to get lock 3"); 157 158 END_TEST; 159} 160 161static bool test_static_initializer(void) { 162 BEGIN_TEST; 163 164 static mtx_t static_mutex = MTX_INIT; 165 mtx_t auto_mutex; 166 memset(&auto_mutex, 0xae, sizeof(auto_mutex)); 167 mtx_init(&auto_mutex, mtx_plain); 168 169 EXPECT_BYTES_EQ((const uint8_t*)&static_mutex, (const uint8_t*)&auto_mutex, sizeof(mtx_t), "MTX_INIT and mtx_init differ!"); 170 171 END_TEST; 172} 173 174typedef struct { 175 mtx_t mutex; 176 zx_handle_t start_event; 177 zx_handle_t done_event; 178} timeout_args; 179 180static int test_timeout_helper(void* ctx) TA_NO_THREAD_SAFETY_ANALYSIS { 181 timeout_args* args = ctx; 182 ASSERT_EQ(mtx_lock(&args->mutex), thrd_success, "f to lock"); 183 // Inform the main thread that we have acquired the lock. 184 ASSERT_EQ(zx_object_signal(args->start_event, 0, ZX_EVENT_SIGNALED), ZX_OK, 185 "failed to signal"); 186 // Wait until the main thread has completed its test. 187 ASSERT_EQ(zx_object_wait_one(args->done_event, ZX_EVENT_SIGNALED, ZX_TIME_INFINITE, NULL), 188 ZX_OK, "failed to wait"); 189 ASSERT_EQ(mtx_unlock(&args->mutex), thrd_success, "failed to unlock"); 190 return 0; 191} 192 193static bool test_timeout_elapsed(void) { 194 BEGIN_TEST; 195 196 const zx_duration_t kRelativeDeadline = ZX_MSEC(100); 197 198 timeout_args args; 199 ASSERT_EQ(thrd_success, mtx_init(&args.mutex, mtx_plain), "could not create mutex"); 200 ASSERT_EQ(zx_event_create(0, &args.start_event), ZX_OK, "could not create event"); 201 ASSERT_EQ(zx_event_create(0, &args.done_event), ZX_OK, "could not create event"); 202 203 thrd_t helper; 204 ASSERT_EQ(thrd_create(&helper, test_timeout_helper, &args), thrd_success, ""); 205 // Wait for the helper thread to acquire the lock. 206 ASSERT_EQ(zx_object_wait_one(args.start_event, ZX_EVENT_SIGNALED, ZX_TIME_INFINITE, NULL), 207 ZX_OK, "failed to wait"); 208 209 for (int i = 0; i < 5; ++i) { 210 zx_time_t now = zx_clock_get(ZX_CLOCK_UTC); 211 struct timespec then = { 212 .tv_sec = now / ZX_SEC(1), 213 .tv_nsec = now % ZX_SEC(1), 214 }; 215 then.tv_nsec += kRelativeDeadline; 216 if (then.tv_nsec > (long)ZX_SEC(1)) { 217 then.tv_nsec -= ZX_SEC(1); 218 then.tv_sec += 1; 219 } 220 int rc = mtx_timedlock(&args.mutex, &then); 221 ASSERT_EQ(rc, thrd_timedout, "wait should time out"); 222 zx_duration_t elapsed = zx_time_sub_time(zx_clock_get(ZX_CLOCK_UTC), now); 223 EXPECT_GE(elapsed, kRelativeDeadline, "wait returned early"); 224 } 225 226 // Inform the helper thread that we are done. 227 ASSERT_EQ(zx_object_signal(args.done_event, 0, ZX_EVENT_SIGNALED), 228 ZX_OK, "failed to signal"); 229 ASSERT_EQ(thrd_join(helper, NULL), thrd_success, "failed to join"); 230 231 mtx_destroy(&args.mutex); 232 ASSERT_EQ(zx_handle_close(args.start_event), ZX_OK, "failed to close event"); 233 ASSERT_EQ(zx_handle_close(args.done_event), ZX_OK, "failed to close event"); 234 235 END_TEST; 236} 237 238BEGIN_TEST_CASE(mtx_tests) 239RUN_TEST(test_initializer) 240RUN_TEST(test_mutexes) 241RUN_TEST(test_try_mutexes) 242RUN_TEST(test_static_initializer) 243RUN_TEST(test_timeout_elapsed) 244END_TEST_CASE(mtx_tests) 245 246#ifndef BUILD_COMBINED_TESTS 247int main(int argc, char** argv) { 248 return unittest_run_all_tests(argc, argv) ? 0 : -1; 249} 250#endif 251