1 // Copyright 2018 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 #ifndef ZIRCON_SYSTEM_UTEST_CORE_CONDITION_GENERIC_CONDITION_GENERIC_H_
6 #define ZIRCON_SYSTEM_UTEST_CORE_CONDITION_GENERIC_CONDITION_GENERIC_H_
7 
8 #include <sched.h>
9 #include <threads.h>
10 #include <zircon/syscalls.h>
11 
12 #include <unittest/unittest.h>
13 
14 template <typename Mutex, typename Condition>
15 class GenericConditionTest {
16 public:
condition_test()17     static bool condition_test() {
18         BEGIN_TEST;
19 
20         Context ctx;
21 
22         thrd_t thread1, thread2, thread3;
23 
24         thrd_create(&thread1, cond_thread, &ctx);
25         thrd_create(&thread2, cond_thread, &ctx);
26         thrd_create(&thread3, cond_thread, &ctx);
27 
28         // Wait for all the threads to report that they've started.
29         while (true) {
30             ctx.mutex.lock();
31             int threads = ctx.threads_started;
32             ctx.mutex.unlock();
33             if (threads == 3) {
34                 break;
35             }
36             sched_yield();
37         }
38 
39         ctx.cond.broadcast();
40 
41         // Wait for all the threads to report that they were woken.
42         while (true) {
43             ctx.mutex.lock();
44             int threads = ctx.threads_woke_first_barrier;
45             ctx.mutex.unlock();
46             if (threads == 3) {
47                 break;
48             }
49             sched_yield();
50         }
51 
52         for (int iteration = 0; iteration < 3; iteration++) {
53             ctx.cond.signal();
54 
55             // Wait for one thread to report that it was woken.
56             while (true) {
57                 ctx.mutex.lock();
58                 int threads = ctx.threads_waked;
59                 ctx.mutex.unlock();
60                 if (threads == iteration + 1) {
61                     break;
62                 }
63                 sched_yield();
64             }
65         }
66 
67         thrd_join(thread1, nullptr);
68         thrd_join(thread2, nullptr);
69         thrd_join(thread3, nullptr);
70 
71         END_TEST;
72     }
73 
condition_timeout_test()74     static bool condition_timeout_test() {
75         BEGIN_TEST;
76 
77         Condition cond;
78         Mutex mutex;
79 
80         mutex.lock();
81         zx_status_t result = cond.timedwait(&mutex, ZX_MSEC(1));
82         mutex.unlock();
83 
84         EXPECT_EQ(result, ZX_ERR_TIMED_OUT, "Lock should have timeout");
85 
86         END_TEST;
87     }
88 
89  private:
90     struct Context {
91         Mutex mutex;
92         Condition cond;
93         int threads_waked = 0;
94         int threads_started = 0;
95         int threads_woke_first_barrier = 0;
96     };
97 
cond_thread(void * arg)98     static int cond_thread(void* arg) {
99         auto* ctx = static_cast<Context*>(arg);
100 
101         ctx->mutex.lock();
102         ctx->threads_started++;
103         ctx->cond.wait(&ctx->mutex);
104         ctx->threads_woke_first_barrier++;
105         ctx->cond.wait(&ctx->mutex);
106         ctx->threads_waked++;
107         ctx->mutex.unlock();
108         return 0;
109     }
110 };
111 
112 #endif // ZIRCON_SYSTEM_UTEST_CORE_CONDITION_GENERIC_CONDITION_GENERIC_H_
113