Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
BoostAsioWriteQueue.h
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#ifndef SF_BOOSTASIOWRITEQUEUE_H
7#define SF_BOOSTASIOWRITEQUEUE_H
8
9#include <boost/asio/bind_executor.hpp>
10#include <boost/asio/buffer.hpp>
11#include <boost/asio/post.hpp>
12#include <boost/asio/strand.hpp>
13#include <boost/asio/write.hpp>
14#include <boost/system/error_code.hpp>
15#include <atomic>
16#include <deque>
17#include <functional>
18#include <vector>
19
20namespace Skyfire
21{
22namespace Net
23{
24 template<class AsyncWriteStream>
26 {
27 public:
28 typedef std::function<void(boost::system::error_code const&, size_t)> CompletionHandler;
29
30 explicit BoostAsioWriteQueue(AsyncWriteStream& stream) :
31 _stream(stream),
32 _strand(boost::asio::make_strand(stream.get_executor())),
33 _queue(),
35 _writeInProgress(false)
36 {
37 }
38
39 bool HasPendingOutput() const
40 {
41 return _pendingBytes.load(std::memory_order_acquire) != 0;
42 }
43
44 void Queue(std::vector<char> data, CompletionHandler handler)
45 {
46 if (data.empty())
47 {
48 boost::asio::post(_strand,
49 [handler = std::move(handler)]() mutable
50 {
51 if (handler)
52 handler(boost::system::error_code(), 0);
53 });
54 return;
55 }
56
57 _pendingBytes.fetch_add(data.size(), std::memory_order_acq_rel);
58 boost::asio::post(_strand,
59 [this, data = std::move(data), handler = std::move(handler)]() mutable
60 {
61 bool startWrite = !_writeInProgress && _queue.empty();
62 _queue.push_back(Entry(std::move(data), std::move(handler)));
63
64 if (startWrite)
65 StartWrite();
66 });
67 }
68
69 private:
70 struct Entry
71 {
72 Entry(std::vector<char> data, CompletionHandler handler) :
73 Data(std::move(data)),
74 Handler(std::move(handler))
75 {
76 }
77
78 std::vector<char> Data;
80 };
81
83 {
84 if (_queue.empty())
85 {
86 _writeInProgress = false;
87 return;
88 }
89
90 _writeInProgress = true;
91 boost::asio::async_write(_stream, boost::asio::buffer(_queue.front().Data),
92 boost::asio::bind_executor(_strand,
93 [this](boost::system::error_code const& error, size_t transferredBytes)
94 {
95 HandleWrite(error, transferredBytes);
96 }));
97 }
98
99 void HandleWrite(boost::system::error_code const& error, size_t transferredBytes)
100 {
101 if (_queue.empty())
102 {
103 _writeInProgress = false;
104 return;
105 }
106
107 Entry completed(std::move(_queue.front()));
108 _queue.pop_front();
109 SubtractPending(completed.Data.size());
110 _writeInProgress = false;
111
112 if (completed.Handler)
113 completed.Handler(error, transferredBytes);
114
115 if (error)
116 {
117 FailQueued(error);
118 return;
119 }
120
121 StartWrite();
122 }
123
124 void FailQueued(boost::system::error_code const& error)
125 {
126 while (!_queue.empty())
127 {
128 Entry entry(std::move(_queue.front()));
129 _queue.pop_front();
130 SubtractPending(entry.Data.size());
131
132 if (entry.Handler)
133 entry.Handler(error, 0);
134 }
135 }
136
137 void SubtractPending(size_t bytes)
138 {
139 _pendingBytes.fetch_sub(bytes, std::memory_order_acq_rel);
140 }
141
142 AsyncWriteStream& _stream;
143 boost::asio::strand<typename AsyncWriteStream::executor_type> _strand;
144 std::deque<Entry> _queue;
145 std::atomic<size_t> _pendingBytes;
147 };
148}
149}
150
151#endif
std::function< void(boost::system::error_code const &, size_t)> CompletionHandler
BoostAsioWriteQueue(AsyncWriteStream &stream)
void HandleWrite(boost::system::error_code const &error, size_t transferredBytes)
void Queue(std::vector< char > data, CompletionHandler handler)
void FailQueued(boost::system::error_code const &error)
boost::asio::strand< typename AsyncWriteStream::executor_type > _strand
std::vector< char > Data
CompletionHandler Handler
Entry(std::vector< char > data, CompletionHandler handler)