Add ValueLatch interface and use it in IPC tests 71/33671/10
authorLukasz Kostyra <l.kostyra@samsung.com>
Tue, 13 Jan 2015 10:13:44 +0000 (11:13 +0100)
committerLukasz Kostyra <l.kostyra@samsung.com>
Wed, 21 Jan 2015 14:43:22 +0000 (15:43 +0100)
[Feature]       New ValueLatch interface.
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install, run tests

Change-Id: I90e4f7523f57b2f5cb8543e932e0fadffff5b824

common/utils/value-latch.hpp [new file with mode: 0644]
tests/unit_tests/ipc/ut-ipc.cpp
tests/unit_tests/utils/ut-value-latch.cpp [new file with mode: 0644]

diff --git a/common/utils/value-latch.hpp b/common/utils/value-latch.hpp
new file mode 100644 (file)
index 0000000..72c7873
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+*
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License
+*/
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Definition of ValueLatch template, used to wait for variable to be set.
+ */
+
+#ifndef COMMON_UTILS_VALUE_LATCH_H
+#define COMMON_UTILS_VALUE_LATCH_H
+
+#include "utils/exception.hpp"
+
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+
+namespace vasum {
+namespace utils {
+
+template <typename T>
+class ValueLatch {
+public:
+    /**
+     * Assigns value to kept variable and sets Latch.
+     *
+     * @param value Value to set.
+     */
+    void set(const T& value);
+
+    /**
+     * Assigns value to kept variable and sets Latch.
+     *
+     * @param value Value to set.
+     */
+    void set(T&& value);
+
+    /**
+     * Waits until set() is called, then set value is moved to caller.
+     *
+     * @return Value provided by set().
+     */
+    T get();
+
+    /**
+     * Waits until set() is called, or until timeout occurs. Then, set value is moved to caller.
+     *
+     * @param timeoutMs Maximum time to wait for value to be set.
+     *
+     * @return Value provided by set().
+     */
+    T get(const unsigned int timeoutMs);
+
+private:
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    std::unique_ptr<T> mValue;
+};
+
+template <typename T>
+void ValueLatch<T>::set(const T& value)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mValue) {
+        throw UtilsException("Cannot set ValueLatch multiple times!");
+    }
+    mValue.reset(new T(value));
+    mCondition.notify_one();
+}
+
+template <typename T>
+void ValueLatch<T>::set(T&& value)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mValue) {
+        throw UtilsException("Cannot set ValueLatch multiple times!");
+    }
+    mValue.reset(new T(std::move(value)));
+    mCondition.notify_one();
+}
+
+template <typename T>
+T ValueLatch<T>::get()
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    mCondition.wait(lock, [this]() {
+        return (bool)mValue;
+    });
+    std::unique_ptr<T> retValue(std::move(mValue));
+    return T(std::move(*retValue));
+}
+
+template <typename T>
+T ValueLatch<T>::get(const unsigned int timeoutMs)
+{
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (mCondition.wait_for(lock, std::chrono::milliseconds(timeoutMs), [this]() {
+                                return (bool)mValue;
+                            }) ) {
+        std::unique_ptr<T> retValue(std::move(mValue));
+        return T(std::move(*retValue));
+    } else {
+        throw UtilsException("Timeout occured.");
+    }
+}
+
+} // namespace utils
+} // namespace vasum
+
+#endif // COMMON_UTILS_VALUE_LATCH_H
index de420df..65ee470 100644 (file)
@@ -37,6 +37,7 @@
 #include "ipc/types.hpp"
 #include "utils/glib-loop.hpp"
 #include "utils/latch.hpp"
+#include "utils/value-latch.hpp"
 
 #include "config/fields.hpp"
 #include "logger/logger.hpp"
@@ -150,14 +151,9 @@ std::shared_ptr<SendData> longEchoCallback(const FileDescriptor, std::shared_ptr
 FileDescriptor connect(Service& s, Client& c)
 {
     // Connects the Client to the Service and returns Clients FileDescriptor
-    std::mutex mutex;
-    std::condition_variable cv;
-
-    FileDescriptor peerFD = 0;
-    auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFD) {
-        std::unique_lock<std::mutex> lock(mutex);
-        peerFD = newFD;
-        cv.notify_all();
+    ValueLatch<FileDescriptor> peerFDLatch;
+    auto newPeerCallback = [&peerFDLatch](const FileDescriptor newFD) {
+        peerFDLatch.set(newFD);
     };
 
     s.setNewPeerCallback(newPeerCallback);
@@ -168,38 +164,25 @@ FileDescriptor connect(Service& s, Client& c)
 
     c.start();
 
-
-    std::unique_lock<std::mutex> lock(mutex);
-    BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(3 * TIMEOUT), [&peerFD]() {
-        return peerFD != 0;
-    }));
-    // Remove the callback
+    FileDescriptor peerFD = peerFDLatch.get(TIMEOUT);
     s.setNewPeerCallback(nullptr);
-    BOOST_REQUIRE(peerFD != 0);
-
+    BOOST_REQUIRE_NE(peerFD, 0);
     return peerFD;
 }
 
-
-
 #if GLIB_CHECK_VERSION(2,36,0)
 
 std::pair<FileDescriptor, IPCGSource::Pointer> connectServiceGSource(Service& s, Client& c)
 {
-    std::mutex mutex;
-    std::condition_variable cv;
-
-    FileDescriptor peerFD = 0;
+    ValueLatch<FileDescriptor> peerFDLatch;
     IPCGSource::Pointer ipcGSourcePtr = IPCGSource::create(s.getFDs(), std::bind(&Service::handle, &s, _1, _2));
 
-    auto newPeerCallback = [&cv, &peerFD, &mutex, ipcGSourcePtr](const FileDescriptor newFD) {
+    auto newPeerCallback = [&peerFDLatch, ipcGSourcePtr](const FileDescriptor newFD) {
         if (ipcGSourcePtr) {
             //TODO: Remove this if
             ipcGSourcePtr->addFD(newFD);
         }
-        std::unique_lock<std::mutex> lock(mutex);
-        peerFD = newFD;
-        cv.notify_all();
+        peerFDLatch.set(newFD);
     };
 
 
@@ -211,28 +194,18 @@ std::pair<FileDescriptor, IPCGSource::Pointer> connectServiceGSource(Service& s,
 
     c.start();
 
-    std::unique_lock<std::mutex> lock(mutex);
-    BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(3 * TIMEOUT), [&peerFD]() {
-        return peerFD != 0;
-    }));
-    // remove the callback
+    FileDescriptor peerFD = peerFDLatch.get(TIMEOUT);
     s.setNewPeerCallback(nullptr);
-    BOOST_REQUIRE(peerFD != 0);
-
+    BOOST_REQUIRE_NE(peerFD, 0);
     return std::make_pair(peerFD, ipcGSourcePtr);
 }
 
 std::pair<FileDescriptor, IPCGSource::Pointer> connectClientGSource(Service& s, Client& c)
 {
     // Connects the Client to the Service and returns Clients FileDescriptor
-    std::mutex mutex;
-    std::condition_variable cv;
-
-    FileDescriptor peerFD = 0;
-    auto newPeerCallback = [&cv, &peerFD, &mutex](const FileDescriptor newFD) {
-        std::unique_lock<std::mutex> lock(mutex);
-        peerFD = newFD;
-        cv.notify_all();
+    ValueLatch<FileDescriptor> peerFDLatch;
+    auto newPeerCallback = [&peerFDLatch](const FileDescriptor newFD) {
+        peerFDLatch.set(newFD);
     };
     s.setNewPeerCallback(newPeerCallback);
 
@@ -248,14 +221,9 @@ std::pair<FileDescriptor, IPCGSource::Pointer> connectClientGSource(Service& s,
 
     ipcGSourcePtr->attach();
 
-    std::unique_lock<std::mutex> lock(mutex);
-    BOOST_CHECK(cv.wait_for(lock, std::chrono::milliseconds(3 * TIMEOUT), [&peerFD]() {
-        return peerFD != 0;
-    }));
-    // Remove the callback
+    FileDescriptor peerFD = peerFDLatch.get(TIMEOUT);
     s.setNewPeerCallback(nullptr);
-    BOOST_REQUIRE(peerFD != 0);
-
+    BOOST_REQUIRE_NE(peerFD, 0);
     return std::make_pair(peerFD, ipcGSourcePtr);
 }
 
@@ -413,11 +381,8 @@ BOOST_AUTO_TEST_CASE(SyncServiceToClientEcho)
 
 BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho)
 {
-    std::mutex mutex;
-    std::condition_variable cv;
-
     std::shared_ptr<SendData> sentData(new SendData(34));
-    std::shared_ptr<SendData> recvData;
+    ValueLatch<SendData> recvDataLatch;
 
     // Setup Service and Client
     Service s(socketPath);
@@ -427,31 +392,22 @@ BOOST_AUTO_TEST_CASE(AsyncClientToServiceEcho)
     c.start();
 
     //Async call
-    auto dataBack = [&cv, &recvData, &mutex](ipc::Status status, std::shared_ptr<SendData>& data) {
+    auto dataBack = [&recvDataLatch](ipc::Status status, std::shared_ptr<SendData>& data) {
         if (status == ipc::Status::OK) {
-            std::unique_lock<std::mutex> lock(mutex);
-            recvData = data;
+            recvDataLatch.set(*data);
         }
-        cv.notify_one();
     };
     c.callAsync<SendData, SendData>(1, sentData, dataBack);
 
     // Wait for the response
-    std::unique_lock<std::mutex> lock(mutex);
-    BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(TIMEOUT), [&recvData]() {
-        return static_cast<bool>(recvData);
-    }));
-    BOOST_REQUIRE(recvData);
+    std::shared_ptr<SendData> recvData(new SendData(recvDataLatch.get(TIMEOUT)));
     BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
 }
 
 BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho)
 {
     std::shared_ptr<SendData> sentData(new SendData(56));
-    std::shared_ptr<SendData> recvData;
-
-    std::mutex mutex;
-    std::condition_variable cv;
+    ValueLatch<SendData> recvDataLatch;
 
     Service s(socketPath);
     Client c(socketPath);
@@ -459,23 +415,16 @@ BOOST_AUTO_TEST_CASE(AsyncServiceToClientEcho)
     FileDescriptor peerFD = connect(s, c);
 
     // Async call
-    auto dataBack = [&cv, &recvData, &mutex](ipc::Status status, std::shared_ptr<SendData>& data) {
+    auto dataBack = [&recvDataLatch](ipc::Status status, std::shared_ptr<SendData>& data) {
         if (status == ipc::Status::OK) {
-            std::unique_lock<std::mutex> lock(mutex);
-            recvData = data;
+            recvDataLatch.set(*data);
         }
-        cv.notify_one();
     };
 
     s.callAsync<SendData, SendData>(1, peerFD, sentData, dataBack);
 
     // Wait for the response
-    std::unique_lock<std::mutex> lock(mutex);
-    BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(TIMEOUT), [&recvData]() {
-        return recvData.get() != nullptr;
-    }));
-
-    BOOST_REQUIRE(recvData);
+    std::shared_ptr<SendData> recvData(new SendData(recvDataLatch.get(TIMEOUT)));
     BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal);
 }
 
@@ -521,6 +470,7 @@ BOOST_AUTO_TEST_CASE(ParseError)
 
 BOOST_AUTO_TEST_CASE(DisconnectedPeerError)
 {
+    ValueLatch<ipc::Status> retStatusLatch;
     Service s(socketPath);
 
     auto method = [](const FileDescriptor, std::shared_ptr<ThrowOnAcceptData>&) {
@@ -531,27 +481,18 @@ BOOST_AUTO_TEST_CASE(DisconnectedPeerError)
     s.addMethodHandler<SendData, ThrowOnAcceptData>(1, method);
     s.start();
 
-    std::mutex mutex;
-    std::condition_variable cv;
-    ipc::Status retStatus = ipc::Status::UNDEFINED;
-
     Client c(socketPath);
     c.start();
 
-    auto dataBack = [&cv, &retStatus, &mutex](ipc::Status status, std::shared_ptr<SendData>&) {
-        std::unique_lock<std::mutex> lock(mutex);
-        retStatus = status;
-        cv.notify_one();
+    auto dataBack = [&retStatusLatch](ipc::Status status, std::shared_ptr<SendData>&) {
+        retStatusLatch.set(status);
     };
 
     std::shared_ptr<SendData> sentData(new SendData(78));
     c.callAsync<SendData, SendData>(1, sentData, dataBack);
 
     // Wait for the response
-    std::unique_lock<std::mutex> lock(mutex);
-    BOOST_REQUIRE(cv.wait_for(lock, std::chrono::milliseconds(TIMEOUT), [&retStatus]() {
-        return retStatus != ipc::Status::UNDEFINED;
-    }));
+    ipc::Status retStatus = retStatusLatch.get(TIMEOUT);
 
     // The disconnection might have happened:
     // - after sending the message (PEER_DISCONNECTED)
@@ -663,6 +604,7 @@ BOOST_AUTO_TEST_CASE(AddSignalOffline)
 
 #if GLIB_CHECK_VERSION(2,36,0)
 
+// FIXME This test causes segfault, however it should work in GDB.
 BOOST_AUTO_TEST_CASE(ServiceGSource)
 {
     utils::Latch l;
diff --git a/tests/unit_tests/utils/ut-value-latch.cpp b/tests/unit_tests/utils/ut-value-latch.cpp
new file mode 100644 (file)
index 0000000..a16128e
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Lukasz Kostyra <l.kostyra@samsung.com>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+
+/**
+ * @file
+ * @author  Lukasz Kostyra (l.kostyra@samsung.com)
+ * @brief   Unit tests of ValueLatch interface
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "utils/value-latch.hpp"
+
+#include <thread>
+#include <string>
+
+BOOST_AUTO_TEST_SUITE(ValueLatchSuite)
+
+using namespace vasum::utils;
+
+namespace
+{
+    const int TIMEOUT = 1000; // ms
+    const int EXPECTED_TIMEOUT = 200; // ms
+    const std::string TEST_STRING = "some_random text";
+
+    struct ComplexType
+    {
+        float value;
+        std::string str;
+    };
+
+    struct ComplexMovableType
+    {
+        explicit ComplexMovableType(const ComplexType& val)
+            : value(val) {};
+        ComplexMovableType(const ComplexMovableType&) = delete;
+        ComplexMovableType(ComplexMovableType&&) = default;
+
+        ComplexType value;
+    };
+} // namespace
+
+BOOST_AUTO_TEST_CASE(SimpleTypeTest)
+{
+    ValueLatch<int> testLatch;
+
+    std::thread testThread([&testLatch]() {
+        testLatch.set(3);
+    });
+
+    testThread.join();
+
+    BOOST_REQUIRE_EQUAL(testLatch.get(TIMEOUT), 3);
+}
+
+BOOST_AUTO_TEST_CASE(ComplexTypeTest)
+{
+    ValueLatch<ComplexType> testLatch;
+
+    std::thread testThread([&testLatch]() {
+        testLatch.set({ 2.5f, TEST_STRING });
+    });
+
+    testThread.join();
+
+    ComplexType test(testLatch.get(TIMEOUT));
+    BOOST_REQUIRE_EQUAL(test.value, 2.5f);
+    BOOST_REQUIRE_EQUAL(test.str, TEST_STRING);
+}
+
+BOOST_AUTO_TEST_CASE(ComplexMovableTypeTest)
+{
+    ValueLatch<ComplexMovableType> testLatch;
+
+    std::thread testThread([&testLatch]() {
+        testLatch.set( ComplexMovableType({ 2.5f, TEST_STRING }) );
+    });
+
+    testThread.join();
+
+    ComplexMovableType test(testLatch.get(TIMEOUT));
+    BOOST_REQUIRE_EQUAL(test.value.value, 2.5f);
+    BOOST_REQUIRE_EQUAL(test.value.str, TEST_STRING);
+}
+
+BOOST_AUTO_TEST_CASE(TimeoutTest)
+{
+    ValueLatch<int> testLatch;
+
+    BOOST_REQUIRE_THROW(testLatch.get(EXPECTED_TIMEOUT), vasum::UtilsException);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleSetTest)
+{
+    ValueLatch<int> testLatch;
+
+    testLatch.set(3);
+    BOOST_REQUIRE_THROW(testLatch.set(2), vasum::UtilsException);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleGetTest)
+{
+    ValueLatch<int> testLatch;
+
+    testLatch.set(3);
+    testLatch.get(TIMEOUT);
+    BOOST_REQUIRE_THROW(testLatch.get(EXPECTED_TIMEOUT), vasum::UtilsException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()