Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #pragma once
18 : : #include <boost/asio/any_io_executor.hpp>
19 : : #include <functional>
20 : : #include <memory>
21 : : #include <optional>
22 : : #include <utility>
23 : : #include <vector>
24 : :
25 : : #include "wirestead/base/common.hpp"
26 : : #include "wirestead/base/visibility.hpp"
27 : : #include "wirestead/diagnostics/error_types.hpp"
28 : : #include "wirestead/memory/safe_span.hpp"
29 : : #include "wirestead/wrapper/runtime_stats.hpp"
30 : :
31 : : namespace wirestead {
32 : : namespace interface {
33 : : class WIRESTEAD_API Channel {
34 : : public:
35 : : using OnBytes = std::function<void(memory::ConstByteSpan)>;
36 : : using OnState = std::function<void(base::LinkState)>;
37 : : using OnBackpressure = std::function<void(size_t /*queued_bytes*/)>;
38 : :
39 : : virtual ~Channel();
40 : :
41 : : virtual void start() = 0;
42 : : virtual void stop() = 0;
43 : : virtual bool is_connected() const = 0;
44 : : virtual bool is_backpressure_active() const = 0;
45 : 0 : virtual wrapper::RuntimeStats stats() const { return {}; }
46 : 0 : virtual void reset_stats() {}
47 : :
48 : : // Most recent error recorded by this channel, if any. Default no-op
49 : : // override (std::nullopt) so any transport not yet wired up to the
50 : : // shared ErrorInfoHolder plumbing (#445) just reports "no detail
51 : : // available" instead of failing to compile or requiring a stub
52 : : // override everywhere.
53 : 10 : virtual std::optional<diagnostics::ErrorInfo> last_error_info() const { return std::nullopt; }
54 : :
55 : : virtual boost::asio::any_io_executor get_executor() = 0;
56 : :
57 : : // Single send API (copies into internal queue)
58 : : virtual bool async_write_copy(memory::ConstByteSpan data) = 0;
59 : : // Zero-copy APIs (ownership transfer or shared ownership)
60 : : virtual bool async_write_move(std::vector<uint8_t>&& data) = 0;
61 : : virtual bool async_write_shared(std::shared_ptr<const std::vector<uint8_t>> data) = 0;
62 : :
63 : : // Explicit non-blocking drop-if-full send APIs. These must not enqueue into
64 : : // Reliable pending queues when backpressure is already active.
65 : : virtual bool async_try_write_copy(memory::ConstByteSpan data) = 0;
66 : : virtual bool async_try_write_move(std::vector<uint8_t>&& data) = 0;
67 : : virtual bool async_try_write_shared(std::shared_ptr<const std::vector<uint8_t>> data) = 0;
68 : :
69 : : // Callbacks. Thread-safe: may be called at any time, including after
70 : : // start(), from any thread. Replacing a callback is synchronized against
71 : : // concurrent invocation on the io thread - the implementation either
72 : : // guards storage with a mutex (copy-under-lock, invoke outside the lock)
73 : : // or dispatches the assignment onto the same strand the io thread runs
74 : : // on. Re-registering a callback takes effect for subsequent events; it
75 : : // does not retroactively affect an invocation already in progress (#436).
76 : : virtual void on_bytes(OnBytes cb) = 0;
77 : : virtual void on_state(OnState cb) = 0;
78 : : virtual void on_backpressure(OnBackpressure cb) = 0;
79 : : };
80 : :
81 : : // Snapshot type for a callback an implementation hands out to its io thread.
82 : : //
83 : : // The storage discipline above is unchanged - the mutex still guards the
84 : : // member, and the callback is still invoked outside the lock. What changes is
85 : : // the cost of the snapshot: copying a std::function heap-allocates whenever
86 : : // the target does not fit its small-object buffer, and the receive path takes
87 : : // one such copy per received chunk. Sharing an immutable copy turns that into
88 : : // a refcount bump. The pointed-to callback is const, so a snapshot taken by
89 : : // the io thread stays valid and unchanged even while a setter installs a
90 : : // replacement, which is the same guarantee the copy provided (#436).
91 : : template <typename Callback>
92 : : using SharedCallback = std::shared_ptr<const Callback>;
93 : :
94 : : // Returns null for an empty callback, so a non-null SharedCallback always
95 : : // holds something invocable and callers need only the pointer check.
96 : : // Call this outside the lock - it allocates.
97 : : template <typename Callback>
98 : 1229731 : SharedCallback<Callback> share_callback(Callback cb) {
99 [ + + ]: 1229731 : if (!cb) return {};
100 : 1228808 : return std::make_shared<const Callback>(std::move(cb));
101 : : }
102 : : } // namespace interface
103 : : } // namespace wirestead
|