feat: add C & Zig bindings for unix acceptor and connector
This commit is contained in:
parent
628e990f6e
commit
ed91a28f5f
9 changed files with 283 additions and 0 deletions
|
|
@ -59,6 +59,7 @@ pub fn build(b: *std.Build) void {
|
||||||
"src/c/metronome.cpp",
|
"src/c/metronome.cpp",
|
||||||
"src/c/signal.cpp",
|
"src/c/signal.cpp",
|
||||||
"src/c/timeout.cpp",
|
"src/c/timeout.cpp",
|
||||||
|
"src/c/unx.cpp",
|
||||||
"src/c/trace.cpp",
|
"src/c/trace.cpp",
|
||||||
"src/cbor.cpp",
|
"src/cbor.cpp",
|
||||||
"src/executor_asio.cpp",
|
"src/executor_asio.cpp",
|
||||||
|
|
@ -122,6 +123,7 @@ pub fn build(b: *std.Build) void {
|
||||||
"test/spawn_exit.cpp",
|
"test/spawn_exit.cpp",
|
||||||
"test/tests.cpp",
|
"test/tests.cpp",
|
||||||
"test/timeout_test.cpp",
|
"test/timeout_test.cpp",
|
||||||
|
"test/unx_c_api.cpp",
|
||||||
}, .flags = &cppflags });
|
}, .flags = &cppflags });
|
||||||
tests.linkLibrary(lib);
|
tests.linkLibrary(lib);
|
||||||
tests.linkLibrary(asio_dep.artifact("asio"));
|
tests.linkLibrary(asio_dep.artifact("asio"));
|
||||||
|
|
|
||||||
38
include/thespian/c/unx.h
Normal file
38
include/thespian/c/unx.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// NOLINTBEGIN(modernize-use-trailing-return-type)
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// UNIX-domain socket utilities (acceptors/connectors)
|
||||||
|
|
||||||
|
// mode indicates whether the path is a filesystem entry or an abstract socket.
|
||||||
|
typedef enum thespian_unx_mode {
|
||||||
|
THESPIAN_UNX_MODE_FILE = 0,
|
||||||
|
THESPIAN_UNX_MODE_ABSTRACT = 1,
|
||||||
|
} thespian_unx_mode;
|
||||||
|
|
||||||
|
struct thespian_unx_acceptor_handle;
|
||||||
|
struct thespian_unx_acceptor_handle *
|
||||||
|
thespian_unx_acceptor_create(const char *tag);
|
||||||
|
int thespian_unx_acceptor_listen(struct thespian_unx_acceptor_handle *,
|
||||||
|
const char *path, thespian_unx_mode mode);
|
||||||
|
int thespian_unx_acceptor_close(struct thespian_unx_acceptor_handle *);
|
||||||
|
void thespian_unx_acceptor_destroy(struct thespian_unx_acceptor_handle *);
|
||||||
|
|
||||||
|
struct thespian_unx_connector_handle;
|
||||||
|
struct thespian_unx_connector_handle *
|
||||||
|
thespian_unx_connector_create(const char *tag);
|
||||||
|
int thespian_unx_connector_connect(struct thespian_unx_connector_handle *,
|
||||||
|
const char *path, thespian_unx_mode mode);
|
||||||
|
int thespian_unx_connector_cancel(struct thespian_unx_connector_handle *);
|
||||||
|
void thespian_unx_connector_destroy(struct thespian_unx_connector_handle *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// NOLINTEND(modernize-use-trailing-return-type)
|
||||||
|
|
@ -23,6 +23,11 @@ struct acceptor {
|
||||||
acceptor_ref ref;
|
acceptor_ref ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// C++ helpers used by the C binding layer
|
||||||
|
void acceptor_listen(acceptor_impl *h, std::string_view path, mode m);
|
||||||
|
void acceptor_close(acceptor_impl *h);
|
||||||
|
void destroy_acceptor(acceptor_impl *h);
|
||||||
|
|
||||||
struct connector_impl;
|
struct connector_impl;
|
||||||
using connector_dtor = void (*)(connector_impl *);
|
using connector_dtor = void (*)(connector_impl *);
|
||||||
using connector_ref = std::unique_ptr<connector_impl, connector_dtor>;
|
using connector_ref = std::unique_ptr<connector_impl, connector_dtor>;
|
||||||
|
|
@ -39,4 +44,9 @@ struct connector {
|
||||||
connector_ref ref;
|
connector_ref ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// C++ helpers for C API
|
||||||
|
void connector_connect(connector_impl *h, std::string_view path, mode m);
|
||||||
|
void connector_cancel(connector_impl *h);
|
||||||
|
void destroy_connector(connector_impl *h);
|
||||||
|
|
||||||
} // namespace thespian::unx
|
} // namespace thespian::unx
|
||||||
|
|
|
||||||
134
src/c/unx.cpp
Normal file
134
src/c/unx.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include <thespian/c/context.h>
|
||||||
|
#include <thespian/c/unx.h>
|
||||||
|
#include <thespian/unx.hpp>
|
||||||
|
|
||||||
|
using thespian::unx::acceptor_impl;
|
||||||
|
using thespian::unx::connector_impl;
|
||||||
|
using thespian::unx::mode;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static auto to_cpp_mode(thespian_unx_mode m) -> mode {
|
||||||
|
return m == THESPIAN_UNX_MODE_ABSTRACT ? mode::abstract : mode::file;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto thespian_unx_acceptor_create(const char *tag)
|
||||||
|
-> struct thespian_unx_acceptor_handle * {
|
||||||
|
try {
|
||||||
|
auto *h = thespian::unx::acceptor::create(tag).ref.release();
|
||||||
|
return reinterpret_cast<struct thespian_unx_acceptor_handle *>(h); // NOLINT
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
return nullptr;
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_acceptor_create error");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thespian_unx_acceptor_listen(struct thespian_unx_acceptor_handle *handle,
|
||||||
|
const char *path, thespian_unx_mode m)
|
||||||
|
-> int {
|
||||||
|
try {
|
||||||
|
thespian::unx::acceptor_listen(
|
||||||
|
reinterpret_cast<acceptor_impl *>(handle), // NOLINT
|
||||||
|
path, to_cpp_mode(m));
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
return -1;
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_acceptor_listen error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thespian_unx_acceptor_close(struct thespian_unx_acceptor_handle *handle)
|
||||||
|
-> int {
|
||||||
|
try {
|
||||||
|
thespian::unx::acceptor_close(
|
||||||
|
reinterpret_cast<acceptor_impl *>(handle)); // NOLINT
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
return -1;
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_acceptor_close error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void thespian_unx_acceptor_destroy(
|
||||||
|
struct thespian_unx_acceptor_handle *handle) {
|
||||||
|
try {
|
||||||
|
thespian::unx::destroy_acceptor(
|
||||||
|
reinterpret_cast<acceptor_impl *>(handle)); // NOLINT
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_acceptor_destroy error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thespian_unx_connector_create(const char *tag)
|
||||||
|
-> struct thespian_unx_connector_handle * {
|
||||||
|
try {
|
||||||
|
auto *h = thespian::unx::connector::create(tag).ref.release();
|
||||||
|
return reinterpret_cast<struct thespian_unx_connector_handle *>( // NOLINT
|
||||||
|
h);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
return nullptr;
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_connector_create error");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thespian_unx_connector_connect(
|
||||||
|
struct thespian_unx_connector_handle *handle, const char *path,
|
||||||
|
thespian_unx_mode m) -> int {
|
||||||
|
try {
|
||||||
|
thespian::unx::connector_connect(
|
||||||
|
reinterpret_cast<connector_impl *>(handle), // NOLINT
|
||||||
|
path, to_cpp_mode(m));
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
return -1;
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_connector_connect error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thespian_unx_connector_cancel(struct thespian_unx_connector_handle *handle)
|
||||||
|
-> int {
|
||||||
|
try {
|
||||||
|
thespian::unx::connector_cancel(
|
||||||
|
reinterpret_cast<connector_impl *>(handle)); // NOLINT
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
return -1;
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_connector_cancel error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void thespian_unx_connector_destroy(
|
||||||
|
struct thespian_unx_connector_handle *handle) {
|
||||||
|
try {
|
||||||
|
thespian::unx::destroy_connector(
|
||||||
|
reinterpret_cast<connector_impl *>(handle)); // NOLINT
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
thespian_set_last_error(e.what());
|
||||||
|
} catch (...) {
|
||||||
|
thespian_set_last_error("unknown thespian_unx_connector_destroy error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
@ -1396,6 +1396,22 @@ void connector::connect(string_view path, mode m) { ref->connect(path, m); }
|
||||||
|
|
||||||
void connector::cancel() { ref->cancel(); }
|
void connector::cancel() { ref->cancel(); }
|
||||||
|
|
||||||
|
// C API helpers - must be defined where acceptor_impl/connector_impl are
|
||||||
|
// complete
|
||||||
|
|
||||||
|
void acceptor_listen(acceptor_impl *h, std::string_view path, mode m) {
|
||||||
|
h->listen(path, m);
|
||||||
|
}
|
||||||
|
void acceptor_close(acceptor_impl *h) { h->close(); }
|
||||||
|
|
||||||
|
void connector_connect(connector_impl *h, std::string_view path, mode m) {
|
||||||
|
h->connect(path, m);
|
||||||
|
}
|
||||||
|
void connector_cancel(connector_impl *h) { h->cancel(); }
|
||||||
|
|
||||||
|
void destroy_acceptor(acceptor_impl *h) { delete h; }
|
||||||
|
void destroy_connector(connector_impl *h) { delete h; }
|
||||||
|
|
||||||
} // namespace unx
|
} // namespace unx
|
||||||
|
|
||||||
namespace endpoint {
|
namespace endpoint {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const c = @cImport({
|
||||||
@cInclude("thespian/c/metronome.h");
|
@cInclude("thespian/c/metronome.h");
|
||||||
@cInclude("thespian/c/timeout.h");
|
@cInclude("thespian/c/timeout.h");
|
||||||
@cInclude("thespian/c/signal.h");
|
@cInclude("thespian/c/signal.h");
|
||||||
|
@cInclude("thespian/c/unx.h");
|
||||||
});
|
});
|
||||||
const c_posix = if (builtin.os.tag != .windows) @cImport({
|
const c_posix = if (builtin.os.tag != .windows) @cImport({
|
||||||
@cInclude("thespian/backtrace.h");
|
@cInclude("thespian/backtrace.h");
|
||||||
|
|
@ -741,6 +742,61 @@ pub const metronome = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const unx_mode = enum(u8) {
|
||||||
|
file = 0,
|
||||||
|
abstract = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const unx_acceptor = struct {
|
||||||
|
handle: *c.struct_thespian_unx_acceptor_handle,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init(tag_: []const u8) !Self {
|
||||||
|
return .{ .handle = c.thespian_unx_acceptor_create(tag_) orelse return log_last_error(error.ThespianUnxAcceptorInitFailed) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listen(self: *const Self, path: []const u8, mode: unx_mode) !void {
|
||||||
|
const mval: u8 = @intCast(mode);
|
||||||
|
const ret = c.thespian_unx_acceptor_listen(self.handle, path, mval);
|
||||||
|
if (ret < 0) return error.ThespianUnxAcceptorListenFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self: *const Self) !void {
|
||||||
|
const ret = c.thespian_unx_acceptor_close(self.handle);
|
||||||
|
if (ret < 0) return error.ThespianUnxAcceptorCloseFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *const Self) void {
|
||||||
|
c.thespian_unx_acceptor_destroy(self.handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const unx_connector = struct {
|
||||||
|
handle: *c.struct_thespian_unx_connector_handle,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn init(tag_: []const u8) !Self {
|
||||||
|
return .{ .handle = c.thespian_unx_connector_create(tag_) orelse return log_last_error(error.ThespianUnxConnectorInitFailed) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect(self: *const Self, path: []const u8, mode: unx_mode) !void {
|
||||||
|
const mval: u8 = @intCast(mode);
|
||||||
|
const ret = c.thespian_unx_connector_connect(self.handle, path, mval);
|
||||||
|
if (ret < 0) return error.ThespianUnxConnectorConnectFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel(self: *const Self) !void {
|
||||||
|
const ret = c.thespian_unx_connector_cancel(self.handle);
|
||||||
|
if (ret < 0) return error.ThespianUnxConnectorCancelFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *const Self) void {
|
||||||
|
c.thespian_unx_connector_destroy(self.handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const timeout = struct {
|
pub const timeout = struct {
|
||||||
handle: ?*c.struct_thespian_timeout_handle,
|
handle: ?*c.struct_thespian_timeout_handle,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ extern "C" auto runtestcase(const char *name) -> int {
|
||||||
tests["perf_spawn"] = perf_spawn;
|
tests["perf_spawn"] = perf_spawn;
|
||||||
tests["spawn_exit"] = spawn_exit;
|
tests["spawn_exit"] = spawn_exit;
|
||||||
tests["timeout_test"] = timeout_test;
|
tests["timeout_test"] = timeout_test;
|
||||||
|
tests["unx_c_api"] = unx_c_api;
|
||||||
|
|
||||||
env_t env{};
|
env_t env{};
|
||||||
env_t log_env{};
|
env_t log_env{};
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,4 @@ testcase perf_ring;
|
||||||
testcase perf_spawn;
|
testcase perf_spawn;
|
||||||
testcase spawn_exit;
|
testcase spawn_exit;
|
||||||
testcase timeout_test;
|
testcase timeout_test;
|
||||||
|
testcase unx_c_api;
|
||||||
|
|
|
||||||
25
test/unx_c_api.cpp
Normal file
25
test/unx_c_api.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "tests.hpp"
|
||||||
|
#include <thespian/c/unx.h>
|
||||||
|
|
||||||
|
using namespace thespian;
|
||||||
|
|
||||||
|
// Very small smoke test for the new C API. We don't actually open sockets,
|
||||||
|
// just verify that the create/destroy functions work and return non-null.
|
||||||
|
|
||||||
|
auto unx_c_api(thespian::context &ctx, bool &result, thespian::env_t env)
|
||||||
|
-> thespian::result {
|
||||||
|
(void)ctx;
|
||||||
|
(void)env;
|
||||||
|
|
||||||
|
struct thespian_unx_acceptor_handle *a = thespian_unx_acceptor_create("tag");
|
||||||
|
check(a != nullptr);
|
||||||
|
thespian_unx_acceptor_destroy(a);
|
||||||
|
|
||||||
|
struct thespian_unx_connector_handle *c =
|
||||||
|
thespian_unx_connector_create("tag");
|
||||||
|
check(c != nullptr);
|
||||||
|
thespian_unx_connector_destroy(c);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
return ok();
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue