From: Sangwan Kwon Date: Wed, 15 Jan 2020 05:06:19 +0000 (+0900) Subject: Remove boost dependency from rmi X-Git-Tag: submit/tizen/20200810.073515~97 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a2cde7e447e5da0b5158a35b756977b2e767a996;p=platform%2Fcore%2Fsecurity%2Fvist.git Remove boost dependency from rmi Signed-off-by: Sangwan Kwon --- diff --git a/src/vist/rmi/CMakeLists.txt b/src/vist/rmi/CMakeLists.txt index d06d07d..329367e 100644 --- a/src/vist/rmi/CMakeLists.txt +++ b/src/vist/rmi/CMakeLists.txt @@ -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}) diff --git a/src/vist/rmi/gateway.cpp b/src/vist/rmi/gateway.cpp index 5ecc9ec..1bbb640 100644 --- a/src/vist/rmi/gateway.cpp +++ b/src/vist/rmi/gateway.cpp @@ -19,8 +19,6 @@ #include #include #include -#include -#include #include #include @@ -51,15 +49,7 @@ public: return reply; }; - switch (type) { - case ServiceType::OnDemand: - this->server = std::make_unique(path, dispatcher); - break; - case ServiceType::General: /// fall through - default: - this->server = std::make_unique(path, dispatcher); - break; - } + this->server = std::make_unique(path, dispatcher, type); } inline void start(int timeout, std::function stopper) @@ -73,7 +63,7 @@ public: } private: - std::unique_ptr server; + std::unique_ptr server; }; Gateway::Gateway(const std::string& path, ServiceType type) : diff --git a/src/vist/rmi/impl/client.hpp b/src/vist/rmi/impl/client.hpp index d673c64..e244621 100644 --- a/src/vist/rmi/impl/client.hpp +++ b/src/vist/rmi/impl/client.hpp @@ -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. @@ -16,28 +16,31 @@ #pragma once -#include +#include +#include +#include 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 index 0000000..eeaf761 --- /dev/null +++ b/src/vist/rmi/impl/connection.cpp @@ -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 + +#include + +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 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 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 index 0000000..5da2651 --- /dev/null +++ b/src/vist/rmi/impl/connection.hpp @@ -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 +#include + +#include +#include + +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 index 0000000..e902369 --- /dev/null +++ b/src/vist/rmi/impl/eventfd.cpp @@ -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 + +#include +#include +#include + +#include + +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 index 0000000..9dc529a --- /dev/null +++ b/src/vist/rmi/impl/eventfd.hpp @@ -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 + +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 index 1ef64c2..0000000 --- a/src/vist/rmi/impl/general/client.hpp +++ /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 -#include - -#include -#include - -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 index 2a9d25e..0000000 --- a/src/vist/rmi/impl/general/protocol.cpp +++ /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 -#include - -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& 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 index 6690bf4..0000000 --- a/src/vist/rmi/impl/general/protocol.hpp +++ /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 -#include - -#include - -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 { - 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 index 05d256e..0000000 --- a/src/vist/rmi/impl/general/server.hpp +++ /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 -#include - -#include - -#include -#include - -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(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(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 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 index a67d7d5..0000000 --- a/src/vist/rmi/impl/general/tests/protocol.cpp +++ /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 -#include - -#include -#include - -#include -#include - -#include - -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(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 index eae2494..0000000 --- a/src/vist/rmi/impl/general/tests/server-client.cpp +++ /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 -#include -#include - -#include -#include - -#include - -#include - -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 index 0000000..da08828 --- /dev/null +++ b/src/vist/rmi/impl/mainloop.cpp @@ -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 +#include + +#include + +#include +#include + +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 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); + auto onErrorPtr = (onError != nullptr) ? std::make_shared(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 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 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 index 0000000..67ad856 --- /dev/null +++ b/src/vist/rmi/impl/mainloop.hpp @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace vist { +namespace rmi { +namespace impl { + +class Mainloop { +public: + using OnEvent = std::function; + using OnError = std::function; + using Stopper = std::function; + + 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>; + using Listener = std::unordered_map; + + 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 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 index 555d3ec..0000000 --- a/src/vist/rmi/impl/ondemand/client.hpp +++ /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 -#include -#include - -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 index 75e346a..0000000 --- a/src/vist/rmi/impl/ondemand/connection.cpp +++ /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 - -#include - -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 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 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 index 080391d..0000000 --- a/src/vist/rmi/impl/ondemand/connection.hpp +++ /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 -#include - -#include -#include - -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 index e3ec163..0000000 --- a/src/vist/rmi/impl/ondemand/eventfd.cpp +++ /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 - -#include -#include -#include - -#include - -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 index c017fe3..0000000 --- a/src/vist/rmi/impl/ondemand/eventfd.hpp +++ /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 - -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 index 613632b..0000000 --- a/src/vist/rmi/impl/ondemand/mainloop.cpp +++ /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 -#include - -#include - -#include -#include - -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 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); - auto onErrorPtr = (onError != nullptr) ? std::make_shared(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 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 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 index 8ce748c..0000000 --- a/src/vist/rmi/impl/ondemand/mainloop.hpp +++ /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 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace vist { -namespace rmi { -namespace impl { -namespace ondemand { - -class Mainloop { -public: - using OnEvent = std::function; - using OnError = std::function; - using Stopper = std::function; - - 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>; - using Listener = std::unordered_map; - - 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 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 index 9b90015..0000000 --- a/src/vist/rmi/impl/ondemand/server.hpp +++ /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 -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -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(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 index 110e3fc..0000000 --- a/src/vist/rmi/impl/ondemand/socket.cpp +++ /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 - -#include -#include -#include - -#include -#include -#include - -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 index 993fe90..0000000 --- a/src/vist/rmi/impl/ondemand/socket.hpp +++ /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 - -#include -#include -#include - -#include -#include - -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 - void send(const T* buffer, const std::size_t size = sizeof(T)) const; - - template - 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 -void Socket::send(const T *buffer, const std::size_t size) const -{ - std::size_t written = 0; - while (written < size) { - auto rest = reinterpret_cast(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 -void Socket::recv(T *buffer, const std::size_t size) const -{ - std::size_t readen = 0; - while (readen < size) { - auto rest = reinterpret_cast(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 index d952dd0..0000000 --- a/src/vist/rmi/impl/ondemand/systemd-socket.hpp +++ /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 -#include - -#include -#include -#include - -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 index f5f2297..0000000 --- a/src/vist/rmi/impl/ondemand/tests/connection.cpp +++ /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 -#include -#include -#include - -#include -#include - -#include - -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 index 435b5d1..0000000 --- a/src/vist/rmi/impl/ondemand/tests/mainloop.cpp +++ /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 -#include - -#include -#include -#include - -#include - -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::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::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 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 index 86e0eaa..0000000 --- a/src/vist/rmi/impl/ondemand/tests/socket.cpp +++ /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 - -#include -#include -#include -#include -#include - -#include - -using namespace vist::rmi::impl::ondemand; - -TEST(SocketTests, socket_read_write) -{ - std::string sockPath = "./test.sock"; - Socket socket(sockPath); - - int input = std::numeric_limits::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::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(); -} diff --git a/src/vist/rmi/impl/server.hpp b/src/vist/rmi/impl/server.hpp index cb56f90..e8b4604 100644 --- a/src/vist/rmi/impl/server.hpp +++ b/src/vist/rmi/impl/server.hpp @@ -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. @@ -16,22 +16,47 @@ #pragma once -#include +#include +#include +#include +#include +#include -#include +#include +#include + +#include +#include +#include #include +#include +#include namespace vist { namespace rmi { namespace impl { -namespace interface { using Task = std::function; using Stopper = std::function; +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(SystemdSocket::Create(path)); + break; + case ServiceType::General: /// fall through + default: + this->socket = std::make_unique(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(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; + 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 index 0000000..d304545 --- /dev/null +++ b/src/vist/rmi/impl/socket.cpp @@ -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 + +#include +#include +#include + +#include +#include +#include + +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 index 0000000..0fa6757 --- /dev/null +++ b/src/vist/rmi/impl/socket.hpp @@ -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 + +#include +#include +#include + +#include +#include + +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 + void send(const T* buffer, const std::size_t size = sizeof(T)) const; + + template + 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 +void Socket::send(const T *buffer, const std::size_t size) const +{ + std::size_t written = 0; + while (written < size) { + auto rest = reinterpret_cast(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 +void Socket::recv(T *buffer, const std::size_t size) const +{ + std::size_t readen = 0; + while (readen < size) { + auto rest = reinterpret_cast(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 index 0000000..38dd2d2 --- /dev/null +++ b/src/vist/rmi/impl/systemd-socket.hpp @@ -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 +#include + +#include +#include +#include + +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 index 0000000..7e79624 --- /dev/null +++ b/src/vist/rmi/impl/tests/connection.cpp @@ -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 +#include +#include +#include + +#include +#include + +#include + +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 index 0000000..6119ffe --- /dev/null +++ b/src/vist/rmi/impl/tests/mainloop.cpp @@ -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 +#include + +#include +#include +#include + +#include + +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::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::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 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 index 0000000..a657ed6 --- /dev/null +++ b/src/vist/rmi/impl/tests/server-client.cpp @@ -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 +#include +#include + +#include +#include + +#include + +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 index 0000000..2437e3a --- /dev/null +++ b/src/vist/rmi/impl/tests/socket.cpp @@ -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 + +#include +#include +#include +#include +#include + +#include + +using namespace vist::rmi::impl; + +TEST(SocketTests, socket_read_write) +{ + std::string sockPath = "./test.sock"; + Socket socket(sockPath); + + int input = std::numeric_limits::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::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(); +} diff --git a/src/vist/rmi/remote.cpp b/src/vist/rmi/remote.cpp index 9f8923e..033adea 100644 --- a/src/vist/rmi/remote.cpp +++ b/src/vist/rmi/remote.cpp @@ -18,12 +18,6 @@ #include -#ifdef TIZEN -#include -#else -#include -#endif - #include #include #include @@ -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(path); -#else - this->client = std::make_unique(path); -#endif } Message request(Message& message) { - return this->client->request(message); + return this->client.request(message); } private: - std::unique_ptr client; + Client client; }; Remote::Remote(const std::string& path) : pImpl(new Impl(path)) diff --git a/src/vist/service/vistd.cpp b/src/vist/service/vistd.cpp index b078d97..4fb1566 100644 --- a/src/vist/service/vistd.cpp +++ b/src/vist/service/vistd.cpp @@ -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."; }