Skip to content

Commit 05c65ff

Browse files
authored
Merge pull request #69 from unum-cloud/main-dev
Windows support
2 parents 5535ef6 + 654ab46 commit 05c65ff

14 files changed

Lines changed: 132 additions & 51 deletions

File tree

.clang-format

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ Language: Cpp
22
BasedOnStyle: LLVM
33
IndentWidth: 4
44
TabWidth: 4
5+
StatementMacros: ['PyObject_HEAD']
56
NamespaceIndentation: None
67
ColumnLimit: 120
78
ReflowComments: true

.github/workflows/release.yml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
env:
88
BUILD_TYPE: Release
99
GH_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
10+
PYTHONUTF8: 1
1011

1112
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
1213
permissions:
@@ -33,24 +34,30 @@ jobs:
3334
needs: versioning
3435
strategy:
3536
matrix:
36-
os: [ubuntu-20.04, macOS-11]
37+
os: [ubuntu-20.04, macOS-11, windows-2022]
3738

3839
steps:
3940
- uses: actions/checkout@v3
4041
with:
4142
ref: 'main'
4243
- uses: actions/setup-python@v3
43-
- uses: crazy-max/ghaction-setup-docker@v1.0.0
44+
45+
- name: Setup Docker
46+
if: matrix.os != 'windows-2022'
47+
uses: crazy-max/ghaction-setup-docker@v1.0.0
4448
with:
4549
version: 23.0.1
46-
- uses: docker/setup-qemu-action@v2.1.0
50+
51+
- name: Setup QEMU
52+
if: matrix.os != 'windows-2022'
53+
uses: docker/setup-qemu-action@v2.1.0
4754

4855
- name: Install CIBuildWheel
4956
run: python -m pip install cibuildwheel
5057

5158
- name: Build wheels
5259
run: python -m cibuildwheel
53-
60+
5461
- uses: actions/upload-artifact@v3
5562
with:
5663
path: ./wheelhouse/*.whl
@@ -82,11 +89,12 @@ jobs:
8289

8390
deploy_docs:
8491
name: Deploy Docs
85-
environment:
92+
environment:
8693
name: github-pages
8794
url: ${{ steps.deployment.outputs.page_url }}
8895
runs-on: ubuntu-22.04
89-
needs: publish_python
96+
if: ${{ always() }}
97+
needs: publish_wheels
9098
steps:
9199
- name: Checkout
92100
uses: actions/checkout@v3

CITATION.cff

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
cff-version: 1.2.0
2+
message: "If you use this software, please cite it as below."
3+
authors:
4+
- family-names: "Vardanian"
5+
given-names: "Ashot"
6+
orcid: "https://orcid.org/0000-0002-4882-1815"
7+
title: "UCall by Unum Cloud"
8+
version: 0.5.0
9+
doi: 10.5281/zenodo.7951497
10+
date-released: 2023-01-03
11+
url: "https://github.com/unum-cloud/ucall"

CMakeLists.txt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,22 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/build/lib" CACHE PATH "P
1919
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/build/lib" CACHE PATH "Path to shared libs")
2020
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/build/bin")
2121

22-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
23-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
24-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
25-
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
26-
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
22+
if(MSVC)
23+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
24+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /DEBUG")
25+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
26+
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /O2 /DEBUG")
27+
else()
28+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
29+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
30+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
31+
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
32+
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
33+
endif()
34+
2735

2836
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
2937
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=1")
30-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
3138
endif()
3239

3340
# Check if we are running on Linux

examples/login/ucall_server.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include <charconv> // `std::to_chars`
55
#include <cstdio> // `std::fprintf`
66
#include <thread>
7-
#include <unistd.h> // `STDOUT_FILENO`
87
#include <vector>
98

109
#include <cxxopts.hpp>
@@ -41,13 +40,13 @@ int main(int argc, char** argv) {
4140

4241
ucall_server_t server{};
4342
ucall_config_t config{};
44-
config.interface = result["nic"].as<std::string>().c_str();
43+
config.hostname = result["nic"].as<std::string>().c_str();
4544
config.port = result["port"].as<int>();
4645
config.max_threads = result["threads"].as<int>();
4746
config.max_concurrent_connections = 1024;
4847
config.queue_depth = 4096 * config.max_threads;
4948
config.max_lifetime_exchanges = UINT32_MAX;
50-
config.logs_file_descriptor = result["silent"].as<bool>() ? -1 : STDOUT_FILENO;
49+
config.logs_file_descriptor = result["silent"].as<bool>() ? -1 : fileno(stdin);
5150
config.logs_format = "human";
5251
// config.use_ssl = true;
5352
// config.ssl_private_key_path = "./examples/login/certs/main.key";
@@ -57,11 +56,11 @@ int main(int argc, char** argv) {
5756

5857
ucall_init(&config, &server);
5958
if (!server) {
60-
std::printf("Failed to start server: %s:%i\n", config.interface, config.port);
59+
std::printf("Failed to start server: %s:%i\n", config.hostname, config.port);
6160
return -1;
6261
}
6362

64-
std::printf("Initialized server: %s:%i\n", config.interface, config.port);
63+
std::printf("Initialized server: %s:%i\n", config.hostname, config.port);
6564
std::printf("- %zu threads\n", static_cast<std::size_t>(config.max_threads));
6665
std::printf("- %zu max concurrent connections\n", static_cast<std::size_t>(config.max_concurrent_connections));
6766
if (result["silent"].as<bool>())

include/ucall/ucall.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ typedef void (*ucall_callback_t)(ucall_call_t, ucall_callback_tag_t);
4949
* @brief Configuration parameters for `ucall_init()`.
5050
*/
5151
typedef struct ucall_config_t {
52-
char const* interface;
52+
char const* hostname;
5353
uint16_t port;
5454
uint16_t queue_depth;
5555
uint16_t max_callbacks;

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,11 @@ repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
3030
[tool.cibuildwheel.macos]
3131
archs = ["x86_64", "universal2", "arm64"]
3232
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
33+
34+
[tool.cibuildwheel.windows]
35+
before-build = [
36+
"rd /s /q {project}\\CMakeCache.txt {project}\\build {project}\\build_debug {project}\\CMakeFiles.txt {project}\\_deps || echo Done",
37+
"md build\\ucall"
38+
]
39+
archs = ["x86", "AMD64"]
40+
skip = ["*win32*", "pp*"]

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ def build_extension(self, ext):
5858
# Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level
5959
# across all generators.
6060
build_args = []
61+
if sys.platform.startswith('win32'):
62+
build_args += ['--config', 'Release']
63+
6164
if 'CMAKE_BUILD_PARALLEL_LEVEL' not in os.environ:
6265
# self.parallel is a Python 3 only way to set parallel jobs by hand
6366
# using -j in the build_ext call, not supported by pip or PyPA-build.
@@ -114,7 +117,7 @@ def run(self):
114117
],
115118

116119
packages=['ucall'],
117-
package_dir={'': 'src/'},
120+
package_dir={'': 'src'},
118121
ext_modules=[
119122
CMakeExtension('ucall.uring'),
120123
CMakeExtension('ucall.posix'),

src/engine_posix.cpp

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,38 @@
22
* @brief JSON-RPC implementation for TCP/IP stack with POSIX calls.
33
* @author Ashot Vardanian
44
*/
5+
6+
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
7+
#define UCALL_IS_WINDOWS
8+
9+
#include <Ws2tcpip.h>
10+
#include <io.h>
11+
#include <winsock2.h>
12+
13+
#define SHUT_WR SD_SEND
14+
#define SHUT_RD SD_RECEIVE
15+
#define SHUT_RDWR SD_BOTH
16+
// SO_REUSEPORT is not supported on Windows.
17+
#define SO_REUSEPORT 0
18+
19+
#pragma comment(lib, "Ws2_32.lib")
20+
#define UNICODE
21+
22+
#else
523
#include <arpa/inet.h> // `inet_addr`
6-
#include <errno.h> // `strerror`
7-
#include <fcntl.h> // `fcntl`
824
#include <netinet/in.h> // `sockaddr_in`
9-
#include <stdlib.h> // `std::aligned_malloc`
25+
1026
#include <sys/ioctl.h>
1127
#include <sys/socket.h> // `recv`, `setsockopt`
12-
#include <sys/types.h>
28+
1329
#include <sys/uio.h>
1430
#include <unistd.h>
31+
#endif
32+
33+
#include <errno.h> // `strerror`
34+
#include <fcntl.h> // `fcntl`
35+
#include <stdlib.h> // `std::aligned_malloc`
36+
#include <sys/types.h>
1537

1638
#include <charconv> // `std::to_chars`
1739
#include <chrono> // `std::chrono`
@@ -244,13 +266,13 @@ void forward_packet(engine_t& engine) noexcept {
244266

245267
int ssl_send(void* ctx, const unsigned char* buf, size_t len) {
246268
mbedtls_net_context* conn = reinterpret_cast<mbedtls_net_context*>(ctx);
247-
ssize_t ret = send(conn->fd, buf, len, 0);
269+
ssize_t ret = send(conn->fd, reinterpret_cast<char const*>(buf), len, 0);
248270
return ret;
249271
}
250272

251273
int ssl_recv(void* ctx, unsigned char* buf, size_t len) {
252274
mbedtls_net_context* conn = reinterpret_cast<mbedtls_net_context*>(ctx);
253-
ssize_t ret = recv(conn->fd, buf, len, 0);
275+
ssize_t ret = recv(conn->fd, reinterpret_cast<char*>(buf), len, 0);
254276
return ret;
255277
}
256278

@@ -329,11 +351,14 @@ void ucall_take_call(ucall_server_t server, uint16_t) {
329351
if (auto error_ptr = std::get_if<default_error_t>(&json_or_error); error_ptr)
330352
return ucall_call_reply_error(&engine, error_ptr->code, error_ptr->note.data(), error_ptr->note.size());
331353
parsed_request_t request = std::get<parsed_request_t>(json_or_error);
332-
auto res = std::from_chars(request.content_length.begin(), request.content_length.end(), bytes_expected);
333-
bytes_expected += (request.body.begin() - buffer_ptr);
354+
auto res = std::from_chars(request.content_length.data(),
355+
request.content_length.data() + request.content_length.size(), bytes_expected);
356+
bytes_expected += (request.body.data() - buffer_ptr);
334357

335358
if (res.ec == std::errc::invalid_argument || bytes_expected <= 0)
359+
#if !defined(UCALL_IS_WINDOWS)
336360
if (ioctl(engine.connection, FIONREAD, &bytes_expected) == -1 || bytes_expected == 0)
361+
#endif
337362
bytes_expected = bytes_received; // TODO what?
338363

339364
// Either process it in the statically allocated memory,
@@ -352,7 +377,11 @@ void ucall_take_call(ucall_server_t server, uint16_t) {
352377
if (parser.allocate(bytes_expected, bytes_expected / 2) != sj::SUCCESS)
353378
return ucall_call_reply_error_out_of_memory(&engine);
354379

380+
#if defined(UCALL_IS_WINDOWS)
381+
buffer_ptr = (char*)_aligned_malloc(round_up_to<align_k>(bytes_expected + sj::SIMDJSON_PADDING), align_k);
382+
#else
355383
buffer_ptr = (char*)std::aligned_alloc(align_k, round_up_to<align_k>(bytes_expected + sj::SIMDJSON_PADDING));
384+
#endif
356385
if (!buffer_ptr)
357386
return ucall_call_reply_error_out_of_memory(&engine);
358387

@@ -364,7 +393,11 @@ void ucall_take_call(ucall_server_t server, uint16_t) {
364393
engine.stats.bytes_received += bytes_received;
365394
engine.stats.packets_received++;
366395
forward_packet(engine);
396+
#if defined(UCALL_IS_WINDOWS)
397+
_aligned_free(buffer_ptr);
398+
#else
367399
std::free(buffer_ptr);
400+
#endif
368401
buffer_ptr = nullptr;
369402
}
370403

@@ -396,8 +429,8 @@ void ucall_init(ucall_config_t* config_inout, ucall_server_t* server_out) {
396429
config.queue_depth = 128u;
397430
if (!config.max_callbacks)
398431
config.max_callbacks = 128u;
399-
if (!config.interface)
400-
config.interface = "0.0.0.0";
432+
if (!config.hostname)
433+
config.hostname = "0.0.0.0";
401434
if (config.use_ssl &&
402435
!(config.ssl_private_key_path || config.ssl_certificates_paths || config.ssl_certificates_count))
403436
return;
@@ -420,7 +453,7 @@ void ucall_init(ucall_config_t* config_inout, ucall_server_t* server_out) {
420453
// By default, let's open TCP port for IPv4.
421454
struct sockaddr_in address;
422455
address.sin_family = AF_INET;
423-
address.sin_addr.s_addr = inet_addr(config.interface);
456+
address.sin_addr.s_addr = inet_addr(config.hostname);
424457
address.sin_port = htons(config.port);
425458

426459
// Try allocating all the necessary memory.
@@ -435,8 +468,8 @@ void ucall_init(ucall_config_t* config_inout, ucall_server_t* server_out) {
435468
if (socket_descriptor < 0)
436469
goto cleanup;
437470
// Optionally configure the socket, but don't always expect it to succeed.
438-
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &socket_options,
439-
sizeof(socket_options)) == -1)
471+
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
472+
reinterpret_cast<char const*>(&socket_options), sizeof(socket_options)) == -1)
440473
errno;
441474
if (bind(socket_descriptor, (struct sockaddr*)&address, sizeof(address)) < 0)
442475
goto cleanup;

src/engine_uring.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,8 @@ void ucall_init(ucall_config_t* config_inout, ucall_server_t* server_out) {
307307
config.max_lifetime_micro_seconds = 100'000u;
308308
if (!config.max_lifetime_exchanges)
309309
config.max_lifetime_exchanges = 100u;
310-
if (!config.interface)
311-
config.interface = "0.0.0.0";
310+
if (!config.hostname)
311+
config.hostname = "0.0.0.0";
312312

313313
// Allocate
314314
int socket_options{1};
@@ -334,7 +334,7 @@ void ucall_init(ucall_config_t* config_inout, ucall_server_t* server_out) {
334334
// By default, let's open TCP port for IPv4.
335335
struct sockaddr_in address {};
336336
address.sin_family = AF_INET;
337-
address.sin_addr.s_addr = inet_addr(config.interface);
337+
address.sin_addr.s_addr = inet_addr(config.hostname);
338338
address.sin_port = htons(config.port);
339339

340340
// Initialize `io_uring` first, it is the most likely to fail.

0 commit comments

Comments
 (0)