Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
AsyncRequestStreams.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Common/CompilerOffsetOf.h"
5#include "AsyncStreams.h"
8namespace SC
9{
10struct AsyncResult;
11
12template <typename AsyncRequestType, typename AsyncEventLoopType>
14{
16
18 void setAutoCloseDescriptor(bool value) { autoCloseDescriptor = value; }
19
20 AsyncRequestType request;
21
22 protected:
25
26 AsyncEventLoopType* eventLoop = nullptr;
27 BufferViewID bufferID;
28 bool autoCloseDescriptor = false;
29
30 virtual Result asyncRead() override
31 {
32 SC_ASYNC_STREAMS_ASSERT_RELEASE(request.isFree());
33 if (eventLoop == nullptr)
34 return Result::Error(AsyncStreamsResultCategory, AsyncStreamsError::AsyncEventLoopMissing);
35 if (this->getBufferOrPause(0, bufferID, request.buffer))
36 {
37 request.callback.template bind<Self, &Self::afterRead>(*this);
38 const Result startResult = request.start(*eventLoop);
39 if (not startResult)
40 {
41 this->getBuffersPool().unrefBuffer(bufferID);
42 bufferID = {};
43 return startResult; // Error occurred during request start
44 }
45 }
46 return Result(true);
47 }
48
49 virtual Result asyncDestroyReadable() override
50 {
51 if (request.isFree())
52 {
53 finalizeReadableDestruction();
54 return Result(true);
55 }
56 else
57 {
58 return request.stop(*eventLoop, &getStopCallback());
59 }
60 }
61
62 void afterRead(typename AsyncRequestType::Result& result)
63 {
64 Span<char> data;
65 if (result.get(data))
66 {
67 SC_ASYNC_STREAMS_ASSERT_RELEASE(request.isFree());
68 if (result.isEnded())
69 {
70 this->getBuffersPool().unrefBuffer(bufferID);
71 bufferID = {};
72 this->pushEnd();
73 }
74 else
75 {
76 const bool continuePushing = this->push(bufferID, data.sizeInBytes());
77 SC_ASYNC_STREAMS_ASSERT_RELEASE(result.getAsync().isFree());
78 // Only unref if destroy() wasn't called during push callback (which would have already unref'd)
79 if (not this->hasBeenDestroyed())
80 {
81 this->getBuffersPool().unrefBuffer(bufferID);
82 bufferID = {};
83 }
84 // Check if we're still pushing (so not, paused, destroyed or errored etc.)
85 if (continuePushing)
86 {
87 if (this->getBufferOrPause(0, bufferID, result.getAsync().buffer))
88 {
89 request.callback.template bind<Self, &Self::afterRead>(*this);
90 result.reactivateRequest(true);
91 }
92 }
93 }
94 }
95 else
96 {
97 if (bufferID.isValid())
98 {
99 this->getBuffersPool().unrefBuffer(bufferID);
100 bufferID = {};
101 }
102 this->emitError(result.isValid());
103 }
104 }
105
106 void finalizeReadableDestruction()
107 {
108 if (bufferID.isValid())
109 {
110 this->getBuffersPool().unrefBuffer(bufferID);
111 bufferID = {};
112 }
113 if (autoCloseDescriptor)
114 {
115 SC_ASYNC_STREAMS_ASSERT_RELEASE(request.closeHandle());
116 }
117 SC_ASYNC_STREAMS_ASSERT_RELEASE(this->finishedDestroyingReadable());
118 request = {};
119 }
120
121 template <typename T_AsyncResult>
122 static void stopReadableCallback(T_AsyncResult& result)
123 {
124 SC_COMPILER_WARNING_PUSH_OFFSETOF;
125 Self& stream = SC_COMPILER_FIELD_OFFSET(Self, request, static_cast<AsyncRequestType&>(result.async));
126 stream.finalizeReadableDestruction();
127 SC_COMPILER_WARNING_POP_OFFSETOF;
128 }
129
130 private:
131 // clang-format off
132 static Function<void(AsyncResult&)>& getStopCallback() { static Function<void(AsyncResult&)> cb = &stopReadableCallback<AsyncResult>; return cb; }
133 // clang-format on
134
135 public:
136 template <typename DescriptorType>
137 Result init(AsyncBuffersPool& buffersPool, AsyncEventLoopType& loop, const DescriptorType& descriptor)
138 {
139 if (request.isCancelling())
140 return Result::Error(AsyncStreamsResultCategory, AsyncStreamsError::AsyncRequestDestroying);
141 this->eventLoop = &loop;
142 SC_TRY(descriptor.get(this->request.handle,
143 Result::Error(AsyncStreamsResultCategory, AsyncStreamsError::AsyncDescriptorMissing)));
144 return AsyncReadableStream::init(buffersPool);
145 }
146};
147
148template <typename AsyncRequestType, typename AsyncEventLoopType>
150{
152
154 void setAutoCloseDescriptor(bool value) { autoCloseDescriptor = value; }
155
156 AsyncRequestType request;
157
158 protected:
161
162 AsyncEventLoopType* eventLoop = nullptr;
163 BufferViewID bufferID;
164 bool autoCloseDescriptor = false;
165
166 Function<void(BufferViewID)> callback;
167
168 virtual Result asyncWrite(BufferViewID newBufferID, Function<void(BufferViewID)> cb) override
169 {
170 if (eventLoop == nullptr)
171 return Result::Error(AsyncStreamsResultCategory, AsyncStreamsError::AsyncEventLoopMissing);
172 bufferID = newBufferID;
173 SC_ASYNC_STREAMS_ASSERT_RELEASE(not callback.isValid());
174 callback = move(cb);
175 SC_TRY(this->getBuffersPool().getReadableData(bufferID, request.buffer));
176 request.callback.template bind<Self, &Self::afterWrite>(*this);
177 const Result res = request.start(*eventLoop);
178 if (res)
179 {
180 this->getBuffersPool().refBuffer(bufferID);
181 }
182 return res;
183 }
184
185 virtual Result asyncDestroyWritable() override
186 {
187 if (request.isFree())
188 {
189 finalizeWritableDestruction();
190 return Result(true);
191 }
192 else
193 {
194 return request.stop(*eventLoop, &getStopCallback());
195 }
196 }
197
198 virtual bool canEndWritable() override { return request.isFree(); }
199
200 void afterWrite(typename AsyncRequestType::Result& result)
201 {
202 BufferViewID savedBufferID = bufferID;
203 this->getBuffersPool().unrefBuffer(bufferID);
204 bufferID = {};
205 auto cb = move(callback);
206 callback = {};
207 this->finishedWriting(savedBufferID, move(cb), result.isValid());
208 }
209
210 void finalizeWritableDestruction()
211 {
212 if (autoCloseDescriptor)
213 {
214 SC_ASYNC_STREAMS_ASSERT_RELEASE(request.closeHandle());
215 }
216 request = {};
218 }
219
220 template <typename T_AsyncResult>
221 static void stopWritableCallback(T_AsyncResult& result)
222 {
223 SC_COMPILER_WARNING_PUSH_OFFSETOF;
224 Self& stream = SC_COMPILER_FIELD_OFFSET(Self, request, static_cast<AsyncRequestType&>(result.async));
225 stream.finalizeWritableDestruction();
226 SC_COMPILER_WARNING_POP_OFFSETOF;
227 }
228
229 private:
230 // clang-format off
231 static Function<void(AsyncResult&)>& getStopCallback() { static Function<void(AsyncResult&)> cb = &stopWritableCallback<AsyncResult>; return cb; }
232 // clang-format on
233 public:
234 template <typename DescriptorType>
235 Result init(AsyncBuffersPool& buffersPool, AsyncEventLoopType& loop, const DescriptorType& descriptor)
236 {
237 this->eventLoop = &loop;
238 SC_TRY(descriptor.get(this->request.handle,
239 Result::Error(AsyncStreamsResultCategory, AsyncStreamsError::AsyncDescriptorMissing)));
240 return AsyncWritableStream::init(buffersPool);
241 }
242};
243
244// clang-format off
246template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncReadableFileStream : public AsyncRequestReadableStream<typename AsyncEventLoopType::FileRead, AsyncEventLoopType>{};
248template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncWritableFileStream : public AsyncRequestWritableStream<typename AsyncEventLoopType::FileWrite, AsyncEventLoopType>{};
250template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncReadableSocketStream : public AsyncRequestReadableStream<typename AsyncEventLoopType::SocketReceive, AsyncEventLoopType>{};
252template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncWritableSocketStream : public AsyncRequestWritableStream<typename AsyncEventLoopType::SocketSend, AsyncEventLoopType>{};
253// clang-format on
254
255} // namespace SC
Definition AsyncStreams.h:67
void refBuffer(AsyncBufferView::ID bufferID)
Increments a buffer reference count.
void unrefBuffer(AsyncBufferView::ID bufferID)
Decrements a buffer reference count.
Uses an SC::AsyncFileRead to stream data from a file.
Definition AsyncRequestStreams.h:246
Uses an SC::AsyncSocketReceive to stream data from a socket.
Definition AsyncRequestStreams.h:250
Async source abstraction emitting data events in caller provided byte buffers.
Definition AsyncStreams.h:229
void pushEnd()
Use pushEnd from inside AsyncReadableStream::asyncRead to signal production end.
void emitError(Result error)
Signals an async error received.
bool push(AsyncBufferView::ID bufferID, size_t newSize)
Use push from inside AsyncReadableStream::asyncRead function to queue received data.
bool hasBeenDestroyed() const
Returns true if the stream has been already destroyed (asynchronously through destroy())
Definition AsyncStreams.h:270
Result finishedDestroyingReadable()
Called from inside asyncDestroy to transition from Destroying to Destroyed state (emitting eventClose...
AsyncBuffersPool & getBuffersPool()
Obtains the AsyncBuffersPool to request more buffers.
bool getBufferOrPause(size_t minumumSizeInBytes, AsyncBufferView::ID &bufferID, Span< char > &data)
Returns an unused buffer from pool or pauses the stream if none is available.
Result init(AsyncBuffersPool &buffersPool)
Emitted when the underlying resource has been closed.
Definition AsyncRequestStreams.h:14
virtual Result asyncDestroyReadable() override
Function that a readable stream can re-implement to release its internal resources.
Definition AsyncRequestStreams.h:49
void setAutoCloseDescriptor(bool value)
Automatically closes descriptor during read stream close event.
Definition AsyncRequestStreams.h:18
virtual Result asyncRead() override
Function that every stream must define to implement its custom read operation.
Definition AsyncRequestStreams.h:30
Definition AsyncRequestStreams.h:150
void setAutoCloseDescriptor(bool value)
Automatically closes descriptor during write stream finish event.
Definition AsyncRequestStreams.h:154
virtual Result asyncDestroyWritable() override
Function that a writable stream can re-implement to release its internal resources.
Definition AsyncRequestStreams.h:185
virtual bool canEndWritable() override
Allows keeping a writable in ENDING state until it has finished flushing all pending data.
Definition AsyncRequestStreams.h:198
AsyncBufferView::ID BufferViewID
AsyncFileRead / AsyncFileWrite / AsyncSocketReceive / AsyncSocketSend.
Definition AsyncRequestStreams.h:159
virtual Result asyncWrite(BufferViewID newBufferID, Function< void(BufferViewID)> cb) override
Function that every stream must define to implement its custom write operation.
Definition AsyncRequestStreams.h:168
Uses an SC::AsyncFileWrite to stream data to a file.
Definition AsyncRequestStreams.h:248
Uses an SC::AsyncSocketSend to stream data to a socket.
Definition AsyncRequestStreams.h:252
Async destination abstraction where bytes can be written to.
Definition AsyncStreams.h:365
void finishedDestroyingWritable()
Function that MUST be called by re-implementations of asyncDestroyWritable once they're done.
Result init(AsyncBuffersPool &buffersPool)
Emitted when the underlying resource has been closed.
void finishedWriting(AsyncBufferView::ID bufferID, Function< void(AsyncBufferView::ID)> &&cb, Result res)
Signals that the given buffer (previously queued by write) has been fully written.
AsyncBuffersPool & getBuffersPool()
Obtains the buffers pool to access its data.