[fbl] Add wrapper around Condition Variable

Test: utest/fbl/condition_variable_tests.cpp

Change-Id: I63be83d4935f7d8a19ec15e0c6298d1a5e425665
diff --git a/system/ulib/fbl/include/fbl/condition_variable.h b/system/ulib/fbl/include/fbl/condition_variable.h
new file mode 100644
index 0000000..21eab06
--- /dev/null
+++ b/system/ulib/fbl/include/fbl/condition_variable.h
@@ -0,0 +1,50 @@
+// 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.
+
+#pragma once
+
+#ifdef __cplusplus
+
+// ConditionVariable is a C++ helper class intended to wrap a condition variable synchronization
+// primitive. It is also responsible for automatically initializing and destroying the internal
+// object.
+//
+// This object is currently only supported in userspace.
+#ifndef _KERNEL
+
+#include <threads.h>
+
+#include <fbl/macros.h>
+#include <fbl/mutex.h>
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+namespace fbl {
+
+class ConditionVariable {
+public:
+    ConditionVariable() { cnd_init(&cond_); }
+    ~ConditionVariable() { cnd_destroy(&cond_); }
+    DISALLOW_COPY_ASSIGN_AND_MOVE(ConditionVariable);
+
+    void Wait(Mutex* mutex) __TA_REQUIRES(mutex) {
+        cnd_wait(&cond_, mutex->GetInternal());
+    }
+
+    void Signal() {
+        cnd_signal(&cond_);
+    }
+
+    void Broadcast() {
+        cnd_broadcast(&cond_);
+    }
+
+private:
+    cnd_t cond_;
+};
+
+}
+
+#endif  // ifndef _KERNEL
+#endif  // ifdef __cplusplus
diff --git a/system/utest/fbl/condition_variable_tests.cpp b/system/utest/fbl/condition_variable_tests.cpp
new file mode 100644
index 0000000..c933664
--- /dev/null
+++ b/system/utest/fbl/condition_variable_tests.cpp
@@ -0,0 +1,53 @@
+// 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.
+
+#include <fbl/auto_lock.h>
+#include <fbl/condition_variable.h>
+#include <fbl/mutex.h>
+#include <unittest/unittest.h>
+
+namespace fbl {
+namespace {
+
+bool EmptySignalTest() {
+    BEGIN_TEST;
+
+    ConditionVariable cvar;
+    cvar.Signal();
+    cvar.Broadcast();
+
+    END_TEST;
+}
+
+bool WaitTest() {
+    BEGIN_TEST;
+
+    struct State {
+        Mutex mutex;
+        ConditionVariable cvar;
+    } state;
+
+    thrd_t thread;
+    AutoLock lock(&state.mutex);
+
+    thrd_create(&thread, [](void* arg) {
+        auto state = reinterpret_cast<State*>(arg);
+        AutoLock lock(&state->mutex);
+        state->cvar.Signal();
+        return 0;
+    }, &state);
+
+    state.cvar.Wait(&state.mutex);
+    thrd_join(thread, NULL);
+
+    END_TEST;
+}
+
+}  // namespace
+}  // namespace fbl
+
+BEGIN_TEST_CASE(ConditionVariableTests)
+RUN_TEST(fbl::EmptySignalTest)
+RUN_TEST(fbl::WaitTest)
+END_TEST_CASE(ConditionVariableTests);
diff --git a/system/utest/fbl/rules.mk b/system/utest/fbl/rules.mk
index 4fc4e89..2fac772 100644
--- a/system/utest/fbl/rules.mk
+++ b/system/utest/fbl/rules.mk
@@ -50,6 +50,7 @@
 # See: TODO(ZX-1053)
 #
 fbl_device_tests += \
+    $(LOCAL_DIR)/condition_variable_tests.cpp \
     $(LOCAL_DIR)/ref_counted_tests.cpp \
     $(LOCAL_DIR)/ref_counted_upgradeable_tests.cpp \
     $(LOCAL_DIR)/slab_allocator_tests.cpp \