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 : :
46 : : // Hard byte limit of the entire write queue, not its currently free space.
47 : : // nullopt means unavailable (including custom channels that do not report it).
48 : : // A reported limit must match write admission; it is not an acceptance promise.
49 : 961 : virtual std::optional<size_t> write_queue_limit() const { return std::nullopt; }
50 : 0 : virtual wrapper::RuntimeStats stats() const { return {}; }
51 : 0 : virtual void reset_stats() {}
52 : :
53 : : // Most recent error recorded by this channel, if any. Default no-op
54 : : // override (std::nullopt) so any transport not yet wired up to the
55 : : // shared ErrorInfoHolder plumbing (#445) just reports "no detail
56 : : // available" instead of failing to compile or requiring a stub
57 : : // override everywhere.
58 : 37 : virtual std::optional<diagnostics::ErrorInfo> last_error_info() const { return std::nullopt; }
59 : :
60 : : virtual boost::asio::any_io_executor get_executor() = 0;
61 : :
62 : : // Single send API (copies into internal queue)
63 : : virtual bool async_write_copy(memory::ConstByteSpan data) = 0;
64 : : // Zero-copy APIs (ownership transfer or shared ownership)
65 : : virtual bool async_write_move(std::vector<uint8_t>&& data) = 0;
66 : : virtual bool async_write_shared(std::shared_ptr<const std::vector<uint8_t>> data) = 0;
67 : :
68 : : // Explicit non-blocking drop-if-full send APIs. These must not enqueue into
69 : : // Reliable pending queues when backpressure is already active.
70 : : virtual bool async_try_write_copy(memory::ConstByteSpan data) = 0;
71 : : virtual bool async_try_write_move(std::vector<uint8_t>&& data) = 0;
72 : : virtual bool async_try_write_shared(std::shared_ptr<const std::vector<uint8_t>> data) = 0;
73 : :
74 : : // Callbacks. Thread-safe: may be called at any time, including after
75 : : // start(), from any thread. Replacing a callback is synchronized against
76 : : // concurrent invocation on the io thread - the implementation either
77 : : // guards storage with a mutex (copy-under-lock, invoke outside the lock)
78 : : // or dispatches the assignment onto the same strand the io thread runs
79 : : // on. Re-registering a callback takes effect for subsequent events; it
80 : : // does not retroactively affect an invocation already in progress (#436).
81 : : virtual void on_bytes(OnBytes cb) = 0;
82 : : virtual void on_state(OnState cb) = 0;
83 : : virtual void on_backpressure(OnBackpressure cb) = 0;
84 : : };
85 : :
86 : : // Snapshot type for a callback an implementation hands out to its io thread.
87 : : //
88 : : // The storage discipline above is unchanged - the mutex still guards the
89 : : // member, and the callback is still invoked outside the lock. What changes is
90 : : // the cost of the snapshot: copying a std::function heap-allocates whenever
91 : : // the target does not fit its small-object buffer, and the receive path takes
92 : : // one such copy per received chunk. Sharing an immutable copy turns that into
93 : : // a refcount bump. The pointed-to callback is const, so a snapshot taken by
94 : : // the io thread stays valid and unchanged even while a setter installs a
95 : : // replacement, which is the same guarantee the copy provided (#436).
96 : : template <typename Callback>
97 : : using SharedCallback = std::shared_ptr<const Callback>;
98 : :
99 : : // Returns null for an empty callback, so a non-null SharedCallback always
100 : : // holds something invocable and callers need only the pointer check.
101 : : // Call this outside the lock - it allocates.
102 : : template <typename Callback>
103 : 1177833 : SharedCallback<Callback> share_callback(Callback cb) {
104 [ + + ]: 1177833 : if (!cb) return {};
105 : 1168818 : return std::make_shared<const Callback>(std::move(cb));
106 : : }
107 : : } // namespace interface
108 : : } // namespace wirestead
|