# common
ADD_SUBDIRECTORY(common)
+ADD_SUBDIRECTORY(event)
ADD_SUBDIRECTORY(ipc)
+ADD_SUBDIRECTORY(klass)
ADD_SUBDIRECTORY(logger)
+ADD_SUBDIRECTORY(rmi)
ADD_SUBDIRECTORY(sdk)
+ADD_SUBDIRECTORY(transport)
# policy
ADD_SUBDIRECTORY(policy)
--- /dev/null
+# Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# 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
+
+ADD_VIST_COMMON_LIBRARY(vist_event eventfd.cpp
+ mainloop.cpp)
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 eventfd.cpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of event notification.
+ */
+
+#include "eventfd.hpp"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <stdexcept>
+
+namespace vist {
+namespace event {
+
+EventFD::EventFD(unsigned int initval, int flags)
+ : fd(::eventfd(initval, flags))
+{
+ if (this->fd == -1)
+ throw std::runtime_error("Failed to create eventfd.");
+}
+
+EventFD::~EventFD()
+{
+ ::close(fd);
+}
+
+void EventFD::send(void)
+{
+ const std::uint64_t val = 1;
+ if (::write(this->fd, &val, sizeof(val)) == -1)
+ throw std::runtime_error("Failed to write to eventfd.");
+}
+
+void EventFD::receive(void)
+{
+ std::uint64_t val;
+ if (::read(this->fd, &val, sizeof(val)) == -1)
+ throw std::runtime_error("Failed to read from eventfd.");
+}
+
+int EventFD::getFd(void) const noexcept
+{
+ return this->fd;
+}
+
+} // namespace event
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 eventfd.hpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief A file descriptor for event notification.
+ */
+
+#pragma once
+
+#include <sys/eventfd.h>
+
+namespace vist {
+namespace event {
+
+class EventFD final {
+public:
+ explicit EventFD(unsigned int initval = 0, int flags = EFD_SEMAPHORE | EFD_CLOEXEC);
+ ~EventFD();
+
+ EventFD(const EventFD&) = delete;
+ EventFD& operator=(const EventFD&) = delete;
+
+ EventFD(EventFD&&) = delete;
+ EventFD& operator=(EventFD&&) = delete;
+
+ void send(void);
+ void receive(void);
+
+ int getFd(void) const noexcept;
+
+private:
+ int fd;
+};
+
+} // namespace event
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 mainloop.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of mainloop.
+ */
+
+#include "mainloop.hpp"
+
+#include <vist/logger.hpp>
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <cstring>
+
+namespace vist {
+namespace event {
+
+Mainloop::Mainloop() :
+ epollFd(::epoll_create1(EPOLL_CLOEXEC)),
+ stopped(false)
+{
+ if (epollFd == -1)
+ throw std::runtime_error("Failed to create epoll instance.");
+}
+
+Mainloop::~Mainloop()
+{
+ ::close(this->epollFd);
+}
+
+void Mainloop::addHandler(const int fd, OnEvent&& onEvent, OnError&& onError)
+{
+ std::lock_guard<Mutex> lock(mutex);
+
+ if (this->listener.find(fd) != this->listener.end())
+ throw std::runtime_error("Event is already registered.");
+
+ ::epoll_event event;
+ std::memset(&event, 0, sizeof(epoll_event));
+
+ event.events = EPOLLIN | EPOLLHUP | EPOLLRDHUP;
+ event.data.fd = fd;
+
+ if (::epoll_ctl(this->epollFd, EPOLL_CTL_ADD, fd, &event) == -1)
+ throw std::runtime_error("Failed to add event source.");
+
+ auto onEventPtr = std::make_shared<OnEvent>(onEvent);
+ auto onErrorPtr = (onError != nullptr) ? std::make_shared<OnError>(onError) : nullptr;
+
+ auto handler = std::make_pair(std::move(onEventPtr), std::move(onErrorPtr));
+
+ this->listener.insert({fd, handler});
+}
+
+void Mainloop::removeHandler(const int fd)
+{
+ std::lock_guard<Mutex> lock(mutex);
+
+ auto iter = this->listener.find(fd);
+ if (iter == this->listener.end())
+ return;
+
+ this->listener.erase(iter);
+
+ ::epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+bool Mainloop::prepare(void)
+{
+ auto wakeup = [this]() {
+ this->wakeupSignal.receive();
+ this->removeHandler(wakeupSignal.getFd());
+ this->stopped = true;
+ };
+
+ this->addHandler(this->wakeupSignal.getFd(), wakeup);
+}
+
+bool Mainloop::dispatch(int timeout) noexcept
+{
+ int nfds;
+ ::epoll_event event[MAX_EPOLL_EVENTS];
+
+ do {
+ errno = 0;
+ nfds = ::epoll_wait(epollFd, event, MAX_EPOLL_EVENTS, timeout);
+ } while ((nfds == -1) && (errno == EINTR));
+
+ if (nfds <= 0)
+ return false;
+
+ for (int i = 0; i < nfds; i++) {
+ std::shared_ptr<OnEvent> onEvent;
+ std::shared_ptr<OnError> onError;
+
+ {
+ std::lock_guard<Mutex> lock(mutex);
+
+ auto iter = this->listener.find(event[i].data.fd);
+ if (iter == this->listener.end())
+ continue;
+
+ onEvent = iter->second.first;
+ onError = iter->second.second;
+ }
+
+ try {
+ if ((event[i].events & (EPOLLHUP | EPOLLRDHUP))) {
+ if (onError != nullptr)
+ (*onError)();
+ } else {
+ (*onEvent)();
+ }
+
+ } catch (std::exception& e) {
+ ERROR(VIST) << "EXCEPTION ON MAINLOOP: " << e.what();
+ }
+ }
+
+ return true;
+}
+
+void Mainloop::run(int timeout)
+{
+ bool done = false;
+ this->stopped = false;
+
+ this->prepare();
+
+ while (!this->stopped && !done) {
+ done = !dispatch(timeout);
+ }
+}
+
+void Mainloop::stop(void)
+{
+ this->wakeupSignal.send();
+}
+
+} // namespace event
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 mainloop.hpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief The loop for events.
+ */
+
+#pragma once
+
+#include <vist/event/eventfd.hpp>
+
+#include <sys/epoll.h>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+
+namespace vist {
+namespace event {
+
+class Mainloop {
+public:
+ using OnEvent = std::function<void(void)>;
+ using OnError = std::function<void(void)>;
+
+ Mainloop();
+ virtual ~Mainloop();
+
+ Mainloop(const Mainloop&) = delete;
+ Mainloop& operator=(const Mainloop&) = delete;
+
+ Mainloop(Mainloop&&) = delete;
+ Mainloop& operator=(Mainloop&&) = delete;
+
+ void addHandler(const int fd, OnEvent&& onEvent, OnError&& = nullptr);
+ void removeHandler(const int fd);
+
+ void run(int timeout = -1);
+ void stop(void);
+
+private:
+ // recursive_mutex makes additional calls to lock in calling thread.
+ // And other threads will block (for calls to lock).
+ // So, addHandler() can be called during dispatch().
+ using Mutex = std::recursive_mutex;
+ using Handler = std::pair<std::shared_ptr<OnEvent>, std::shared_ptr<OnError>>;
+ using Listener = std::unordered_map<int, Handler>;
+
+ bool prepare(void);
+
+ bool dispatch(const int timeout) noexcept;
+
+ Mutex mutex;
+ Listener listener;
+ EventFD wakeupSignal;
+
+ int epollFd;
+ std::atomic<bool> stopped;
+
+ const int MAX_EPOLL_EVENTS = 16;
+};
+
+} // namespace event
+} // namespace vist
--- /dev/null
+# Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# 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(GLOB KLASS_TESTS "tests/*.cpp")
+ADD_VIST_TEST(${KLASS_TESTS})
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 function.hpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Hold the class member fuction.
+ */
+
+#pragma once
+
+#include <type_traits>
+
+namespace vist {
+namespace klass {
+
+template<typename T>
+using remove_cv_t = typename std::remove_cv<T>::type;
+template<typename T>
+using remove_ref_t = typename std::remove_reference<T>::type;
+template<typename T>
+using remove_cv_ref_t = remove_cv_t<remove_ref_t<T>>;
+
+template<typename R, typename K, typename... Ps>
+class Function {
+public:
+ using Klass = K;
+ using Return = R;
+ using Parameters = std::tuple<remove_cv_ref_t<Ps>...>;
+ using Pointer = Return (Klass::*)(Ps...);
+
+ auto get(void) noexcept -> const Pointer&;
+
+private:
+ explicit Function(Pointer pointer);
+
+ template<typename RR, typename KK, typename... PPs>
+ friend Function<RR, KK, PPs...> make_function(RR (KK::* member)(PPs...));
+
+ Pointer pointer;
+};
+
+template<typename R, typename K, typename... Ps>
+Function<R, K, Ps...>::Function(Pointer pointer) : pointer(pointer)
+{
+}
+
+template<typename R, typename K, typename... Ps>
+auto Function<R, K, Ps...>::get(void) noexcept -> const Pointer&
+{
+ return this->pointer;
+}
+
+template<typename R, typename K, typename... Ps>
+Function<R, K, Ps...> make_function(R (K::* member)(Ps...))
+{
+ constexpr bool notVoid = !(std::is_same<R, void>::value);
+ static_assert(notVoid, "Return type cannot be void.");
+
+ using IsValid = std::is_member_function_pointer<decltype(member)>;
+ static_assert(IsValid::value, "Pamameter should be member function type.");
+
+ return Function<R, K, Ps...>(member);
+}
+
+} // namespace klass
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 functor.hpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Functor is callable object which binds instance with member function.
+ */
+
+#pragma once
+
+#include <vist/archive.hpp>
+#include <vist/klass/function.hpp>
+
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <unordered_map>
+
+namespace vist {
+namespace klass {
+
+struct AbstractFunctor {
+ template<typename R, typename...Args>
+ R invoke(Args&&... args);
+ inline Archive invoke(Archive& archive);
+
+protected:
+ virtual Archive dispatch(Archive& archive) = 0;
+};
+
+using FunctorMap = std::unordered_map<std::string, std::shared_ptr<AbstractFunctor>>;
+
+template<typename R, typename K, typename... Ps>
+class Functor : public AbstractFunctor {
+public:
+ using Klass = K;
+ using MemFunc = Function<R, K, Ps...>;
+ using Invokable = std::function<R(K&, Ps...)>;
+
+ explicit Functor(std::shared_ptr<Klass> instance, MemFunc memFunc);
+
+ template<typename... Args>
+ auto operator()(Args&&... args) -> typename MemFunc::Return;
+ inline auto operator()(Archive& archive) -> typename MemFunc::Return;
+
+protected:
+ inline Archive dispatch(Archive& archive) override;
+
+private:
+ template<typename T>
+ auto operator()(T& tuple, EmptySequence) -> typename MemFunc::Return;
+ template<typename T, std::size_t... I>
+ auto operator()(T& tuple, IndexSequence<I...>) -> typename MemFunc::Return;
+
+ std::shared_ptr<Klass> instance;
+ MemFunc memFunc;
+};
+
+template<typename R, typename...Args>
+R AbstractFunctor::invoke(Args&&... args)
+{
+ Archive parameters;
+ parameters.pack(std::forward<Args>(args)...);
+
+ auto result = this->dispatch(parameters);
+ R ret;
+ result >> ret;
+ return ret;
+}
+
+Archive AbstractFunctor::invoke(Archive& archive)
+{
+ return this->dispatch(archive);
+}
+
+template<typename R, typename K, typename... Ps>
+Functor<R, K, Ps...>::Functor(std::shared_ptr<Klass> instance, MemFunc memFunc)
+ : instance(instance), memFunc(std::move(memFunc))
+{
+}
+
+template<typename R, typename K, typename... Ps>
+template<typename... Args>
+auto Functor<R, K, Ps...>::operator()(Args&&... args) -> typename MemFunc::Return
+{
+ const Invokable& invokable = this->memFunc.get();
+ return invokable(*this->instance, std::forward<Args>(args)...);
+}
+
+template<typename R, typename K, typename... Ps>
+auto Functor<R, K, Ps...>::operator()(Archive& archive) -> typename MemFunc::Return
+{
+ using ParamsTuple = typename MemFunc::Parameters;
+ constexpr auto size = std::tuple_size<ParamsTuple>::value;
+
+ ParamsTuple params;
+ archive.transform(params);
+
+ return (*this)(params, make_index_sequence<size>());
+}
+
+template<typename R, typename K, typename... Ps>
+Archive Functor<R, K, Ps...>::dispatch(Archive& archive)
+{
+ Archive ret;
+ return (ret << (*this)(archive));
+}
+
+template<typename R, typename K, typename... Ps>
+template<typename T>
+auto Functor<R, K, Ps...>::operator()(T& tuple,
+ EmptySequence) -> typename MemFunc::Return
+{
+ return (*this)();
+}
+
+template<typename R, typename K, typename... Ps>
+template<typename T, std::size_t... I>
+auto Functor<R, K, Ps...>::operator()(T& tuple,
+ IndexSequence<I...>) -> typename MemFunc::Return
+{
+ return (*this)(std::get<I>(tuple)...);
+}
+
+template<typename R, typename K, typename... Ps>
+Functor<R, K, Ps...> make_functor(std::shared_ptr<K> instance, R (K::* member)(Ps...))
+{
+ if (instance == nullptr)
+ throw std::invalid_argument("Instance can't be nullptr.");
+
+ return Functor<R, K, Ps...>(instance, make_function(member));
+}
+
+template<typename R, typename K, typename... Ps>
+std::shared_ptr<Functor<R, K, Ps...>> make_functor_ptr(std::shared_ptr<K> instance,
+ R (K::* member)(Ps...))
+{
+ if (instance == nullptr)
+ throw std::invalid_argument("Instance can't be nullptr.");
+
+ return std::make_shared<Functor<R, K, Ps...>>(instance, make_function(member));
+}
+
+} // namespace klass
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+
+#include <vist/archive.hpp>
+#include <vist/klass/functor.hpp>
+
+#include <iostream>
+#include <memory>
+
+#include <gtest/gtest.h>
+
+using namespace vist;
+using namespace vist::klass;
+
+struct Foo {
+ bool setName(const std::string& name)
+ {
+ this->name = name;
+ return false;
+ }
+
+ std::string getName(void)
+ {
+ return this->name;
+ }
+
+ int echo(const std::string& a, const std::string b, std::string& c)
+ {
+ std::cout << a << ", " << b << ", " << c << std::endl;
+ return false;
+ }
+
+// void impossible(void) {}
+
+ std::string name;
+};
+
+TEST(FunctorTests, functor)
+{
+ auto foo = std::make_shared<Foo>();
+ auto fooSetName = make_functor(foo, &Foo::setName);
+ auto fooGetName = make_functor(foo, &Foo::getName);
+ auto fooEcho = make_functor(foo, &Foo::echo);
+// auto fooImp = make_functor(foo, &Foo::impossible);
+
+ std::string input = "Foo name";
+ bool ret = true;
+ ret = fooSetName(input);
+ EXPECT_EQ(ret, false);
+
+ std::string output = fooGetName();
+ EXPECT_EQ(output, input);
+
+ std::string a("aaa"), b("bbb"), c("ccc");
+ ret = true;
+ ret = fooEcho(a, b, c);
+ EXPECT_EQ(ret, false);
+}
+
+TEST(FunctorTests, functor_map)
+{
+ auto foo = std::make_shared<Foo>();
+
+ FunctorMap fooMap;
+ fooMap["setName"] = make_functor_ptr(foo, &Foo::setName);
+ fooMap["getName"] = make_functor_ptr(foo, &Foo::getName);
+ fooMap["echo"] = make_functor_ptr(foo, &Foo::echo);
+
+ auto fooSetNamePtr = fooMap.at("setName");
+ auto fooGetNamePtr = fooMap.at("getName");
+ auto fooEchoPtr = fooMap.at("echo");
+
+ std::string input = "Foo name";
+ bool ret = true;
+ ret = fooSetNamePtr->invoke<bool>(input);
+ EXPECT_EQ(ret, false);
+
+ std::string output = fooGetNamePtr->invoke<std::string>();
+ EXPECT_EQ(output, input);
+
+ std::string a("aaaa"), b("bbbb"), c("cccc");
+ ret = true;
+ ret = fooEchoPtr->invoke<bool>(a, b, c);
+ EXPECT_EQ(ret, false);
+}
+
+TEST(FunctorTests, archive)
+{
+ auto foo = std::make_shared<Foo>();
+
+ FunctorMap fooMap;
+ fooMap["echo"] = make_functor_ptr(foo, &Foo::echo);
+
+ std::string a("aaaa"), b("bbbb"), c("cccc");
+ Archive archive;
+ archive << a << b << c;
+
+ auto fooEchoPtr = fooMap.at("echo");
+ auto result = fooEchoPtr->invoke(archive);
+ bool ret = true;
+ result >> ret;
+ EXPECT_EQ(ret, false);
+}
--- /dev/null
+# Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# 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
+
+ADD_VIST_COMMON_LIBRARY(vist_rmi client.cpp
+ server.cpp)
+
+FILE(GLOB RMI_TESTS "tests/*.cpp")
+ADD_VIST_TEST(${RMI_TESTS})
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 client.cpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of client application.
+ */
+
+#include "client.hpp"
+
+namespace vist {
+namespace rmi {
+
+Client::Client(const std::string& remotePath) : connection(remotePath)
+{
+}
+
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 client.hpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Client application for invoking remote function(method).
+ */
+
+#pragma once
+
+#include <vist/transport/connection.hpp>
+#include <vist/transport/message.hpp>
+
+#include <string>
+#include <mutex>
+
+using namespace vist::transport;
+
+namespace vist {
+namespace rmi {
+
+class Client {
+public:
+ explicit Client(const std::string& remotePath);
+ virtual ~Client() = default;
+
+ Client(const Client&) = delete;
+ Client& operator=(const Client&) = delete;
+
+ Client(Client&&) = delete;
+ Client& operator=(Client&&) = delete;
+
+ template<typename R, typename... Args>
+ R invoke(const std::string& name, Args&&... args);
+
+private:
+ Connection connection;
+ std::mutex mutex;
+};
+
+template<typename R, typename... Args>
+R Client::invoke(const std::string& name, Args&&... args)
+{
+ Message msg(Message::Type::MethodCall, name);
+ msg.enclose(std::forward<Args>(args)...);
+
+ std::lock_guard<std::mutex> lock(this->mutex);
+
+ Message reply = this->connection.request(msg);
+ R ret;
+ reply.disclose(ret);
+
+ return ret;
+}
+
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 server.cpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of server application.
+ */
+
+#include "server.hpp"
+
+#include <vist/logger.hpp>
+#include <vist/transport/message.hpp>
+
+namespace vist {
+namespace rmi {
+
+void Server::start(void)
+{
+ for (const auto& path : this->socketPaths) {
+ auto socket = std::make_shared<Socket>(path);
+ auto accept = [this, socket]() {
+ this->onAccept(std::make_shared<Connection>(socket->accept()));
+ };
+
+ this->mainloop.addHandler(socket->getFd(), std::move(accept));
+ }
+
+ this->mainloop.run();
+}
+
+void Server::stop(void)
+{
+ {
+ std::lock_guard<std::mutex> lock(this->connectionMutex);
+
+ for (auto iter : this->connectionMap)
+ this->mainloop.removeHandler(iter.first);
+ }
+
+ this->mainloop.stop();
+}
+
+void Server::listen(const std::string& socketPath)
+{
+ this->socketPaths.insert(socketPath);
+}
+
+void Server::onAccept(std::shared_ptr<Connection>&& connection)
+{
+ if (connection == nullptr)
+ throw std::invalid_argument("Wrong connection.");
+
+ auto onRead = [this, connection]() {
+ std::shared_ptr<Connection> conn;
+
+ std::lock_guard<std::mutex> lock(this->connectionMutex);
+
+ auto iter = this->connectionMap.find(connection->getFd());
+ if (iter == this->connectionMap.end())
+ throw std::runtime_error("Faild to find connection.");
+
+ conn = iter->second;
+
+ this->dispatch(conn);
+ };
+
+ auto onError = [this, connection]() {
+ ERROR(VIST) << "Connection error occured. fd: " << connection->getFd();
+ this->onClose(connection);
+ };
+
+ int clientFd = connection->getFd();
+ this->mainloop.addHandler(clientFd, std::move(onRead), std::move(onError));
+ INFO(VIST) << "Connection is accepted. fd: " << clientFd;
+
+ {
+ std::lock_guard<std::mutex> lock(this->connectionMutex);
+
+ this->dispatch(connection);
+ this->connectionMap[clientFd] = std::move(connection);
+ }
+}
+
+void Server::onClose(const std::shared_ptr<Connection>& connection)
+{
+ if (connection == nullptr)
+ throw std::invalid_argument("Wrong connection.");
+
+ {
+ std::lock_guard<std::mutex> lock(this->connectionMutex);
+
+ auto iter = this->connectionMap.find(connection->getFd());
+ if (iter == this->connectionMap.end())
+ throw std::runtime_error("Faild to find connection.");
+
+ this->mainloop.removeHandler(iter->first);
+ INFO(VIST) << "Connection is closed. fd: " << iter->first;
+ this->connectionMap.erase(iter);
+ }
+}
+
+void Server::dispatch(const std::shared_ptr<Connection>& connection)
+{
+ Message request = connection->recv();
+ std::string funcName = request.signature;
+
+ {
+ std::lock_guard<std::mutex> lock(this->functorMutex);
+
+ auto iter = this->functorMap.find(funcName);
+ if (iter == this->functorMap.end())
+ throw std::runtime_error("Faild to find function.");
+
+ DEBUG(VIST) << "Remote method invokation: " << funcName;
+
+ auto functor = iter->second;
+ auto result = functor->invoke(request.buffer);
+
+ Message reply(Message::Type::Reply, funcName);
+ reply.enclose(result);
+
+ connection->send(reply);
+ }
+}
+
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 server.hpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Server application for exposing function(method).
+ */
+
+#pragma once
+
+#include <vist/event/mainloop.hpp>
+#include <vist/klass/functor.hpp>
+#include <vist/transport/connection.hpp>
+#include <vist/transport/socket.hpp>
+
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <mutex>
+#include <memory>
+
+using namespace vist::klass;
+using namespace vist::transport;
+using namespace vist::event;
+
+namespace vist {
+namespace rmi {
+
+class Server {
+public:
+ explicit Server() = default;
+ virtual ~Server() = default;
+
+ Server(const Server&) = delete;
+ Server& operator=(const Server&) = delete;
+
+ Server(Server&&) = delete;
+ Server& operator=(Server&&) = delete;
+
+ void start(void);
+ void stop(void);
+
+ void listen(const std::string& socketPath);
+
+ template<typename O, typename F>
+ void expose(O&& object, const std::string& name, F&& func);
+
+private:
+ using ConnectionMap = std::unordered_map<int, std::shared_ptr<Connection>>;
+
+ void onAccept(std::shared_ptr<Connection>&& connection);
+ void onClose(const std::shared_ptr<Connection>& connection);
+
+ void dispatch(const std::shared_ptr<Connection>& connection);
+
+ Mainloop mainloop;
+
+ std::set<std::string> socketPaths;
+
+ ConnectionMap connectionMap;
+ std::mutex connectionMutex;
+
+ FunctorMap functorMap;
+ std::mutex functorMutex;
+};
+
+template<typename O, typename F>
+void Server::expose(O&& object, const std::string& name, F&& func)
+{
+ auto functor = make_functor_ptr(std::forward<O>(object), std::forward<F>(func));
+ this->functorMap[name] = functor;
+}
+
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+
+#include <vist/rmi/server.hpp>
+#include <vist/rmi/client.hpp>
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi;
+using namespace vist::transport;
+
+// Server side methods
+struct Foo {
+ bool setName(const std::string& name)
+ {
+ this->name = name;
+ return false;
+ }
+
+ std::string getName(void)
+ {
+ return this->name;
+ }
+
+ std::string name;
+};
+
+TEST(RmiTests, server_client)
+{
+ std::string sockPath = ("/tmp/test-server");
+
+ // server-side
+ Server server;
+ server.listen(sockPath);
+
+ auto foo = std::make_shared<Foo>();
+ server.expose(foo, "Foo::setName", &Foo::setName);
+ server.expose(foo, "Foo::getName", &Foo::getName);
+
+ auto client = std::thread([&]() {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ // client-side
+ Client client(sockPath);
+
+ std::string param = "RMI-TEST";
+ bool ret = client.invoke<bool>("Foo::setName", param);
+ EXPECT_EQ(ret, false);
+
+ std::string name = client.invoke<std::string>("Foo::getName");
+ EXPECT_EQ(name, param);
+
+ server.stop();
+ });
+
+ server.start();
+
+ if (client.joinable())
+ client.join();
+}
--- /dev/null
+# Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# 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
+
+ADD_VIST_COMMON_LIBRARY(vist_transport connection.cpp
+ socket.cpp
+ message.cpp)
+
+FILE(GLOB TRANSPORT_TESTS "tests/*.cpp")
+ADD_VIST_TEST(${TRANSPORT_TESTS})
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 connection.cpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of the socket communication session.
+ */
+
+#include "connection.hpp"
+
+#include <utility>
+
+namespace vist {
+namespace transport {
+
+Connection::Connection(transport::Socket&& socket) noexcept : socket(std::move(socket))
+{
+}
+
+Connection::Connection(const std::string& path) :
+ socket(transport::Socket::connect(path))
+{
+}
+
+void Connection::send(Message& message)
+{
+ std::lock_guard<std::mutex> lock(this->sendMutex);
+
+ message.header.id = this->sequence++;
+ this->socket.send(&message.header);
+
+ this->socket.send(message.buffer.get(), message.header.length);
+}
+
+Message Connection::recv(void) const
+{
+ std::lock_guard<std::mutex> lock(this->recvMutex);
+ Message::Header header;
+ this->socket.recv(&header);
+
+ Message message(header);
+ this->socket.recv(message.buffer.get(), message.size());
+ message.disclose(message.signature);
+
+ return message;
+}
+
+Message Connection::request(Message& message)
+{
+ this->send(message);
+ return this->recv();
+}
+
+int Connection::getFd(void) const noexcept
+{
+ return this->socket.getFd();
+}
+
+} // namespace transport
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 connection.hpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Define the socket communication session.
+ */
+
+#pragma once
+
+#include <vist/transport/message.hpp>
+#include <vist/transport/socket.hpp>
+
+#include <mutex>
+#include <utility>
+
+namespace vist {
+namespace transport {
+
+class Connection {
+public:
+ explicit Connection(transport::Socket&& socket) noexcept;
+ explicit Connection(const std::string& path);
+ virtual ~Connection() = default;
+
+ Connection(const Connection&) = delete;
+ Connection& operator=(const Connection&) = delete;
+
+ Connection(Connection&&) = default;
+ Connection& operator=(Connection&&) = default;
+
+ // server-side
+ void send(Message& message);
+ Message recv(void) const;
+
+ // client-side
+ Message request(Message& message);
+
+ int getFd(void) const noexcept;
+
+private:
+ transport::Socket socket;
+
+ // SOCK_STREAM are full-duplex byte streams
+ mutable std::mutex sendMutex;
+ mutable std::mutex recvMutex;
+
+ unsigned int sequence = 0;
+};
+
+} // namespace transport
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 message.cpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementaion of message.
+ */
+
+#include "message.hpp"
+
+namespace vist {
+namespace transport {
+
+Message::Message(unsigned int type, const std::string& signature) :
+ header({0, type, signature.size()}),
+ signature(signature)
+{
+ this->enclose(signature);
+}
+
+Message::Message(Header header) : header(header)
+{
+ this->buffer.reserve(this->header.length);
+}
+
+std::size_t Message::size(void) const noexcept
+{
+ return this->header.length;
+}
+
+} // namespace transport
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 message.hpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief The unit of socket communication.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <vist/archive.hpp>
+
+namespace vist {
+namespace transport {
+
+struct Message final {
+ enum Type : unsigned int {
+ Invalid,
+ MethodCall,
+ Reply,
+ Error,
+ Signal
+ };
+
+ struct Header {
+ unsigned int id;
+ unsigned int type;
+ size_t length;
+ };
+
+ explicit Message(void) = default;
+ explicit Message(unsigned int type, const std::string& signature);
+ explicit Message(Header header);
+
+ ~Message(void) noexcept = default;
+
+ Message(const Message&) = default;
+ Message(Message&&) = default;
+
+ Message& operator=(const Message&) = default;
+ Message& operator=(Message&&) = default;
+
+ template<typename... Args>
+ void enclose(Args&&... args);
+ template<typename... Args>
+ void disclose(Args&... args);
+
+ std::size_t size(void) const noexcept;
+
+ Header header;
+ std::string signature;
+ Archive buffer;
+};
+
+template<typename... Args>
+void Message::enclose(Args&&... args)
+{
+ this->buffer.pack(std::forward<Args>(args)...);
+ header.length = this->buffer.size();
+}
+
+template<typename... Args>
+void Message::disclose(Args&... args)
+{
+ this->buffer.unpack(args...);
+}
+
+} // namespace transport
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 socket.cpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of Unix Domain Socket.
+ */
+
+#include "socket.hpp"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <fstream>
+#include <iostream>
+
+namespace vist {
+namespace transport {
+
+namespace {
+
+void set_cloexec(int fd)
+{
+ if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ throw std::runtime_error("Failed to set CLOSEXEC.");
+}
+
+} // anonymous namespace
+
+Socket::Socket(int fd) noexcept : fd(fd)
+{
+}
+
+Socket::Socket(const std::string& path)
+{
+ if (path.size() >= sizeof(::sockaddr_un::sun_path))
+ throw std::invalid_argument("Socket path size is wrong.");
+
+ int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ throw std::runtime_error("Failed to create socket.");
+
+ set_cloexec(fd);
+
+ ::sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ ::strncpy(addr.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path));
+
+ if (addr.sun_path[0] == '@')
+ addr.sun_path[0] = '\0';
+
+ struct stat buf;
+ if (::stat(path.c_str(), &buf) == 0)
+ if (::unlink(path.c_str()) == -1)
+ throw std::runtime_error("Failed to remove exist socket.");
+
+ if (::bind(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(::sockaddr_un)) == -1) {
+ ::close(fd);
+ throw std::runtime_error("Failed to bind.");
+ }
+
+ if (::listen(fd, MAX_BACKLOG_SIZE) == -1) {
+ ::close(fd);
+ throw std::runtime_error("Failed to liten.");
+ }
+
+ this->fd = fd;
+}
+
+Socket::Socket(Socket&& that) : fd(that.fd)
+{
+ that.fd = -1;
+}
+
+Socket& Socket::operator=(Socket&& that)
+{
+ if (this == &that)
+ return *this;
+
+ this->fd = that.fd;
+ that.fd = -1;
+
+ return *this;
+}
+
+Socket::~Socket(void)
+{
+ if (fd != -1)
+ ::close(fd);
+}
+
+Socket Socket::accept(void) const
+{
+ int fd = ::accept(this->fd, nullptr, nullptr);
+ if (fd == -1)
+ throw std::runtime_error("Failed to accept.");
+
+ set_cloexec(fd);
+
+ return Socket(fd);
+}
+
+Socket Socket::connect(const std::string& path)
+{
+ if (path.size() >= sizeof(::sockaddr_un::sun_path))
+ throw std::invalid_argument("Socket path size is wrong.");
+
+ int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ throw std::runtime_error("Failed to create socket.");
+
+ set_cloexec(fd);
+
+ ::sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ ::strncpy(addr.sun_path, path.c_str(), sizeof(::sockaddr_un::sun_path));
+
+ if (addr.sun_path[0] == '@')
+ addr.sun_path[0] = '\0';
+
+ if (::connect(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(sockaddr_un)) == -1) {
+ ::close(fd);
+ throw std::runtime_error("Failed to connect.");
+ }
+
+ return Socket(fd);
+}
+
+int Socket::getFd(void) const noexcept
+{
+ return this->fd;
+}
+
+} // namespace transport
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 socket.hpp
+ * @author Jaemin Ryu (jm77.ryu@samsung.com)
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Define Unix Domain Socket.
+ */
+
+#pragma once
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <cstddef>
+#include <stdexcept>
+#include <string>
+
+namespace vist {
+namespace transport {
+
+class Socket {
+public:
+ explicit Socket(int fd) noexcept;
+ explicit Socket(const std::string& path);
+ virtual ~Socket(void);
+
+ Socket(const Socket&) = delete;
+ Socket& operator=(const Socket&) = delete;
+
+ Socket(Socket&&);
+ Socket& operator=(Socket&&);
+
+ Socket accept(void) const;
+ static Socket connect(const std::string& path);
+
+ template<typename T>
+ void send(const T* buffer, const std::size_t size = sizeof(T)) const;
+
+ template<typename T>
+ void recv(T* buffer, const std::size_t size = sizeof(T)) const;
+
+ int getFd(void) const noexcept;
+
+private:
+ const int MAX_BACKLOG_SIZE = 100;
+
+ int fd = -1;
+};
+
+template<typename T>
+void Socket::send(const T *buffer, const std::size_t size) const
+{
+ std::size_t written = 0;
+ while (written < size) {
+ auto rest = reinterpret_cast<const unsigned char*>(buffer) + written;
+ auto bytes = ::write(this->fd, rest, size - written);
+ if (bytes >= 0)
+ written += bytes;
+ else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ else
+ std::runtime_error("Failed to write.");
+ }
+}
+
+template<typename T>
+void Socket::recv(T *buffer, const std::size_t size) const
+{
+ std::size_t readen = 0;
+ while (readen < size) {
+ auto rest = reinterpret_cast<unsigned char*>(buffer) + readen;
+ auto bytes = ::read(this->fd, rest, size - readen);
+ if (bytes >= 0)
+ readen += bytes;
+ else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ else
+ std::runtime_error("Failed to read.");
+ }
+}
+
+} // namespace transport
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+
+#include <vist/transport/connection.hpp>
+#include <vist/transport/socket.hpp>
+#include <vist/event/mainloop.hpp>
+#include <vist/event/eventfd.hpp>
+
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using namespace vist::transport;
+using namespace vist::event;
+
+TEST(TransportTests, socket_communication)
+{
+ std::string sockPath = ("/tmp/vist-test.sock");
+
+ // server-side
+ Mainloop mainloop;
+ Socket socket(sockPath);
+
+ std::string requestSignature = "request signature";
+ int request1 = 100;
+ bool request2 = true;
+ std::string request3 = "request argument";
+
+ std::string responseSignature = "response signature";
+ int response1 = 300;
+ bool response2 = false;
+ std::string response3 = "response argument";
+
+ auto onAccept = [&]() {
+ Connection conn(socket.accept());
+ Message request = conn.recv();
+ EXPECT_EQ(requestSignature, request.signature);
+
+ int recv1;
+ bool recv2;
+ std::string recv3;
+ request.disclose(recv1, recv2, recv3);
+ EXPECT_EQ(request1, recv1);
+ EXPECT_EQ(request2, recv2);
+ EXPECT_EQ(request3, recv3);
+
+ Message reply(Message::Type::Reply, responseSignature);
+ reply.enclose(response1, response2, response3);
+ conn.send(reply);
+
+ mainloop.removeHandler(socket.getFd());
+ mainloop.stop();
+ };
+
+ mainloop.addHandler(socket.getFd(), std::move(onAccept));
+ auto serverThread = std::thread([&]() { mainloop.run(); });
+
+ // client-side
+ Connection conn(sockPath);
+ Message msg(Message::Type::Signal, requestSignature);
+ msg.enclose(request1, request2, request3);
+
+ Message reply = conn.request(msg);
+ EXPECT_EQ(reply.signature, responseSignature);
+
+ int recv1;
+ bool recv2;
+ std::string recv3;
+ reply.disclose(recv1, recv2, recv3);
+ EXPECT_EQ(response1, recv1);
+ EXPECT_EQ(response2, recv2);
+ EXPECT_EQ(response3, recv3);
+
+ if (serverThread.joinable())
+ serverThread.join();
+}
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+
+#include <vist/transport/socket.hpp>
+
+#include <chrono>
+#include <cstring>
+#include <limits>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using namespace vist::transport;
+
+TEST(TransportTests, socket_read_write)
+{
+ std::string sockPath = "/tmp/vist-test.sock";
+ Socket socket(sockPath);
+
+ int input = std::numeric_limits<int>::max();
+ bool input2 = true;
+
+ int output = 0;
+ bool output2 = false;
+
+ auto client = std::thread([&]() {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ // Send input to server.
+ Socket connected = Socket::connect(sockPath);
+ connected.send(&input);
+
+ // Recv input2 from server.
+ connected.recv(&output2);
+
+ EXPECT_EQ(input2, output2);
+ });
+
+ Socket accepted = socket.accept();
+
+ // Recv input from client.
+ accepted.recv(&output);
+ EXPECT_EQ(input, output);
+
+ // Send input2 to client.
+ accepted.send(&input2);
+
+ if (client.joinable())
+ client.join();
+}
+
+TEST(TransportTests, socket_abstract)
+{
+ std::string sockPath = "@vist-sock";
+ Socket socket(sockPath);
+
+ int input = std::numeric_limits<int>::max();
+ bool input2 = true;
+
+ int output = 0;
+ bool output2 = false;
+
+ auto client = std::thread([&]() {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ // Send input to server.
+ Socket connected = Socket::connect(sockPath);
+ connected.send(&input);
+
+ // Recv input2 from server.
+ connected.recv(&output2);
+
+ EXPECT_EQ(input2, output2);
+ });
+
+ Socket accepted = socket.accept();
+
+ // Recv input from client.
+ accepted.recv(&output);
+ EXPECT_EQ(input, output);
+
+ // Send input2 to client.
+ accepted.send(&input2);
+
+ if (client.joinable())
+ client.join();
+}