diff --git a/build.zig.zon b/build.zig.zon index 95ed4e7..93daaf3 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -8,8 +8,8 @@ .hash = "12206a4050ebb2e2bf84ed477ea5fe0d0325f9292eef2cb8bd47ccda75a9b3d93516", }, .tracy = .{ - .url = "https://github.com/neurocyte/zig-tracy/archive/80b914d2391209de9ed5a1fd6f440642df55cbd4.tar.gz", - .hash = "1220351c8410936854e3baa10aa7bbe775196b3974a3d670808bebbab00631439285", + .url = "https://github.com/neurocyte/zig-tracy/archive/e04e31c64498149a324491b8534758e6af43a5c2.tar.gz", + .hash = "1220d0fb2bff7b453dbb39d1db3eb472b6680e2564f2b23b0e947671be47bbdd188f", }, }, .paths = .{ diff --git a/include/cbor/cbor.hpp b/include/cbor/cbor.hpp index 2985caa..f835e38 100644 --- a/include/cbor/cbor.hpp +++ b/include/cbor/cbor.hpp @@ -17,7 +17,7 @@ constexpr auto cbor_magic_false = 0xf4; constexpr auto cbor_magic_type_array = 4; constexpr auto cbor_magic_type_map = 5; -enum class type { +enum class type : uint8_t { number, bytes, string, @@ -326,7 +326,7 @@ public: } auto operator*() -> value_accessor { if (n) - return {b, e}; + return {.b = b, .e = e}; throw std::out_of_range("cbor iterator out of range"); } }; @@ -337,10 +337,10 @@ public: auto b = raw_cbegin(); auto e = raw_cend(); auto n = decode_range_header(b, e); - return {b, e, n}; + return {.b = b, .e = e, .n = n}; } [[nodiscard]] auto end() const -> value_iterator { - return {raw_cend(), raw_cend()}; + return {.b = raw_cend(), .e = raw_cend()}; } class range { @@ -355,10 +355,10 @@ public: auto b = raw_cbegin(); auto e = raw_cend(); auto n = decode_range_header(b, e); - return {b, e, n}; + return {.b = b, .e = e, .n = n}; } [[nodiscard]] auto end() const -> value_iterator { - return {raw_cend(), raw_cend()}; + return {.b = raw_cend(), .e = raw_cend()}; } auto is_null() -> bool { diff --git a/include/thespian/env.hpp b/include/thespian/env.hpp index 26e6b0c..cee21a1 100644 --- a/include/thespian/env.hpp +++ b/include/thespian/env.hpp @@ -3,7 +3,6 @@ #include "handle.hpp" #include "trace.hpp" -#include #include #include diff --git a/include/thespian/unx.hpp b/include/thespian/unx.hpp index 949746c..be18b49 100644 --- a/include/thespian/unx.hpp +++ b/include/thespian/unx.hpp @@ -2,11 +2,10 @@ #include #include -#include namespace thespian::unx { -enum class mode { file, abstract }; +enum class mode : uint8_t { file, abstract }; struct acceptor_impl; using acceptor_dtor = void (*)(acceptor_impl *); diff --git a/src/backtrace.cpp b/src/backtrace.cpp index fd6963d..05cffcd 100644 --- a/src/backtrace.cpp +++ b/src/backtrace.cpp @@ -8,6 +8,7 @@ #include #include +namespace { static void msg(const char *msg, const char *arg) { if (write(STDERR_FILENO, msg, strlen(msg)) != 0) { } @@ -46,7 +47,7 @@ static auto get_debugger() -> const char * { } const char *const debugger = get_debugger(); -void start_debugger(const char * dbg, const char **argv) { +void start_debugger(const char *dbg, const char **argv) { #if defined(PR_SET_PTRACER) prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); // NOLINT #endif @@ -61,6 +62,7 @@ void start_debugger(const char * dbg, const char **argv) { waitpid(child_pid, &stat, 0); } } +} // namespace extern "C" void sighdl_debugger(int no, siginfo_t * /*sigi*/, void * /*uco*/) { get_pid_binpath(); @@ -73,14 +75,15 @@ extern "C" void sighdl_debugger(int no, siginfo_t * /*sigi*/, void * /*uco*/) { extern "C" void sighdl_backtrace(int no, siginfo_t * /*sigi*/, void * /*uco*/) { get_pid_binpath(); const char *argv[] = {// NOLINT - lldb, "--batch", "-p", pid_s, + lldb, "--batch", "-p", pid_s, "--one-line", "bt", binpath, nullptr}; start_debugger(lldb, argv); (void)raise(no); } +namespace { static void install_crash_handler(void (*hdlr)(int, siginfo_t *, void *)) { - struct sigaction action {}; + struct sigaction action{}; sigemptyset(&action.sa_mask); action.sa_flags = SA_SIGINFO | SA_RESETHAND; #ifdef SA_FULLDUMP @@ -93,6 +96,7 @@ static void install_crash_handler(void (*hdlr)(int, siginfo_t *, void *)) { sigaction(SIGTRAP, &action, nullptr); sigaction(SIGFPE, &action, nullptr); } +} // namespace extern "C" void install_debugger() { install_crash_handler(sighdl_debugger); } extern "C" void install_backtrace() { install_crash_handler(sighdl_backtrace); } diff --git a/src/c/handle.cpp b/src/c/handle.cpp index 8b4d233..8011077 100644 --- a/src/c/handle.cpp +++ b/src/c/handle.cpp @@ -66,5 +66,4 @@ auto thespian_handle_is_expired(thespian_handle h) -> bool { h)}; return h_->expired(); } - } diff --git a/src/c/metronome.cpp b/src/c/metronome.cpp index 62ed79d..57d7a21 100644 --- a/src/c/metronome.cpp +++ b/src/c/metronome.cpp @@ -14,8 +14,7 @@ using thespian::stop_metronome; extern "C" { -auto thespian_metronome_create_ms(uint64_t ms) - -> thespian_metronome_handle * { +auto thespian_metronome_create_ms(uint64_t ms) -> thespian_metronome_handle * { try { auto *handle = thespian::create_metronome(milliseconds(ms)).ref.release(); return reinterpret_cast(handle); // NOLINT @@ -27,8 +26,7 @@ auto thespian_metronome_create_ms(uint64_t ms) return nullptr; } } -auto thespian_metronome_create_us(uint64_t us) - -> thespian_metronome_handle * { +auto thespian_metronome_create_us(uint64_t us) -> thespian_metronome_handle * { try { auto *handle = thespian::create_metronome(microseconds(us)).ref.release(); return reinterpret_cast(handle); // NOLINT diff --git a/src/c/trace.cpp b/src/c/trace.cpp index 7daa731..50a768d 100644 --- a/src/c/trace.cpp +++ b/src/c/trace.cpp @@ -18,5 +18,4 @@ void thespian_trace_to_cbor_file(const char *file_name) { void thespian_trace_to_mermaid_file(const char *file_name) { thespian::trace_to_mermaid_file(file_name); } - } diff --git a/src/cbor.cpp b/src/cbor.cpp index 0c472f6..8c328f9 100644 --- a/src/cbor.cpp +++ b/src/cbor.cpp @@ -71,6 +71,7 @@ auto buffer::push_string(const string_view &s) -> buffer & { using json_iter = string::const_iterator; +namespace { static auto match_wsp_char(json_iter &b, const json_iter &e) -> bool { if (b == e) return false; @@ -347,6 +348,7 @@ static auto push_json_value(buffer &buf, json_iter &b, const json_iter &e) return false; } +} // namespace auto buffer::push_json(const string &s) -> void { json_iter b = s.cbegin(); @@ -360,6 +362,7 @@ auto buffer::push_json(const string &s) -> void { using iter = buffer::const_iterator; +namespace { static auto decode_int_length_recurse(size_t length, iter &b, const iter &e, int64_t i) -> int64_t { if (b == e) @@ -426,6 +429,7 @@ static auto decode_type(iter &b, const iter &e) ++b; return make_tuple(uint8_t(type >> 5), uint8_t(type & 31), type); } +} // namespace auto buffer::decode_range_header(iter &b, const iter &e) -> size_t { const auto [major, minor, type] = decode_type(b, e); @@ -460,6 +464,7 @@ auto buffer::decode_map_header(iter &b, const iter &e) -> size_t { return decode_pint(minor, b, e); } +namespace { static auto skip_string(uint8_t type, iter &b, const iter &e) -> void { auto len = decode_pint(type, b, e); while (len) { @@ -559,6 +564,7 @@ static auto match_type(iter &b, const iter &e, type &v) -> bool { } return true; } +} // namespace auto buffer::match_value(iter &b, const iter &e, type t) -> bool { type v{type::any}; @@ -570,6 +576,7 @@ auto buffer::match_value(iter &b, const iter &e, type t) -> bool { return false; } +namespace { static auto match_uint(iter &b, const iter &e, unsigned long long int &val) -> bool { const auto [major, minor, type] = decode_type(b, e); @@ -596,6 +603,7 @@ static auto match_int(iter &b, const iter &e, signed long long int &val) } return true; } +} // namespace auto buffer::match_value(iter &b, const iter &e, unsigned long long int lit) -> bool { @@ -613,6 +621,7 @@ auto buffer::match_value(iter &b, const iter &e, signed long long int lit) return false; } +namespace { static auto match_bool(iter &b, const iter &e, bool &v) -> bool { const auto [major, minor, type] = decode_type(b, e); if (major == 7) { // special @@ -627,6 +636,7 @@ static auto match_bool(iter &b, const iter &e, bool &v) -> bool { } return false; } +} // namespace auto buffer::match_value(iter &b, const iter &e, bool lit) -> bool { bool val{}; @@ -635,6 +645,7 @@ auto buffer::match_value(iter &b, const iter &e, bool lit) -> bool { return false; } +namespace { static auto match_string(iter &b, const iter &e, string_view &val) -> bool { const auto [major, minor, type] = decode_type(b, e); switch (major) { @@ -649,6 +660,7 @@ static auto match_string(iter &b, const iter &e, string_view &val) -> bool { } return true; } +} // namespace auto buffer::match_value(iter &b, const iter &e, const string_view lit) -> bool { @@ -669,6 +681,7 @@ auto extract(type &t) -> buffer::extractor { return [&t](iter &b, const iter &e) { return match_type(b, e, t); }; } +namespace { template static auto extract_int(T &i) -> buffer::extractor { return [&i](iter &b, const iter &e) { signed long long int i_{}; @@ -681,6 +694,8 @@ template static auto extract_int(T &i) -> buffer::extractor { return false; }; } +} // namespace + // clang-format off auto extract(signed long long int &i) -> buffer::extractor { return extract_int(i); } auto extract(signed long int &i) -> buffer::extractor { return extract_int(i); } @@ -692,6 +707,7 @@ auto extract(signed char &i) -> buffer::extractor { return extract_int(i); } auto extract(unsigned long long int &i) -> buffer::extractor { return [&i](iter &b, const iter &e) { return match_uint(b, e, i); }; } +namespace { template static auto extract_uint(T &i) -> buffer::extractor { return [&i](iter &b, const iter &e) { unsigned long long int i_{}; @@ -704,6 +720,7 @@ template static auto extract_uint(T &i) -> buffer::extractor { return false; }; } +} // namespace // clang-format off auto extract(unsigned long int &i) -> buffer::extractor { return extract_uint(i); } auto extract(unsigned int &i) -> buffer::extractor { return extract_uint(i); } @@ -748,6 +765,7 @@ auto buffer::match_value(iter &b, const iter &e, const extractor &ex) -> bool { return ex(b, e); } +namespace { static auto tohex(ostream &os, uint8_t v) -> ostream & { return os << hex << setfill('0') << setw(2) << static_cast(v); } @@ -866,6 +884,7 @@ static auto to_json_(const iter &b_, const iter &e) -> string { } return ss.str(); } +} // namespace auto buffer::to_json() const -> string { return to_json_(raw_cbegin(), raw_cend()); @@ -885,8 +904,8 @@ auto buffer::range::to_json(ostream &os) const -> void { extern "C" void cbor_to_json(cbor_buffer buf, cbor_to_json_callback cb) { auto cbor = cbor::buffer(); const uint8_t *data = buf.base; - std::copy(data, data + buf.len, - back_inserter(cbor)); // NOLINT(*-pointer-arithmetic) + std::copy(data, data + buf.len, // NOLINT(*-pointer-arithmetic) + back_inserter(cbor)); auto json = cbor.to_json(); cb({json.data(), json.size()}); } @@ -909,11 +928,13 @@ auto buffer::value_accessor::type_() const -> type { return t; } +namespace { template static auto get(iter b, const iter &e) -> T { T val; extract(val)(b, e); return val; } +} // namespace // clang-format off buffer::value_accessor::operator type() const { return get(b, e); } diff --git a/src/cbor.zig b/src/cbor.zig index cf97696..fe3f633 100644 --- a/src/cbor.zig +++ b/src/cbor.zig @@ -522,6 +522,21 @@ fn matchFloatValue(comptime T: type, iter: *[]const u8, val: T) Error!bool { return if (try matchFloat(T, iter, &v)) v == val else false; } +pub fn matchEnum(comptime T: type, iter_: *[]const u8, val: *T) Error!bool { + var iter = iter_.*; + var str: []const u8 = undefined; + if (try matchString(&iter, &str)) if (std.meta.stringToEnum(T, str)) |val_| { + val.* = val_; + iter_.* = iter; + return true; + }; + return false; +} + +fn matchEnumValue(comptime T: type, iter: *[]const u8, val: T) Error!bool { + return matchStringValue(iter, @tagName(val)); +} + fn skipString(iter: *[]const u8, minor: u5) Error!void { const len: usize = @intCast(try decodePInt(iter, minor)); if (iter.len < len) @@ -655,8 +670,9 @@ pub fn matchValue(iter: *[]const u8, value: anytype) Error!bool { else matchError(T), .array => |info| if (info.child == u8) matchStringValue(iter, &value) else matchArray(iter, value, info), - .float => return matchFloatValue(T, iter, value), + .float => matchFloatValue(T, iter, value), .comptime_float => matchFloatValue(f64, iter, value), + .@"enum" => matchEnumValue(T, iter, value), else => @compileError("cannot match value type '" ++ @typeName(T) ++ "' to cbor stream"), }; } @@ -855,6 +871,7 @@ fn Extractor(comptime T: type) type { return false; }, .float => return matchFloat(T, iter, self.dest), + .@"enum" => return matchEnum(T, iter, self.dest), else => extractError(T), } } @@ -897,8 +914,11 @@ pub fn extract_cbor(dest: *[]const u8) CborExtractor { } pub fn JsonStream(comptime T: type) type { + return JsonStreamWriter(T.Writer); +} + +pub fn JsonStreamWriter(comptime Writer: type) type { return struct { - const Writer = T.Writer; const JsonWriter = json.WriteStream(Writer, .{ .checked_to_fixed_depth = 256 }); fn jsonWriteArray(w: *JsonWriter, iter: *[]const u8, minor: u5) !void { @@ -954,6 +974,12 @@ pub fn toJson(cbor_buf: []const u8, json_buf: []u8) (JsonEncodeError || error{No return fbs.getWritten(); } +pub fn toJsonWriter(cbor_buf: []const u8, writer: anytype, options: std.json.StringifyOptions) !void { + var s = json.writeStream(writer, options); + var iter: []const u8 = cbor_buf; + try JsonStreamWriter(@TypeOf(writer)).jsonWriteValue(&s, &iter); +} + pub fn toJsonAlloc(a: std.mem.Allocator, cbor_buf: []const u8) (JsonEncodeError)![]const u8 { var buf = std.ArrayList(u8).init(a); defer buf.deinit(); diff --git a/src/executor_asio.cpp b/src/executor_asio.cpp index 0dbb27c..69f905d 100644 --- a/src/executor_asio.cpp +++ b/src/executor_asio.cpp @@ -481,11 +481,11 @@ namespace unx { struct socket_impl { explicit socket_impl(strand &strand) - : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, socket_{ - *ctx->asio} {} + : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, + socket_{*ctx->asio} {} explicit socket_impl(strand &strand, int fd) - : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, socket_{*ctx->asio, - fd} {} + : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, + socket_{*ctx->asio, fd} {} auto bind(string_view path) -> error_code { error_code ec; ec = socket_.bind(path, ec); @@ -615,8 +615,8 @@ namespace file_descriptor { struct watcher_impl { explicit watcher_impl(strand &strand, int fd) - : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, fd_{*ctx->asio, - fd} {} + : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, + fd_{*ctx->asio, fd} {} void wait_read(watcher::handler h) { if (!read_in_progress_) { @@ -680,8 +680,8 @@ struct file_stream_impl { explicit file_stream_impl( strand &strand, const asio::windows::stream_handle::native_handle_type &handle) - : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, handle_{*ctx->asio, - handle} {} + : ctx{strand.ref->ctx}, strand_{strand.ref->strand_}, + handle_{*ctx->asio, handle} {} void start_read(file_stream::read_handler h) { if (!read_in_progress_) { diff --git a/src/hub.cpp b/src/hub.cpp index 2c8c24c..fc96f57 100644 --- a/src/hub.cpp +++ b/src/hub.cpp @@ -91,7 +91,7 @@ struct hub_impl { set msgs; for (const auto val : r) msgs.insert(string(val)); - subscribers_.emplace_back(move(from), [msgs](auto m) { + subscribers_.emplace_back(move(from), [msgs](const auto &m) { string tag; return m(extract(tag), more) && (msgs.find(tag) != msgs.end()); }); diff --git a/src/instance.cpp b/src/instance.cpp index ae6d864..f0596d6 100644 --- a/src/instance.cpp +++ b/src/instance.cpp @@ -108,9 +108,11 @@ struct context_impl : context { }; thread_local instance *current_instance{}; // NOLINT +namespace { auto impl(context &ctx) -> context_impl & { return *reinterpret_cast(&ctx); // NOLINT } +} // namespace auto context::create() -> ref { return {new context_impl{executor::context::create()}, context_impl::destroy}; @@ -129,10 +131,12 @@ void context::on_last_exit(last_exit_handler h) { namespace debug { +namespace { void register_instance(context_impl &ctx, const handle &h); void unregister_instance(context_impl &ctx, const string &name); auto get_name(context_impl &ctx, const string &name) -> handle; auto get_names(context_impl &ctx) -> buffer; +} // namespace } // namespace debug @@ -147,6 +151,7 @@ constexpr auto context_error = "call out of context"; auto handle_ref(handle &h) -> ref & { return h.ref_; } auto handle_ref(const handle &h) -> const ref & { return h.ref_; } +namespace { auto make_handle(ref &&r) -> handle { handle h{}; handle_ref(h) = move(r); @@ -159,12 +164,13 @@ template auto exit_message(string_view e, Ts &&...parms) { return buffer(array("exit", e, forward(parms)...)); } -[[nodiscard]] auto exit() -> result { return to_error(exit_message("normal")); } - template [[nodiscard]] auto exit(std::string_view e, Ts &&...parms) -> result { return to_error(exit_message(e, std::forward(parms)...)); } +} // namespace + +[[nodiscard]] auto exit() -> result { return to_error(exit_message("normal")); } [[nodiscard]] auto exit(std::string_view e) -> result { return to_error(exit_message(e)); @@ -186,7 +192,9 @@ template return exit("UNEXPECTED_MESSAGE:" + b.to_json()); } +namespace { auto deadsend(const buffer &m, const ref &from) -> result; +} // namespace const auto exit_normal_msg = array("exit", "normal"); const auto exit_noreceive_msg = array("exit", "noreceive"); const auto exit_nosyncreceive_msg = array("exit", "nosyncreceive"); @@ -221,7 +229,7 @@ struct instance : std::enable_shared_from_this { env_(move(env)) { if (eh) exit_handlers_.emplace_front(eh); - receiver_ = [this, b{move(b)}](auto, auto) { + receiver_ = [this, b{move(b)}](const auto &, const auto &) { this->do_trace(channel::lifetime, "init"); receiver b_; b_.swap(receiver_); @@ -245,8 +253,8 @@ struct instance : std::enable_shared_from_this { } static auto spawn(context_impl &ctx, behaviour b, exit_handler eh, - string_view name, const ref &link, - env_t env) -> expected { + string_view name, const ref &link, env_t env) + -> expected { auto pimpl = make_shared(ctx, move(b), move(eh), name, move(env)); pimpl->lifetime_ = pimpl; handle_ref(pimpl->self_ref_) = pimpl->lifetime_; @@ -316,7 +324,7 @@ struct instance : std::enable_shared_from_this { auto trace_enabled(channel c) { return env_.enabled(c); } template void submit_msg_task(F f) { - strand_.post(msg_task_T(f)); + strand_.post(msg_task_T(move(f))); } [[nodiscard]] auto dispatch_sync_raw(buffer m) -> result { @@ -447,8 +455,8 @@ struct instance : std::enable_shared_from_this { return send_raw(array(std::forward(parms)...)); } - auto spawn_link(behaviour b, string_view name, - env_t env) -> expected { + auto spawn_link(behaviour b, string_view name, env_t env) + -> expected { auto ret = instance::spawn(ctx, move(b), exit_handler{}, name, lifetime_, move(env)); if (not ret) @@ -497,6 +505,7 @@ struct instance : std::enable_shared_from_this { env_t env_; }; +namespace { auto deadsend(const buffer &m, const ref &from) -> result { if (current_instance and !current_instance->is_in_shutdown() and !m("exit", type::more)) { @@ -505,6 +514,7 @@ auto deadsend(const buffer &m, const ref &from) -> result { } return ok(); } +} // namespace [[nodiscard]] auto handle::send_raw(buffer m) const -> result { if (auto p = ref_.lock()) { @@ -517,6 +527,7 @@ auto operator==(const handle &a, const handle &b) -> bool { return a.ref_.lock() == b.ref_.lock(); } +namespace { auto ref_m(const void *p, string name) -> buffer { stringstream ss; ss << p; @@ -530,7 +541,9 @@ auto ref_m(const ref &r) -> buffer { return ref_m(p.get(), p->name()); return ref_m(nullptr, "none"); } +} // namespace +namespace { auto private_call() -> instance & { if (!current_instance) throw domain_error{context_error}; @@ -544,6 +557,7 @@ auto private_call_noexcept() noexcept -> instance * { } auto trace_enabled(channel c) { return private_call().env_.enabled(c); } +} // namespace auto spawn(behaviour b, string_view name) -> expected { auto &p = private_call(); @@ -551,15 +565,15 @@ auto spawn(behaviour b, string_view name) -> expected { p.env_); } -auto spawn(behaviour b, string_view name, - env_t env) -> expected { +auto spawn(behaviour b, string_view name, env_t env) + -> expected { auto &p = private_call(); return instance::spawn(p.ctx, move(b), exit_handler{}, name, thespian::ref{}, move(env)); } -auto context::spawn(behaviour b, string_view name, - env_t env) -> expected { +auto context::spawn(behaviour b, string_view name, env_t env) + -> expected { return instance::spawn(impl(*this), move(b), exit_handler{}, name, thespian::ref{}, move(env)); } @@ -577,8 +591,8 @@ auto context::spawn_link(behaviour b, exit_handler eh, string_view name, move(env)); } -auto context::spawn_link(behaviour b, exit_handler eh, - string_view name) -> expected { +auto context::spawn_link(behaviour b, exit_handler eh, string_view name) + -> expected { return spawn_link(move(b), move(eh), name, env_t{}); } @@ -588,8 +602,8 @@ auto self_ref() -> handle & { return private_call().self_ref_; } auto spawn_link(behaviour b, string_view name) -> expected { return private_call().spawn_link(move(b), name); } -auto spawn_link(behaviour b, string_view name, - env_t env) -> expected { +auto spawn_link(behaviour b, string_view name, env_t env) + -> expected { return private_call().spawn_link(move(b), name, move(env)); } auto send_raw(buffer m) -> result { return private_call().send_raw(move(m)); } @@ -603,12 +617,6 @@ auto on_trace(trace_handler h) -> trace_handler { return private_call().env_.on_trace(move(h)); } -auto get_strand(const handle &h) -> executor::strand * { - if (auto p = handle_ref(h).lock()) - return &p->strand_; - return nullptr; -} - struct signal_impl { signal_impl(const signal_impl &) = delete; signal_impl(signal_impl &&) = delete; @@ -827,8 +835,8 @@ struct udp_impl { } } - [[nodiscard]] auto sendto(string_view data, in6_addr ip, - port_t port) -> size_t { + [[nodiscard]] auto sendto(string_view data, in6_addr ip, port_t port) + -> size_t { if (is_trace_enabled_) owner_.env_.trace( array("udp", tag_, "sendto", data, executor::to_string(ip), port)); @@ -868,8 +876,8 @@ auto udp::create(string tag) -> udp { struct file_descriptor_impl { file_descriptor_impl(const file_descriptor_impl &) = delete; file_descriptor_impl(file_descriptor_impl &&) = delete; - auto - operator=(const file_descriptor_impl &) -> file_descriptor_impl & = delete; + auto operator=(const file_descriptor_impl &) + -> file_descriptor_impl & = delete; auto operator=(file_descriptor_impl &&) -> file_descriptor_impl & = delete; file_descriptor_impl(string_view tag, int fd) @@ -1263,16 +1271,7 @@ void connector::cancel() { ref->cancel(); } namespace unx { -auto unx_mode_string(mode m) -> const char * { - switch (m) { - case mode::file: - return "file"; - case mode::abstract: - return "abstract"; - } - return "unknown"; -} - +namespace { auto unx_mode_path(mode m, string_view path) -> string { switch (m) { case mode::file: @@ -1285,6 +1284,7 @@ auto unx_mode_path(mode m, string_view path) -> string { } return "unknown"; } +} // namespace struct acceptor_impl { explicit acceptor_impl(string_view tag) @@ -1498,16 +1498,16 @@ struct connection { return ok(); } - static auto start(int fd, const handle &owner, - const char *tag) -> expected { + static auto start(int fd, const handle &owner, const char *tag) + -> expected { return spawn( [fd, owner, tag]() { link(owner); trap(true); - ::thespian::receive( - [p{make_shared(fd, owner, tag)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::thespian::receive([p{make_shared(fd, owner, tag)}]( + const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, tag); @@ -1516,9 +1516,8 @@ struct connection { static void attach(int fd, handle owner, const char *tag) { private_call().name_ = tag; ::thespian::receive( - [p{make_shared(fd, owner, tag)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + [p{make_shared(fd, owner, tag)}]( + const auto &from, const auto &m) { return p->receive(from, m); }); } }; @@ -1579,11 +1578,11 @@ struct connector { return spawn_link( [=]() { trap(true); - ::thespian::receive( - [p{make_shared(ip, port, owner, retry_time, - retry_count)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::thespian::receive([p{make_shared( + ip, port, owner, retry_time, retry_count)}]( + const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, tag); @@ -1633,10 +1632,10 @@ struct acceptor { return spawn_link( [=]() { trap(true); - ::thespian::receive( - [p{make_shared(ip, port, owner)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::thespian::receive([p{make_shared(ip, port, owner)}]( + const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, tag); @@ -1714,11 +1713,11 @@ struct connector { return spawn_link( [path{string(path)}, m_, owner, retry_time, retry_count]() { trap(true); - ::thespian::receive( - [p{make_shared(path, m_, owner, retry_time, - retry_count)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::thespian::receive([p{make_shared( + path, m_, owner, retry_time, retry_count)}]( + const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, tag); @@ -1770,10 +1769,10 @@ struct acceptor { return spawn_link( [path{string(path)}, m_, owner]() { trap(true); - ::thespian::receive( - [p{make_shared(path, m_, owner)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::thespian::receive([p{make_shared(path, m_, owner)}]( + const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, tag); @@ -1794,14 +1793,19 @@ auto connect(string_view path, mode m, milliseconds retry_time, namespace debug { +namespace { void enable(context_impl &ctx) { ctx.debug_enabled.fetch_add(1, memory_order_relaxed); } +} // namespace void enable(context &ctx) { enable(impl(ctx)); } +namespace { auto isenabled(context_impl &ctx) -> bool { return ctx.debug_enabled.load(memory_order_relaxed) > 0; } +} // namespace auto isenabled(context &ctx) -> bool { return isenabled(impl(ctx)); } +namespace { void disable(context_impl &ctx) { auto prev = ctx.debug_enabled.fetch_sub(1, memory_order_relaxed); if (prev == 1) { @@ -1809,8 +1813,10 @@ void disable(context_impl &ctx) { ctx.registry.clear(); } } +} // namespace void disable(context &ctx) { disable(impl(ctx)); } +namespace { void register_instance(context_impl &ctx, const handle &h) { if (not isenabled(ctx)) return; @@ -1856,6 +1862,7 @@ auto get_names(context_impl &ctx) -> buffer { } return b; } +} // namespace namespace tcp { @@ -1994,14 +2001,14 @@ struct connection { return ok(); } - static auto start(context_impl &ctx, int fd, - const string &prompt) -> expected { + static auto start(context_impl &ctx, int fd, const string &prompt) + -> expected { return spawn( [&ctx, fd, prompt]() { - ::thespian::receive( - [p{make_shared(ctx, fd, prompt)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::thespian::receive([p{make_shared(ctx, fd, prompt)}]( + const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, tag); @@ -2051,22 +2058,22 @@ struct acceptor { return ok(); } - static auto start(context_impl &ctx, port_t port, - const string &prompt) -> expected { + static auto start(context_impl &ctx, port_t port, const string &prompt) + -> expected { return spawn( [&ctx, port, prompt]() { - ::thespian::receive( - [p{make_shared(ctx, port, prompt)}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::thespian::receive([p{make_shared(ctx, port, prompt)}]( + const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, tag); } }; -auto create(context &ctx, port_t port, - const string &prompt) -> expected { +auto create(context &ctx, port_t port, const string &prompt) + -> expected { return acceptor::start(impl(ctx), port, prompt); } diff --git a/src/subprocess_windows.zig b/src/subprocess_windows.zig index f8323f2..e9eaea1 100644 --- a/src/subprocess_windows.zig +++ b/src/subprocess_windows.zig @@ -3,14 +3,14 @@ const cbor = @import("cbor"); const tp = @import("thespian.zig"); pid: ?tp.pid, -stdin_behavior: std.process.Child.StdIo, +stdin_behavior: Child.StdIo, const Self = @This(); pub const max_chunk_size = 4096 - 32; pub const Writer = std.io.Writer(*Self, error{Exit}, write); pub const BufferedWriter = std.io.BufferedWriter(max_chunk_size, Writer); -pub fn init(a: std.mem.Allocator, argv: tp.message, tag: [:0]const u8, stdin_behavior: std.process.Child.StdIo) !Self { +pub fn init(a: std.mem.Allocator, argv: tp.message, tag: [:0]const u8, stdin_behavior: Child.StdIo) !Self { return .{ .pid = try Proc.create(a, argv, tag, stdin_behavior), .stdin_behavior = stdin_behavior, @@ -69,7 +69,7 @@ const Proc = struct { receiver: Receiver, args: std.heap.ArenaAllocator, parent: tp.pid, - child: std.process.Child, + child: Child, tag: [:0]const u8, stdin_buffer: std.ArrayList(u8), stream_stdout: ?tp.file_stream = null, @@ -77,7 +77,7 @@ const Proc = struct { const Receiver = tp.Receiver(*Proc); - fn create(a: std.mem.Allocator, argv: tp.message, tag: [:0]const u8, stdin_behavior: std.process.Child.StdIo) !tp.pid { + fn create(a: std.mem.Allocator, argv: tp.message, tag: [:0]const u8, stdin_behavior: Child.StdIo) !tp.pid { const self: *Proc = try a.create(Proc); var args = std.heap.ArenaAllocator.init(a); @@ -94,7 +94,7 @@ const Proc = struct { i += 1; } - var child = std.process.Child.init(argv_, a); + var child = Child.init(argv_, a); child.stdin_behavior = stdin_behavior; child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; @@ -127,7 +127,9 @@ const Proc = struct { _ = self.args.reset(.free_all); self.stream_stdout = tp.file_stream.init("stdout", self.child.stdout.?.handle) catch |e| return self.handle_error(e); + self.child.stdout = null; // ownership transferred self.stream_stderr = tp.file_stream.init("stderr", self.child.stderr.?.handle) catch |e| return self.handle_error(e); + self.child.stderr = null; // ownership transferred if (self.stream_stdout) |stream| stream.start_read() catch |e| return self.handle_error(e); if (self.stream_stderr) |stream| stream.start_read() catch |e| return self.handle_error(e); @@ -205,7 +207,7 @@ const Proc = struct { return self.handle_term(self.child.wait() catch |e| return self.handle_error(e)); } - fn handle_term(self: *Proc, term_: std.process.Child.Term) error{Exit} { + fn handle_term(self: *Proc, term_: Child.Term) error{Exit} { (switch (term_) { .Exited => |val| self.parent.send(.{ self.tag, "term", "exited", val }), .Signal => |val| self.parent.send(.{ self.tag, "term", "signal", val }), @@ -220,3 +222,933 @@ const Proc = struct { return tp.exit_normal(); } }; + +const Child = struct { + const windows = std.os.windows; + const posix = std.posix; + const unicode = std.unicode; + const process = std.process; + const fs = std.fs; + const mem = std.mem; + const File = std.fs.File; + const EnvMap = std.process.EnvMap; + const StdIo = std.process.Child.StdIo; + + pub const Term = union(enum) { + Exited: u8, + Signal: u32, + Stopped: u32, + Unknown: u32, + }; + + pub const SpawnError = error{ + OutOfMemory, + InvalidWtf8, + CurrentWorkingDirectoryUnlinked, + InvalidBatchScriptArg, + } || + posix.ExecveError || + posix.SetIdError || + posix.ChangeCurDirError || + windows.CreateProcessError || + windows.GetProcessMemoryInfoError || + windows.WaitForSingleObjectError; + + id: std.process.Child.Id, + thread_handle: windows.HANDLE, + allocator: mem.Allocator, + stdin: ?File, + stdout: ?File, + stderr: ?File, + term: ?(SpawnError!Term), + argv: []const []const u8, + env_map: ?*const EnvMap, + stdin_behavior: StdIo, + stdout_behavior: StdIo, + stderr_behavior: StdIo, + cwd: ?[]const u8, + cwd_dir: ?fs.Dir = null, + expand_arg0: std.process.Child.Arg0Expand, + + pub fn init(argv: []const []const u8, allocator: mem.Allocator) @This() { + return .{ + .allocator = allocator, + .argv = argv, + .id = undefined, + .thread_handle = undefined, + .term = null, + .env_map = null, + .cwd = null, + .stdin = null, + .stdout = null, + .stderr = null, + .stdin_behavior = .Inherit, + .stdout_behavior = .Inherit, + .stderr_behavior = .Inherit, + .expand_arg0 = .no_expand, + }; + } + + pub fn spawn(self: *@This()) !void { + var saAttr = windows.SECURITY_ATTRIBUTES{ + .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), + .bInheritHandle = windows.TRUE, + .lpSecurityDescriptor = null, + }; + + const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); + + const nul_handle = if (any_ignore) + // "\Device\Null" or "\??\NUL" + windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{ + .access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE | windows.SYNCHRONIZE, + .share_access = windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE, + .sa = &saAttr, + .creation = windows.OPEN_EXISTING, + }) catch |err| switch (err) { + error.PathAlreadyExists => return error.Unexpected, // not possible for "NUL" + error.PipeBusy => return error.Unexpected, // not possible for "NUL" + error.FileNotFound => return error.Unexpected, // not possible for "NUL" + error.AccessDenied => return error.Unexpected, // not possible for "NUL" + error.NameTooLong => return error.Unexpected, // not possible for "NUL" + error.WouldBlock => return error.Unexpected, // not possible for "NUL" + error.NetworkNotFound => return error.Unexpected, // not possible for "NUL" + error.AntivirusInterference => return error.Unexpected, // not possible for "NUL" + else => |e| return e, + } + else + undefined; + defer { + if (any_ignore) posix.close(nul_handle); + } + + var g_hChildStd_IN_Rd: ?windows.HANDLE = null; + var g_hChildStd_IN_Wr: ?windows.HANDLE = null; + switch (self.stdin_behavior) { + StdIo.Pipe => { + try makePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); + }, + StdIo.Ignore => { + g_hChildStd_IN_Rd = nul_handle; + }, + StdIo.Inherit => { + g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null; + }, + StdIo.Close => { + g_hChildStd_IN_Rd = null; + }, + } + errdefer if (self.stdin_behavior == StdIo.Pipe) { + destroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); + }; + + var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; + var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; + switch (self.stdout_behavior) { + StdIo.Pipe => { + try makeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); + }, + StdIo.Ignore => { + g_hChildStd_OUT_Wr = nul_handle; + }, + StdIo.Inherit => { + g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null; + }, + StdIo.Close => { + g_hChildStd_OUT_Wr = null; + }, + } + errdefer if (self.stdout_behavior == StdIo.Pipe) { + destroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); + }; + + var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; + var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; + switch (self.stderr_behavior) { + StdIo.Pipe => { + try makeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); + }, + StdIo.Ignore => { + g_hChildStd_ERR_Wr = nul_handle; + }, + StdIo.Inherit => { + g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null; + }, + StdIo.Close => { + g_hChildStd_ERR_Wr = null; + }, + } + errdefer if (self.stderr_behavior == StdIo.Pipe) { + destroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); + }; + + var siStartInfo = windows.STARTUPINFOW{ + .cb = @sizeOf(windows.STARTUPINFOW), + .hStdError = g_hChildStd_ERR_Wr, + .hStdOutput = g_hChildStd_OUT_Wr, + .hStdInput = g_hChildStd_IN_Rd, + .dwFlags = windows.STARTF_USESTDHANDLES, + + .lpReserved = null, + .lpDesktop = null, + .lpTitle = null, + .dwX = 0, + .dwY = 0, + .dwXSize = 0, + .dwYSize = 0, + .dwXCountChars = 0, + .dwYCountChars = 0, + .dwFillAttribute = 0, + .wShowWindow = 0, + .cbReserved2 = 0, + .lpReserved2 = null, + }; + var piProcInfo: windows.PROCESS_INFORMATION = undefined; + + const cwd_w = if (self.cwd) |cwd| try unicode.wtf8ToWtf16LeAllocZ(self.allocator, cwd) else null; + defer if (cwd_w) |cwd| self.allocator.free(cwd); + const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; + + const maybe_envp_buf = if (self.env_map) |env_map| try process.createWindowsEnvBlock(self.allocator, env_map) else null; + defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); + const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; + + const app_name_wtf8 = self.argv[0]; + const app_name_is_absolute = fs.path.isAbsolute(app_name_wtf8); + + var cwd_path_w_needs_free = false; + const cwd_path_w = x: { + if (app_name_is_absolute) { + cwd_path_w_needs_free = true; + const dir = fs.path.dirname(app_name_wtf8).?; + break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, dir); + } else if (self.cwd) |cwd| { + cwd_path_w_needs_free = true; + break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, cwd); + } else { + break :x &[_:0]u16{}; // empty for cwd + } + }; + defer if (cwd_path_w_needs_free) self.allocator.free(cwd_path_w); + + const app_basename_wtf8 = fs.path.basename(app_name_wtf8); + const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) fs.path.dirname(app_name_wtf8) else null; + const app_dirname_w: ?[:0]u16 = x: { + if (maybe_app_dirname_wtf8) |app_dirname_wtf8| { + break :x try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_dirname_wtf8); + } + break :x null; + }; + defer if (app_dirname_w != null) self.allocator.free(app_dirname_w.?); + + const app_name_w = try unicode.wtf8ToWtf16LeAllocZ(self.allocator, app_basename_wtf8); + defer self.allocator.free(app_name_w); + + run: { + const PATH: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{}; + const PATHEXT: [:0]const u16 = process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{}; + + var cmd_line_cache = CommandLineCache.init(self.allocator, self.argv); + defer cmd_line_cache.deinit(); + + var app_buf = std.ArrayListUnmanaged(u16){}; + defer app_buf.deinit(self.allocator); + + try app_buf.appendSlice(self.allocator, app_name_w); + + var dir_buf = std.ArrayListUnmanaged(u16){}; + defer dir_buf.deinit(self.allocator); + + if (cwd_path_w.len > 0) { + try dir_buf.appendSlice(self.allocator, cwd_path_w); + } + if (app_dirname_w) |app_dir| { + if (dir_buf.items.len > 0) try dir_buf.append(self.allocator, fs.path.sep); + try dir_buf.appendSlice(self.allocator, app_dir); + } + if (dir_buf.items.len > 0) { + const normalized_len = windows.normalizePath(u16, dir_buf.items) catch return error.BadPathName; + dir_buf.shrinkRetainingCapacity(normalized_len); + } + + createProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { + const original_err = switch (no_path_err) { + error.InvalidArg0 => return error.FileNotFound, + error.FileNotFound, error.InvalidExe, error.AccessDenied => |e| e, + error.UnrecoverableInvalidExe => return error.InvalidExe, + else => |e| return e, + }; + + if (app_dirname_w != null or app_name_is_absolute) { + return original_err; + } + + var it = mem.tokenizeScalar(u16, PATH, ';'); + while (it.next()) |search_path| { + dir_buf.clearRetainingCapacity(); + try dir_buf.appendSlice(self.allocator, search_path); + const normalized_len = windows.normalizePath(u16, dir_buf.items) catch continue; + dir_buf.shrinkRetainingCapacity(normalized_len); + + if (createProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) { + break :run; + } else |err| switch (err) { + error.InvalidArg0 => return error.FileNotFound, + error.FileNotFound, error.AccessDenied, error.InvalidExe => continue, + error.UnrecoverableInvalidExe => return error.InvalidExe, + else => |e| return e, + } + } else { + return original_err; + } + }; + } + + if (g_hChildStd_IN_Wr) |h| { + self.stdin = File{ .handle = h }; + } else { + self.stdin = null; + } + if (g_hChildStd_OUT_Rd) |h| { + self.stdout = File{ .handle = h }; + } else { + self.stdout = null; + } + if (g_hChildStd_ERR_Rd) |h| { + self.stderr = File{ .handle = h }; + } else { + self.stderr = null; + } + + self.id = piProcInfo.hProcess; + self.thread_handle = piProcInfo.hThread; + self.term = null; + + if (self.stdin_behavior == StdIo.Pipe) { + posix.close(g_hChildStd_IN_Rd.?); + } + if (self.stderr_behavior == StdIo.Pipe) { + posix.close(g_hChildStd_ERR_Wr.?); + } + if (self.stdout_behavior == StdIo.Pipe) { + posix.close(g_hChildStd_OUT_Wr.?); + } + } + + fn makePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { + var rd_h: windows.HANDLE = undefined; + var wr_h: windows.HANDLE = undefined; + try windows.CreatePipe(&rd_h, &wr_h, sattr); + errdefer destroyPipe(rd_h, wr_h); + try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0); + rd.* = rd_h; + wr.* = wr_h; + } + + fn destroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { + if (rd) |h| posix.close(h); + if (wr) |h| posix.close(h); + } + + var pipe_name_counter = std.atomic.Value(u32).init(1); + + fn makeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { + var tmp_bufw: [128]u16 = undefined; + + const pipe_path = blk: { + var tmp_buf: [128]u8 = undefined; + const pipe_path = std.fmt.bufPrintZ( + &tmp_buf, + "\\\\.\\pipe\\zig-childprocess-{d}-{d}", + .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) }, + ) catch unreachable; + const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable; + tmp_bufw[len] = 0; + break :blk tmp_bufw[0..len :0]; + }; + + const read_handle = windows.kernel32.CreateNamedPipeW( + pipe_path.ptr, + windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED, + windows.PIPE_TYPE_BYTE, + 1, + 4096, + 4096, + 0, + sattr, + ); + if (read_handle == windows.INVALID_HANDLE_VALUE) { + switch (windows.kernel32.GetLastError()) { + else => |err| return windows.unexpectedError(err), + } + } + errdefer posix.close(read_handle); + + var sattr_copy = sattr.*; + const write_handle = windows.kernel32.CreateFileW( + pipe_path.ptr, + windows.GENERIC_WRITE, + 0, + &sattr_copy, + windows.OPEN_EXISTING, + windows.FILE_ATTRIBUTE_NORMAL, + null, + ); + if (write_handle == windows.INVALID_HANDLE_VALUE) { + switch (windows.kernel32.GetLastError()) { + else => |err| return windows.unexpectedError(err), + } + } + errdefer posix.close(write_handle); + + try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0); + + rd.* = read_handle; + wr.* = write_handle; + } + + const CommandLineCache = struct { + cmd_line: ?[:0]u16 = null, + script_cmd_line: ?[:0]u16 = null, + cmd_exe_path: ?[:0]u16 = null, + argv: []const []const u8, + allocator: mem.Allocator, + + fn init(allocator: mem.Allocator, argv: []const []const u8) CommandLineCache { + return .{ + .allocator = allocator, + .argv = argv, + }; + } + + fn deinit(self: *CommandLineCache) void { + if (self.cmd_line) |cmd_line| self.allocator.free(cmd_line); + if (self.script_cmd_line) |script_cmd_line| self.allocator.free(script_cmd_line); + if (self.cmd_exe_path) |cmd_exe_path| self.allocator.free(cmd_exe_path); + } + + fn commandLine(self: *CommandLineCache) ![:0]u16 { + if (self.cmd_line == null) { + self.cmd_line = try argvToCommandLine(self.allocator, self.argv); + } + return self.cmd_line.?; + } + + fn scriptCommandLine(self: *CommandLineCache, script_path: []const u16) ![:0]u16 { + if (self.script_cmd_line) |v| self.allocator.free(v); + self.script_cmd_line = try argvToScriptCommandLine( + self.allocator, + script_path, + self.argv[1..], + ); + return self.script_cmd_line.?; + } + + fn cmdExePath(self: *CommandLineCache) ![:0]u16 { + if (self.cmd_exe_path == null) { + self.cmd_exe_path = try wcdExePath(self.allocator); + } + return self.cmd_exe_path.?; + } + }; + + fn createProcessPathExt( + allocator: mem.Allocator, + dir_buf: *std.ArrayListUnmanaged(u16), + app_buf: *std.ArrayListUnmanaged(u16), + pathext: [:0]const u16, + cmd_line_cache: *CommandLineCache, + envp_ptr: ?[*]u16, + cwd_ptr: ?[*:0]u16, + lpStartupInfo: *windows.STARTUPINFOW, + lpProcessInformation: *windows.PROCESS_INFORMATION, + ) !void { + const app_name_len = app_buf.items.len; + const dir_path_len = dir_buf.items.len; + + if (app_name_len == 0) return error.FileNotFound; + + defer app_buf.shrinkRetainingCapacity(app_name_len); + defer dir_buf.shrinkRetainingCapacity(dir_path_len); + + var dir = dir: { + try dir_buf.append(allocator, 0); + defer dir_buf.shrinkRetainingCapacity(dir_path_len); + const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; + const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z); + break :dir fs.cwd().openDirW(prefixed_path.span().ptr, .{ .iterate = true }) catch + return error.FileNotFound; + }; + defer dir.close(); + + try app_buf.append(allocator, '*'); + try app_buf.append(allocator, 0); + const app_name_wildcard = app_buf.items[0 .. app_buf.items.len - 1 :0]; + + var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined; + const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2); + if (file_information_buf.len < file_info_maximum_single_entry_size) { + @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry"); + } + var io_status: windows.IO_STATUS_BLOCK = undefined; + + const num_supported_pathext = @typeInfo(CreateProcessSupportedExtension).Enum.fields.len; + var pathext_seen = [_]bool{false} ** num_supported_pathext; + var any_pathext_seen = false; + var unappended_exists = false; + + while (true) { + const app_name_len_bytes = std.math.cast(u16, app_name_wildcard.len * 2) orelse return error.NameTooLong; + var app_name_unicode_string = windows.UNICODE_STRING{ + .Length = app_name_len_bytes, + .MaximumLength = app_name_len_bytes, + .Buffer = @constCast(app_name_wildcard.ptr), + }; + const rc = windows.ntdll.NtQueryDirectoryFile( + dir.fd, + null, + null, + null, + &io_status, + &file_information_buf, + file_information_buf.len, + .FileDirectoryInformation, + windows.FALSE, // single result + &app_name_unicode_string, + windows.FALSE, // restart iteration + ); + + switch (rc) { + .SUCCESS => {}, + .NO_SUCH_FILE => return error.FileNotFound, + .NO_MORE_FILES => break, + .ACCESS_DENIED => return error.AccessDenied, + else => return windows.unexpectedStatus(rc), + } + + std.debug.assert(io_status.Information != 0); + + var it = windows.FileInformationIterator(windows.FILE_DIRECTORY_INFORMATION){ .buf = &file_information_buf }; + while (it.next()) |info| { + if (info.FileAttributes & windows.FILE_ATTRIBUTE_DIRECTORY != 0) continue; + const filename = @as([*]u16, @ptrCast(&info.FileName))[0 .. info.FileNameLength / 2]; + if (filename.len == app_name_len) { + unappended_exists = true; + } else if (createProcessSupportsExtension(filename[app_name_len..])) |pathext_ext| { + pathext_seen[@intFromEnum(pathext_ext)] = true; + any_pathext_seen = true; + } + } + } + + const unappended_err = unappended: { + if (unappended_exists) { + if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) { + '/', '\\' => {}, + else => try dir_buf.append(allocator, fs.path.sep), + }; + try dir_buf.appendSlice(allocator, app_buf.items[0..app_name_len]); + try dir_buf.append(allocator, 0); + const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; + + const is_bat_or_cmd = bat_or_cmd: { + const app_name = app_buf.items[0..app_name_len]; + const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :bat_or_cmd false; + const ext = app_name[ext_start..]; + const ext_enum = createProcessSupportsExtension(ext) orelse break :bat_or_cmd false; + switch (ext_enum) { + .cmd, .bat => break :bat_or_cmd true, + else => break :bat_or_cmd false, + } + }; + const cmd_line_w = if (is_bat_or_cmd) + try cmd_line_cache.scriptCommandLine(full_app_name) + else + try cmd_line_cache.commandLine(); + const app_name_w = if (is_bat_or_cmd) + try cmd_line_cache.cmdExePath() + else + full_app_name; + + if (createProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, lpStartupInfo, lpProcessInformation)) |_| { + return; + } else |err| switch (err) { + error.FileNotFound, + error.AccessDenied, + => break :unappended err, + error.InvalidExe => { + const app_name = app_buf.items[0..app_name_len]; + const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :unappended err; + const ext = app_name[ext_start..]; + if (windows.eqlIgnoreCaseWTF16(ext, unicode.utf8ToUtf16LeStringLiteral(".EXE"))) { + return error.UnrecoverableInvalidExe; + } + break :unappended err; + }, + else => return err, + } + } + break :unappended error.FileNotFound; + }; + + if (!any_pathext_seen) return unappended_err; + + var ext_it = mem.tokenizeScalar(u16, pathext, ';'); + while (ext_it.next()) |ext| { + const ext_enum = createProcessSupportsExtension(ext) orelse continue; + if (!pathext_seen[@intFromEnum(ext_enum)]) continue; + + dir_buf.shrinkRetainingCapacity(dir_path_len); + if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) { + '/', '\\' => {}, + else => try dir_buf.append(allocator, fs.path.sep), + }; + try dir_buf.appendSlice(allocator, app_buf.items[0..app_name_len]); + try dir_buf.appendSlice(allocator, ext); + try dir_buf.append(allocator, 0); + const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0]; + + const is_bat_or_cmd = switch (ext_enum) { + .cmd, .bat => true, + else => false, + }; + const cmd_line_w = if (is_bat_or_cmd) + try cmd_line_cache.scriptCommandLine(full_app_name) + else + try cmd_line_cache.commandLine(); + const app_name_w = if (is_bat_or_cmd) + try cmd_line_cache.cmdExePath() + else + full_app_name; + + if (createProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, lpStartupInfo, lpProcessInformation)) |_| { + return; + } else |err| switch (err) { + error.FileNotFound => continue, + error.AccessDenied => continue, + error.InvalidExe => { + if (windows.eqlIgnoreCaseWTF16(ext, unicode.utf8ToUtf16LeStringLiteral(".EXE"))) { + return error.UnrecoverableInvalidExe; + } + continue; + }, + else => return err, + } + } + + return unappended_err; + } + + fn argvToCommandLine( + allocator: mem.Allocator, + argv: []const []const u8, + ) ![:0]u16 { + var buf = std.ArrayList(u8).init(allocator); + defer buf.deinit(); + + if (argv.len != 0) { + const arg0 = argv[0]; + + var needs_quotes = arg0.len == 0; + for (arg0) |c| { + if (c <= ' ') { + needs_quotes = true; + } else if (c == '"') { + return error.InvalidArg0; + } + } + if (needs_quotes) { + try buf.append('"'); + try buf.appendSlice(arg0); + try buf.append('"'); + } else { + try buf.appendSlice(arg0); + } + + for (argv[1..]) |arg| { + try buf.append(' '); + + needs_quotes = for (arg) |c| { + if (c <= ' ' or c == '"') { + break true; + } + } else arg.len == 0; + if (!needs_quotes) { + try buf.appendSlice(arg); + continue; + } + + try buf.append('"'); + var backslash_count: usize = 0; + for (arg) |byte| { + switch (byte) { + '\\' => { + backslash_count += 1; + }, + '"' => { + try buf.appendNTimes('\\', backslash_count * 2 + 1); + try buf.append('"'); + backslash_count = 0; + }, + else => { + try buf.appendNTimes('\\', backslash_count); + try buf.append(byte); + backslash_count = 0; + }, + } + } + try buf.appendNTimes('\\', backslash_count * 2); + try buf.append('"'); + } + } + + return try unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items); + } + + fn argvToScriptCommandLine( + allocator: mem.Allocator, + script_path: []const u16, + script_args: []const []const u8, + ) ![:0]u16 { + var buf = try std.ArrayList(u8).initCapacity(allocator, 64); + defer buf.deinit(); + + buf.appendSliceAssumeCapacity("cmd.exe /d /e:ON /v:OFF /c \""); + + buf.appendAssumeCapacity('"'); + if (mem.indexOfAny(u16, script_path, &[_]u16{ mem.nativeToLittle(u16, '\\'), mem.nativeToLittle(u16, '/') }) == null) { + try buf.appendSlice(".\\"); + } + try unicode.wtf16LeToWtf8ArrayList(&buf, script_path); + buf.appendAssumeCapacity('"'); + + for (script_args) |arg| { + if (std.mem.indexOfAny(u8, arg, "\x00\r\n") != null) { + return error.InvalidBatchScriptArg; + } + + try buf.append(' '); + + var needs_quotes = arg.len == 0 or arg[arg.len - 1] == '\\'; + if (!needs_quotes) { + for (arg) |c| { + switch (c) { + 'A'...'Z', 'a'...'z', '0'...'9', '#', '$', '*', '+', '-', '.', '/', ':', '?', '@', '\\', '_' => {}, + else => { + needs_quotes = true; + break; + }, + } + } + } + if (needs_quotes) { + try buf.append('"'); + } + var backslashes: usize = 0; + for (arg) |c| { + switch (c) { + '\\' => { + backslashes += 1; + }, + '"' => { + try buf.appendNTimes('\\', backslashes); + try buf.append('"'); + backslashes = 0; + }, + '%' => { + try buf.appendSlice("%%cd:~,"); + backslashes = 0; + }, + else => { + backslashes = 0; + }, + } + try buf.append(c); + } + if (needs_quotes) { + try buf.appendNTimes('\\', backslashes); + try buf.append('"'); + } + } + + try buf.append('"'); + + return try unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items); + } + + fn wcdExePath(allocator: mem.Allocator) error{ OutOfMemory, Unexpected }![:0]u16 { + var buf = try std.ArrayListUnmanaged(u16).initCapacity(allocator, 128); + errdefer buf.deinit(allocator); + while (true) { + const unused_slice = buf.unusedCapacitySlice(); + const len = windows.kernel32.GetSystemDirectoryW(@ptrCast(unused_slice), @intCast(unused_slice.len)); + if (len == 0) { + switch (windows.kernel32.GetLastError()) { + else => |err| return windows.unexpectedError(err), + } + } + if (len > unused_slice.len) { + try buf.ensureUnusedCapacity(allocator, len); + } else { + buf.items.len = len; + break; + } + } + switch (buf.items[buf.items.len - 1]) { + '/', '\\' => {}, + else => try buf.append(allocator, fs.path.sep), + } + try buf.appendSlice(allocator, unicode.utf8ToUtf16LeStringLiteral("cmd.exe")); + return try buf.toOwnedSliceSentinel(allocator, 0); + } + + const CreateProcessSupportedExtension = enum { + bat, + cmd, + com, + exe, + }; + + fn createProcessSupportsExtension(ext: []const u16) ?CreateProcessSupportedExtension { + if (ext.len != 4) return null; + const State = enum { + start, + dot, + b, + ba, + c, + cm, + co, + e, + ex, + }; + var state: State = .start; + for (ext) |c| switch (state) { + .start => switch (c) { + '.' => state = .dot, + else => return null, + }, + .dot => switch (c) { + 'b', 'B' => state = .b, + 'c', 'C' => state = .c, + 'e', 'E' => state = .e, + else => return null, + }, + .b => switch (c) { + 'a', 'A' => state = .ba, + else => return null, + }, + .c => switch (c) { + 'm', 'M' => state = .cm, + 'o', 'O' => state = .co, + else => return null, + }, + .e => switch (c) { + 'x', 'X' => state = .ex, + else => return null, + }, + .ba => switch (c) { + 't', 'T' => return .bat, + else => return null, + }, + .cm => switch (c) { + 'd', 'D' => return .cmd, + else => return null, + }, + .co => switch (c) { + 'm', 'M' => return .com, + else => return null, + }, + .ex => switch (c) { + 'e', 'E' => return .exe, + else => return null, + }, + }; + return null; + } + + pub const CREATE_NO_WINDOW = 0x08000000; + + fn createProcess( + app_name: [*:0]u16, + cmd_line: [*:0]u16, + envp_ptr: ?[*]u16, + cwd_ptr: ?[*:0]u16, + lpStartupInfo: *windows.STARTUPINFOW, + lpProcessInformation: *windows.PROCESS_INFORMATION, + ) !void { + return windows.CreateProcessW( + app_name, + cmd_line, + null, + null, + windows.TRUE, + windows.CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, + @as(?*anyopaque, @ptrCast(envp_ptr)), + cwd_ptr, + lpStartupInfo, + lpProcessInformation, + ); + } + + pub fn wait(self: *@This()) !Term { + if (self.term) |term_| { + self.cleanupStreams(); + return term_; + } + + defer self.id = undefined; + try self.waitUnwrapped(); + return self.term.?; + } + + fn waitUnwrapped(self: *@This()) !void { + const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false); + + self.term = @as(SpawnError!Term, x: { + var exit_code: windows.DWORD = undefined; + if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) { + break :x Term{ .Unknown = 0 }; + } else { + break :x Term{ .Exited = @as(u8, @truncate(exit_code)) }; + } + }); + + posix.close(self.id); + posix.close(self.thread_handle); + self.cleanupStreams(); + return result; + } + + fn cleanupStreams(self: *@This()) void { + if (self.stdin) |*stdin| { + stdin.close(); + self.stdin = null; + } + if (self.stdout) |*stdout| { + stdout.close(); + self.stdout = null; + } + if (self.stderr) |*stderr| { + stderr.close(); + self.stderr = null; + } + } + + pub fn kill(self: *@This()) !Term { + if (self.term) |term_| { + self.cleanupStreams(); + return term_; + } + + windows.TerminateProcess(self.id, 1) catch |err| switch (err) { + error.PermissionDenied => { + windows.WaitForSingleObjectEx(self.id, 0, false) catch return err; + return error.AlreadyTerminated; + }, + else => return err, + }; + try self.waitUnwrapped(); + return self.term.?; + } +}; diff --git a/src/thespian.zig b/src/thespian.zig index b9a3731..8db8d9c 100644 --- a/src/thespian.zig +++ b/src/thespian.zig @@ -207,6 +207,10 @@ pub const message = struct { pub fn match(self: Self, m: anytype) error{Exit}!bool { return if (cbor.match(self.buf, m)) |ret| ret else |e| exit_error(e, @errorReturnTrace()); } + + pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + return cbor.toJsonWriter(self.buf, writer, .{}); + } }; pub fn exit_message(e: anytype, stack_trace: ?*std.builtin.StackTrace) message { @@ -332,6 +336,7 @@ pub const channel = struct { pub const event: c.thespian_trace_channel = 2048; pub const widget: c.thespian_trace_channel = 4096; pub const input: c.thespian_trace_channel = 8192; + pub const debug: c.thespian_trace_channel = 16384; pub const all = c.thespian_trace_channel_all; }; @@ -423,7 +428,8 @@ pub fn trace(chan: trace_channel, value: anytype) void { env.get().trace(value.to(message.c_buffer_type)); } else { var trace_buffer: [512]u8 = undefined; - const m = message.fmtbuf(&trace_buffer, value); + const m = message.fmtbuf(&trace_buffer, value) catch |e| + std.debug.panic("TRACE ERROR: {}", .{e}); env.get().trace(m.to(message.c_buffer_type)); } } diff --git a/src/trace.cpp b/src/trace.cpp index bc187db..dac8a86 100644 --- a/src/trace.cpp +++ b/src/trace.cpp @@ -91,6 +91,7 @@ template struct trace_file { } }; +namespace { auto to_mermaid(ostream &s, const buffer &m) -> void { string_view from; string_view typ; @@ -124,6 +125,7 @@ auto to_mermaid(ostream &s, const buffer &m) -> void { s << " Note right of " << from << ": EXIT " << msg << '\n'; } } +} // namespace static const auto cbor_cr = array("\n"); diff --git a/test/cbor_match.cpp b/test/cbor_match.cpp index c6a0289..40326b3 100644 --- a/test/cbor_match.cpp +++ b/test/cbor_match.cpp @@ -3,7 +3,6 @@ #include #include -#include #include using cbor::A; diff --git a/test/debug.cpp b/test/debug.cpp index 36563fe..971bb7a 100644 --- a/test/debug.cpp +++ b/test/debug.cpp @@ -11,8 +11,8 @@ #include #if defined(_WIN32) -#include #include +#include #include #include #endif @@ -60,9 +60,10 @@ struct debuggee { static auto start() -> expected { return spawn_link( [=]() { - ::receive([p{make_shared()}](auto from, auto m) { - return p->receive(move(from), move(m)); - }); + ::receive( + [p{make_shared()}](const auto &from, const auto &m) { + return p->receive(from, m); + }); return ok(); }, "debuggee"); @@ -188,9 +189,9 @@ auto debug(context &ctx, bool &result, env_t env_) -> ::result { ret2 = debuggee.send("ping"); if (not ret2) return ret2; - receive([p{make_shared(debug_tcp, debuggee)}](auto from, - auto m) { - return p->receive(move(from), move(m)); + receive([p{make_shared(debug_tcp, debuggee)}]( + const auto &from, const auto &m) { + return p->receive(from, m); }); return ok(); }, diff --git a/test/endpoint_tcp.cpp b/test/endpoint_tcp.cpp index 593372d..308a06f 100644 --- a/test/endpoint_tcp.cpp +++ b/test/endpoint_tcp.cpp @@ -12,8 +12,8 @@ #include #if defined(_WIN32) -#include #include +#include #include #include #endif @@ -86,8 +86,9 @@ auto endpoint_tcp(context &ctx, bool &result, env_t env_) -> ::result { if (not ret) return ret; - receive([p{make_shared(ep_listen)}](auto from, auto m) { - return p->receive(move(from), move(m)); + receive([p{make_shared(ep_listen)}](const auto &from, + const auto &m) { + return p->receive(from, m); }); return ok(); }, diff --git a/test/endpoint_unx.cpp b/test/endpoint_unx.cpp index da29011..0a01cdd 100644 --- a/test/endpoint_unx.cpp +++ b/test/endpoint_unx.cpp @@ -91,8 +91,9 @@ auto endpoint_unx(context &ctx, bool &result, env_t env_) -> ::result { [path]() { link(env().proc("log")); handle ep_listen = listen(path).value(); - receive([p{make_shared(ep_listen)}](auto from, auto m) { - return p->receive(move(from), move(m)); + receive([p{make_shared(ep_listen)}](const auto &from, + const auto &m) { + return p->receive(from, m); }); return ok(); }, diff --git a/test/hub_filter.cpp b/test/hub_filter.cpp index a68431d..e875696 100644 --- a/test/hub_filter.cpp +++ b/test/hub_filter.cpp @@ -33,7 +33,7 @@ auto sub_plain(const hub &h, const handle &controller, string name) -> result { ret = controller.send("ready", name); if (not ret) return ret; - receive([=](auto /*from*/, auto m) { + receive([=](const auto & /*from*/, const auto &m) { string_view cmd; check(m("parmA", "parmB", "parmC", extract(cmd))); check(cmd == "continue" || cmd == "done"); @@ -50,13 +50,13 @@ auto sub_plain(const hub &h, const handle &controller, string name) -> result { auto sub_filtered(const hub &h, const handle &controller, string name) -> result { auto ret = h.subscribe( - [](auto m) { return m(type::any, type::any, type::any, "done"); }); + [](const auto &m) { return m(type::any, type::any, type::any, "done"); }); if (not ret) return ret; ret = controller.send("ready", name); if (not ret) return ret; - receive([=](auto /*from*/, auto m) { + receive([=](const auto & /*from*/, const auto &m) { check(m("parmA", "parmB", "parmC", "done")); auto ret = controller.send("done", name); if (not ret) @@ -66,7 +66,7 @@ auto sub_filtered(const hub &h, const handle &controller, string name) return ok(); } -mapresult> +mapresult> subscription_defs // NOLINT = { {"sub_plain", sub_plain}, @@ -95,7 +95,7 @@ auto controller() -> result { submap not_ready = subs; submap done = subs; - receive([=](auto, auto m) mutable { + receive([=](const auto &, const auto &m) mutable { string name; if (m("ready", extract(name))) { not_ready.erase(name); diff --git a/test/ip_tcp_client_server.cpp b/test/ip_tcp_client_server.cpp index 3cc630f..766df78 100644 --- a/test/ip_tcp_client_server.cpp +++ b/test/ip_tcp_client_server.cpp @@ -10,8 +10,8 @@ #if !defined(_WIN32) #include #else -#include #include +#include #include #include #endif @@ -80,7 +80,9 @@ struct client_connection { [fd, connector]() { ::thespian::receive( [p{make_shared(fd, connector)}]( - auto /*from*/, auto m) { return p->receive(move(m)); }); + const auto & /*from*/, const auto &m) { + return p->receive(m); + }); return ok(); }, "client_connection"); @@ -111,9 +113,8 @@ struct client { static auto init(handle server, port_t server_port) -> result { ::thespian::receive( - [p{make_shared(server, server_port)}](auto /*from*/, auto m) { - return p->receive(move(m)); - }); + [p{make_shared(server, server_port)}]( + const auto & /*from*/, const auto &m) { return p->receive(m); }); return ok(); } }; @@ -155,9 +156,10 @@ struct server_connection { [[nodiscard]] static auto init(int fd, const handle &server) { return spawn_link( [fd, server]() { - ::thespian::receive( - [p{make_shared(fd, server)}]( - auto /*from*/, auto m) { return p->receive(move(m)); }); + ::thespian::receive([p{make_shared(fd, server)}]( + const auto & /*from*/, const auto &m) { + return p->receive(m); + }); return ok(); }, "server_connection"); @@ -210,7 +212,7 @@ struct server { if (not ret) return to_result(ret); thespian::receive( - [p](auto /*from*/, auto m) { return p->receive(move(m)); }); + [p](const auto & /*from*/, const auto &m) { return p->receive(m); }); return ok(); } }; diff --git a/test/ip_udp_echo.cpp b/test/ip_udp_echo.cpp index 65264b0..4174a05 100644 --- a/test/ip_udp_echo.cpp +++ b/test/ip_udp_echo.cpp @@ -9,8 +9,8 @@ #if !defined(_WIN32) #include #else -#include #include +#include #include #include #endif @@ -116,8 +116,9 @@ auto ip_udp_echo(context &ctx, bool &result, env_t env_) -> ::result { return to_result(ctx.spawn_link( [=]() { link(env().proc("log")); - receive([p{make_shared()}](auto /*from*/, auto m) { - return p->receive(move(m)); + receive([p{make_shared()}](const auto & /*from*/, + const auto &m) { + return p->receive(m); }); return ok(); }, diff --git a/test/metronome_test.cpp b/test/metronome_test.cpp index 4be00f8..6a7ef1a 100644 --- a/test/metronome_test.cpp +++ b/test/metronome_test.cpp @@ -6,7 +6,6 @@ #include #include -#include using cbor::buffer; using std::move; @@ -64,7 +63,9 @@ auto initA() { } }; shared_ptr state{new state_t}; - receive([state](auto, auto m) mutable { return state->receive(m); }); + receive([state](const auto &, const auto &m) mutable { + return state->receive(m); + }); return ok(); } diff --git a/test/perf_hub.cpp b/test/perf_hub.cpp index 445224a..39a29bc 100644 --- a/test/perf_hub.cpp +++ b/test/perf_hub.cpp @@ -35,7 +35,7 @@ using clk = std::chrono::system_clock; namespace { -enum class tag { +enum class tag : uint8_t { ready = 1, done = 2, }; diff --git a/test/perf_ring.cpp b/test/perf_ring.cpp index 3c5ddba..4af5778 100644 --- a/test/perf_ring.cpp +++ b/test/perf_ring.cpp @@ -28,8 +28,8 @@ auto slave(const handle &last, int n) -> result { if (n) next = spawn_link([=]() { return slave(last, n - 1); }, "slave").value(); - receive([n, next, last](auto, auto m) { - return n ? next.send_raw(move(m)) : last.send_raw(move(m)); + receive([n, next, last](const auto &, const auto &m) { + return n ? next.send_raw(m) : last.send_raw(m); }); return ok(); } @@ -41,11 +41,11 @@ auto controller(const int slaves) -> result { auto ret = spawn_link([=]() { return slave(last, slaves); }, "slave"); if (not ret) return to_result(ret); - handle first = ret.value(); + const handle &first = ret.value(); auto ret2 = first.send("forward"); if (not ret2) return ret2; - receive([loop, first, verbose](auto, auto m) mutable { + receive([loop, first, verbose](const auto &, const auto &m) mutable { if (loop) { if (verbose) auto _ = env().proc("log").send(loop); diff --git a/test/perf_spawn.cpp b/test/perf_spawn.cpp index 5f04a8e..3e4a8fb 100644 --- a/test/perf_spawn.cpp +++ b/test/perf_spawn.cpp @@ -3,8 +3,6 @@ #include "thespian/env.hpp" #include -#include - using cbor::extract; using std::move; using thespian::context; @@ -21,7 +19,7 @@ using thespian::unexpected; namespace { auto slave(const int slaves, int spawned) -> result { - receive([slaves, spawned](auto, auto m) mutable { + receive([slaves, spawned](const auto &, const auto &m) mutable { int n{0}; if (!m(extract(n))) return unexpected(m); @@ -50,7 +48,7 @@ auto controller(const int slaves) -> result { auto ret2 = ret.value().send(slaves - 1); if (not ret2) return ret2; - receive([](auto, auto) { return exit(); }); + receive([](const auto &, const auto &) { return exit(); }); return ok(); } } // namespace diff --git a/test/spawn_exit.cpp b/test/spawn_exit.cpp index 0e26a78..2bb7c3a 100644 --- a/test/spawn_exit.cpp +++ b/test/spawn_exit.cpp @@ -34,7 +34,7 @@ auto initE() { auto ret = env().proc("A").send("shutdown"); if (not ret) return ret; - receive([](auto, auto) { return ok(); }); + receive([](const auto &, const auto &) { return ok(); }); return ok(); } @@ -42,7 +42,7 @@ auto initD() { auto ret = spawn(initE, "E"); if (not ret) return to_result(ret); - receive([](auto, auto m) { + receive([](const auto &, const auto &m) { if (m("die")) return exit("died"); return unexpected(m); @@ -56,7 +56,7 @@ auto initC() { if (not ret) return to_result(ret); trap(true); - receive([](auto /*from*/, auto m) { + receive([](const auto & /*from*/, const auto &m) { if (m("exit", "died")) return exit(); return unexpected(m); @@ -66,7 +66,7 @@ auto initC() { auto initB() { env().str("initBsays") = "noyoudont"; - receive([](auto from, auto m) { + receive([](const auto &from, const auto &m) { if (m("shutdown")) { auto ret = from.send("done"); if (not ret) @@ -87,7 +87,7 @@ auto initA() { if (not ret) return ret; spawn_link(initC, "C").value(); - receive([i{0}](auto, auto m) mutable { + receive([i{0}](const auto &, const auto &m) mutable { if (m("shutdown") || m("done")) ++i; else diff --git a/test/tests.cpp b/test/tests.cpp index b37b873..aba9ea0 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -67,7 +67,7 @@ struct logger { [name, &trace_m, verbose]() { thespian::receive( [p{make_shared(name, trace_m, verbose)}]( - auto, auto m) { return p->receive(move(m)); }); + const auto &, const auto &m) { return p->receive(m); }); return ok(); }, string("logger_") + name, move(env)) diff --git a/test/tests.h b/test/tests.h index ec7147a..dee6414 100644 --- a/test/tests.h +++ b/test/tests.h @@ -1,2 +1 @@ int runtestcase(const char *name); // NOLINT - diff --git a/test/tests.hpp b/test/tests.hpp index 7a79741..4cf3e4b 100644 --- a/test/tests.hpp +++ b/test/tests.hpp @@ -4,7 +4,6 @@ #include #include -#include constexpr auto check(bool expression) -> void { if (!expression) @@ -12,7 +11,7 @@ constexpr auto check(bool expression) -> void { } using testcase = auto(thespian::context &ctx, bool &result, thespian::env_t env) - -> thespian::result; + -> thespian::result; testcase cbor_match; testcase debug; diff --git a/test/timeout_test.cpp b/test/timeout_test.cpp index 038b9a5..80dd083 100644 --- a/test/timeout_test.cpp +++ b/test/timeout_test.cpp @@ -7,7 +7,6 @@ #include #include -#include using cbor::array; using cbor::buffer; @@ -47,7 +46,9 @@ auto initA() -> result { } }; shared_ptr state{new state_t}; - receive([state](auto, auto m) mutable { return state->receive(m); }); + receive([state](const auto &, const auto &m) mutable { + return state->receive(m); + }); return ok(); }