Remove boost dependency from rmi
authorSangwan Kwon <sangwan.kwon@samsung.com>
Wed, 15 Jan 2020 05:06:19 +0000 (14:06 +0900)
committer권상완/Security 2Lab(SR)/Engineer/삼성전자 <sangwan.kwon@samsung.com>
Fri, 17 Jan 2020 06:24:01 +0000 (15:24 +0900)
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
39 files changed:
src/vist/rmi/CMakeLists.txt
src/vist/rmi/gateway.cpp
src/vist/rmi/impl/client.hpp
src/vist/rmi/impl/connection.cpp [new file with mode: 0644]
src/vist/rmi/impl/connection.hpp [new file with mode: 0644]
src/vist/rmi/impl/eventfd.cpp [new file with mode: 0644]
src/vist/rmi/impl/eventfd.hpp [new file with mode: 0644]
src/vist/rmi/impl/general/client.hpp [deleted file]
src/vist/rmi/impl/general/protocol.cpp [deleted file]
src/vist/rmi/impl/general/protocol.hpp [deleted file]
src/vist/rmi/impl/general/server.hpp [deleted file]
src/vist/rmi/impl/general/tests/protocol.cpp [deleted file]
src/vist/rmi/impl/general/tests/server-client.cpp [deleted file]
src/vist/rmi/impl/mainloop.cpp [new file with mode: 0644]
src/vist/rmi/impl/mainloop.hpp [new file with mode: 0644]
src/vist/rmi/impl/ondemand/client.hpp [deleted file]
src/vist/rmi/impl/ondemand/connection.cpp [deleted file]
src/vist/rmi/impl/ondemand/connection.hpp [deleted file]
src/vist/rmi/impl/ondemand/eventfd.cpp [deleted file]
src/vist/rmi/impl/ondemand/eventfd.hpp [deleted file]
src/vist/rmi/impl/ondemand/mainloop.cpp [deleted file]
src/vist/rmi/impl/ondemand/mainloop.hpp [deleted file]
src/vist/rmi/impl/ondemand/server.hpp [deleted file]
src/vist/rmi/impl/ondemand/socket.cpp [deleted file]
src/vist/rmi/impl/ondemand/socket.hpp [deleted file]
src/vist/rmi/impl/ondemand/systemd-socket.hpp [deleted file]
src/vist/rmi/impl/ondemand/tests/connection.cpp [deleted file]
src/vist/rmi/impl/ondemand/tests/mainloop.cpp [deleted file]
src/vist/rmi/impl/ondemand/tests/socket.cpp [deleted file]
src/vist/rmi/impl/server.hpp
src/vist/rmi/impl/socket.cpp [new file with mode: 0644]
src/vist/rmi/impl/socket.hpp [new file with mode: 0644]
src/vist/rmi/impl/systemd-socket.hpp [new file with mode: 0644]
src/vist/rmi/impl/tests/connection.cpp [new file with mode: 0644]
src/vist/rmi/impl/tests/mainloop.cpp [new file with mode: 0644]
src/vist/rmi/impl/tests/server-client.cpp [new file with mode: 0644]
src/vist/rmi/impl/tests/socket.cpp [new file with mode: 0644]
src/vist/rmi/remote.cpp
src/vist/service/vistd.cpp

index d06d07d991299ba4fecc91b093a538c00e7af54e..329367e6f03d0f6c7a6d21e0c7fb219e1b419e81 100644 (file)
@@ -1,4 +1,4 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#  Copyright (c) 2019-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.
@@ -16,16 +16,14 @@ SET(TARGET vist-rmi)
 SET(${TARGET}_SRCS gateway.cpp
                                   remote.cpp
                                   message.cpp
-                                  impl/general/protocol.cpp
-                                  impl/ondemand/socket.cpp
-                                  impl/ondemand/eventfd.cpp
-                                  impl/ondemand/mainloop.cpp
-                                  impl/ondemand/connection.cpp)
+                                  impl/socket.cpp
+                                  impl/eventfd.cpp
+                                  impl/mainloop.cpp
+                                  impl/connection.cpp)
 
 ADD_LIBRARY(${TARGET} SHARED ${${TARGET}_SRCS})
 SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
 TARGET_LINK_LIBRARIES(${TARGET} ${TARGET_VIST_COMMON_LIB}
-                                                               boost_system
                                                                pthread)
 INSTALL(TARGETS ${TARGET}
                DESTINATION ${CMAKE_INSTALL_LIBDIR}
@@ -39,14 +37,10 @@ INSTALL(TARGETS ${TARGET}
 
 ADD_LIBRARY(${TARGET}-static STATIC ${${TARGET}_SRCS})
 TARGET_LINK_LIBRARIES(${TARGET} ${TARGET_VIST_COMMON_LIB}
-                                                               boost_system
                                                                pthread)
 
 FILE(GLOB RMI_TESTS "tests/*.cpp")
 ADD_VIST_TEST(${RMI_TESTS})
 
-FILE(GLOB RMI_GENERAL_TESTS "impl/general/tests/*.cpp")
-ADD_VIST_TEST(${RMI_GENERAL_TESTS})
-
-FILE(GLOB RMI_ONDEMAND_TESTS "impl/ondemand/tests/*.cpp")
-ADD_VIST_TEST(${RMI_ONDEMAND_TESTS})
+FILE(GLOB RMI_IMPL_TESTS "impl/tests/*.cpp")
+ADD_VIST_TEST(${RMI_IMPL_TESTS})
index 5ecc9ec6b8776a948f09ffa216a0b06f9b12bc09..1bbb640512b88885ef7ed16e998d04aaacea569d 100644 (file)
@@ -19,8 +19,6 @@
 #include <vist/exception.hpp>
 #include <vist/rmi/message.hpp>
 #include <vist/rmi/impl/server.hpp>
-#include <vist/rmi/impl/general/server.hpp>
-#include <vist/rmi/impl/ondemand/server.hpp>
 
 #include <memory>
 #include <string>
@@ -51,15 +49,7 @@ public:
                        return reply;
                };
 
-               switch (type) {
-               case ServiceType::OnDemand:
-                       this->server = std::make_unique<ondemand::Server>(path, dispatcher);
-                       break;
-               case ServiceType::General: /// fall through
-               default:
-                       this->server = std::make_unique<general::Server>(path, dispatcher);
-                       break;
-               }
+               this->server = std::make_unique<Server>(path, dispatcher, type);
        }
 
        inline void start(int timeout, std::function<bool()> stopper)
@@ -73,7 +63,7 @@ public:
        }
 
 private:
-       std::unique_ptr<interface::Server> server;
+       std::unique_ptr<Server> server;
 };
 
 Gateway::Gateway(const std::string& path, ServiceType type) :
index d673c64670bc18f8981e071367f5eeed1a1172bc..e244621032b22680c7b48198c021b4bd55fbedcf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2019-present Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2020 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.
 
 #pragma once
 
-#include <string>
+#include <vist/logger.hpp>
+#include <vist/rmi/impl/client.hpp>
+#include <vist/rmi/impl/connection.hpp>
 
 namespace vist {
 namespace rmi {
 namespace impl {
-namespace interface {
 
 class Client {
 public:
-       Client(const std::string&) {}
-       virtual ~Client() = default;
-
-       Client(const Client&) = delete;
-       Client& operator=(const Client&) = delete;
-
-       Client(Client&&) = default;
-       Client& operator=(Client&&) = default;
-
-       virtual Message request(Message& message) = 0;
+       Client(const std::string& path) : connection(path)
+       {
+               DEBUG(VIST) << "Success to connect to : " << path
+                                       << " by fd[" << connection.getFd() << "]";
+       }
+
+       Message request(Message& message)
+       {
+               return this->connection.request(message);
+       }
+
+private:
+       Connection connection;
 };
 
-} // namespace interface
 } // namespace impl
 } // namespace rmi
 } // namespace vist
diff --git a/src/vist/rmi/impl/connection.cpp b/src/vist/rmi/impl/connection.cpp
new file mode 100644 (file)
index 0000000..eeaf761
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  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 "connection.hpp"
+
+#include <vist/logger.hpp>
+
+#include <utility>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+Connection::Connection(Socket&& socket) noexcept : socket(std::move(socket))
+{
+}
+
+Connection::Connection(const std::string& path) :
+       socket(Socket::connect(path))
+{
+       DEBUG(VIST) << "Connect to " << path << " by fd: " << socket.getFd();
+}
+
+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.getBuffer().data(), 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.getBuffer().data(), 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 impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/connection.hpp b/src/vist/rmi/impl/connection.hpp
new file mode 100644 (file)
index 0000000..5da2651
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *  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
+ */
+
+#pragma once
+
+#include <vist/rmi/message.hpp>
+#include <vist/rmi/impl/socket.hpp>
+
+#include <mutex>
+#include <utility>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class Connection {
+public:
+       explicit Connection(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:
+       Socket socket;
+
+       // SOCK_STREAM are full-duplex byte streams
+       mutable std::mutex sendMutex;
+       mutable std::mutex recvMutex;
+
+       unsigned int sequence = 0;
+};
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/eventfd.cpp b/src/vist/rmi/impl/eventfd.cpp
new file mode 100644 (file)
index 0000000..e902369
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  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 "eventfd.hpp"
+
+#include <vist/exception.hpp>
+
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdint>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+EventFD::EventFD(unsigned int initval, int flags)
+       : fd(::eventfd(initval, flags))
+{
+       if (this->fd == -1)
+               THROW(ErrCode::RuntimeError) << "Failed to create eventfd.";
+}
+
+EventFD::~EventFD()
+{
+       ::close(fd);
+}
+
+void EventFD::send(void)
+{
+       const std::uint64_t val = 1;
+       errno = 0;
+       if (::write(this->fd, &val, sizeof(val)) == -1)
+               THROW(ErrCode::RuntimeError) << "Failed to write to eventfd: " << errno;
+}
+
+void EventFD::receive(void)
+{
+       std::uint64_t val = 0;
+       errno = 0;
+       if (::read(this->fd, &val, sizeof(val)) == -1)
+               THROW(ErrCode::RuntimeError) << "Failed to read from eventfd: " << errno;
+}
+
+int EventFD::getFd(void) const noexcept
+{
+       return this->fd;
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/eventfd.hpp b/src/vist/rmi/impl/eventfd.hpp
new file mode 100644 (file)
index 0000000..9dc529a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  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
+ */
+
+#pragma once
+
+#include <sys/eventfd.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+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 impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/general/client.hpp b/src/vist/rmi/impl/general/client.hpp
deleted file mode 100644 (file)
index 1ef64c2..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include <vist/rmi/impl/client.hpp>
-#include <vist/rmi/impl/general/protocol.hpp>
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-using boost::asio::local::stream_protocol;
-
-class Client : public interface::Client {
-public:
-       Client(const std::string& path) : interface::Client(path), socket(this->context)
-       {
-               try {
-                       this->socket.connect(Protocol::Endpoint(path));
-               }  catch(const std::exception& e) {
-                       ERROR(VIST) << "Failed to connect socket: " << e.what();
-                       std::rethrow_exception(std::current_exception());
-               }
-       }
-
-       Message request(Message& message) override
-       {
-               return Protocol::Request(this->socket, message);
-       }
-
-private:
-       Protocol::Context context;
-       Protocol::Socket socket;
-};
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/general/protocol.cpp b/src/vist/rmi/impl/general/protocol.cpp
deleted file mode 100644 (file)
index 2a9d25e..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- *  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
- */
-
-#include "protocol.hpp"
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-Message Protocol::Recv(Socket& socket)
-{
-       DEBUG(VIST) << "Socket read event occured.";
-       Message::Header header;
-       const auto& headerBuffer = boost::asio::buffer(&header, sizeof(Message::Header));
-       auto readen = boost::asio::read(socket, headerBuffer);
-       if (readen != sizeof(Message::Header))
-               THROW(ErrCode::ProtocolBroken) << "Failed to receive message header.";
-
-       Message content(header);
-       const auto& contentBuffer = boost::asio::buffer(content.getBuffer());
-       readen = boost::asio::read(socket, contentBuffer);
-       if (readen != content.size())
-               THROW(ErrCode::ProtocolBroken) << "Failed to receive message content.";
-
-       content.disclose(content.signature);
-
-       return content;
-}
-
-void Protocol::Send(Socket& socket, Message& message)
-{ 
-       DEBUG(VIST) << "Socket write event occured.";
-       const auto& headerBuffer = boost::asio::buffer(&message.header,
-                                                                                                  sizeof(Message::Header));
-       auto written = boost::asio::write(socket, headerBuffer);
-       if (written != sizeof(Message::Header))
-               THROW(ErrCode::ProtocolBroken) << "Failed to send message header.";
-
-       const auto& contentBuffer = boost::asio::buffer(message.getBuffer(), message.size());
-       written = boost::asio::write(socket, contentBuffer);
-       if (written != message.size())
-               THROW(ErrCode::ProtocolBroken) << "Failed to send message content.";
-}
-
-Message Protocol::Request(Socket& socket, Message& message)
-{
-       Protocol::Send(socket, message);
-       return Protocol::Recv(socket);
-}
-
-void Protocol::Async::dispatch(const interface::Task& task)
-{
-       auto self = shared_from_this();
-       const auto& header = boost::asio::buffer(&this->message.header,
-                                                                                        sizeof(Message::Header));
-       auto handler = [self, task](const auto& error, std::size_t size) {
-               if (error) {
-                       if (error == boost::asio::error::eof) {
-                               DEBUG(VIST) << "Socket EoF event occured.";
-                               return;
-                       } else {
-                               THROW(ErrCode::RuntimeError) << error.message();
-                       }
-               }
-
-               if (size != sizeof(Message::Header))
-                       THROW(ErrCode::ProtocolBroken) << error.message();
-
-               /// Resize message buffer to revieved header length.
-               self->message.resize(self->message.size());
-
-               const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer());
-               auto readen = boost::asio::read(self->socket, contentBuffer);
-               if (readen != self->message.size())
-                       THROW(ErrCode::ProtocolBroken) << "Failed to receive message content."
-                               << readen << ", " << self->message.size();
-
-               self->message.disclose(self->message.signature);
-               self->process(task);
-       };
-
-       boost::asio::async_read(self->socket, header, handler);
-}
-
-void Protocol::Async::process(const interface::Task& task)
-{
-       bool raised = false;
-       std::string errMsg;
-       auto onError = [&raised, &errMsg](const std::string& message) {
-               ERROR(VIST) << "Failed to process task: " << message;
-               raised = true;
-               errMsg = message;
-       };
-
-       try {
-               /// Process dispatched task.
-               auto result = task(message);
-               this->message = result;
-       } catch (const vist::Exception<ErrCode>& e) {
-               onError(e.what());
-       } catch (const std::exception& e) {
-               onError(e.what());
-       }
-
-       if (raised)
-               this->message = Message(Message::Type::Error, errMsg);
-
-       auto self = shared_from_this();
-       const auto& headerBuffer = boost::asio::buffer(&this->message.header,
-                                                                                                  sizeof(Message::Header));
-       auto handler = [self, task](const auto& error, std::size_t size) {
-               if (error || size != sizeof(Message::Header))
-                       THROW(ErrCode::ProtocolBroken) << "Failed to send message header: "
-                                                                                  << error.message();
-
-               const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer());
-               auto written = boost::asio::write(self->socket, contentBuffer);
-               if (written != self->message.size())
-                       THROW(ErrCode::ProtocolBroken) << "Failed to send message content.";
-
-               /// Re-dispatch for next request.
-               self->dispatch(task);
-       };
-
-       boost::asio::async_write(self->socket, headerBuffer, handler);
-}
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/general/protocol.hpp b/src/vist/rmi/impl/general/protocol.hpp
deleted file mode 100644 (file)
index 6690bf4..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/server.hpp>
-
-#include <boost/asio.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-struct Protocol {
-       using Acceptor = boost::asio::local::stream_protocol::acceptor;
-       using Context = boost::asio::io_service;
-       using Endpoint = boost::asio::local::stream_protocol::endpoint;
-       using Socket = boost::asio::local::stream_protocol::socket;
-
-       static Message Recv(Socket& socket);
-       static void Send(Socket& socket, Message& message);
-
-       static Message Request(Socket& socket, Message& message);
-
-       /// Passing shared_from_this() to lambda() guarantees
-       /// that the lambda() always refer to a live object.
-       class Async : public std::enable_shared_from_this<Async> {
-       public:
-               explicit Async(Context& context) : socket(context) {}
-               void dispatch(const interface::Task& task);
-               void process(const interface::Task& task);
-
-               inline Socket& getSocket()
-               {
-                       return this->socket;
-               }
-
-       private:
-               Message message; 
-               Socket socket;
-       };
-};
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/general/server.hpp b/src/vist/rmi/impl/general/server.hpp
deleted file mode 100644 (file)
index 05d256e..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include "protocol.hpp"
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-#include <memory>
-
-#include <errno.h>
-#include <unistd.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-class Server : public interface::Server {
-public:
-       Server(const std::string& path, const interface::Task& task) : interface::Server(path, task)
-       {
-               errno = 0;
-               if (::unlink(path.c_str()) == -1 && errno != ENOENT)
-                       THROW(ErrCode::RuntimeError) << "Failed to remove file."; 
-
-               this->acceptor = std::make_unique<Protocol::Acceptor>(this->context,
-                                                                                                                         Protocol::Endpoint(path));
-               this->accept(task);
-       }
-       virtual ~Server() = default;
-
-       Server(const Server&) = delete;
-       Server& operator=(const Server&) = delete;
-
-       Server(Server&&) = default;
-       Server& operator=(Server&&) = default;
-
-       /// boost-based service does not support timeout and stopper.
-       void run(int timeout = -1, interface::Stopper stopper = nullptr) override
-       {
-               (void) timeout;
-               (void) stopper;
-               this->context.run();
-       }
-
-       void stop() override
-       {
-               this->context.stop();
-       }
-
-private:
-       void accept(const interface::Task& task) override
-       {
-               auto asyncSession = std::make_shared<Protocol::Async>(this->context);
-               auto handler = [this, asyncSession, task](const auto& error) {
-                       DEBUG(VIST) << "New session is accepted.";
-
-                       if (error)
-                               THROW(ErrCode::RuntimeError) << error.message();
-
-                       asyncSession->dispatch(task);
-
-                       this->accept(task);
-               };
-               this->acceptor->async_accept(asyncSession->getSocket(), handler);
-       }
-
-       Protocol::Context context;
-       std::unique_ptr<Protocol::Acceptor> acceptor;
-};
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/general/tests/protocol.cpp b/src/vist/rmi/impl/general/tests/protocol.cpp
deleted file mode 100644 (file)
index a67d7d5..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- *  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
- */
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/general/protocol.hpp>
-
-#include <string>
-#include <thread>
-
-#include <gtest/gtest.h>
-#include <boost/asio.hpp>
-
-#include <unistd.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::general;
-using boost::asio::local::stream_protocol;
-
-namespace {
-
-/// Request variables
-std::string requestSignature = "request signature";
-int request1 = 100;
-bool request2 = true;
-std::string request3 = "request argument";
-
-/// Response variables
-std::string responseSignature = "response signature";
-int response1 = 300;
-bool response2 = false;
-std::string response3 = "response argument";
-
-} // anonymous namespace
-
-TEST(ProtocolTests, sync_server_sync_client)
-{
-       std::string sockPath = "vist-test.sock";
-       ::unlink(sockPath.c_str());
-
-       /// Server configuration
-       boost::asio::io_service context;
-       stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath));
-       stream_protocol::socket sock(context);
-
-       auto handler = [&](const auto& error) {
-               EXPECT_EQ(error, boost::system::errc::success);
-
-               Message request = Protocol::Recv(sock);
-               EXPECT_EQ(request.signature, requestSignature);
-
-               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);
-               Protocol::Send(sock, reply);
-       };
-       acceptor.async_accept(sock, handler);
-
-       auto serverThread = std::thread([&]() {
-               context.run(); 
-       });
-
-       { /// Client configuration
-               boost::asio::io_service context;
-               stream_protocol::socket sock(context);
-               sock.connect(stream_protocol::endpoint(sockPath));
-
-               Message request(Message::Type::MethodCall, requestSignature);
-               request.enclose(request1, request2, request3);
-
-               auto reply = Protocol::Request(sock, request);
-               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);
-       }
-
-       context.stop();
-
-       if (serverThread.joinable())
-               serverThread.join();
-}
-
-TEST(ProtocolTests, async_server_sync_client)
-{
-       std::string sockPath = "vist-test.sock";
-       ::unlink(sockPath.c_str());
-
-       /// Server configuration
-       boost::asio::io_service context;
-       stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath));
-
-       auto async = std::make_shared<Protocol::Async>(context);
-       auto handler = [&](const auto& error) {
-               EXPECT_EQ(error, boost::system::errc::success);
-               auto task = [&](auto& message) -> Message {
-                       EXPECT_EQ(message.signature, requestSignature);
-
-                       int recv1;
-                       bool recv2;
-                       std::string recv3;
-                       message.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);
-                       return reply;
-               };
-
-               async->dispatch(task);
-       };
-       acceptor.async_accept(async->getSocket(), handler);
-
-       auto serverThread = std::thread([&]() {
-               context.run(); 
-       });
-
-       { /// Client configuration
-               boost::asio::io_service context;
-               stream_protocol::socket sock(context);
-               sock.connect(stream_protocol::endpoint(sockPath));
-
-               Message request(Message::Type::MethodCall, requestSignature);
-               request.enclose(request1, request2, request3);
-
-               auto requestClosure = [&]() {
-                       auto reply = Protocol::Request(sock, request);
-                       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);
-               };
-
-               for (int i = 0; i < 3; i++)
-                       requestClosure();
-       }
-
-       context.stop();
-
-       if (serverThread.joinable())
-               serverThread.join();
-}
diff --git a/src/vist/rmi/impl/general/tests/server-client.cpp b/src/vist/rmi/impl/general/tests/server-client.cpp
deleted file mode 100644 (file)
index eae2494..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  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
- */
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/general/server.hpp>
-#include <vist/rmi/impl/general/client.hpp>
-
-#include <string>
-#include <thread>
-
-#include <boost/asio.hpp>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::general;
-using boost::asio::local::stream_protocol;
-
-namespace {
-
-/// Request variables
-std::string requestSignature = "request signature";
-int request1 = 100;
-bool request2 = true;
-std::string request3 = "request argument";
-
-/// Response variables
-std::string responseSignature = "response signature";
-int response1 = 300;
-bool response2 = false;
-std::string response3 = "response argument";
-
-} // anonymous namespace
-
-#ifndef TIZEN
-TEST(ServerClientTests, server)
-{
-       std::string sockPath = "vist-test.sock";
-
-       auto task = [&](Message& message) -> Message {
-               EXPECT_EQ(message.signature, requestSignature);
-
-               int recv1;
-               bool recv2;
-               std::string recv3;
-               message.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);
-               return reply;
-       };
-
-       Server server(sockPath, task); 
-       auto serverThread = std::thread([&]() {
-               server.run(); 
-       });
-
-       { /// Client configuration
-               auto clientClosure = [&]() {
-                       Client client(sockPath);
-
-                       Message message(Message::Type::MethodCall, requestSignature);
-                       message.enclose(request1, request2, request3);
-
-                       for (int i = 0; i < 3; i++) {
-                               auto reply = client.request(message);
-                               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);
-                       }
-               };
-
-               for (int i = 0; i < 3; i++)
-                       clientClosure();
-       }
-
-       server.stop();
-
-       if (serverThread.joinable())
-               serverThread.join();
-}
-#endif
diff --git a/src/vist/rmi/impl/mainloop.cpp b/src/vist/rmi/impl/mainloop.cpp
new file mode 100644 (file)
index 0000000..da08828
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *  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 "mainloop.hpp"
+
+#include <vist/exception.hpp>
+#include <vist/logger.hpp>
+
+#include <cstring>
+
+#include <unistd.h>
+#include <errno.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+Mainloop::Mainloop() :
+       epollFd(::epoll_create1(EPOLL_CLOEXEC)),
+       stopped(false)
+{
+       if (epollFd == -1)
+               THROW(ErrCode::RuntimeError) << "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()) {
+               WARN(VIST) << "Event is already registered.";
+               return;
+       }
+
+       ::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(ErrCode::RuntimeError) << "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});
+       DEBUG(VIST) << "FD[" << fd << "] listens to events.";
+}
+
+void Mainloop::removeHandler(const int fd)
+{
+       std::lock_guard<Mutex> lock(mutex);
+
+       auto iter = this->listener.find(fd);
+       if (iter == this->listener.end())
+               THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
+
+       this->listener.erase(iter);
+
+       ::epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+Mainloop::Handler Mainloop::getHandler(const int fd)
+{
+       std::lock_guard<Mutex> lock(mutex);
+
+       auto iter = this->listener.find(fd);
+       if (iter == this->listener.end())
+               THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
+
+       return std::make_pair(iter->second.first, iter->second.second);
+}
+
+void Mainloop::prepare(void)
+{
+       auto wakeup = [this]() {
+               this->wakeupSignal.receive();
+               this->removeHandler(this->wakeupSignal.getFd());
+               this->stopped = true;
+       };
+
+       DEBUG(VIST) << "Add eventfd to mainloop for wakeup: " << this->wakeupSignal.getFd();
+       this->addHandler(this->wakeupSignal.getFd(), wakeup);
+}
+
+void Mainloop::wait(int timeout, Stopper stopper)
+{
+       int nfds = 0;
+       do {
+               errno = 0;
+               nfds = ::epoll_wait(this->epollFd, this->events.data(), MAX_EVENTS, timeout);
+               if (errno == EINTR)
+                       WARN(VIST) << "The call was interrupted by a signal handler.";
+       } while ((nfds == -1) && (errno == EINTR));
+
+       if (nfds == 0) {
+               DEBUG(VIST) << "Mainloop is stopped by timeout.";
+
+               if (stopper())
+                       this->stopped = true;
+
+               return;
+       }
+
+       if (nfds < 0)
+               THROW(ErrCode::RuntimeError) << "Failed to wait epoll events: " << errno;
+
+       this->dispatch(nfds);
+}
+
+void Mainloop::dispatch(int size) {
+       for (int i = 0; i < size; i++) {
+               auto handler = this->getHandler(this->events[i].data.fd);
+               auto onEvent = handler.first;
+               auto onError = handler.second;
+
+               try {
+                       if ((this->events[i].events & (EPOLLHUP | EPOLLRDHUP))) {
+                               WARN(VIST) << "Connected client might be disconnected.";
+                               if (onError != nullptr)
+                                       (*onError)();
+                       } else {
+                               (*onEvent)();
+                       }
+
+               } catch (const std::exception& e) {
+                       ERROR(VIST) << e.what();
+               }
+       }
+}
+
+void Mainloop::run(int timeout, Stopper stopper)
+{
+       this->stopped = false;
+       this->prepare();
+
+       if (stopper == nullptr)
+               stopper = []() -> bool { return true; };
+
+       while (!this->stopped)
+               this->wait(timeout, stopper);
+}
+
+void Mainloop::stop(void)
+{
+       this->wakeupSignal.send();
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/mainloop.hpp b/src/vist/rmi/impl/mainloop.hpp
new file mode 100644 (file)
index 0000000..67ad856
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  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
+ */
+
+#pragma once
+
+#include <vist/rmi/impl/eventfd.hpp>
+
+#include <atomic>
+#include <array>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+
+#include <sys/epoll.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class Mainloop {
+public:
+       using OnEvent = std::function<void(void)>;
+       using OnError = std::function<void(void)>;
+       using Stopper = std::function<bool(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);
+
+       /// Stopper is a predicate what returns a condition to stop mainloop
+       /// when timeout is occured.
+       void run(int timeout = -1, Stopper = nullptr);
+       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>;
+
+       Handler getHandler(const int fd);
+
+       void prepare(void);
+
+       void wait(int timeout, Stopper stopper);
+       void dispatch(int size);
+
+       Mutex mutex;
+       Listener listener;
+       EventFD wakeupSignal;
+
+       int epollFd;
+       std::atomic<bool> stopped;
+
+       static constexpr int MAX_EVENTS = 16;
+       std::array<::epoll_event, MAX_EVENTS> events;
+};
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/client.hpp b/src/vist/rmi/impl/ondemand/client.hpp
deleted file mode 100644 (file)
index 555d3ec..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Copyright (c) 2020 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
- */
-
-#pragma once
-
-#include <vist/logger.hpp>
-#include <vist/rmi/impl/client.hpp>
-#include <vist/rmi/impl/ondemand/connection.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Client : public interface::Client {
-public:
-       Client(const std::string& path) : interface::Client(path), connection(path)
-       {
-               DEBUG(VIST) << "Success to connect to : " << path
-                                       << " by fd[" << connection.getFd() << "]";
-       }
-
-       Message request(Message& message) override
-       {
-               return this->connection.request(message);
-       }
-
-private:
-       Connection connection;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/connection.cpp b/src/vist/rmi/impl/ondemand/connection.cpp
deleted file mode 100644 (file)
index 75e346a..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *  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 "connection.hpp"
-
-#include <vist/logger.hpp>
-
-#include <utility>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-Connection::Connection(Socket&& socket) noexcept : socket(std::move(socket))
-{
-}
-
-Connection::Connection(const std::string& path) :
-       socket(Socket::connect(path))
-{
-       DEBUG(VIST) << "Connect to " << path << " by fd: " << socket.getFd();
-}
-
-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.getBuffer().data(), 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.getBuffer().data(), 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 ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/connection.hpp b/src/vist/rmi/impl/ondemand/connection.hpp
deleted file mode 100644 (file)
index 080391d..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/ondemand/socket.hpp>
-
-#include <mutex>
-#include <utility>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Connection {
-public:
-       explicit Connection(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:
-       Socket socket;
-
-       // SOCK_STREAM are full-duplex byte streams
-       mutable std::mutex sendMutex;
-       mutable std::mutex recvMutex;
-
-       unsigned int sequence = 0;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/eventfd.cpp b/src/vist/rmi/impl/ondemand/eventfd.cpp
deleted file mode 100644 (file)
index e3ec163..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- *  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 "eventfd.hpp"
-
-#include <vist/exception.hpp>
-
-#include <errno.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cstdint>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-EventFD::EventFD(unsigned int initval, int flags)
-       : fd(::eventfd(initval, flags))
-{
-       if (this->fd == -1)
-               THROW(ErrCode::RuntimeError) << "Failed to create eventfd.";
-}
-
-EventFD::~EventFD()
-{
-       ::close(fd);
-}
-
-void EventFD::send(void)
-{
-       const std::uint64_t val = 1;
-       errno = 0;
-       if (::write(this->fd, &val, sizeof(val)) == -1)
-               THROW(ErrCode::RuntimeError) << "Failed to write to eventfd: " << errno;
-}
-
-void EventFD::receive(void)
-{
-       std::uint64_t val = 0;
-       errno = 0;
-       if (::read(this->fd, &val, sizeof(val)) == -1)
-               THROW(ErrCode::RuntimeError) << "Failed to read from eventfd: " << errno;
-}
-
-int EventFD::getFd(void) const noexcept
-{
-       return this->fd;
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/eventfd.hpp b/src/vist/rmi/impl/ondemand/eventfd.hpp
deleted file mode 100644 (file)
index c017fe3..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include <sys/eventfd.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-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 ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/mainloop.cpp b/src/vist/rmi/impl/ondemand/mainloop.cpp
deleted file mode 100644 (file)
index 613632b..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- *  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 "mainloop.hpp"
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-#include <cstring>
-
-#include <unistd.h>
-#include <errno.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-Mainloop::Mainloop() :
-       epollFd(::epoll_create1(EPOLL_CLOEXEC)),
-       stopped(false)
-{
-       if (epollFd == -1)
-               THROW(ErrCode::RuntimeError) << "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()) {
-               WARN(VIST) << "Event is already registered.";
-               return;
-       }
-
-       ::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(ErrCode::RuntimeError) << "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});
-       DEBUG(VIST) << "FD[" << fd << "] listens to events.";
-}
-
-void Mainloop::removeHandler(const int fd)
-{
-       std::lock_guard<Mutex> lock(mutex);
-
-       auto iter = this->listener.find(fd);
-       if (iter == this->listener.end())
-               THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
-
-       this->listener.erase(iter);
-
-       ::epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);
-}
-
-Mainloop::Handler Mainloop::getHandler(const int fd)
-{
-       std::lock_guard<Mutex> lock(mutex);
-
-       auto iter = this->listener.find(fd);
-       if (iter == this->listener.end())
-               THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
-
-       return std::make_pair(iter->second.first, iter->second.second);
-}
-
-void Mainloop::prepare(void)
-{
-       auto wakeup = [this]() {
-               this->wakeupSignal.receive();
-               this->removeHandler(this->wakeupSignal.getFd());
-               this->stopped = true;
-       };
-
-       DEBUG(VIST) << "Add eventfd to mainloop for wakeup: " << this->wakeupSignal.getFd();
-       this->addHandler(this->wakeupSignal.getFd(), wakeup);
-}
-
-void Mainloop::wait(int timeout, Stopper stopper)
-{
-       int nfds = 0;
-       do {
-               errno = 0;
-               nfds = ::epoll_wait(this->epollFd, this->events.data(), MAX_EVENTS, timeout);
-               if (errno == EINTR)
-                       WARN(VIST) << "The call was interrupted by a signal handler.";
-       } while ((nfds == -1) && (errno == EINTR));
-
-       if (nfds == 0) {
-               DEBUG(VIST) << "Mainloop is stopped by timeout.";
-
-               if (stopper())
-                       this->stopped = true;
-
-               return;
-       }
-
-       if (nfds < 0)
-               THROW(ErrCode::RuntimeError) << "Failed to wait epoll events: " << errno;
-
-       this->dispatch(nfds);
-}
-
-void Mainloop::dispatch(int size) {
-       for (int i = 0; i < size; i++) {
-               auto handler = this->getHandler(this->events[i].data.fd);
-               auto onEvent = handler.first;
-               auto onError = handler.second;
-
-               try {
-                       if ((this->events[i].events & (EPOLLHUP | EPOLLRDHUP))) {
-                               WARN(VIST) << "Connected client might be disconnected.";
-                               if (onError != nullptr)
-                                       (*onError)();
-                       } else {
-                               (*onEvent)();
-                       }
-
-               } catch (const std::exception& e) {
-                       ERROR(VIST) << e.what();
-               }
-       }
-}
-
-void Mainloop::run(int timeout, Stopper stopper)
-{
-       this->stopped = false;
-       this->prepare();
-
-       if (stopper == nullptr)
-               stopper = []() -> bool { return true; };
-
-       while (!this->stopped)
-               this->wait(timeout, stopper);
-}
-
-void Mainloop::stop(void)
-{
-       this->wakeupSignal.send();
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/mainloop.hpp b/src/vist/rmi/impl/ondemand/mainloop.hpp
deleted file mode 100644 (file)
index 8ce748c..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include <vist/rmi/impl/ondemand/eventfd.hpp>
-
-#include <atomic>
-#include <array>
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <stdexcept>
-#include <string>
-#include <unordered_map>
-
-#include <sys/epoll.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Mainloop {
-public:
-       using OnEvent = std::function<void(void)>;
-       using OnError = std::function<void(void)>;
-       using Stopper = std::function<bool(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);
-
-       /// Stopper is a predicate what returns a condition to stop mainloop
-       /// when timeout is occured.
-       void run(int timeout = -1, Stopper = nullptr);
-       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>;
-
-       Handler getHandler(const int fd);
-
-       void prepare(void);
-
-       void wait(int timeout, Stopper stopper);
-       void dispatch(int size);
-
-       Mutex mutex;
-       Listener listener;
-       EventFD wakeupSignal;
-
-       int epollFd;
-       std::atomic<bool> stopped;
-
-       static constexpr int MAX_EVENTS = 16;
-       std::array<::epoll_event, MAX_EVENTS> events;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/server.hpp b/src/vist/rmi/impl/ondemand/server.hpp
deleted file mode 100644 (file)
index 9b90015..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include <vist/rmi/impl/server.hpp>
-#include <vist/rmi/impl/ondemand/connection.hpp>
-#include <vist/rmi/impl/ondemand/mainloop.hpp>
-#include <vist/rmi/impl/ondemand/socket.hpp>
-#include <vist/rmi/impl/ondemand/systemd-socket.hpp>
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-#include <memory>
-#include <mutex>
-#include <set>
-#include <string>
-#include <unordered_map>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Server : public interface::Server {
-public:
-       Server(const std::string& path, const interface::Task& task) :
-               interface::Server(path, task),
-               socket(SystemdSocket::Create(path))
-       {
-               this->accept(task);
-       }
-
-       virtual ~Server() = default;
-
-       Server(const Server&) = delete;
-       Server& operator=(const Server&) = delete;
-
-       Server(Server&&) = default;
-       Server& operator=(Server&&) = default;
-
-       void run(int timeout = -1, interface::Stopper stopper = nullptr) override
-       {
-               this->mainloop.run(timeout, stopper);
-       }
-
-       void stop(void) override
-       {
-               this->mainloop.removeHandler(this->socket.getFd());
-               this->mainloop.stop();
-       }
-
-private:
-       void accept(const interface::Task& task) override
-       {
-               auto handler = [this, task]() {
-                       DEBUG(VIST) << "New session is accepted.";
-
-                       auto connection = std::make_shared<Connection>(this->socket.accept());
-                       auto onRead = [connection, task]() {
-                               Message request = connection->recv();
-                               DEBUG(VIST) << "Session header: " << request.signature;
-
-                               try {
-                                       Message reply = task(request);
-                                       connection->send(reply);
-                               } catch (const std::exception& e) {
-                                       ERROR(VIST) << e.what();
-                                       Message reply = Message(Message::Type::Error, e.what());
-                                       connection->send(reply);
-                               }
-                       };
-
-                       auto onClose = [this, connection]() {
-                               DEBUG(VIST) << "Connection closed. fd: " << connection->getFd();
-                               this->mainloop.removeHandler(connection->getFd());
-                       };
-
-                       this->mainloop.addHandler(connection->getFd(),
-                                                                       std::move(onRead), std::move(onClose));
-               };
-
-               INFO(VIST) << "Ready for new connection.";
-               this->mainloop.addHandler(this->socket.getFd(), std::move(handler));
-       }
-
-       Socket socket;
-       Mainloop mainloop;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/socket.cpp b/src/vist/rmi/impl/ondemand/socket.cpp
deleted file mode 100644 (file)
index 110e3fc..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- *  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 "socket.hpp"
-
-#include <vist/logger.hpp>
-
-#include <fstream>
-#include <iostream>
-#include <fcntl.h>
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-namespace {
-
-void set_cloexec(int fd)
-{
-       if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
-               THROW(ErrCode::RuntimeError) << "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(ErrCode::LogicError) << "Socket path size is wrong.";
-
-       int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
-       if (fd == -1)
-               THROW(ErrCode::RuntimeError) << "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) - 1);
-       addr.sun_path[sizeof(sockaddr_un::sun_path) - 1] = '\0';
-
-       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(ErrCode::RuntimeError) << "Failed to remove exist socket.";
-
-       if (::bind(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(::sockaddr_un)) == -1) {
-               ::close(fd);
-               THROW(ErrCode::RuntimeError) << "Failed to bind.";
-       }
-
-       if (::listen(fd, MAX_BACKLOG_SIZE) == -1) {
-               ::close(fd);
-               THROW(ErrCode::RuntimeError) << "Failed to liten.";
-       }
-
-       this->fd = fd;
-
-       DEBUG(VIST) << "Socket is created: " << path << ", and is listening.. 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
-{
-       errno = 0;
-       int fd = ::accept(this->fd, nullptr, nullptr);
-       if (fd == -1)
-               THROW(ErrCode::RuntimeError) << "Failed to accept: " << errno;
-
-       set_cloexec(fd);
-
-       return Socket(fd);
-}
-
-Socket Socket::connect(const std::string& path)
-{
-       if (path.size() >= sizeof(::sockaddr_un::sun_path))
-               THROW(ErrCode::LogicError) << "Socket path size is wrong.";
-
-       int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
-       if (fd == -1)
-               THROW(ErrCode::RuntimeError) << "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';
-
-       DEBUG(VIST) << "Start to connect: " << path;
-       errno = 0;
-       if (::connect(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(sockaddr_un)) == -1) {
-               ::close(fd);
-               ERROR(VIST) << "Failed to connect to: " << path;
-               THROW(ErrCode::RuntimeError) << "Failed to connect to: " << path
-                                                                        << ", with: " << errno;
-       }
-
-       return Socket(fd);
-}
-
-int Socket::getFd(void) const noexcept
-{
-       return this->fd;
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/socket.hpp b/src/vist/rmi/impl/ondemand/socket.hpp
deleted file mode 100644 (file)
index 993fe90..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- *  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
- */
-
-#pragma once
-
-#include <vist/exception.hpp>
-
-#include <cstddef>
-#include <string>
-#include <stdexcept>
-
-#include <unistd.h>
-#include <errno.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-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;
-};
-
-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);
-               errno = 0;
-               if (bytes >= 0)
-                       written += bytes;
-               else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
-                       continue;
-               else
-                       THROW(ErrCode::RuntimeError) << "Failed to write to socket: " << errno;
-       }
-}
-
-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);
-               errno = 0;
-               if (bytes >= 0)
-                       readen += bytes;
-               else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
-                       continue;
-               else
-                       THROW(ErrCode::RuntimeError) << "Failed to read from socket: " << errno;
-       }
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/systemd-socket.hpp b/src/vist/rmi/impl/ondemand/systemd-socket.hpp
deleted file mode 100644 (file)
index d952dd0..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *  Copyright (c) 2020 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
- */
-
-#pragma once
-
-#include <vist/logger.hpp>
-#include <vist/exception.hpp>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <systemd/sd-daemon.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class SystemdSocket {
-public:
-       static int Create(const std::string& path)
-       {
-               static int fds = -1;
-
-               if (fds == -1)
-                       fds = ::sd_listen_fds(0);
-
-               if (fds < 0)
-                       THROW(ErrCode::RuntimeError) << "Failed to get listened systemd fds.";
-
-               for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fds; ++fd) {
-                       if (::sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0) > 0) {
-                               INFO(VIST) << "Systemd socket of service is found with fd: " << fd;
-                               return fd;
-                       }
-               }
-
-               THROW(ErrCode::RuntimeError) << "Failed to find listened systemd fds.";
-       }
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
diff --git a/src/vist/rmi/impl/ondemand/tests/connection.cpp b/src/vist/rmi/impl/ondemand/tests/connection.cpp
deleted file mode 100644 (file)
index f5f2297..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- *  Copyright (c) 2020 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/message.hpp>
-#include <vist/rmi/impl/ondemand/connection.hpp>
-#include <vist/rmi/impl/ondemand/socket.hpp>
-#include <vist/rmi/impl/ondemand/mainloop.hpp>
-
-#include <string>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::ondemand;
-
-TEST(ConnectionTests, socket_communication)
-{
-       std::string sockPath = ("@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);
-
-       /// Do not request multiple times. (The above sever only processes once.)
-       {
-               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();
-}
diff --git a/src/vist/rmi/impl/ondemand/tests/mainloop.cpp b/src/vist/rmi/impl/ondemand/tests/mainloop.cpp
deleted file mode 100644 (file)
index 435b5d1..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- *  Copyright (c) 2020 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/impl/ondemand/socket.hpp>
-#include <vist/rmi/impl/ondemand/mainloop.hpp>
-
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::ondemand;
-
-TEST(MainloopTests, single)
-{
-       std::string sockPath = "@sock";
-       Socket socket(sockPath);
-       Mainloop mainloop;
-
-       int input = std::numeric_limits<int>::max();
-       bool input2 = true;
-
-       int output = 0;
-       bool output2 = false;
-
-       auto onAccept = [&]() {
-               Socket accepted = socket.accept();
-
-               // Recv input from client.
-               accepted.recv(&output);
-               EXPECT_EQ(input, output);
-
-               // Send input2 to client.
-               accepted.send(&input2);
-
-               mainloop.removeHandler(socket.getFd());
-               mainloop.stop();
-       };
-
-       mainloop.addHandler(socket.getFd(), std::move(onAccept));
-       auto server = std::thread([&]() { mainloop.run(); });
-
-       // Send input to server.
-       Socket connected = Socket::connect(sockPath);
-       connected.send(&input);
-
-       // Recv input2 from server.
-       connected.recv(&output2);
-       EXPECT_EQ(input2, output2);
-
-       if (server.joinable())
-               server.join();
-}
-
-TEST(MainloopTests, multiflexing)
-{
-       std::string sockPath = "@sock";
-       Socket socket(sockPath);
-       Mainloop mainloop;
-
-       int input = std::numeric_limits<int>::max();
-       bool input2 = true;
-
-       int output = 0;
-       bool output2 = false;
-
-       auto onAccept = [&]() {
-               Socket accepted = socket.accept();
-
-               // Recv input from client.
-               accepted.recv(&output);
-               EXPECT_EQ(input, output);
-
-               // Send input2 to client.
-               accepted.send(&input2);
-       };
-
-       /// Set timeout to stop
-       mainloop.addHandler(socket.getFd(), std::move(onAccept));
-       auto server = std::thread([&]() { mainloop.run(1000); });
-
-       auto task = [&]() {
-               // Send input to server.
-               Socket connected = Socket::connect(sockPath);
-               connected.send(&input);
-
-               // Recv input2 from server.
-               connected.recv(&output2);
-               EXPECT_EQ(input2, output2);
-       };
-
-       std::vector<std::thread> clients;
-       clients.emplace_back(std::thread(task));
-       clients.emplace_back(std::thread(task));
-       clients.emplace_back(std::thread(task));
-
-       if (server.joinable())
-               server.join();
-
-       for (auto& client : clients)
-               if (client.joinable())
-                       client.join();
-}
-
-TEST(MainloopTests, stopper)
-{
-       auto stopper = []() -> bool { return true; };
-       Mainloop mainloop;
-       mainloop.run(1000, stopper);
-       EXPECT_TRUE(true);
-
-       mainloop.run(1000);
-       EXPECT_TRUE(true);
-}
diff --git a/src/vist/rmi/impl/ondemand/tests/socket.cpp b/src/vist/rmi/impl/ondemand/tests/socket.cpp
deleted file mode 100644 (file)
index 86e0eaa..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *  Copyright (c) 2020 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/impl/ondemand/socket.hpp>
-
-#include <string>
-#include <limits>
-#include <thread>
-#include <chrono>
-#include <cstring>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi::impl::ondemand;
-
-TEST(SocketTests, socket_read_write)
-{
-       std::string sockPath = "./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(SocketTests, socket_abstract)
-{
-       std::string sockPath = "@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();
-}
index cb56f90cd16e3714305a453c80e386bb38b88497..e8b46040c46e2efbdb64ffcaafcb50f167628a21 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *  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.
 
 #pragma once
 
-#include <vist/rmi/message.hpp>
+#include <vist/rmi/gateway.hpp>
+#include <vist/rmi/impl/connection.hpp>
+#include <vist/rmi/impl/mainloop.hpp>
+#include <vist/rmi/impl/socket.hpp>
+#include <vist/rmi/impl/systemd-socket.hpp>
 
-#include <functional>
+#include <vist/exception.hpp>
+#include <vist/logger.hpp>
+
+#include <memory>
+#include <mutex>
+#include <set>
 #include <string>
+#include <unordered_map>
+#include <functional>
 
 namespace vist {
 namespace rmi {
 namespace impl {
-namespace interface {
 
 using Task = std::function<Message(Message&)>;
 using Stopper = std::function<bool(void)>;
+using ServiceType = Gateway::ServiceType;
 
 class Server {
 public:
-       explicit Server(const std::string&, const Task&) {}
+       Server(const std::string& path, const Task& task, ServiceType type = ServiceType::General)
+       {
+               switch (type) {
+               case ServiceType::OnDemand:
+                       this->socket = std::make_unique<Socket>(SystemdSocket::Create(path));
+                       break;
+               case ServiceType::General: /// fall through
+               default:
+                       this->socket = std::make_unique<Socket>(path);
+                       break;
+               }
+
+               this->accept(task);
+       }
+
        virtual ~Server() = default;
 
        Server(const Server&) = delete;
@@ -40,14 +65,55 @@ public:
        Server(Server&&) = default;
        Server& operator=(Server&&) = default;
 
-       virtual void run(int timeout = -1, Stopper stopper = nullptr) = 0;
-       virtual void stop() = 0;
+       void run(int timeout = -1, Stopper stopper = nullptr)
+       {
+               this->mainloop.run(timeout, stopper);
+       }
+
+       void stop(void)
+       {
+               this->mainloop.removeHandler(this->socket->getFd());
+               this->mainloop.stop();
+       }
 
 private:
-       virtual void accept(const Task& task) = 0;
+       void accept(const Task& task)
+       {
+               auto handler = [this, task]() {
+                       DEBUG(VIST) << "New session is accepted.";
+
+                       auto connection = std::make_shared<Connection>(this->socket->accept());
+                       auto onRead = [connection, task]() {
+                               Message request = connection->recv();
+                               DEBUG(VIST) << "Session header: " << request.signature;
+
+                               try {
+                                       Message reply = task(request);
+                                       connection->send(reply);
+                               } catch (const std::exception& e) {
+                                       ERROR(VIST) << e.what();
+                                       Message reply = Message(Message::Type::Error, e.what());
+                                       connection->send(reply);
+                               }
+                       };
+
+                       auto onClose = [this, connection]() {
+                               DEBUG(VIST) << "Connection closed. fd: " << connection->getFd();
+                               this->mainloop.removeHandler(connection->getFd());
+                       };
+
+                       this->mainloop.addHandler(connection->getFd(),
+                                                                       std::move(onRead), std::move(onClose));
+               };
+
+               INFO(VIST) << "Ready for new connection.";
+               this->mainloop.addHandler(this->socket->getFd(), std::move(handler));
+       }
+
+       std::unique_ptr<Socket> socket;
+       Mainloop mainloop;
 };
 
-} // namespace interface
 } // namespace impl
 } // namespace rmi
 } // namespace vist
diff --git a/src/vist/rmi/impl/socket.cpp b/src/vist/rmi/impl/socket.cpp
new file mode 100644 (file)
index 0000000..d304545
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *  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 "socket.hpp"
+
+#include <vist/logger.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+namespace {
+
+void set_cloexec(int fd)
+{
+       if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+               THROW(ErrCode::RuntimeError) << "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(ErrCode::LogicError) << "Socket path size is wrong.";
+
+       int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd == -1)
+               THROW(ErrCode::RuntimeError) << "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) - 1);
+       addr.sun_path[sizeof(sockaddr_un::sun_path) - 1] = '\0';
+
+       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(ErrCode::RuntimeError) << "Failed to remove exist socket.";
+
+       if (::bind(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(::sockaddr_un)) == -1) {
+               ::close(fd);
+               THROW(ErrCode::RuntimeError) << "Failed to bind.";
+       }
+
+       if (::listen(fd, MAX_BACKLOG_SIZE) == -1) {
+               ::close(fd);
+               THROW(ErrCode::RuntimeError) << "Failed to liten.";
+       }
+
+       this->fd = fd;
+
+       DEBUG(VIST) << "Socket is created: " << path << ", and is listening.. 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
+{
+       errno = 0;
+       int fd = ::accept(this->fd, nullptr, nullptr);
+       if (fd == -1)
+               THROW(ErrCode::RuntimeError) << "Failed to accept: " << errno;
+
+       set_cloexec(fd);
+
+       return Socket(fd);
+}
+
+Socket Socket::connect(const std::string& path)
+{
+       if (path.size() >= sizeof(::sockaddr_un::sun_path))
+               THROW(ErrCode::LogicError) << "Socket path size is wrong.";
+
+       int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+       if (fd == -1)
+               THROW(ErrCode::RuntimeError) << "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';
+
+       DEBUG(VIST) << "Start to connect: " << path;
+       errno = 0;
+       if (::connect(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(sockaddr_un)) == -1) {
+               ::close(fd);
+               ERROR(VIST) << "Failed to connect to: " << path;
+               THROW(ErrCode::RuntimeError) << "Failed to connect to: " << path
+                                                                        << ", with: " << errno;
+       }
+
+       return Socket(fd);
+}
+
+int Socket::getFd(void) const noexcept
+{
+       return this->fd;
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/socket.hpp b/src/vist/rmi/impl/socket.hpp
new file mode 100644 (file)
index 0000000..0fa6757
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  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
+ */
+
+#pragma once
+
+#include <vist/exception.hpp>
+
+#include <cstddef>
+#include <string>
+#include <stdexcept>
+
+#include <unistd.h>
+#include <errno.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+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;
+};
+
+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);
+               errno = 0;
+               if (bytes >= 0)
+                       written += bytes;
+               else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+                       continue;
+               else
+                       THROW(ErrCode::RuntimeError) << "Failed to write to socket: " << errno;
+       }
+}
+
+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);
+               errno = 0;
+               if (bytes >= 0)
+                       readen += bytes;
+               else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+                       continue;
+               else
+                       THROW(ErrCode::RuntimeError) << "Failed to read from socket: " << errno;
+       }
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/systemd-socket.hpp b/src/vist/rmi/impl/systemd-socket.hpp
new file mode 100644 (file)
index 0000000..38dd2d2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2020-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
+ */
+
+#pragma once
+
+#include <vist/logger.hpp>
+#include <vist/exception.hpp>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <systemd/sd-daemon.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class SystemdSocket {
+public:
+       static int Create(const std::string& path)
+       {
+               static int fds = -1;
+
+               if (fds == -1)
+                       fds = ::sd_listen_fds(0);
+
+               if (fds < 0)
+                       THROW(ErrCode::RuntimeError) << "Failed to get listened systemd fds.";
+
+               for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fds; ++fd) {
+                       if (::sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0) > 0) {
+                               INFO(VIST) << "Systemd socket of service is found with fd: " << fd;
+                               return fd;
+                       }
+               }
+
+               THROW(ErrCode::RuntimeError) << "Failed to find listened systemd fds.";
+       }
+};
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
diff --git a/src/vist/rmi/impl/tests/connection.cpp b/src/vist/rmi/impl/tests/connection.cpp
new file mode 100644 (file)
index 0000000..7e79624
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *  Copyright (c) 2020 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/message.hpp>
+#include <vist/rmi/impl/connection.hpp>
+#include <vist/rmi/impl/socket.hpp>
+#include <vist/rmi/impl/mainloop.hpp>
+
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi;
+using namespace vist::rmi::impl;
+
+TEST(ConnectionTests, socket_communication)
+{
+       std::string sockPath = ("@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);
+
+       /// Do not request multiple times. (The above sever only processes once.)
+       {
+               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();
+}
diff --git a/src/vist/rmi/impl/tests/mainloop.cpp b/src/vist/rmi/impl/tests/mainloop.cpp
new file mode 100644 (file)
index 0000000..6119ffe
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ *  Copyright (c) 2020 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/impl/socket.hpp>
+#include <vist/rmi/impl/mainloop.hpp>
+
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi;
+using namespace vist::rmi::impl;
+
+TEST(MainloopTests, single)
+{
+       std::string sockPath = "@sock";
+       Socket socket(sockPath);
+       Mainloop mainloop;
+
+       int input = std::numeric_limits<int>::max();
+       bool input2 = true;
+
+       int output = 0;
+       bool output2 = false;
+
+       auto onAccept = [&]() {
+               Socket accepted = socket.accept();
+
+               // Recv input from client.
+               accepted.recv(&output);
+               EXPECT_EQ(input, output);
+
+               // Send input2 to client.
+               accepted.send(&input2);
+
+               mainloop.removeHandler(socket.getFd());
+               mainloop.stop();
+       };
+
+       mainloop.addHandler(socket.getFd(), std::move(onAccept));
+       auto server = std::thread([&]() { mainloop.run(); });
+
+       // Send input to server.
+       Socket connected = Socket::connect(sockPath);
+       connected.send(&input);
+
+       // Recv input2 from server.
+       connected.recv(&output2);
+       EXPECT_EQ(input2, output2);
+
+       if (server.joinable())
+               server.join();
+}
+
+TEST(MainloopTests, multiflexing)
+{
+       std::string sockPath = "@sock";
+       Socket socket(sockPath);
+       Mainloop mainloop;
+
+       int input = std::numeric_limits<int>::max();
+       bool input2 = true;
+
+       int output = 0;
+       bool output2 = false;
+
+       auto onAccept = [&]() {
+               Socket accepted = socket.accept();
+
+               // Recv input from client.
+               accepted.recv(&output);
+               EXPECT_EQ(input, output);
+
+               // Send input2 to client.
+               accepted.send(&input2);
+       };
+
+       /// Set timeout to stop
+       mainloop.addHandler(socket.getFd(), std::move(onAccept));
+       auto server = std::thread([&]() { mainloop.run(1000); });
+
+       auto task = [&]() {
+               // Send input to server.
+               Socket connected = Socket::connect(sockPath);
+               connected.send(&input);
+
+               // Recv input2 from server.
+               connected.recv(&output2);
+               EXPECT_EQ(input2, output2);
+       };
+
+       std::vector<std::thread> clients;
+       clients.emplace_back(std::thread(task));
+       clients.emplace_back(std::thread(task));
+       clients.emplace_back(std::thread(task));
+
+       if (server.joinable())
+               server.join();
+
+       for (auto& client : clients)
+               if (client.joinable())
+                       client.join();
+}
+
+TEST(MainloopTests, stopper)
+{
+       auto stopper = []() -> bool { return true; };
+       Mainloop mainloop;
+       mainloop.run(1000, stopper);
+       EXPECT_TRUE(true);
+
+       mainloop.run(1000);
+       EXPECT_TRUE(true);
+}
diff --git a/src/vist/rmi/impl/tests/server-client.cpp b/src/vist/rmi/impl/tests/server-client.cpp
new file mode 100644 (file)
index 0000000..a657ed6
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  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
+ */
+
+#include <vist/rmi/message.hpp>
+#include <vist/rmi/impl/server.hpp>
+#include <vist/rmi/impl/client.hpp>
+
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi;
+using namespace vist::rmi::impl;
+
+namespace {
+
+/// Request variables
+std::string requestSignature = "request signature";
+int request1 = 100;
+bool request2 = true;
+std::string request3 = "request argument";
+
+/// Response variables
+std::string responseSignature = "response signature";
+int response1 = 300;
+bool response2 = false;
+std::string response3 = "response argument";
+
+} // anonymous namespace
+
+TEST(ServerClientTests, not_ondemand)
+{
+       std::string sockPath = "./vist-test.sock";
+
+       auto task = [&](Message& message) -> Message {
+               EXPECT_EQ(message.signature, requestSignature);
+
+               int recv1;
+               bool recv2;
+               std::string recv3;
+               message.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);
+               return reply;
+       };
+
+       Server server(sockPath, task);
+       auto serverThread = std::thread([&]() {
+               server.run();
+       });
+
+       { /// Client configuration
+               auto clientClosure = [&]() {
+                       Client client(sockPath);
+
+                       Message message(Message::Type::MethodCall, requestSignature);
+                       message.enclose(request1, request2, request3);
+
+                       for (int i = 0; i < 3; i++) {
+                               auto reply = client.request(message);
+                               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);
+                       }
+               };
+
+               for (int i = 0; i < 3; i++)
+                       clientClosure();
+       }
+
+       server.stop();
+
+       if (serverThread.joinable())
+               serverThread.join();
+}
diff --git a/src/vist/rmi/impl/tests/socket.cpp b/src/vist/rmi/impl/tests/socket.cpp
new file mode 100644 (file)
index 0000000..2437e3a
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2020 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/impl/socket.hpp>
+
+#include <string>
+#include <limits>
+#include <thread>
+#include <chrono>
+#include <cstring>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi::impl;
+
+TEST(SocketTests, socket_read_write)
+{
+       std::string sockPath = "./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(SocketTests, socket_abstract)
+{
+       std::string sockPath = "@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();
+}
index 9f8923ee0238a5a7dc455a7390199971f72cdb72..033adeaeae5d67ad7c5e9069078ddd9166f81079 100644 (file)
 
 #include <vist/rmi/impl/client.hpp>
 
-#ifdef TIZEN
-#include <vist/rmi/impl/ondemand/client.hpp>
-#else
-#include <vist/rmi/impl/general/client.hpp>
-#endif
-
 #include <memory>
 #include <mutex>
 #include <string>
@@ -35,22 +29,17 @@ using namespace vist::rmi::impl;
 
 class Remote::Impl {
 public:
-       explicit Impl(const std::string& path)
+       explicit Impl(const std::string& path) : client(path)
        {
-#ifdef TIZEN
-               this->client = std::make_unique<ondemand::Client>(path);
-#else
-               this->client = std::make_unique<general::Client>(path);
-#endif
        }
 
        Message request(Message& message)
        {
-               return this->client->request(message);
+               return this->client.request(message);
        }
 
 private:
-       std::unique_ptr<interface::Client> client;
+       Client client;
 };
 
 Remote::Remote(const std::string& path) : pImpl(new Impl(path))
index b078d97b3ec9476581a7b2a3147e706fc4f5dc60..4fb156608ce7d6a7b52b46a18e9e56cdea98368d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2019-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.
@@ -39,9 +39,10 @@ void Vistd::start()
 {
        INFO(VIST) << "Vistd daemon starts.";
 
-       rmi::Gateway::ServiceType type = rmi::Gateway::ServiceType::General;
 #ifdef TIZEN
-       type = rmi::Gateway::ServiceType::OnDemand;
+       rmi::Gateway::ServiceType type = rmi::Gateway::ServiceType::OnDemand;
+#else
+       rmi::Gateway::ServiceType type = rmi::Gateway::ServiceType::General;
 #endif
 
        rmi::Gateway gateway(SOCK_ADDR, type);
@@ -49,8 +50,13 @@ void Vistd::start()
 
        auto& pm = policy::PolicyManager::Instance();
 
+#ifdef TIZEN
        /// Shutdown service if timeout is occured without activated admin
        gateway.start(3000, [&pm]() -> bool { return !pm.isActivated(); });
+#else
+       (void)pm;
+       gateway.start();
+#endif
 
        INFO(VIST) << "Vistd daemon stopped.";
 }