Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion host/include/librmcs/agent/common.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,89 @@
#pragma once

#include <functional>
#include <type_traits>
#include <utility>

namespace librmcs::agent {

struct AdvancedOptions {
/**
* @brief Advanced transport options passed during agent construction.
*
* `bind_advanced_options()` returns an object derived from `AdvancedOptions` whose `thread_setup`
* callback depends on state stored in that derived object. Copying or moving the base type would
* slice away that state while retaining a now-dangling function pointer, so `AdvancedOptions` is
* intentionally non-copyable and non-movable.
*
* Construct `AdvancedOptions` directly during agent construction, or explicitly copy the required
* plain data fields into a fresh `AdvancedOptions` instance instead of copying an existing object.
*
* @warning Never copy, assign, or reuse any function pointer from an object returned by
* `bind_advanced_options()` in any other `AdvancedOptions` instance; doing so is undefined
* behavior.
*/
class AdvancedOptions {
public:
AdvancedOptions() = default;

AdvancedOptions(const AdvancedOptions&) = delete;
AdvancedOptions& operator=(const AdvancedOptions&) = delete;
AdvancedOptions(AdvancedOptions&&) = delete;
AdvancedOptions& operator=(AdvancedOptions&&) = delete;

~AdvancedOptions() = default;

bool dangerously_skip_version_checks = false;

/**
* @brief Callback invoked on the transport event thread before transport I/O handling begins.
*
* This hook is intended only for per-thread environment setup, such as thread priority,
* CPU affinity, thread naming, or other OS-level thread configuration.
*
* @warning This callback runs during transport construction, before `Handler` and the
* enclosing agent object finish construction.
* @warning The callback must not access the agent object being constructed, any of its
* members, or any transport/protocol APIs. In particular, do not capture and use `this`
* from the constructing agent object.
*/
void (*thread_setup)(const AdvancedOptions&) noexcept = nullptr;

AdvancedOptions& set_dangerously_skip_version_checks(bool value) {
dangerously_skip_version_checks = value;
return *this;
}

AdvancedOptions& set_thread_setup(void (*value)(const AdvancedOptions&) noexcept) {
thread_setup = value;
return *this;
}
};

/**
* @brief Binds callable to `AdvancedOptions`.
*
* @warning The returned object must outlive any use of its function pointers.
* @warning Do not store, copy, move, or slice the returned object as `AdvancedOptions`.
*/
template <typename FunctorT>
requires std::is_nothrow_invocable_v<const std::decay_t<FunctorT>&>
auto bind_advanced_options(FunctorT&& thread_setup_impl) {
using Functor = std::decay_t<FunctorT>;

class OptionsImpl : public AdvancedOptions {
public:
explicit OptionsImpl(Functor thread_setup_impl)
: thread_setup_impl_(std::move(thread_setup_impl)) {
thread_setup = [](const AdvancedOptions& self) noexcept {
std::invoke(static_cast<const OptionsImpl&>(self).thread_setup_impl_);
};
}

private:
Functor thread_setup_impl_;
};

return OptionsImpl{std::forward<FunctorT>(thread_setup_impl)};
}

} // namespace librmcs::agent
7 changes: 1 addition & 6 deletions host/src/protocol/handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,7 @@ Handler::Handler(
uint16_t usb_vid, int32_t usb_pid, std::string_view serial_filter,
const agent::AdvancedOptions& options, data::DataCallback& callback)
: impl_(new Impl(
transport::usb::create_transport(
usb_vid, usb_pid, serial_filter,
transport::usb::ConnectionOptions{
.dangerously_skip_version_checks = options.dangerously_skip_version_checks,
}),
callback)) {}
transport::usb::create_transport(usb_vid, usb_pid, serial_filter, options), callback)) {}

Handler::Handler(Handler&& other) noexcept
: impl_(std::exchange(other.impl_, nullptr)) {}
Expand Down
5 changes: 2 additions & 3 deletions host/src/transport/transport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <string_view>

#include "core/src/protocol/constant.hpp"
#include "librmcs/agent/common.hpp"

namespace librmcs::host::transport {

Expand Down Expand Up @@ -140,9 +141,7 @@ class Transport {

namespace usb {

struct ConnectionOptions {
bool dangerously_skip_version_checks = false;
};
using ConnectionOptions = agent::AdvancedOptions;

std::unique_ptr<Transport> create_transport(
uint16_t usb_vid, int32_t usb_pid, std::string_view serial_filter,
Expand Down
15 changes: 14 additions & 1 deletion host/src/transport/usb/usb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,20 @@ class Usb : public Transport {
}};

init_transmit_transfers();
event_thread_ = std::thread{[this]() { handle_events(); }};

if (options.thread_setup) {
std::atomic<bool> thread_setup_done{false};
event_thread_ = std::thread{[this, &options, &thread_setup_done]() {
options.thread_setup(options);
thread_setup_done.store(true, std::memory_order_release);
thread_setup_done.notify_one();
handle_events();
}};
// Wait until thread_setup returns, so any bound options state remains alive.
thread_setup_done.wait(false, std::memory_order_acquire);
} else {
event_thread_ = std::thread{[this]() { handle_events(); }};
}

rollback_on_failure.disable();
}
Expand Down
Loading