| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef ZIRCON_SYSTEM_UTEST_CORE_CONDITION_GENERIC_CONDITION_GENERIC_H_ |
| #define ZIRCON_SYSTEM_UTEST_CORE_CONDITION_GENERIC_CONDITION_GENERIC_H_ |
| |
| #include <sched.h> |
| #include <threads.h> |
| #include <zircon/syscalls.h> |
| |
| #include <unittest/unittest.h> |
| |
| template <typename Mutex, typename Condition> |
| class GenericConditionTest { |
| public: |
| static bool condition_test() { |
| BEGIN_TEST; |
| |
| Context ctx; |
| |
| thrd_t thread1, thread2, thread3; |
| |
| thrd_create(&thread1, cond_thread, &ctx); |
| thrd_create(&thread2, cond_thread, &ctx); |
| thrd_create(&thread3, cond_thread, &ctx); |
| |
| // Wait for all the threads to report that they've started. |
| while (true) { |
| ctx.mutex.lock(); |
| int threads = ctx.threads_started; |
| ctx.mutex.unlock(); |
| if (threads == 3) { |
| break; |
| } |
| sched_yield(); |
| } |
| |
| ctx.cond.broadcast(); |
| |
| // Wait for all the threads to report that they were woken. |
| while (true) { |
| ctx.mutex.lock(); |
| int threads = ctx.threads_woke_first_barrier; |
| ctx.mutex.unlock(); |
| if (threads == 3) { |
| break; |
| } |
| sched_yield(); |
| } |
| |
| for (int iteration = 0; iteration < 3; iteration++) { |
| ctx.cond.signal(); |
| |
| // Wait for one thread to report that it was woken. |
| while (true) { |
| ctx.mutex.lock(); |
| int threads = ctx.threads_waked; |
| ctx.mutex.unlock(); |
| if (threads == iteration + 1) { |
| break; |
| } |
| sched_yield(); |
| } |
| } |
| |
| thrd_join(thread1, nullptr); |
| thrd_join(thread2, nullptr); |
| thrd_join(thread3, nullptr); |
| |
| END_TEST; |
| } |
| |
| static bool condition_timeout_test() { |
| BEGIN_TEST; |
| |
| Condition cond; |
| Mutex mutex; |
| |
| mutex.lock(); |
| zx_status_t result = cond.timedwait(&mutex, ZX_MSEC(1)); |
| mutex.unlock(); |
| |
| EXPECT_EQ(result, ZX_ERR_TIMED_OUT, "Lock should have timeout"); |
| |
| END_TEST; |
| } |
| |
| private: |
| struct Context { |
| Mutex mutex; |
| Condition cond; |
| int threads_waked = 0; |
| int threads_started = 0; |
| int threads_woke_first_barrier = 0; |
| }; |
| |
| static int cond_thread(void* arg) { |
| auto* ctx = static_cast<Context*>(arg); |
| |
| ctx->mutex.lock(); |
| ctx->threads_started++; |
| ctx->cond.wait(&ctx->mutex); |
| ctx->threads_woke_first_barrier++; |
| ctx->cond.wait(&ctx->mutex); |
| ctx->threads_waked++; |
| ctx->mutex.unlock(); |
| return 0; |
| } |
| }; |
| |
| #endif // ZIRCON_SYSTEM_UTEST_CORE_CONDITION_GENERIC_CONDITION_GENERIC_H_ |