--- /dev/null
+/*
+* 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
#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"
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);
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);
};
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);
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);
}
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);
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);
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);
}
BOOST_AUTO_TEST_CASE(DisconnectedPeerError)
{
+ ValueLatch<ipc::Status> retStatusLatch;
Service s(socketPath);
auto method = [](const FileDescriptor, std::shared_ptr<ThrowOnAcceptData>&) {
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)
#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;
--- /dev/null
+/*
+ * 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()