This is an automated email from the ASF dual-hosted git repository.

sdanilov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 8fda1b6a1c IGNITE-18007 MacOS support (#1272)
8fda1b6a1c is described below

commit 8fda1b6a1c523ef24974c27843005e7ca0568c70
Author: Semyon Danilov <[email protected]>
AuthorDate: Tue Nov 1 20:12:30 2022 +0300

    IGNITE-18007 MacOS support (#1272)
---
 modules/platforms/cpp/DEVNOTES.md                  |  26 +-
 .../platforms/cpp/ignite/network/CMakeLists.txt    |  17 +-
 .../cpp/ignite/network/detail/linux/sockets.cpp    |  30 ++
 .../cpp/ignite/network/detail/linux/utils.cpp      |  28 ++
 .../network/detail/macos/macos_async_client.cpp    | 171 +++++++++++
 .../detail/macos/macos_async_worker_thread.cpp     | 312 +++++++++++++++++++++
 .../cpp/ignite/network/length_prefix_codec.cpp     |   2 +-
 modules/platforms/cpp/ignite/network/network.cpp   |   8 +-
 modules/platforms/cpp/ignite/schema/CMakeLists.txt |  33 ++-
 .../platforms/cpp/tests/client-test/CMakeLists.txt |   2 +-
 modules/platforms/cpp/tests/client-test/main.cpp   |  41 +++
 .../detail/{linux_process.h => unix_process.h}     |  11 +-
 .../platforms/cpp/tests/test-common/process.cpp    |   8 +-
 13 files changed, 665 insertions(+), 24 deletions(-)

diff --git a/modules/platforms/cpp/DEVNOTES.md 
b/modules/platforms/cpp/DEVNOTES.md
index e0c7c243bb..36177f0309 100644
--- a/modules/platforms/cpp/DEVNOTES.md
+++ b/modules/platforms/cpp/DEVNOTES.md
@@ -17,7 +17,7 @@ mkdir cmake-build-debug
 cd cmake-build-debug
 conan install .. --build=missing -s build_type=Debug
 cmake .. -DENABLE_TESTS=ON
-cmake --build . -j8 
+cmake --build . -j8
 ```
 
 ### For Linux Developers
@@ -27,7 +27,17 @@ mkdir cmake-build-debug
 cd cmake-build-debug
 conan install .. --build=missing -s build_type=Debug -s 
compiler.libcxx=libstdc++11
 cmake .. -DENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
-cmake --build . -j8 
+cmake --build . -j8
+```
+
+### For macOS Developers
+Building in debug mode with tests. In this dir:
+```shell
+mkdir cmake-build-debug
+cd cmake-build-debug
+conan install .. --build=missing -s build_type=Debug -s compiler.libcxx=libc++
+cmake .. -DENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
+cmake --build . -j8
 ```
 
 ### For Windows users
@@ -47,7 +57,17 @@ mkdir cmake-build-release
 cd cmake-build-release
 conan install .. --build=missing -s build_type=Release -s 
compiler.libcxx=libstdc++11
 cmake .. -DENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Release
-cmake --build . -j8 
+cmake --build . -j8
+```
+
+### For macOS users
+Building in release mode without tests. In this dir:
+```shell
+mkdir cmake-build-release
+cd cmake-build-release
+conan install .. --build=missing -s build_type=Release -s 
compiler.libcxx=libc++
+cmake .. -DENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Release
+cmake --build . -j8
 ```
 
 ## Run Tests
diff --git a/modules/platforms/cpp/ignite/network/CMakeLists.txt 
b/modules/platforms/cpp/ignite/network/CMakeLists.txt
index d69976fce1..ad806b4ddc 100644
--- a/modules/platforms/cpp/ignite/network/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/network/CMakeLists.txt
@@ -37,7 +37,16 @@ if (WIN32)
         detail/win/win_async_connecting_thread.cpp
         detail/win/win_async_worker_thread.cpp
     )
-else()
+elseif(APPLE)
+    list(APPEND SOURCES
+        detail/linux/connecting_context.cpp
+        detail/macos/macos_async_client.cpp
+        detail/linux/linux_async_client_pool.cpp
+        detail/macos/macos_async_worker_thread.cpp
+        detail/linux/sockets.cpp
+        detail/linux/utils.cpp
+    )
+elseif(UNIX)
     list(APPEND SOURCES
         detail/linux/connecting_context.cpp
         detail/linux/linux_async_client.cpp
@@ -57,5 +66,11 @@ if (WIN32)
     target_link_libraries(${TARGET} wsock32 ws2_32 iphlpapi crypt32)
 endif()
 
+if (APPLE)
+    find_package(epoll-shim REQUIRED)
+    target_link_libraries(${TARGET} epoll-shim::epoll-shim)
+    add_compile_definitions(EPOLL_SHIM_NO_VARIADICS)
+endif()
+
 set_target_properties(${TARGET} PROPERTIES VERSION ${CMAKE_PROJECT_VERSION})
 set_target_properties(${TARGET} PROPERTIES POSITION_INDEPENDENT_CODE 1)
diff --git a/modules/platforms/cpp/ignite/network/detail/linux/sockets.cpp 
b/modules/platforms/cpp/ignite/network/detail/linux/sockets.cpp
index 6bada64ce9..4558b4879e 100644
--- a/modules/platforms/cpp/ignite/network/detail/linux/sockets.cpp
+++ b/modules/platforms/cpp/ignite/network/detail/linux/sockets.cpp
@@ -28,6 +28,7 @@
 
 namespace ignite::network::detail {
 
+#if defined(__linux__)
 std::string get_socket_error_message(int error) {
     std::stringstream res;
 
@@ -44,6 +45,35 @@ std::string get_socket_error_message(int error) {
 
     return res.str();
 }
+#elif defined(__APPLE__)
+std::string get_socket_error_message(int error) {
+    std::stringstream res;
+
+    res << "error_code=" << error;
+
+    if (error == 0)
+        return res.str();
+
+    char err_buf[1024] = {0};
+
+    const int err_res = strerror_r(error, err_buf, sizeof(err_buf));
+
+    switch (err_res) {
+        case 0:
+            res << ", msg=" << err_buf;
+            break;
+        case ERANGE:
+            // Buffer too small.
+            break;
+        default:
+        case EINVAL:
+            // Invalid error code.
+            break;
+    }
+
+    return res.str();
+}
+#endif
 
 std::string get_last_socket_error_message() {
     int last_error = errno;
diff --git a/modules/platforms/cpp/ignite/network/detail/linux/utils.cpp 
b/modules/platforms/cpp/ignite/network/detail/linux/utils.cpp
index cfcbb9560b..ce35b9b5a9 100644
--- a/modules/platforms/cpp/ignite/network/detail/linux/utils.cpp
+++ b/modules/platforms/cpp/ignite/network/detail/linux/utils.cpp
@@ -21,6 +21,7 @@
 
 namespace ignite::network::detail {
 
+#if defined(__linux__)
 std::string get_last_system_error() {
     int error_code = errno;
 
@@ -35,5 +36,32 @@ std::string get_last_system_error() {
 
     return error_details;
 }
+#elif defined(__APPLE__)
+std::string get_last_system_error() {
+    int error_code = errno;
+
+    std::string error_details;
+    if (error_code != 0) {
+        char err_buf[1024] = {0};
+
+        const int res = strerror_r(error_code, err_buf, sizeof(err_buf));
+
+        switch (res) {
+            case 0:
+                error_details.assign(err_buf);
+                break;
+            case ERANGE:
+                // Buffer too small.
+                break;
+            default:
+            case EINVAL:
+                // Invalid error code.
+                break;
+        }
+    }
+
+    return error_details;
+}
+#endif
 
 } // namespace ignite::network::detail
diff --git 
a/modules/platforms/cpp/ignite/network/detail/macos/macos_async_client.cpp 
b/modules/platforms/cpp/ignite/network/detail/macos/macos_async_client.cpp
new file mode 100644
index 0000000000..49944edd7c
--- /dev/null
+++ b/modules/platforms/cpp/ignite/network/detail/macos/macos_async_client.cpp
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 <ignite/network/detail/linux/linux_async_client.h>
+
+
+#include <algorithm>
+#include <cstring>
+
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+// We don't want to use epoll-shim macro here, because we have other close() 
functions.
+#undef close
+
+namespace ignite::network::detail {
+
+linux_async_client::linux_async_client(int fd, end_point addr, tcp_range range)
+   : m_state(state::CONNECTED)
+   , m_fd(fd)
+   , m_epoll(-1)
+   , m_id(0)
+   , m_addr(std::move(addr))
+   , m_range(std::move(range))
+   , m_send_packets()
+   , m_send_mutex()
+   , m_recv_packet(BUFFER_SIZE)
+   , m_close_err() {
+}
+
+linux_async_client::~linux_async_client() {
+   shutdown(std::nullopt);
+
+   close();
+}
+
+bool linux_async_client::shutdown(std::optional<ignite_error> err) {
+   std::lock_guard<std::mutex> lock(m_send_mutex);
+   if (m_state != state::CONNECTED)
+       return false;
+
+   m_close_err = err ? std::move(*err) : ignite_error("Connection closed by 
application");
+   ::shutdown(m_fd, SHUT_RDWR);
+   m_state = state::SHUTDOWN;
+
+   return true;
+}
+
+bool linux_async_client::close() {
+   if (state::CLOSED == m_state)
+       return false;
+
+   stop_monitoring();
+   ::close(m_fd);
+   m_fd = -1;
+   m_state = state::CLOSED;
+
+   return true;
+}
+
+bool linux_async_client::send(std::vector<std::byte> &&data) {
+   std::lock_guard<std::mutex> lock(m_send_mutex);
+
+   m_send_packets.emplace_back(std::move(data));
+   if (m_send_packets.size() > 1)
+       return true;
+
+   return send_next_packet_locked();
+}
+
+bool linux_async_client::send_next_packet_locked() {
+   if (m_send_packets.empty())
+       return true;
+
+   auto &packet = m_send_packets.front();
+   auto dataView = packet.get_bytes_view();
+
+   ssize_t ret = ::send(m_fd, dataView.data(), dataView.size(), 0);
+   if (ret < 0)
+       return false;
+
+   packet.skip(static_cast<int32_t>(ret));
+
+   enable_send_notifications();
+
+   return true;
+}
+
+bytes_view linux_async_client::receive() {
+   ssize_t res = recv(m_fd, m_recv_packet.data(), m_recv_packet.size(), 0);
+   if (res < 0)
+       return {};
+
+   return {m_recv_packet.data(), size_t(res)};
+}
+
+bool linux_async_client::start_monitoring(int epoll0) {
+   if (epoll0 < 0)
+       return false;
+
+   epoll_event event{};
+   memset(&event, 0, sizeof(event));
+   event.data.ptr = this;
+   event.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
+
+   int res = epoll_ctl(epoll0, EPOLL_CTL_ADD, m_fd, &event);
+   if (res < 0)
+       return false;
+
+   m_epoll = epoll0;
+
+   return true;
+}
+
+void linux_async_client::stop_monitoring() // 
NOLINT(readability-make-member-function-const)
+{
+   epoll_event event{};
+   memset(&event, 0, sizeof(event));
+
+   epoll_ctl(m_epoll, EPOLL_CTL_DEL, m_fd, &event);
+}
+
+void linux_async_client::enable_send_notifications() {
+   epoll_event event{};
+   memset(&event, 0, sizeof(event));
+   event.data.ptr = this;
+   event.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
+
+   epoll_ctl(m_epoll, EPOLL_CTL_MOD, m_fd, &event);
+}
+
+void linux_async_client::disable_send_notifications() {
+   epoll_event event{};
+   memset(&event, 0, sizeof(event));
+   event.data.ptr = this;
+   event.events = EPOLLIN | EPOLLRDHUP;
+
+   epoll_ctl(m_epoll, EPOLL_CTL_MOD, m_fd, &event);
+}
+
+bool linux_async_client::process_sent() {
+   std::lock_guard<std::mutex> lock(m_send_mutex);
+
+   if (m_send_packets.empty()) {
+       disable_send_notifications();
+
+       return true;
+   }
+
+   if (m_send_packets.front().empty())
+       m_send_packets.pop_front();
+
+   return send_next_packet_locked();
+}
+
+} // namespace ignite::network::detail
diff --git 
a/modules/platforms/cpp/ignite/network/detail/macos/macos_async_worker_thread.cpp
 
b/modules/platforms/cpp/ignite/network/detail/macos/macos_async_worker_thread.cpp
new file mode 100644
index 0000000000..1ac114f378
--- /dev/null
+++ 
b/modules/platforms/cpp/ignite/network/detail/macos/macos_async_worker_thread.cpp
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 <ignite/network/detail/linux/linux_async_worker_thread.h>
+#include <ignite/network/detail/linux/linux_async_client_pool.h>
+
+#include "../utils.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include <netdb.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+// We don't want to use epoll-shim macro here, because we have other close() 
functions.
+#undef close
+
+namespace ignite::network::detail {
+
+namespace {
+
+fibonacci_sequence<10> fibonacci10;
+
+} // ignite::network::detail
+
+linux_async_worker_thread::linux_async_worker_thread(linux_async_client_pool 
&client_pool)
+   : m_client_pool(client_pool)
+   , m_stopping(true)
+   , m_epoll(-1)
+   , m_stop_event(-1)
+   , m_non_connected()
+   , m_current_connection()
+   , m_current_client()
+   , m_failed_attempts(0)
+   , m_last_connection_time()
+   , m_min_addrs(0)
+   , m_thread() {
+   memset(&m_last_connection_time, 0, sizeof(m_last_connection_time));
+}
+
+linux_async_worker_thread::~linux_async_worker_thread() {
+   stop();
+}
+
+void linux_async_worker_thread::start(size_t limit, std::vector<tcp_range> 
addrs) {
+   m_epoll = epoll_create(1);
+   if (m_epoll < 0)
+       throw_last_system_error("Failed to create epoll instance");
+
+   m_stop_event = eventfd(0, EFD_NONBLOCK);
+   if (m_stop_event < 0) {
+       std::string msg = get_last_system_error("Failed to create stop event 
instance", "");
+       epoll_shim_close(m_stop_event);
+       throw ignite_error(status_code::OS, msg);
+   }
+
+   epoll_event event{};
+   memset(&event, 0, sizeof(event));
+
+   event.events = EPOLLIN;
+
+   int res = epoll_ctl(m_epoll, EPOLL_CTL_ADD, m_stop_event, &event);
+   if (res < 0) {
+       std::string msg = get_last_system_error("Failed to create stop event 
instance", "");
+       epoll_shim_close(m_stop_event);
+       epoll_shim_close(m_epoll);
+       throw ignite_error(status_code::OS, msg);
+   }
+
+   m_stopping = false;
+   m_failed_attempts = 0;
+   m_non_connected = std::move(addrs);
+
+   m_current_connection.reset();
+   m_current_client.reset();
+
+   if (!limit || limit > m_non_connected.size())
+       m_min_addrs = 0;
+   else
+       m_min_addrs = m_non_connected.size() - limit;
+
+   m_thread = std::thread(&linux_async_worker_thread::run, this);
+}
+
+void linux_async_worker_thread::stop() {
+   if (m_stopping)
+       return;
+
+   m_stopping = true;
+
+   int64_t value = 1;
+   ssize_t res = epoll_shim_write(m_stop_event, &value, sizeof(value));
+
+   (void)res;
+   assert(res == sizeof(value));
+
+   m_thread.join();
+
+   epoll_shim_close(m_stop_event);
+   epoll_shim_close(m_epoll);
+
+   m_non_connected.clear();
+   m_current_connection.reset();
+}
+
+void linux_async_worker_thread::run() {
+   while (!m_stopping) {
+       handle_new_connections();
+
+       if (m_stopping)
+           break;
+
+       handle_connection_events();
+   }
+}
+
+void linux_async_worker_thread::handle_new_connections() {
+   if (!should_initiate_new_connection())
+       return;
+
+   if (calculate_connection_timeout() > 0)
+       return;
+
+   addrinfo *addr = nullptr;
+   if (m_current_connection)
+       addr = m_current_connection->next();
+
+   if (!addr) {
+       // TODO: Use round-robin instead.
+       size_t idx = rand() % m_non_connected.size();
+       const tcp_range &range = m_non_connected.at(idx);
+
+       m_current_connection = std::make_unique<connecting_context>(range);
+       addr = m_current_connection->next();
+       if (!addr) {
+           m_current_connection.reset();
+           report_connection_error(end_point(), "Can not resolve a single 
address from range: " + range.to_string());
+           ++m_failed_attempts;
+           return;
+       }
+   }
+
+   // Create a socket for connecting to server
+   int socket_fd = socket(addr->ai_family, addr->ai_socktype, 
addr->ai_protocol);
+   if (SOCKET_ERROR == socket_fd) {
+       report_connection_error(
+           m_current_connection->current_address(), "Socket creation failed: " 
+ get_last_socket_error_message());
+       return;
+   }
+
+   try_set_socket_options(socket_fd, linux_async_client::BUFFER_SIZE, true, 
true, true);
+   bool success = set_non_blocking_mode(socket_fd, true);
+   if (!success) {
+       report_connection_error(
+           m_current_connection->current_address(), "Can not make non-blocking 
socket: " + get_last_socket_error_message());
+       return;
+   }
+
+   m_current_client = m_current_connection->to_client(socket_fd);
+   bool ok = m_current_client->start_monitoring(m_epoll);
+   if (!ok)
+       throw_last_system_error("Can not add file descriptor to epoll");
+
+   // Connect to server.
+   int res = connect(socket_fd, addr->ai_addr, addr->ai_addrlen);
+   if (SOCKET_ERROR == res) {
+       int last_error = errno;
+
+       clock_gettime(CLOCK_MONOTONIC, &m_last_connection_time);
+
+       if (last_error != EWOULDBLOCK && last_error != EINPROGRESS) {
+           handle_connection_failed("Failed to establish connection with the 
host: " + get_socket_error_message(last_error));
+           return;
+       }
+   }
+}
+
+void linux_async_worker_thread::handle_connection_events() {
+   enum { MAX_EVENTS = 16 };
+   epoll_event events[MAX_EVENTS];
+
+   int timeout = calculate_connection_timeout();
+
+   int res = epoll_wait(m_epoll, events, MAX_EVENTS, timeout);
+
+   if (res <= 0)
+       return;
+
+   for (int i = 0; i < res; ++i) {
+       epoll_event &current_event = events[i];
+       auto client = static_cast<linux_async_client *>(current_event.data.ptr);
+       if (!client)
+           continue;
+
+       if (client == m_current_client.get()) {
+           if (current_event.events & (EPOLLRDHUP | EPOLLERR)) {
+               handle_connection_failed("Can not establish connection");
+               continue;
+           }
+
+           handle_connection_success(client);
+       }
+
+       if (current_event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) {
+           handle_connection_closed(client);
+           continue;
+       }
+
+       if (current_event.events & EPOLLIN) {
+           auto msg = client->receive();
+           if (msg.empty()) {
+               handle_connection_closed(client);
+               continue;
+           }
+
+           m_client_pool.handle_message_received(client->id(), msg);
+       }
+
+       if (current_event.events & EPOLLOUT) {
+           bool ok = client->process_sent();
+           if (!ok) {
+               handle_connection_closed(client);
+               continue;
+           }
+
+           m_client_pool.handle_message_sent(client->id());
+       }
+   }
+}
+
+void linux_async_worker_thread::report_connection_error(const end_point &addr, 
std::string msg) {
+   ignite_error err(status_code::NETWORK, std::move(msg));
+   m_client_pool.handle_connection_error(addr, err);
+}
+
+void linux_async_worker_thread::handle_connection_failed(std::string msg) {
+   assert(m_current_client);
+
+   m_current_client->stop_monitoring();
+   m_current_client->close();
+
+   report_connection_error(m_current_client->address(), std::move(msg));
+
+   m_current_client.reset();
+   ++m_failed_attempts;
+}
+
+void linux_async_worker_thread::handle_connection_closed(linux_async_client 
*client) {
+   client->stop_monitoring();
+
+   m_non_connected.push_back(client->get_range());
+
+   m_client_pool.close_and_release(client->id(), std::nullopt);
+}
+
+void linux_async_worker_thread::handle_connection_success(linux_async_client 
*client) {
+   m_non_connected.erase(std::find(m_non_connected.begin(), 
m_non_connected.end(), client->get_range()));
+
+   m_client_pool.add_client(std::move(m_current_client));
+
+   m_current_client.reset();
+   m_current_connection.reset();
+
+   m_failed_attempts = 0;
+
+   clock_gettime(CLOCK_MONOTONIC, &m_last_connection_time);
+}
+
+int linux_async_worker_thread::calculate_connection_timeout() const {
+   if (!should_initiate_new_connection())
+       return -1;
+
+   if (m_last_connection_time.tv_sec == 0)
+       return 0;
+
+   int timeout = int(fibonacci10.get_value(m_failed_attempts) * 1000);
+
+   timespec now{};
+   clock_gettime(CLOCK_MONOTONIC, &now);
+
+   int passed =
+       int((now.tv_sec - m_last_connection_time.tv_sec) * 1000 + (now.tv_nsec 
- m_last_connection_time.tv_nsec) / 1000000);
+
+   timeout -= passed;
+   if (timeout < 0)
+       timeout = 0;
+
+   return timeout;
+}
+
+bool linux_async_worker_thread::should_initiate_new_connection() const {
+   return !m_current_client && m_non_connected.size() > m_min_addrs;
+}
+
+} // namespace ignite::network::detail
diff --git a/modules/platforms/cpp/ignite/network/length_prefix_codec.cpp 
b/modules/platforms/cpp/ignite/network/length_prefix_codec.cpp
index 28b5c0fd97..64e779f4a5 100644
--- a/modules/platforms/cpp/ignite/network/length_prefix_codec.cpp
+++ b/modules/platforms/cpp/ignite/network/length_prefix_codec.cpp
@@ -31,7 +31,7 @@ length_prefix_codec::length_prefix_codec()
 data_buffer_owning length_prefix_codec::encode(data_buffer_owning &data) {
     // Just pass data as is, because we encode message size in
     // the application to avoid unnecessary re-allocations and copying.
-    return std::move(data.consume_entirely());
+    return data.consume_entirely();
 }
 
 void length_prefix_codec::reset_buffer() {
diff --git a/modules/platforms/cpp/ignite/network/network.cpp 
b/modules/platforms/cpp/ignite/network/network.cpp
index 342d78f57c..90f21f95ee 100644
--- a/modules/platforms/cpp/ignite/network/network.cpp
+++ b/modules/platforms/cpp/ignite/network/network.cpp
@@ -23,15 +23,17 @@
 
 #ifdef _WIN32
 # include "detail/win/win_async_client_pool.h"
-#else // Other. Assume Linux
+#else
 # include "detail/linux/linux_async_client_pool.h"
 #endif
 
 namespace ignite::network {
 
 std::shared_ptr<async_client_pool> make_async_client_pool(data_filters 
filters) {
-    auto pool =
-        
std::make_shared<IGNITE_SWITCH_WIN_OTHER(detail::win_async_client_pool, 
detail::linux_async_client_pool)>();
+    auto pool = std::make_shared<IGNITE_SWITCH_WIN_OTHER(
+        detail::win_async_client_pool,
+        detail::linux_async_client_pool
+    )>();
 
     return std::make_shared<async_client_pool_adapter>(std::move(filters), 
std::move(pool));
 }
diff --git a/modules/platforms/cpp/ignite/schema/CMakeLists.txt 
b/modules/platforms/cpp/ignite/schema/CMakeLists.txt
index 9b69be6cae..1c1f4fb5a0 100644
--- a/modules/platforms/cpp/ignite/schema/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/schema/CMakeLists.txt
@@ -19,21 +19,40 @@ project(ignite-schema)
 
 set(TARGET ${PROJECT_NAME})
 
-add_library(${TARGET} STATIC
-    big_decimal.cpp big_decimal.h
-    big_integer.cpp big_integer.h
-    binary_tuple_builder.cpp binary_tuple_builder.h
+set(SOURCES
+    big_decimal.cpp
+    big_integer.cpp
+    binary_tuple_builder.cpp
+    binary_tuple_parser.cpp
+    ignite_type.cpp
+)
+
+set(PUBLIC_HEADERS
+    big_decimal.h
+    big_integer.h
+    binary_tuple_builder.h
     binary_tuple_header.h
-    binary_tuple_parser.cpp binary_tuple_parser.h
+    binary_tuple_parser.h
     binary_tuple_schema.h
     column_info.h
     ignite_date.h
     ignite_date_time.h
     ignite_time.h
     ignite_timestamp.h
-    ignite_type.cpp ignite_type.h
-    types.h)
+    ignite_type.h
+    types.h
+)
+
+add_library(${TARGET} STATIC ${SOURCES})
 
 target_link_libraries(${TARGET} ignite-common)
 
 ignite_test(bignum_test bignum_test.cpp LIBS ${TARGET})
+
+install(TARGETS ${TARGET}
+    RUNTIME DESTINATION bin/
+    ARCHIVE DESTINATION lib
+    LIBRARY DESTINATION lib
+)
+
+ignite_install_headers(FILES ${PUBLIC_HEADERS} DESTINATION 
${IGNITE_INCLUDEDIR}/schema)
diff --git a/modules/platforms/cpp/tests/client-test/CMakeLists.txt 
b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
index e1010ee6a3..56d3e52aac 100644
--- a/modules/platforms/cpp/tests/client-test/CMakeLists.txt
+++ b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
@@ -29,7 +29,7 @@ set(SOURCES
 )
 
 add_executable(${TARGET} ${SOURCES})
-target_link_libraries(${TARGET} ignite-test-common ignite-client GTest::gtest)
+target_link_libraries(${TARGET} ignite-test-common ignite-client GTest::GTest)
 
 set(TEST_TARGET IgniteClientTest)
 add_test(NAME ${TEST_TARGET} COMMAND ${TARGET})
diff --git a/modules/platforms/cpp/tests/client-test/main.cpp 
b/modules/platforms/cpp/tests/client-test/main.cpp
index fbe3a35086..2f47c3d146 100644
--- a/modules/platforms/cpp/tests/client-test/main.cpp
+++ b/modules/platforms/cpp/tests/client-test/main.cpp
@@ -22,8 +22,41 @@
 #include <gtest/gtest.h>
 
 #include <chrono>
+#include <csignal>
 #include <thread>
 
+namespace {
+/** Shutdown handler that cleans up resources. */
+std::function<void(int)> shutdown_handler;
+
+/**
+ * Receives OS signal and handles it.
+ *
+ * @param signum Signal value.
+ */
+void signal_handler(int signum) {
+    shutdown_handler(signum);
+
+    signal(signum, SIG_DFL);
+
+    raise(signum);
+}
+}
+
+/**
+ * Sets process abortion (SIGABRT, SIGINT, SIGSEGV signals) handler.
+ *
+ * @param handler Abortion handler.
+ */
+void set_process_abort_handler(std::function<void(int)> handler) {
+    shutdown_handler = std::move(handler);
+
+    // Install signal handlers to clean up resources on early exit.
+    signal(SIGABRT, signal_handler);
+    signal(SIGINT, signal_handler);
+    signal(SIGSEGV, signal_handler);
+}
+
 /**
  * Run prior to any other tests.
  */
@@ -42,7 +75,15 @@ void before_all() {
 int main(int argc, char **argv) {
     int res = 0;
     before_all();
+
     ignite::IgniteRunner runner;
+
+    set_process_abort_handler([&](int signal) {
+        std::cout << "Caught signal " << signal << " during tests" << 
std::endl;
+
+        runner.stop();
+    });
+
     try {
         runner.start(false);
 
diff --git a/modules/platforms/cpp/tests/test-common/detail/linux_process.h 
b/modules/platforms/cpp/tests/test-common/detail/unix_process.h
similarity index 92%
rename from modules/platforms/cpp/tests/test-common/detail/linux_process.h
rename to modules/platforms/cpp/tests/test-common/detail/unix_process.h
index 4f85b94b3f..ad5678cfc8 100644
--- a/modules/platforms/cpp/tests/test-common/detail/linux_process.h
+++ b/modules/platforms/cpp/tests/test-common/detail/unix_process.h
@@ -26,6 +26,9 @@
 #include <string>
 #include <vector>
 
+#ifdef __APPLE__
+#include <csignal>
+#endif
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -33,9 +36,9 @@
 namespace ignite::detail {
 
 /**
- * Implementation of CmdProcess for Windows.
+ * Implementation of CmdProcess for UNIX and UNIX-like systems.
  */
-class LinuxProcess : public ignite::CmdProcess {
+class UnixProcess : public ignite::CmdProcess {
 public:
     /**
      * Constructor.
@@ -44,7 +47,7 @@ public:
      * @param args Arguments.
      * @param workDir Working directory.
      */
-    LinuxProcess(std::string command, std::vector<std::string> args, 
std::string workDir)
+    UnixProcess(std::string command, std::vector<std::string> args, 
std::string workDir)
         : m_running(false)
         , m_command(std::move(command))
         , m_args(std::move(args))
@@ -53,7 +56,7 @@ public:
     /**
      * Destructor.
      */
-    ~LinuxProcess() override { kill(); }
+    ~UnixProcess() override { kill(); }
 
     /**
      * Start process.
diff --git a/modules/platforms/cpp/tests/test-common/process.cpp 
b/modules/platforms/cpp/tests/test-common/process.cpp
index 9c2f9e6e39..a562c35d53 100644
--- a/modules/platforms/cpp/tests/test-common/process.cpp
+++ b/modules/platforms/cpp/tests/test-common/process.cpp
@@ -15,10 +15,10 @@
  * limitations under the License.
  */
 
-#ifdef WIN32
+#ifdef _WIN32
 # include "detail/win_process.h"
 #else
-# include "detail/linux_process.h"
+# include "detail/unix_process.h"
 #endif
 
 #include "cmd_process.h"
@@ -30,11 +30,11 @@
 namespace ignite {
 
 std::unique_ptr<CmdProcess> CmdProcess::make(std::string command, 
std::vector<std::string> args, std::string workDir) {
-#ifdef WIN32
+#ifdef _WIN32
     return std::unique_ptr<CmdProcess>(new 
detail::WinProcess(std::move(command), std::move(args), std::move(workDir)));
 #else
     return std::unique_ptr<CmdProcess>(
-        new detail::LinuxProcess(std::move(command), std::move(args), 
std::move(workDir)));
+        new detail::UnixProcess(std::move(command), std::move(args), 
std::move(workDir)));
 #endif
 }
 

Reply via email to