FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
event_sink.hpp
Go to the documentation of this file.
1#pragma once
2// EventSink (5.15): the net -> engine seam. A thin wrapper over the per-venue inbound
3// MsgRing that the parsers write normalised events into. Two sinks per venue: market data
4// (lossy: a full ring drops the delta and the book resyncs) and order events (never dropped:
5// bounded spin then overflow callback, which the venue treats as fatal).
6//
7// Run-to-completion (fastmm-live [engine] threading = "single"): the engine runs on the venue's
8// thread and a drain hook hands it the ring's events in place. With `on_commit` the hook runs
9// after every commit, so the engine takes each event as soon as it is decoded; a full ring runs it
10// before the policy's spin either way.
11//
12// fastmm-gateway points the sinks at ShmRings the attached strategy process reads
13// (attach(ShmRing*)) and back at a local ring it discards while no strategy is attached. Switch
14// only on the thread that pushes (a posted reactor task), never between a reserve and its commit.
16#include "fastmm/core/msg_ring.hpp"
17#include "fastmm/core/shm_ring.hpp"
18
19#include <x86intrin.h>
20
21#include <cstddef>
22#include <cstdint>
23#include <cstring>
24
25namespace fastmm::venues {
26
27enum class SinkPolicy : std::uint8_t {
28 Drop = 0, // market data: return false, count, notify
29 Spin = 1, // order events: spin up to spin_limit before giving up
30};
31
32class EventSink {
33 public:
34 using OverflowFn = void (*)(void* ctx, const EventSink& sink) noexcept;
35 using DrainFn = void (*)(void* ctx) noexcept;
37 EventSink() noexcept = default;
38 EventSink(MsgRing* ring, SinkPolicy policy, std::uint32_t spin_limit = 1'000'000) noexcept
39 : ring_(ring), policy_(policy), spin_limit_(spin_limit) {}
41 void attach(MsgRing* ring, SinkPolicy policy, std::uint32_t spin_limit = 1'000'000) noexcept {
42 ring_ = ring;
43 shm_ = nullptr;
44 policy_ = policy;
45 spin_limit_ = spin_limit;
46 }
47 // A ring another process reads (fastmm-gateway).
48 void attach(ShmRing* ring, SinkPolicy policy, std::uint32_t spin_limit = 1'000'000) noexcept {
49 ring_ = nullptr;
50 shm_ = ring;
51 policy_ = policy;
52 spin_limit_ = spin_limit;
53 }
54 void set_overflow_callback(OverflowFn fn, void* ctx) noexcept {
55 overflow_fn_ = fn;
56 overflow_ctx_ = ctx;
57 }
58 void set_drain_hook(DrainFn fn, void* ctx, bool on_commit) noexcept {
59 drain_fn_ = fn;
60 drain_ctx_ = ctx;
61 drain_on_commit_ = fn != nullptr && on_commit;
62 }
63 [[nodiscard]] bool attached() const noexcept { return ring_ != nullptr || shm_ != nullptr; }
64 [[nodiscard]] MsgRing* ring() const noexcept { return ring_; }
65 [[nodiscard]] ShmRing* shm_ring() const noexcept { return shm_; }
67 // In-place construction: reserve, fill, commit. nullptr when the ring is full (after the
68 // policy's spin) or detached; the caller must not commit then.
69 template <MessageLike M>
70 [[nodiscard]] M* reserve(std::uint32_t len = static_cast<std::uint32_t>(sizeof(M))) noexcept {
71 return reinterpret_cast<M*>(reserve_bytes(len));
72 }
73 [[nodiscard]] std::byte* reserve_bytes(std::uint32_t len) noexcept {
74 if (!attached()) return nullptr;
75 std::byte* p = try_reserve(len);
76 if (FASTMM_LIKELY(p != nullptr)) return p;
77 if (drain_fn_ != nullptr) {
78 // The consumer is this thread: spinning frees nothing.
79 drain_fn_(drain_ctx_);
80 p = try_reserve(len);
81 if (p != nullptr) return p;
82 } else if (policy_ == SinkPolicy::Spin) {
83 for (std::uint32_t i = 0; i < spin_limit_; ++i) {
84 _mm_pause();
85 p = try_reserve(len);
86 if (p != nullptr) return p;
87 }
88 }
89 on_overflow();
90 return nullptr;
91 }
92 void commit() noexcept {
93 if (FASTMM_LIKELY(ring_ != nullptr)) {
94 ring_->commit();
95 } else {
96 shm_->commit();
97 }
98 ++pushed_;
99 if (drain_on_commit_) drain_fn_(drain_ctx_);
100 }
101
102 // Copy-in: `m.len` bytes starting at the header.
103 [[nodiscard]] bool push(const EventHeader& m) noexcept {
104 std::byte* p = reserve_bytes(m.len);
105 if (p == nullptr) return false;
106 std::memcpy(p, &m, m.len);
107 commit();
108 return true;
109 }
110
111 [[nodiscard]] std::uint64_t pushed() const noexcept { return pushed_; }
112 [[nodiscard]] std::uint64_t overflows() const noexcept { return overflows_; }
113 [[nodiscard]] SinkPolicy policy() const noexcept { return policy_; }
115 private:
116 [[nodiscard]] FASTMM_FORCE_INLINE std::byte* try_reserve(std::uint32_t len) noexcept {
117 return FASTMM_LIKELY(ring_ != nullptr) ? ring_->try_reserve(len) : shm_->try_reserve(len);
118 }
119 void on_overflow() noexcept {
120 ++overflows_;
121 if (overflow_fn_ != nullptr) overflow_fn_(overflow_ctx_, *this);
122 }
123
124 MsgRing* ring_ = nullptr;
125 ShmRing* shm_ = nullptr;
127 std::uint32_t spin_limit_ = 0;
128 std::uint64_t pushed_ = 0;
129 std::uint64_t overflows_ = 0;
130 OverflowFn overflow_fn_ = nullptr;
131 void* overflow_ctx_ = nullptr;
132 DrainFn drain_fn_ = nullptr;
133 void* drain_ctx_ = nullptr;
134 bool drain_on_commit_ = false;
135};
136
137} // namespace fastmm::venues
Definition event_sink.hpp:34
void commit() noexcept
Definition event_sink.hpp:94
void attach(MsgRing *ring, SinkPolicy policy, std::uint32_t spin_limit=1 '000 '000) noexcept
Definition event_sink.hpp:43
MsgRing * ring() const noexcept
Definition event_sink.hpp:66
ShmRing * shm_ring() const noexcept
Definition event_sink.hpp:67
M * reserve(std::uint32_t len=static_cast< std::uint32_t >(sizeof(M))) noexcept
In-place construction: reserve, fill, commit.
Definition event_sink.hpp:72
bool push(const EventHeader &m) noexcept
Copy-in: m.len bytes starting at the header.
Definition event_sink.hpp:105
std::uint64_t overflows() const noexcept
Definition event_sink.hpp:114
void set_overflow_callback(OverflowFn fn, void *ctx) noexcept
Definition event_sink.hpp:56
bool attached() const noexcept
Definition event_sink.hpp:65
void set_drain_hook(DrainFn fn, void *ctx, bool on_commit) noexcept
Definition event_sink.hpp:60
void(*)(void *ctx) noexcept DrainFn
Definition event_sink.hpp:37
SinkPolicy policy() const noexcept
Definition event_sink.hpp:115
EventSink() noexcept=default
void(*)(void *ctx, const EventSink &sink) noexcept OverflowFn
Definition event_sink.hpp:36
std::uint64_t pushed() const noexcept
Definition event_sink.hpp:113
std::byte * reserve_bytes(std::uint32_t len) noexcept
Definition event_sink.hpp:75
Event bus message structs (5.5).
Definition venue.hpp:38
SinkPolicy
Definition event_sink.hpp:29
@ Drop
market data: return false, count, notify
@ Spin
order events: spin up to spin_limit before giving up