-# 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.
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}
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})
#include <vist/exception.hpp>
#include <vist/rmi/message.hpp>
#include <vist/rmi/impl/server.hpp>
-#include <vist/rmi/impl/general/server.hpp>
-#include <vist/rmi/impl/ondemand/server.hpp>
#include <memory>
#include <string>
return reply;
};
- switch (type) {
- case ServiceType::OnDemand:
- this->server = std::make_unique<ondemand::Server>(path, dispatcher);
- break;
- case ServiceType::General: /// fall through
- default:
- this->server = std::make_unique<general::Server>(path, dispatcher);
- break;
- }
+ this->server = std::make_unique<Server>(path, dispatcher, type);
}
inline void start(int timeout, std::function<bool()> stopper)
}
private:
- std::unique_ptr<interface::Server> server;
+ std::unique_ptr<Server> server;
};
Gateway::Gateway(const std::string& path, ServiceType type) :
/*
- * Copyright (c) 2019-present Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#pragma once
-#include <string>
+#include <vist/logger.hpp>
+#include <vist/rmi/impl/client.hpp>
+#include <vist/rmi/impl/connection.hpp>
namespace vist {
namespace rmi {
namespace impl {
-namespace interface {
class Client {
public:
- Client(const std::string&) {}
- virtual ~Client() = default;
-
- Client(const Client&) = delete;
- Client& operator=(const Client&) = delete;
-
- Client(Client&&) = default;
- Client& operator=(Client&&) = default;
-
- virtual Message request(Message& message) = 0;
+ Client(const std::string& path) : connection(path)
+ {
+ DEBUG(VIST) << "Success to connect to : " << path
+ << " by fd[" << connection.getFd() << "]";
+ }
+
+ Message request(Message& message)
+ {
+ return this->connection.request(message);
+ }
+
+private:
+ Connection connection;
};
-} // namespace interface
} // namespace impl
} // namespace rmi
} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include "connection.hpp"
+
+#include <vist/logger.hpp>
+
+#include <utility>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+Connection::Connection(Socket&& socket) noexcept : socket(std::move(socket))
+{
+}
+
+Connection::Connection(const std::string& path) :
+ socket(Socket::connect(path))
+{
+ DEBUG(VIST) << "Connect to " << path << " by fd: " << socket.getFd();
+}
+
+void Connection::send(Message& message)
+{
+ std::lock_guard<std::mutex> lock(this->sendMutex);
+
+ message.header.id = this->sequence++;
+ this->socket.send(&message.header);
+
+ this->socket.send(message.getBuffer().data(), message.header.length);
+}
+
+Message Connection::recv(void) const
+{
+ std::lock_guard<std::mutex> lock(this->recvMutex);
+ Message::Header header;
+ this->socket.recv(&header);
+
+ Message message(header);
+ this->socket.recv(message.getBuffer().data(), message.size());
+ message.disclose(message.signature);
+
+ return message;
+}
+
+Message Connection::request(Message& message)
+{
+ this->send(message);
+ return this->recv();
+}
+
+int Connection::getFd(void) const noexcept
+{
+ return this->socket.getFd();
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <vist/rmi/message.hpp>
+#include <vist/rmi/impl/socket.hpp>
+
+#include <mutex>
+#include <utility>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class Connection {
+public:
+ explicit Connection(Socket&& socket) noexcept;
+ explicit Connection(const std::string& path);
+ virtual ~Connection() = default;
+
+ Connection(const Connection&) = delete;
+ Connection& operator=(const Connection&) = delete;
+
+ Connection(Connection&&) = default;
+ Connection& operator=(Connection&&) = default;
+
+ // server-side
+ void send(Message& message);
+ Message recv(void) const;
+
+ // client-side
+ Message request(Message& message);
+
+ int getFd(void) const noexcept;
+
+private:
+ Socket socket;
+
+ // SOCK_STREAM are full-duplex byte streams
+ mutable std::mutex sendMutex;
+ mutable std::mutex recvMutex;
+
+ unsigned int sequence = 0;
+};
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include "eventfd.hpp"
+
+#include <vist/exception.hpp>
+
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cstdint>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+EventFD::EventFD(unsigned int initval, int flags)
+ : fd(::eventfd(initval, flags))
+{
+ if (this->fd == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to create eventfd.";
+}
+
+EventFD::~EventFD()
+{
+ ::close(fd);
+}
+
+void EventFD::send(void)
+{
+ const std::uint64_t val = 1;
+ errno = 0;
+ if (::write(this->fd, &val, sizeof(val)) == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to write to eventfd: " << errno;
+}
+
+void EventFD::receive(void)
+{
+ std::uint64_t val = 0;
+ errno = 0;
+ if (::read(this->fd, &val, sizeof(val)) == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to read from eventfd: " << errno;
+}
+
+int EventFD::getFd(void) const noexcept
+{
+ return this->fd;
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <sys/eventfd.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class EventFD final {
+public:
+ explicit EventFD(unsigned int initval = 0, int flags = EFD_SEMAPHORE | EFD_CLOEXEC);
+ ~EventFD();
+
+ EventFD(const EventFD&) = delete;
+ EventFD& operator=(const EventFD&) = delete;
+
+ EventFD(EventFD&&) = delete;
+ EventFD& operator=(EventFD&&) = delete;
+
+ void send(void);
+ void receive(void);
+
+ int getFd(void) const noexcept;
+
+private:
+ int fd;
+};
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/rmi/impl/client.hpp>
-#include <vist/rmi/impl/general/protocol.hpp>
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-using boost::asio::local::stream_protocol;
-
-class Client : public interface::Client {
-public:
- Client(const std::string& path) : interface::Client(path), socket(this->context)
- {
- try {
- this->socket.connect(Protocol::Endpoint(path));
- } catch(const std::exception& e) {
- ERROR(VIST) << "Failed to connect socket: " << e.what();
- std::rethrow_exception(std::current_exception());
- }
- }
-
- Message request(Message& message) override
- {
- return Protocol::Request(this->socket, message);
- }
-
-private:
- Protocol::Context context;
- Protocol::Socket socket;
-};
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include "protocol.hpp"
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-Message Protocol::Recv(Socket& socket)
-{
- DEBUG(VIST) << "Socket read event occured.";
- Message::Header header;
- const auto& headerBuffer = boost::asio::buffer(&header, sizeof(Message::Header));
- auto readen = boost::asio::read(socket, headerBuffer);
- if (readen != sizeof(Message::Header))
- THROW(ErrCode::ProtocolBroken) << "Failed to receive message header.";
-
- Message content(header);
- const auto& contentBuffer = boost::asio::buffer(content.getBuffer());
- readen = boost::asio::read(socket, contentBuffer);
- if (readen != content.size())
- THROW(ErrCode::ProtocolBroken) << "Failed to receive message content.";
-
- content.disclose(content.signature);
-
- return content;
-}
-
-void Protocol::Send(Socket& socket, Message& message)
-{
- DEBUG(VIST) << "Socket write event occured.";
- const auto& headerBuffer = boost::asio::buffer(&message.header,
- sizeof(Message::Header));
- auto written = boost::asio::write(socket, headerBuffer);
- if (written != sizeof(Message::Header))
- THROW(ErrCode::ProtocolBroken) << "Failed to send message header.";
-
- const auto& contentBuffer = boost::asio::buffer(message.getBuffer(), message.size());
- written = boost::asio::write(socket, contentBuffer);
- if (written != message.size())
- THROW(ErrCode::ProtocolBroken) << "Failed to send message content.";
-}
-
-Message Protocol::Request(Socket& socket, Message& message)
-{
- Protocol::Send(socket, message);
- return Protocol::Recv(socket);
-}
-
-void Protocol::Async::dispatch(const interface::Task& task)
-{
- auto self = shared_from_this();
- const auto& header = boost::asio::buffer(&this->message.header,
- sizeof(Message::Header));
- auto handler = [self, task](const auto& error, std::size_t size) {
- if (error) {
- if (error == boost::asio::error::eof) {
- DEBUG(VIST) << "Socket EoF event occured.";
- return;
- } else {
- THROW(ErrCode::RuntimeError) << error.message();
- }
- }
-
- if (size != sizeof(Message::Header))
- THROW(ErrCode::ProtocolBroken) << error.message();
-
- /// Resize message buffer to revieved header length.
- self->message.resize(self->message.size());
-
- const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer());
- auto readen = boost::asio::read(self->socket, contentBuffer);
- if (readen != self->message.size())
- THROW(ErrCode::ProtocolBroken) << "Failed to receive message content."
- << readen << ", " << self->message.size();
-
- self->message.disclose(self->message.signature);
- self->process(task);
- };
-
- boost::asio::async_read(self->socket, header, handler);
-}
-
-void Protocol::Async::process(const interface::Task& task)
-{
- bool raised = false;
- std::string errMsg;
- auto onError = [&raised, &errMsg](const std::string& message) {
- ERROR(VIST) << "Failed to process task: " << message;
- raised = true;
- errMsg = message;
- };
-
- try {
- /// Process dispatched task.
- auto result = task(message);
- this->message = result;
- } catch (const vist::Exception<ErrCode>& e) {
- onError(e.what());
- } catch (const std::exception& e) {
- onError(e.what());
- }
-
- if (raised)
- this->message = Message(Message::Type::Error, errMsg);
-
- auto self = shared_from_this();
- const auto& headerBuffer = boost::asio::buffer(&this->message.header,
- sizeof(Message::Header));
- auto handler = [self, task](const auto& error, std::size_t size) {
- if (error || size != sizeof(Message::Header))
- THROW(ErrCode::ProtocolBroken) << "Failed to send message header: "
- << error.message();
-
- const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer());
- auto written = boost::asio::write(self->socket, contentBuffer);
- if (written != self->message.size())
- THROW(ErrCode::ProtocolBroken) << "Failed to send message content.";
-
- /// Re-dispatch for next request.
- self->dispatch(task);
- };
-
- boost::asio::async_write(self->socket, headerBuffer, handler);
-}
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/server.hpp>
-
-#include <boost/asio.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-struct Protocol {
- using Acceptor = boost::asio::local::stream_protocol::acceptor;
- using Context = boost::asio::io_service;
- using Endpoint = boost::asio::local::stream_protocol::endpoint;
- using Socket = boost::asio::local::stream_protocol::socket;
-
- static Message Recv(Socket& socket);
- static void Send(Socket& socket, Message& message);
-
- static Message Request(Socket& socket, Message& message);
-
- /// Passing shared_from_this() to lambda() guarantees
- /// that the lambda() always refer to a live object.
- class Async : public std::enable_shared_from_this<Async> {
- public:
- explicit Async(Context& context) : socket(context) {}
- void dispatch(const interface::Task& task);
- void process(const interface::Task& task);
-
- inline Socket& getSocket()
- {
- return this->socket;
- }
-
- private:
- Message message;
- Socket socket;
- };
-};
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include "protocol.hpp"
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-#include <memory>
-
-#include <errno.h>
-#include <unistd.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace general {
-
-class Server : public interface::Server {
-public:
- Server(const std::string& path, const interface::Task& task) : interface::Server(path, task)
- {
- errno = 0;
- if (::unlink(path.c_str()) == -1 && errno != ENOENT)
- THROW(ErrCode::RuntimeError) << "Failed to remove file.";
-
- this->acceptor = std::make_unique<Protocol::Acceptor>(this->context,
- Protocol::Endpoint(path));
- this->accept(task);
- }
- virtual ~Server() = default;
-
- Server(const Server&) = delete;
- Server& operator=(const Server&) = delete;
-
- Server(Server&&) = default;
- Server& operator=(Server&&) = default;
-
- /// boost-based service does not support timeout and stopper.
- void run(int timeout = -1, interface::Stopper stopper = nullptr) override
- {
- (void) timeout;
- (void) stopper;
- this->context.run();
- }
-
- void stop() override
- {
- this->context.stop();
- }
-
-private:
- void accept(const interface::Task& task) override
- {
- auto asyncSession = std::make_shared<Protocol::Async>(this->context);
- auto handler = [this, asyncSession, task](const auto& error) {
- DEBUG(VIST) << "New session is accepted.";
-
- if (error)
- THROW(ErrCode::RuntimeError) << error.message();
-
- asyncSession->dispatch(task);
-
- this->accept(task);
- };
- this->acceptor->async_accept(asyncSession->getSocket(), handler);
- }
-
- Protocol::Context context;
- std::unique_ptr<Protocol::Acceptor> acceptor;
-};
-
-} // namespace general
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/general/protocol.hpp>
-
-#include <string>
-#include <thread>
-
-#include <gtest/gtest.h>
-#include <boost/asio.hpp>
-
-#include <unistd.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::general;
-using boost::asio::local::stream_protocol;
-
-namespace {
-
-/// Request variables
-std::string requestSignature = "request signature";
-int request1 = 100;
-bool request2 = true;
-std::string request3 = "request argument";
-
-/// Response variables
-std::string responseSignature = "response signature";
-int response1 = 300;
-bool response2 = false;
-std::string response3 = "response argument";
-
-} // anonymous namespace
-
-TEST(ProtocolTests, sync_server_sync_client)
-{
- std::string sockPath = "vist-test.sock";
- ::unlink(sockPath.c_str());
-
- /// Server configuration
- boost::asio::io_service context;
- stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath));
- stream_protocol::socket sock(context);
-
- auto handler = [&](const auto& error) {
- EXPECT_EQ(error, boost::system::errc::success);
-
- Message request = Protocol::Recv(sock);
- EXPECT_EQ(request.signature, requestSignature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- request.disclose(recv1, recv2, recv3);
- EXPECT_EQ(request1, recv1);
- EXPECT_EQ(request2, recv2);
- EXPECT_EQ(request3, recv3);
-
- Message reply(Message::Type::Reply, responseSignature);
- reply.enclose(response1, response2, response3);
- Protocol::Send(sock, reply);
- };
- acceptor.async_accept(sock, handler);
-
- auto serverThread = std::thread([&]() {
- context.run();
- });
-
- { /// Client configuration
- boost::asio::io_service context;
- stream_protocol::socket sock(context);
- sock.connect(stream_protocol::endpoint(sockPath));
-
- Message request(Message::Type::MethodCall, requestSignature);
- request.enclose(request1, request2, request3);
-
- auto reply = Protocol::Request(sock, request);
- EXPECT_EQ(reply.signature, responseSignature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- reply.disclose(recv1, recv2, recv3);
- EXPECT_EQ(response1, recv1);
- EXPECT_EQ(response2, recv2);
- EXPECT_EQ(response3, recv3);
- }
-
- context.stop();
-
- if (serverThread.joinable())
- serverThread.join();
-}
-
-TEST(ProtocolTests, async_server_sync_client)
-{
- std::string sockPath = "vist-test.sock";
- ::unlink(sockPath.c_str());
-
- /// Server configuration
- boost::asio::io_service context;
- stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath));
-
- auto async = std::make_shared<Protocol::Async>(context);
- auto handler = [&](const auto& error) {
- EXPECT_EQ(error, boost::system::errc::success);
- auto task = [&](auto& message) -> Message {
- EXPECT_EQ(message.signature, requestSignature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- message.disclose(recv1, recv2, recv3);
- EXPECT_EQ(request1, recv1);
- EXPECT_EQ(request2, recv2);
- EXPECT_EQ(request3, recv3);
-
- Message reply(Message::Type::Reply, responseSignature);
- reply.enclose(response1, response2, response3);
- return reply;
- };
-
- async->dispatch(task);
- };
- acceptor.async_accept(async->getSocket(), handler);
-
- auto serverThread = std::thread([&]() {
- context.run();
- });
-
- { /// Client configuration
- boost::asio::io_service context;
- stream_protocol::socket sock(context);
- sock.connect(stream_protocol::endpoint(sockPath));
-
- Message request(Message::Type::MethodCall, requestSignature);
- request.enclose(request1, request2, request3);
-
- auto requestClosure = [&]() {
- auto reply = Protocol::Request(sock, request);
- EXPECT_EQ(reply.signature, responseSignature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- reply.disclose(recv1, recv2, recv3);
- EXPECT_EQ(response1, recv1);
- EXPECT_EQ(response2, recv2);
- EXPECT_EQ(response3, recv3);
- };
-
- for (int i = 0; i < 3; i++)
- requestClosure();
- }
-
- context.stop();
-
- if (serverThread.joinable())
- serverThread.join();
-}
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/general/server.hpp>
-#include <vist/rmi/impl/general/client.hpp>
-
-#include <string>
-#include <thread>
-
-#include <boost/asio.hpp>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::general;
-using boost::asio::local::stream_protocol;
-
-namespace {
-
-/// Request variables
-std::string requestSignature = "request signature";
-int request1 = 100;
-bool request2 = true;
-std::string request3 = "request argument";
-
-/// Response variables
-std::string responseSignature = "response signature";
-int response1 = 300;
-bool response2 = false;
-std::string response3 = "response argument";
-
-} // anonymous namespace
-
-#ifndef TIZEN
-TEST(ServerClientTests, server)
-{
- std::string sockPath = "vist-test.sock";
-
- auto task = [&](Message& message) -> Message {
- EXPECT_EQ(message.signature, requestSignature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- message.disclose(recv1, recv2, recv3);
- EXPECT_EQ(request1, recv1);
- EXPECT_EQ(request2, recv2);
- EXPECT_EQ(request3, recv3);
-
- Message reply(Message::Type::Reply, responseSignature);
- reply.enclose(response1, response2, response3);
- return reply;
- };
-
- Server server(sockPath, task);
- auto serverThread = std::thread([&]() {
- server.run();
- });
-
- { /// Client configuration
- auto clientClosure = [&]() {
- Client client(sockPath);
-
- Message message(Message::Type::MethodCall, requestSignature);
- message.enclose(request1, request2, request3);
-
- for (int i = 0; i < 3; i++) {
- auto reply = client.request(message);
- EXPECT_EQ(reply.signature, responseSignature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- reply.disclose(recv1, recv2, recv3);
- EXPECT_EQ(response1, recv1);
- EXPECT_EQ(response2, recv2);
- EXPECT_EQ(response3, recv3);
- }
- };
-
- for (int i = 0; i < 3; i++)
- clientClosure();
- }
-
- server.stop();
-
- if (serverThread.joinable())
- serverThread.join();
-}
-#endif
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include "mainloop.hpp"
+
+#include <vist/exception.hpp>
+#include <vist/logger.hpp>
+
+#include <cstring>
+
+#include <unistd.h>
+#include <errno.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+Mainloop::Mainloop() :
+ epollFd(::epoll_create1(EPOLL_CLOEXEC)),
+ stopped(false)
+{
+ if (epollFd == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to create epoll instance.";
+}
+
+Mainloop::~Mainloop()
+{
+ ::close(this->epollFd);
+}
+
+void Mainloop::addHandler(const int fd, OnEvent&& onEvent, OnError&& onError)
+{
+ std::lock_guard<Mutex> lock(mutex);
+
+ if (this->listener.find(fd) != this->listener.end()) {
+ WARN(VIST) << "Event is already registered.";
+ return;
+ }
+
+ ::epoll_event event;
+ std::memset(&event, 0, sizeof(epoll_event));
+
+ event.events = EPOLLIN | EPOLLHUP | EPOLLRDHUP;
+ event.data.fd = fd;
+
+ if (::epoll_ctl(this->epollFd, EPOLL_CTL_ADD, fd, &event) == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to add event source.";
+
+ auto onEventPtr = std::make_shared<OnEvent>(onEvent);
+ auto onErrorPtr = (onError != nullptr) ? std::make_shared<OnError>(onError) : nullptr;
+
+ auto handler = std::make_pair(std::move(onEventPtr), std::move(onErrorPtr));
+
+ this->listener.insert({fd, handler});
+ DEBUG(VIST) << "FD[" << fd << "] listens to events.";
+}
+
+void Mainloop::removeHandler(const int fd)
+{
+ std::lock_guard<Mutex> lock(mutex);
+
+ auto iter = this->listener.find(fd);
+ if (iter == this->listener.end())
+ THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
+
+ this->listener.erase(iter);
+
+ ::epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+Mainloop::Handler Mainloop::getHandler(const int fd)
+{
+ std::lock_guard<Mutex> lock(mutex);
+
+ auto iter = this->listener.find(fd);
+ if (iter == this->listener.end())
+ THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
+
+ return std::make_pair(iter->second.first, iter->second.second);
+}
+
+void Mainloop::prepare(void)
+{
+ auto wakeup = [this]() {
+ this->wakeupSignal.receive();
+ this->removeHandler(this->wakeupSignal.getFd());
+ this->stopped = true;
+ };
+
+ DEBUG(VIST) << "Add eventfd to mainloop for wakeup: " << this->wakeupSignal.getFd();
+ this->addHandler(this->wakeupSignal.getFd(), wakeup);
+}
+
+void Mainloop::wait(int timeout, Stopper stopper)
+{
+ int nfds = 0;
+ do {
+ errno = 0;
+ nfds = ::epoll_wait(this->epollFd, this->events.data(), MAX_EVENTS, timeout);
+ if (errno == EINTR)
+ WARN(VIST) << "The call was interrupted by a signal handler.";
+ } while ((nfds == -1) && (errno == EINTR));
+
+ if (nfds == 0) {
+ DEBUG(VIST) << "Mainloop is stopped by timeout.";
+
+ if (stopper())
+ this->stopped = true;
+
+ return;
+ }
+
+ if (nfds < 0)
+ THROW(ErrCode::RuntimeError) << "Failed to wait epoll events: " << errno;
+
+ this->dispatch(nfds);
+}
+
+void Mainloop::dispatch(int size) {
+ for (int i = 0; i < size; i++) {
+ auto handler = this->getHandler(this->events[i].data.fd);
+ auto onEvent = handler.first;
+ auto onError = handler.second;
+
+ try {
+ if ((this->events[i].events & (EPOLLHUP | EPOLLRDHUP))) {
+ WARN(VIST) << "Connected client might be disconnected.";
+ if (onError != nullptr)
+ (*onError)();
+ } else {
+ (*onEvent)();
+ }
+
+ } catch (const std::exception& e) {
+ ERROR(VIST) << e.what();
+ }
+ }
+}
+
+void Mainloop::run(int timeout, Stopper stopper)
+{
+ this->stopped = false;
+ this->prepare();
+
+ if (stopper == nullptr)
+ stopper = []() -> bool { return true; };
+
+ while (!this->stopped)
+ this->wait(timeout, stopper);
+}
+
+void Mainloop::stop(void)
+{
+ this->wakeupSignal.send();
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <vist/rmi/impl/eventfd.hpp>
+
+#include <atomic>
+#include <array>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+
+#include <sys/epoll.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class Mainloop {
+public:
+ using OnEvent = std::function<void(void)>;
+ using OnError = std::function<void(void)>;
+ using Stopper = std::function<bool(void)>;
+
+ Mainloop();
+ virtual ~Mainloop();
+
+ Mainloop(const Mainloop&) = delete;
+ Mainloop& operator=(const Mainloop&) = delete;
+
+ Mainloop(Mainloop&&) = delete;
+ Mainloop& operator=(Mainloop&&) = delete;
+
+ void addHandler(const int fd, OnEvent&& onEvent, OnError&& = nullptr);
+ void removeHandler(const int fd);
+
+ /// Stopper is a predicate what returns a condition to stop mainloop
+ /// when timeout is occured.
+ void run(int timeout = -1, Stopper = nullptr);
+ void stop(void);
+
+private:
+ // recursive_mutex makes additional calls to lock in calling thread.
+ // And other threads will block (for calls to lock).
+ // So, addHandler() can be called during dispatch().
+ using Mutex = std::recursive_mutex;
+ using Handler = std::pair<std::shared_ptr<OnEvent>, std::shared_ptr<OnError>>;
+ using Listener = std::unordered_map<int, Handler>;
+
+ Handler getHandler(const int fd);
+
+ void prepare(void);
+
+ void wait(int timeout, Stopper stopper);
+ void dispatch(int size);
+
+ Mutex mutex;
+ Listener listener;
+ EventFD wakeupSignal;
+
+ int epollFd;
+ std::atomic<bool> stopped;
+
+ static constexpr int MAX_EVENTS = 16;
+ std::array<::epoll_event, MAX_EVENTS> events;
+};
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/logger.hpp>
-#include <vist/rmi/impl/client.hpp>
-#include <vist/rmi/impl/ondemand/connection.hpp>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Client : public interface::Client {
-public:
- Client(const std::string& path) : interface::Client(path), connection(path)
- {
- DEBUG(VIST) << "Success to connect to : " << path
- << " by fd[" << connection.getFd() << "]";
- }
-
- Message request(Message& message) override
- {
- return this->connection.request(message);
- }
-
-private:
- Connection connection;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include "connection.hpp"
-
-#include <vist/logger.hpp>
-
-#include <utility>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-Connection::Connection(Socket&& socket) noexcept : socket(std::move(socket))
-{
-}
-
-Connection::Connection(const std::string& path) :
- socket(Socket::connect(path))
-{
- DEBUG(VIST) << "Connect to " << path << " by fd: " << socket.getFd();
-}
-
-void Connection::send(Message& message)
-{
- std::lock_guard<std::mutex> lock(this->sendMutex);
-
- message.header.id = this->sequence++;
- this->socket.send(&message.header);
-
- this->socket.send(message.getBuffer().data(), message.header.length);
-}
-
-Message Connection::recv(void) const
-{
- std::lock_guard<std::mutex> lock(this->recvMutex);
- Message::Header header;
- this->socket.recv(&header);
-
- Message message(header);
- this->socket.recv(message.getBuffer().data(), message.size());
- message.disclose(message.signature);
-
- return message;
-}
-
-Message Connection::request(Message& message)
-{
- this->send(message);
- return this->recv();
-}
-
-int Connection::getFd(void) const noexcept
-{
- return this->socket.getFd();
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/ondemand/socket.hpp>
-
-#include <mutex>
-#include <utility>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Connection {
-public:
- explicit Connection(Socket&& socket) noexcept;
- explicit Connection(const std::string& path);
- virtual ~Connection() = default;
-
- Connection(const Connection&) = delete;
- Connection& operator=(const Connection&) = delete;
-
- Connection(Connection&&) = default;
- Connection& operator=(Connection&&) = default;
-
- // server-side
- void send(Message& message);
- Message recv(void) const;
-
- // client-side
- Message request(Message& message);
-
- int getFd(void) const noexcept;
-
-private:
- Socket socket;
-
- // SOCK_STREAM are full-duplex byte streams
- mutable std::mutex sendMutex;
- mutable std::mutex recvMutex;
-
- unsigned int sequence = 0;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include "eventfd.hpp"
-
-#include <vist/exception.hpp>
-
-#include <errno.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <cstdint>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-EventFD::EventFD(unsigned int initval, int flags)
- : fd(::eventfd(initval, flags))
-{
- if (this->fd == -1)
- THROW(ErrCode::RuntimeError) << "Failed to create eventfd.";
-}
-
-EventFD::~EventFD()
-{
- ::close(fd);
-}
-
-void EventFD::send(void)
-{
- const std::uint64_t val = 1;
- errno = 0;
- if (::write(this->fd, &val, sizeof(val)) == -1)
- THROW(ErrCode::RuntimeError) << "Failed to write to eventfd: " << errno;
-}
-
-void EventFD::receive(void)
-{
- std::uint64_t val = 0;
- errno = 0;
- if (::read(this->fd, &val, sizeof(val)) == -1)
- THROW(ErrCode::RuntimeError) << "Failed to read from eventfd: " << errno;
-}
-
-int EventFD::getFd(void) const noexcept
-{
- return this->fd;
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <sys/eventfd.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class EventFD final {
-public:
- explicit EventFD(unsigned int initval = 0, int flags = EFD_SEMAPHORE | EFD_CLOEXEC);
- ~EventFD();
-
- EventFD(const EventFD&) = delete;
- EventFD& operator=(const EventFD&) = delete;
-
- EventFD(EventFD&&) = delete;
- EventFD& operator=(EventFD&&) = delete;
-
- void send(void);
- void receive(void);
-
- int getFd(void) const noexcept;
-
-private:
- int fd;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include "mainloop.hpp"
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-#include <cstring>
-
-#include <unistd.h>
-#include <errno.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-Mainloop::Mainloop() :
- epollFd(::epoll_create1(EPOLL_CLOEXEC)),
- stopped(false)
-{
- if (epollFd == -1)
- THROW(ErrCode::RuntimeError) << "Failed to create epoll instance.";
-}
-
-Mainloop::~Mainloop()
-{
- ::close(this->epollFd);
-}
-
-void Mainloop::addHandler(const int fd, OnEvent&& onEvent, OnError&& onError)
-{
- std::lock_guard<Mutex> lock(mutex);
-
- if (this->listener.find(fd) != this->listener.end()) {
- WARN(VIST) << "Event is already registered.";
- return;
- }
-
- ::epoll_event event;
- std::memset(&event, 0, sizeof(epoll_event));
-
- event.events = EPOLLIN | EPOLLHUP | EPOLLRDHUP;
- event.data.fd = fd;
-
- if (::epoll_ctl(this->epollFd, EPOLL_CTL_ADD, fd, &event) == -1)
- THROW(ErrCode::RuntimeError) << "Failed to add event source.";
-
- auto onEventPtr = std::make_shared<OnEvent>(onEvent);
- auto onErrorPtr = (onError != nullptr) ? std::make_shared<OnError>(onError) : nullptr;
-
- auto handler = std::make_pair(std::move(onEventPtr), std::move(onErrorPtr));
-
- this->listener.insert({fd, handler});
- DEBUG(VIST) << "FD[" << fd << "] listens to events.";
-}
-
-void Mainloop::removeHandler(const int fd)
-{
- std::lock_guard<Mutex> lock(mutex);
-
- auto iter = this->listener.find(fd);
- if (iter == this->listener.end())
- THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
-
- this->listener.erase(iter);
-
- ::epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);
-}
-
-Mainloop::Handler Mainloop::getHandler(const int fd)
-{
- std::lock_guard<Mutex> lock(mutex);
-
- auto iter = this->listener.find(fd);
- if (iter == this->listener.end())
- THROW(ErrCode::RuntimeError) << "Not found file descriptor.";
-
- return std::make_pair(iter->second.first, iter->second.second);
-}
-
-void Mainloop::prepare(void)
-{
- auto wakeup = [this]() {
- this->wakeupSignal.receive();
- this->removeHandler(this->wakeupSignal.getFd());
- this->stopped = true;
- };
-
- DEBUG(VIST) << "Add eventfd to mainloop for wakeup: " << this->wakeupSignal.getFd();
- this->addHandler(this->wakeupSignal.getFd(), wakeup);
-}
-
-void Mainloop::wait(int timeout, Stopper stopper)
-{
- int nfds = 0;
- do {
- errno = 0;
- nfds = ::epoll_wait(this->epollFd, this->events.data(), MAX_EVENTS, timeout);
- if (errno == EINTR)
- WARN(VIST) << "The call was interrupted by a signal handler.";
- } while ((nfds == -1) && (errno == EINTR));
-
- if (nfds == 0) {
- DEBUG(VIST) << "Mainloop is stopped by timeout.";
-
- if (stopper())
- this->stopped = true;
-
- return;
- }
-
- if (nfds < 0)
- THROW(ErrCode::RuntimeError) << "Failed to wait epoll events: " << errno;
-
- this->dispatch(nfds);
-}
-
-void Mainloop::dispatch(int size) {
- for (int i = 0; i < size; i++) {
- auto handler = this->getHandler(this->events[i].data.fd);
- auto onEvent = handler.first;
- auto onError = handler.second;
-
- try {
- if ((this->events[i].events & (EPOLLHUP | EPOLLRDHUP))) {
- WARN(VIST) << "Connected client might be disconnected.";
- if (onError != nullptr)
- (*onError)();
- } else {
- (*onEvent)();
- }
-
- } catch (const std::exception& e) {
- ERROR(VIST) << e.what();
- }
- }
-}
-
-void Mainloop::run(int timeout, Stopper stopper)
-{
- this->stopped = false;
- this->prepare();
-
- if (stopper == nullptr)
- stopper = []() -> bool { return true; };
-
- while (!this->stopped)
- this->wait(timeout, stopper);
-}
-
-void Mainloop::stop(void)
-{
- this->wakeupSignal.send();
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/rmi/impl/ondemand/eventfd.hpp>
-
-#include <atomic>
-#include <array>
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <stdexcept>
-#include <string>
-#include <unordered_map>
-
-#include <sys/epoll.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Mainloop {
-public:
- using OnEvent = std::function<void(void)>;
- using OnError = std::function<void(void)>;
- using Stopper = std::function<bool(void)>;
-
- Mainloop();
- virtual ~Mainloop();
-
- Mainloop(const Mainloop&) = delete;
- Mainloop& operator=(const Mainloop&) = delete;
-
- Mainloop(Mainloop&&) = delete;
- Mainloop& operator=(Mainloop&&) = delete;
-
- void addHandler(const int fd, OnEvent&& onEvent, OnError&& = nullptr);
- void removeHandler(const int fd);
-
- /// Stopper is a predicate what returns a condition to stop mainloop
- /// when timeout is occured.
- void run(int timeout = -1, Stopper = nullptr);
- void stop(void);
-
-private:
- // recursive_mutex makes additional calls to lock in calling thread.
- // And other threads will block (for calls to lock).
- // So, addHandler() can be called during dispatch().
- using Mutex = std::recursive_mutex;
- using Handler = std::pair<std::shared_ptr<OnEvent>, std::shared_ptr<OnError>>;
- using Listener = std::unordered_map<int, Handler>;
-
- Handler getHandler(const int fd);
-
- void prepare(void);
-
- void wait(int timeout, Stopper stopper);
- void dispatch(int size);
-
- Mutex mutex;
- Listener listener;
- EventFD wakeupSignal;
-
- int epollFd;
- std::atomic<bool> stopped;
-
- static constexpr int MAX_EVENTS = 16;
- std::array<::epoll_event, MAX_EVENTS> events;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/rmi/impl/server.hpp>
-#include <vist/rmi/impl/ondemand/connection.hpp>
-#include <vist/rmi/impl/ondemand/mainloop.hpp>
-#include <vist/rmi/impl/ondemand/socket.hpp>
-#include <vist/rmi/impl/ondemand/systemd-socket.hpp>
-
-#include <vist/exception.hpp>
-#include <vist/logger.hpp>
-
-#include <memory>
-#include <mutex>
-#include <set>
-#include <string>
-#include <unordered_map>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Server : public interface::Server {
-public:
- Server(const std::string& path, const interface::Task& task) :
- interface::Server(path, task),
- socket(SystemdSocket::Create(path))
- {
- this->accept(task);
- }
-
- virtual ~Server() = default;
-
- Server(const Server&) = delete;
- Server& operator=(const Server&) = delete;
-
- Server(Server&&) = default;
- Server& operator=(Server&&) = default;
-
- void run(int timeout = -1, interface::Stopper stopper = nullptr) override
- {
- this->mainloop.run(timeout, stopper);
- }
-
- void stop(void) override
- {
- this->mainloop.removeHandler(this->socket.getFd());
- this->mainloop.stop();
- }
-
-private:
- void accept(const interface::Task& task) override
- {
- auto handler = [this, task]() {
- DEBUG(VIST) << "New session is accepted.";
-
- auto connection = std::make_shared<Connection>(this->socket.accept());
- auto onRead = [connection, task]() {
- Message request = connection->recv();
- DEBUG(VIST) << "Session header: " << request.signature;
-
- try {
- Message reply = task(request);
- connection->send(reply);
- } catch (const std::exception& e) {
- ERROR(VIST) << e.what();
- Message reply = Message(Message::Type::Error, e.what());
- connection->send(reply);
- }
- };
-
- auto onClose = [this, connection]() {
- DEBUG(VIST) << "Connection closed. fd: " << connection->getFd();
- this->mainloop.removeHandler(connection->getFd());
- };
-
- this->mainloop.addHandler(connection->getFd(),
- std::move(onRead), std::move(onClose));
- };
-
- INFO(VIST) << "Ready for new connection.";
- this->mainloop.addHandler(this->socket.getFd(), std::move(handler));
- }
-
- Socket socket;
- Mainloop mainloop;
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include "socket.hpp"
-
-#include <vist/logger.hpp>
-
-#include <fstream>
-#include <iostream>
-#include <fcntl.h>
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-namespace {
-
-void set_cloexec(int fd)
-{
- if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
- THROW(ErrCode::RuntimeError) << "Failed to set CLOSEXEC.";
-}
-
-} // anonymous namespace
-
-Socket::Socket(int fd) noexcept : fd(fd)
-{
-}
-
-Socket::Socket(const std::string& path)
-{
- if (path.size() >= sizeof(::sockaddr_un::sun_path))
- THROW(ErrCode::LogicError) << "Socket path size is wrong.";
-
- int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd == -1)
- THROW(ErrCode::RuntimeError) << "Failed to create socket.";
-
- set_cloexec(fd);
-
- ::sockaddr_un addr;
- addr.sun_family = AF_UNIX;
- ::strncpy(addr.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path) - 1);
- addr.sun_path[sizeof(sockaddr_un::sun_path) - 1] = '\0';
-
- if (addr.sun_path[0] == '@')
- addr.sun_path[0] = '\0';
-
- struct stat buf;
- if (::stat(path.c_str(), &buf) == 0)
- if (::unlink(path.c_str()) == -1)
- THROW(ErrCode::RuntimeError) << "Failed to remove exist socket.";
-
- if (::bind(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(::sockaddr_un)) == -1) {
- ::close(fd);
- THROW(ErrCode::RuntimeError) << "Failed to bind.";
- }
-
- if (::listen(fd, MAX_BACKLOG_SIZE) == -1) {
- ::close(fd);
- THROW(ErrCode::RuntimeError) << "Failed to liten.";
- }
-
- this->fd = fd;
-
- DEBUG(VIST) << "Socket is created: " << path << ", and is listening.. fd[" << fd << "]";
-}
-
-Socket::Socket(Socket&& that) : fd(that.fd)
-{
- that.fd = -1;
-}
-
-Socket& Socket::operator=(Socket&& that)
-{
- if (this == &that)
- return *this;
-
- this->fd = that.fd;
- that.fd = -1;
-
- return *this;
-}
-
-Socket::~Socket(void)
-{
- if (fd != -1)
- ::close(fd);
-}
-
-Socket Socket::accept(void) const
-{
- errno = 0;
- int fd = ::accept(this->fd, nullptr, nullptr);
- if (fd == -1)
- THROW(ErrCode::RuntimeError) << "Failed to accept: " << errno;
-
- set_cloexec(fd);
-
- return Socket(fd);
-}
-
-Socket Socket::connect(const std::string& path)
-{
- if (path.size() >= sizeof(::sockaddr_un::sun_path))
- THROW(ErrCode::LogicError) << "Socket path size is wrong.";
-
- int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd == -1)
- THROW(ErrCode::RuntimeError) << "Failed to create socket.";
-
- set_cloexec(fd);
-
- ::sockaddr_un addr;
- addr.sun_family = AF_UNIX;
- ::strncpy(addr.sun_path, path.c_str(), sizeof(::sockaddr_un::sun_path));
-
- if (addr.sun_path[0] == '@')
- addr.sun_path[0] = '\0';
-
- DEBUG(VIST) << "Start to connect: " << path;
- errno = 0;
- if (::connect(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(sockaddr_un)) == -1) {
- ::close(fd);
- ERROR(VIST) << "Failed to connect to: " << path;
- THROW(ErrCode::RuntimeError) << "Failed to connect to: " << path
- << ", with: " << errno;
- }
-
- return Socket(fd);
-}
-
-int Socket::getFd(void) const noexcept
-{
- return this->fd;
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/exception.hpp>
-
-#include <cstddef>
-#include <string>
-#include <stdexcept>
-
-#include <unistd.h>
-#include <errno.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class Socket {
-public:
- explicit Socket(int fd) noexcept;
- explicit Socket(const std::string& path);
- virtual ~Socket(void);
-
- Socket(const Socket&) = delete;
- Socket& operator=(const Socket&) = delete;
-
- Socket(Socket&&);
- Socket& operator=(Socket&&);
-
- Socket accept(void) const;
- static Socket connect(const std::string& path);
-
- template<typename T>
- void send(const T* buffer, const std::size_t size = sizeof(T)) const;
-
- template<typename T>
- void recv(T* buffer, const std::size_t size = sizeof(T)) const;
-
- int getFd(void) const noexcept;
-
-private:
- const int MAX_BACKLOG_SIZE = 100;
-
- int fd;
-};
-
-template<typename T>
-void Socket::send(const T *buffer, const std::size_t size) const
-{
- std::size_t written = 0;
- while (written < size) {
- auto rest = reinterpret_cast<const unsigned char*>(buffer) + written;
- auto bytes = ::write(this->fd, rest, size - written);
- errno = 0;
- if (bytes >= 0)
- written += bytes;
- else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- else
- THROW(ErrCode::RuntimeError) << "Failed to write to socket: " << errno;
- }
-}
-
-template<typename T>
-void Socket::recv(T *buffer, const std::size_t size) const
-{
- std::size_t readen = 0;
- while (readen < size) {
- auto rest = reinterpret_cast<unsigned char*>(buffer) + readen;
- auto bytes = ::read(this->fd, rest, size - readen);
- errno = 0;
- if (bytes >= 0)
- readen += bytes;
- else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- else
- THROW(ErrCode::RuntimeError) << "Failed to read from socket: " << errno;
- }
-}
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#pragma once
-
-#include <vist/logger.hpp>
-#include <vist/exception.hpp>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <systemd/sd-daemon.h>
-
-namespace vist {
-namespace rmi {
-namespace impl {
-namespace ondemand {
-
-class SystemdSocket {
-public:
- static int Create(const std::string& path)
- {
- static int fds = -1;
-
- if (fds == -1)
- fds = ::sd_listen_fds(0);
-
- if (fds < 0)
- THROW(ErrCode::RuntimeError) << "Failed to get listened systemd fds.";
-
- for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fds; ++fd) {
- if (::sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0) > 0) {
- INFO(VIST) << "Systemd socket of service is found with fd: " << fd;
- return fd;
- }
- }
-
- THROW(ErrCode::RuntimeError) << "Failed to find listened systemd fds.";
- }
-};
-
-} // namespace ondemand
-} // namespace impl
-} // namespace rmi
-} // namespace vist
+++ /dev/null
-/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include <vist/rmi/message.hpp>
-#include <vist/rmi/impl/ondemand/connection.hpp>
-#include <vist/rmi/impl/ondemand/socket.hpp>
-#include <vist/rmi/impl/ondemand/mainloop.hpp>
-
-#include <string>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::ondemand;
-
-TEST(ConnectionTests, socket_communication)
-{
- std::string sockPath = ("@vist-test.sock");
-
- // server-side
- Mainloop mainloop;
- Socket socket(sockPath);
-
- std::string requestSignature = "request signature";
- int request1 = 100;
- bool request2 = true;
- std::string request3 = "request argument";
-
- std::string responseSignature = "response signature";
- int response1 = 300;
- bool response2 = false;
- std::string response3 = "response argument";
-
- auto onAccept = [&]() {
- Connection conn(socket.accept());
- Message request = conn.recv();
- EXPECT_EQ(requestSignature, request.signature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- request.disclose(recv1, recv2, recv3);
- EXPECT_EQ(request1, recv1);
- EXPECT_EQ(request2, recv2);
- EXPECT_EQ(request3, recv3);
-
- Message reply(Message::Type::Reply, responseSignature);
- reply.enclose(response1, response2, response3);
- conn.send(reply);
-
- mainloop.removeHandler(socket.getFd());
- mainloop.stop();
- };
-
- mainloop.addHandler(socket.getFd(), std::move(onAccept));
- auto serverThread = std::thread([&]() { mainloop.run(); });
-
- // client-side
- Connection conn(sockPath);
- Message msg(Message::Type::Signal, requestSignature);
- msg.enclose(request1, request2, request3);
-
- /// Do not request multiple times. (The above sever only processes once.)
- {
- Message reply = conn.request(msg);
- EXPECT_EQ(reply.signature, responseSignature);
-
- int recv1;
- bool recv2;
- std::string recv3;
- reply.disclose(recv1, recv2, recv3);
- EXPECT_EQ(response1, recv1);
- EXPECT_EQ(response2, recv2);
- EXPECT_EQ(response3, recv3);
- }
-
- if (serverThread.joinable())
- serverThread.join();
-}
+++ /dev/null
-/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include <vist/rmi/impl/ondemand/socket.hpp>
-#include <vist/rmi/impl/ondemand/mainloop.hpp>
-
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi;
-using namespace vist::rmi::impl::ondemand;
-
-TEST(MainloopTests, single)
-{
- std::string sockPath = "@sock";
- Socket socket(sockPath);
- Mainloop mainloop;
-
- int input = std::numeric_limits<int>::max();
- bool input2 = true;
-
- int output = 0;
- bool output2 = false;
-
- auto onAccept = [&]() {
- Socket accepted = socket.accept();
-
- // Recv input from client.
- accepted.recv(&output);
- EXPECT_EQ(input, output);
-
- // Send input2 to client.
- accepted.send(&input2);
-
- mainloop.removeHandler(socket.getFd());
- mainloop.stop();
- };
-
- mainloop.addHandler(socket.getFd(), std::move(onAccept));
- auto server = std::thread([&]() { mainloop.run(); });
-
- // Send input to server.
- Socket connected = Socket::connect(sockPath);
- connected.send(&input);
-
- // Recv input2 from server.
- connected.recv(&output2);
- EXPECT_EQ(input2, output2);
-
- if (server.joinable())
- server.join();
-}
-
-TEST(MainloopTests, multiflexing)
-{
- std::string sockPath = "@sock";
- Socket socket(sockPath);
- Mainloop mainloop;
-
- int input = std::numeric_limits<int>::max();
- bool input2 = true;
-
- int output = 0;
- bool output2 = false;
-
- auto onAccept = [&]() {
- Socket accepted = socket.accept();
-
- // Recv input from client.
- accepted.recv(&output);
- EXPECT_EQ(input, output);
-
- // Send input2 to client.
- accepted.send(&input2);
- };
-
- /// Set timeout to stop
- mainloop.addHandler(socket.getFd(), std::move(onAccept));
- auto server = std::thread([&]() { mainloop.run(1000); });
-
- auto task = [&]() {
- // Send input to server.
- Socket connected = Socket::connect(sockPath);
- connected.send(&input);
-
- // Recv input2 from server.
- connected.recv(&output2);
- EXPECT_EQ(input2, output2);
- };
-
- std::vector<std::thread> clients;
- clients.emplace_back(std::thread(task));
- clients.emplace_back(std::thread(task));
- clients.emplace_back(std::thread(task));
-
- if (server.joinable())
- server.join();
-
- for (auto& client : clients)
- if (client.joinable())
- client.join();
-}
-
-TEST(MainloopTests, stopper)
-{
- auto stopper = []() -> bool { return true; };
- Mainloop mainloop;
- mainloop.run(1000, stopper);
- EXPECT_TRUE(true);
-
- mainloop.run(1000);
- EXPECT_TRUE(true);
-}
+++ /dev/null
-/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-#include <vist/rmi/impl/ondemand/socket.hpp>
-
-#include <string>
-#include <limits>
-#include <thread>
-#include <chrono>
-#include <cstring>
-
-#include <gtest/gtest.h>
-
-using namespace vist::rmi::impl::ondemand;
-
-TEST(SocketTests, socket_read_write)
-{
- std::string sockPath = "./test.sock";
- Socket socket(sockPath);
-
- int input = std::numeric_limits<int>::max();
- bool input2 = true;
-
- int output = 0;
- bool output2 = false;
-
- auto client = std::thread([&]() {
- std::this_thread::sleep_for(std::chrono::seconds(1));
-
- // Send input to server.
- Socket connected = Socket::connect(sockPath);
- connected.send(&input);
-
- // Recv input2 from server.
- connected.recv(&output2);
-
- EXPECT_EQ(input2, output2);
- });
-
- Socket accepted = socket.accept();
-
- // Recv input from client.
- accepted.recv(&output);
- EXPECT_EQ(input, output);
-
- // Send input2 to client.
- accepted.send(&input2);
-
- if (client.joinable())
- client.join();
-}
-
-TEST(SocketTests, socket_abstract)
-{
- std::string sockPath = "@sock";
- Socket socket(sockPath);
-
- int input = std::numeric_limits<int>::max();
- bool input2 = true;
-
- int output = 0;
- bool output2 = false;
-
- auto client = std::thread([&]() {
- std::this_thread::sleep_for(std::chrono::seconds(1));
-
- // Send input to server.
- Socket connected = Socket::connect(sockPath);
- connected.send(&input);
-
- // Recv input2 from server.
- connected.recv(&output2);
-
- EXPECT_EQ(input2, output2);
- });
-
- Socket accepted = socket.accept();
-
- // Recv input from client.
- accepted.recv(&output);
- EXPECT_EQ(input, output);
-
- // Send input2 to client.
- accepted.send(&input2);
-
- if (client.joinable())
- client.join();
-}
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#pragma once
-#include <vist/rmi/message.hpp>
+#include <vist/rmi/gateway.hpp>
+#include <vist/rmi/impl/connection.hpp>
+#include <vist/rmi/impl/mainloop.hpp>
+#include <vist/rmi/impl/socket.hpp>
+#include <vist/rmi/impl/systemd-socket.hpp>
-#include <functional>
+#include <vist/exception.hpp>
+#include <vist/logger.hpp>
+
+#include <memory>
+#include <mutex>
+#include <set>
#include <string>
+#include <unordered_map>
+#include <functional>
namespace vist {
namespace rmi {
namespace impl {
-namespace interface {
using Task = std::function<Message(Message&)>;
using Stopper = std::function<bool(void)>;
+using ServiceType = Gateway::ServiceType;
class Server {
public:
- explicit Server(const std::string&, const Task&) {}
+ Server(const std::string& path, const Task& task, ServiceType type = ServiceType::General)
+ {
+ switch (type) {
+ case ServiceType::OnDemand:
+ this->socket = std::make_unique<Socket>(SystemdSocket::Create(path));
+ break;
+ case ServiceType::General: /// fall through
+ default:
+ this->socket = std::make_unique<Socket>(path);
+ break;
+ }
+
+ this->accept(task);
+ }
+
virtual ~Server() = default;
Server(const Server&) = delete;
Server(Server&&) = default;
Server& operator=(Server&&) = default;
- virtual void run(int timeout = -1, Stopper stopper = nullptr) = 0;
- virtual void stop() = 0;
+ void run(int timeout = -1, Stopper stopper = nullptr)
+ {
+ this->mainloop.run(timeout, stopper);
+ }
+
+ void stop(void)
+ {
+ this->mainloop.removeHandler(this->socket->getFd());
+ this->mainloop.stop();
+ }
private:
- virtual void accept(const Task& task) = 0;
+ void accept(const Task& task)
+ {
+ auto handler = [this, task]() {
+ DEBUG(VIST) << "New session is accepted.";
+
+ auto connection = std::make_shared<Connection>(this->socket->accept());
+ auto onRead = [connection, task]() {
+ Message request = connection->recv();
+ DEBUG(VIST) << "Session header: " << request.signature;
+
+ try {
+ Message reply = task(request);
+ connection->send(reply);
+ } catch (const std::exception& e) {
+ ERROR(VIST) << e.what();
+ Message reply = Message(Message::Type::Error, e.what());
+ connection->send(reply);
+ }
+ };
+
+ auto onClose = [this, connection]() {
+ DEBUG(VIST) << "Connection closed. fd: " << connection->getFd();
+ this->mainloop.removeHandler(connection->getFd());
+ };
+
+ this->mainloop.addHandler(connection->getFd(),
+ std::move(onRead), std::move(onClose));
+ };
+
+ INFO(VIST) << "Ready for new connection.";
+ this->mainloop.addHandler(this->socket->getFd(), std::move(handler));
+ }
+
+ std::unique_ptr<Socket> socket;
+ Mainloop mainloop;
};
-} // namespace interface
} // namespace impl
} // namespace rmi
} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include "socket.hpp"
+
+#include <vist/logger.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+namespace {
+
+void set_cloexec(int fd)
+{
+ if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to set CLOSEXEC.";
+}
+
+} // anonymous namespace
+
+Socket::Socket(int fd) noexcept : fd(fd)
+{
+}
+
+Socket::Socket(const std::string& path)
+{
+ if (path.size() >= sizeof(::sockaddr_un::sun_path))
+ THROW(ErrCode::LogicError) << "Socket path size is wrong.";
+
+ int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to create socket.";
+
+ set_cloexec(fd);
+
+ ::sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ ::strncpy(addr.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path) - 1);
+ addr.sun_path[sizeof(sockaddr_un::sun_path) - 1] = '\0';
+
+ if (addr.sun_path[0] == '@')
+ addr.sun_path[0] = '\0';
+
+ struct stat buf;
+ if (::stat(path.c_str(), &buf) == 0)
+ if (::unlink(path.c_str()) == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to remove exist socket.";
+
+ if (::bind(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(::sockaddr_un)) == -1) {
+ ::close(fd);
+ THROW(ErrCode::RuntimeError) << "Failed to bind.";
+ }
+
+ if (::listen(fd, MAX_BACKLOG_SIZE) == -1) {
+ ::close(fd);
+ THROW(ErrCode::RuntimeError) << "Failed to liten.";
+ }
+
+ this->fd = fd;
+
+ DEBUG(VIST) << "Socket is created: " << path << ", and is listening.. fd[" << fd << "]";
+}
+
+Socket::Socket(Socket&& that) : fd(that.fd)
+{
+ that.fd = -1;
+}
+
+Socket& Socket::operator=(Socket&& that)
+{
+ if (this == &that)
+ return *this;
+
+ this->fd = that.fd;
+ that.fd = -1;
+
+ return *this;
+}
+
+Socket::~Socket(void)
+{
+ if (fd != -1)
+ ::close(fd);
+}
+
+Socket Socket::accept(void) const
+{
+ errno = 0;
+ int fd = ::accept(this->fd, nullptr, nullptr);
+ if (fd == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to accept: " << errno;
+
+ set_cloexec(fd);
+
+ return Socket(fd);
+}
+
+Socket Socket::connect(const std::string& path)
+{
+ if (path.size() >= sizeof(::sockaddr_un::sun_path))
+ THROW(ErrCode::LogicError) << "Socket path size is wrong.";
+
+ int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ THROW(ErrCode::RuntimeError) << "Failed to create socket.";
+
+ set_cloexec(fd);
+
+ ::sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ ::strncpy(addr.sun_path, path.c_str(), sizeof(::sockaddr_un::sun_path));
+
+ if (addr.sun_path[0] == '@')
+ addr.sun_path[0] = '\0';
+
+ DEBUG(VIST) << "Start to connect: " << path;
+ errno = 0;
+ if (::connect(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(sockaddr_un)) == -1) {
+ ::close(fd);
+ ERROR(VIST) << "Failed to connect to: " << path;
+ THROW(ErrCode::RuntimeError) << "Failed to connect to: " << path
+ << ", with: " << errno;
+ }
+
+ return Socket(fd);
+}
+
+int Socket::getFd(void) const noexcept
+{
+ return this->fd;
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2018-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <vist/exception.hpp>
+
+#include <cstddef>
+#include <string>
+#include <stdexcept>
+
+#include <unistd.h>
+#include <errno.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class Socket {
+public:
+ explicit Socket(int fd) noexcept;
+ explicit Socket(const std::string& path);
+ virtual ~Socket(void);
+
+ Socket(const Socket&) = delete;
+ Socket& operator=(const Socket&) = delete;
+
+ Socket(Socket&&);
+ Socket& operator=(Socket&&);
+
+ Socket accept(void) const;
+ static Socket connect(const std::string& path);
+
+ template<typename T>
+ void send(const T* buffer, const std::size_t size = sizeof(T)) const;
+
+ template<typename T>
+ void recv(T* buffer, const std::size_t size = sizeof(T)) const;
+
+ int getFd(void) const noexcept;
+
+private:
+ const int MAX_BACKLOG_SIZE = 100;
+
+ int fd;
+};
+
+template<typename T>
+void Socket::send(const T *buffer, const std::size_t size) const
+{
+ std::size_t written = 0;
+ while (written < size) {
+ auto rest = reinterpret_cast<const unsigned char*>(buffer) + written;
+ auto bytes = ::write(this->fd, rest, size - written);
+ errno = 0;
+ if (bytes >= 0)
+ written += bytes;
+ else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ else
+ THROW(ErrCode::RuntimeError) << "Failed to write to socket: " << errno;
+ }
+}
+
+template<typename T>
+void Socket::recv(T *buffer, const std::size_t size) const
+{
+ std::size_t readen = 0;
+ while (readen < size) {
+ auto rest = reinterpret_cast<unsigned char*>(buffer) + readen;
+ auto bytes = ::read(this->fd, rest, size - readen);
+ errno = 0;
+ if (bytes >= 0)
+ readen += bytes;
+ else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ continue;
+ else
+ THROW(ErrCode::RuntimeError) << "Failed to read from socket: " << errno;
+ }
+}
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2020-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#pragma once
+
+#include <vist/logger.hpp>
+#include <vist/exception.hpp>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <systemd/sd-daemon.h>
+
+namespace vist {
+namespace rmi {
+namespace impl {
+
+class SystemdSocket {
+public:
+ static int Create(const std::string& path)
+ {
+ static int fds = -1;
+
+ if (fds == -1)
+ fds = ::sd_listen_fds(0);
+
+ if (fds < 0)
+ THROW(ErrCode::RuntimeError) << "Failed to get listened systemd fds.";
+
+ for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fds; ++fd) {
+ if (::sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0) > 0) {
+ INFO(VIST) << "Systemd socket of service is found with fd: " << fd;
+ return fd;
+ }
+ }
+
+ THROW(ErrCode::RuntimeError) << "Failed to find listened systemd fds.";
+ }
+};
+
+} // namespace impl
+} // namespace rmi
+} // namespace vist
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include <vist/rmi/message.hpp>
+#include <vist/rmi/impl/connection.hpp>
+#include <vist/rmi/impl/socket.hpp>
+#include <vist/rmi/impl/mainloop.hpp>
+
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi;
+using namespace vist::rmi::impl;
+
+TEST(ConnectionTests, socket_communication)
+{
+ std::string sockPath = ("@vist-test.sock");
+
+ // server-side
+ Mainloop mainloop;
+ Socket socket(sockPath);
+
+ std::string requestSignature = "request signature";
+ int request1 = 100;
+ bool request2 = true;
+ std::string request3 = "request argument";
+
+ std::string responseSignature = "response signature";
+ int response1 = 300;
+ bool response2 = false;
+ std::string response3 = "response argument";
+
+ auto onAccept = [&]() {
+ Connection conn(socket.accept());
+ Message request = conn.recv();
+ EXPECT_EQ(requestSignature, request.signature);
+
+ int recv1;
+ bool recv2;
+ std::string recv3;
+ request.disclose(recv1, recv2, recv3);
+ EXPECT_EQ(request1, recv1);
+ EXPECT_EQ(request2, recv2);
+ EXPECT_EQ(request3, recv3);
+
+ Message reply(Message::Type::Reply, responseSignature);
+ reply.enclose(response1, response2, response3);
+ conn.send(reply);
+
+ mainloop.removeHandler(socket.getFd());
+ mainloop.stop();
+ };
+
+ mainloop.addHandler(socket.getFd(), std::move(onAccept));
+ auto serverThread = std::thread([&]() { mainloop.run(); });
+
+ // client-side
+ Connection conn(sockPath);
+ Message msg(Message::Type::Signal, requestSignature);
+ msg.enclose(request1, request2, request3);
+
+ /// Do not request multiple times. (The above sever only processes once.)
+ {
+ Message reply = conn.request(msg);
+ EXPECT_EQ(reply.signature, responseSignature);
+
+ int recv1;
+ bool recv2;
+ std::string recv3;
+ reply.disclose(recv1, recv2, recv3);
+ EXPECT_EQ(response1, recv1);
+ EXPECT_EQ(response2, recv2);
+ EXPECT_EQ(response3, recv3);
+ }
+
+ if (serverThread.joinable())
+ serverThread.join();
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include <vist/rmi/impl/socket.hpp>
+#include <vist/rmi/impl/mainloop.hpp>
+
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi;
+using namespace vist::rmi::impl;
+
+TEST(MainloopTests, single)
+{
+ std::string sockPath = "@sock";
+ Socket socket(sockPath);
+ Mainloop mainloop;
+
+ int input = std::numeric_limits<int>::max();
+ bool input2 = true;
+
+ int output = 0;
+ bool output2 = false;
+
+ auto onAccept = [&]() {
+ Socket accepted = socket.accept();
+
+ // Recv input from client.
+ accepted.recv(&output);
+ EXPECT_EQ(input, output);
+
+ // Send input2 to client.
+ accepted.send(&input2);
+
+ mainloop.removeHandler(socket.getFd());
+ mainloop.stop();
+ };
+
+ mainloop.addHandler(socket.getFd(), std::move(onAccept));
+ auto server = std::thread([&]() { mainloop.run(); });
+
+ // Send input to server.
+ Socket connected = Socket::connect(sockPath);
+ connected.send(&input);
+
+ // Recv input2 from server.
+ connected.recv(&output2);
+ EXPECT_EQ(input2, output2);
+
+ if (server.joinable())
+ server.join();
+}
+
+TEST(MainloopTests, multiflexing)
+{
+ std::string sockPath = "@sock";
+ Socket socket(sockPath);
+ Mainloop mainloop;
+
+ int input = std::numeric_limits<int>::max();
+ bool input2 = true;
+
+ int output = 0;
+ bool output2 = false;
+
+ auto onAccept = [&]() {
+ Socket accepted = socket.accept();
+
+ // Recv input from client.
+ accepted.recv(&output);
+ EXPECT_EQ(input, output);
+
+ // Send input2 to client.
+ accepted.send(&input2);
+ };
+
+ /// Set timeout to stop
+ mainloop.addHandler(socket.getFd(), std::move(onAccept));
+ auto server = std::thread([&]() { mainloop.run(1000); });
+
+ auto task = [&]() {
+ // Send input to server.
+ Socket connected = Socket::connect(sockPath);
+ connected.send(&input);
+
+ // Recv input2 from server.
+ connected.recv(&output2);
+ EXPECT_EQ(input2, output2);
+ };
+
+ std::vector<std::thread> clients;
+ clients.emplace_back(std::thread(task));
+ clients.emplace_back(std::thread(task));
+ clients.emplace_back(std::thread(task));
+
+ if (server.joinable())
+ server.join();
+
+ for (auto& client : clients)
+ if (client.joinable())
+ client.join();
+}
+
+TEST(MainloopTests, stopper)
+{
+ auto stopper = []() -> bool { return true; };
+ Mainloop mainloop;
+ mainloop.run(1000, stopper);
+ EXPECT_TRUE(true);
+
+ mainloop.run(1000);
+ EXPECT_TRUE(true);
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include <vist/rmi/message.hpp>
+#include <vist/rmi/impl/server.hpp>
+#include <vist/rmi/impl/client.hpp>
+
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi;
+using namespace vist::rmi::impl;
+
+namespace {
+
+/// Request variables
+std::string requestSignature = "request signature";
+int request1 = 100;
+bool request2 = true;
+std::string request3 = "request argument";
+
+/// Response variables
+std::string responseSignature = "response signature";
+int response1 = 300;
+bool response2 = false;
+std::string response3 = "response argument";
+
+} // anonymous namespace
+
+TEST(ServerClientTests, not_ondemand)
+{
+ std::string sockPath = "./vist-test.sock";
+
+ auto task = [&](Message& message) -> Message {
+ EXPECT_EQ(message.signature, requestSignature);
+
+ int recv1;
+ bool recv2;
+ std::string recv3;
+ message.disclose(recv1, recv2, recv3);
+ EXPECT_EQ(request1, recv1);
+ EXPECT_EQ(request2, recv2);
+ EXPECT_EQ(request3, recv3);
+
+ Message reply(Message::Type::Reply, responseSignature);
+ reply.enclose(response1, response2, response3);
+ return reply;
+ };
+
+ Server server(sockPath, task);
+ auto serverThread = std::thread([&]() {
+ server.run();
+ });
+
+ { /// Client configuration
+ auto clientClosure = [&]() {
+ Client client(sockPath);
+
+ Message message(Message::Type::MethodCall, requestSignature);
+ message.enclose(request1, request2, request3);
+
+ for (int i = 0; i < 3; i++) {
+ auto reply = client.request(message);
+ EXPECT_EQ(reply.signature, responseSignature);
+
+ int recv1;
+ bool recv2;
+ std::string recv3;
+ reply.disclose(recv1, recv2, recv3);
+ EXPECT_EQ(response1, recv1);
+ EXPECT_EQ(response2, recv2);
+ EXPECT_EQ(response3, recv3);
+ }
+ };
+
+ for (int i = 0; i < 3; i++)
+ clientClosure();
+ }
+
+ server.stop();
+
+ if (serverThread.joinable())
+ serverThread.join();
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include <vist/rmi/impl/socket.hpp>
+
+#include <string>
+#include <limits>
+#include <thread>
+#include <chrono>
+#include <cstring>
+
+#include <gtest/gtest.h>
+
+using namespace vist::rmi::impl;
+
+TEST(SocketTests, socket_read_write)
+{
+ std::string sockPath = "./test.sock";
+ Socket socket(sockPath);
+
+ int input = std::numeric_limits<int>::max();
+ bool input2 = true;
+
+ int output = 0;
+ bool output2 = false;
+
+ auto client = std::thread([&]() {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ // Send input to server.
+ Socket connected = Socket::connect(sockPath);
+ connected.send(&input);
+
+ // Recv input2 from server.
+ connected.recv(&output2);
+
+ EXPECT_EQ(input2, output2);
+ });
+
+ Socket accepted = socket.accept();
+
+ // Recv input from client.
+ accepted.recv(&output);
+ EXPECT_EQ(input, output);
+
+ // Send input2 to client.
+ accepted.send(&input2);
+
+ if (client.joinable())
+ client.join();
+}
+
+TEST(SocketTests, socket_abstract)
+{
+ std::string sockPath = "@sock";
+ Socket socket(sockPath);
+
+ int input = std::numeric_limits<int>::max();
+ bool input2 = true;
+
+ int output = 0;
+ bool output2 = false;
+
+ auto client = std::thread([&]() {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ // Send input to server.
+ Socket connected = Socket::connect(sockPath);
+ connected.send(&input);
+
+ // Recv input2 from server.
+ connected.recv(&output2);
+
+ EXPECT_EQ(input2, output2);
+ });
+
+ Socket accepted = socket.accept();
+
+ // Recv input from client.
+ accepted.recv(&output);
+ EXPECT_EQ(input, output);
+
+ // Send input2 to client.
+ accepted.send(&input2);
+
+ if (client.joinable())
+ client.join();
+}
#include <vist/rmi/impl/client.hpp>
-#ifdef TIZEN
-#include <vist/rmi/impl/ondemand/client.hpp>
-#else
-#include <vist/rmi/impl/general/client.hpp>
-#endif
-
#include <memory>
#include <mutex>
#include <string>
class Remote::Impl {
public:
- explicit Impl(const std::string& path)
+ explicit Impl(const std::string& path) : client(path)
{
-#ifdef TIZEN
- this->client = std::make_unique<ondemand::Client>(path);
-#else
- this->client = std::make_unique<general::Client>(path);
-#endif
}
Message request(Message& message)
{
- return this->client->request(message);
+ return this->client.request(message);
}
private:
- std::unique_ptr<interface::Client> client;
+ Client client;
};
Remote::Remote(const std::string& path) : pImpl(new Impl(path))
/*
- * 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.
{
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);
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.";
}