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 (this->getBufferOrPause(0, bufferID, request.buffer))
34 {
35 request.callback.template bind<Self, &Self::afterRead>(*this);
36 SC_TRY_MSG(eventLoop != nullptr, "AsyncRequestReadableStream eventLoop == nullptr");
37 const Result startResult = request.start(*eventLoop);
38 if (not startResult)
39 {
40 this->getBuffersPool().unrefBuffer(bufferID);
41 bufferID = {};
42 return startResult; // Error occurred during request start
43 }
44 }
45 return Result(true);
46 }
47
48 virtual Result asyncDestroyReadable() override
49 {
50 if (request.isFree())
51 {
52 finalizeReadableDestruction();
53 return Result(true);
54 }
55 else
56 {
57 return request.stop(*eventLoop, &getStopCallback());
58 }
59 }
60
61 void afterRead(typename AsyncRequestType::Result& result)
62 {
63 Span<char> data;
64 if (result.get(data))
65 {
66 SC_ASYNC_STREAMS_ASSERT_RELEASE(request.isFree());
67 if (result.isEnded())
68 {
69 this->getBuffersPool().unrefBuffer(bufferID);
70 bufferID = {};
71 this->pushEnd();
72 }
73 else
74 {
75 const bool continuePushing = this->push(bufferID, data.sizeInBytes());
76 SC_ASYNC_STREAMS_ASSERT_RELEASE(result.getAsync().isFree());
77 // Only unref if destroy() wasn't called during push callback (which would have already unref'd)
78 if (not this->hasBeenDestroyed())
79 {
80 this->getBuffersPool().unrefBuffer(bufferID);
81 bufferID = {};
82 }
83 // Check if we're still pushing (so not, paused, destroyed or errored etc.)
84 if (continuePushing)
85 {
86 if (this->getBufferOrPause(0, bufferID, result.getAsync().buffer))
87 {
88 request.callback.template bind<Self, &Self::afterRead>(*this);
89 result.reactivateRequest(true);
90 }
91 }
92 }
93 }
94 else
95 {
96 if (bufferID.isValid())
97 {
98 this->getBuffersPool().unrefBuffer(bufferID);
99 bufferID = {};
100 }
101 this->emitError(result.isValid());
102 }
103 }
104
105 void finalizeReadableDestruction()
106 {
107 if (bufferID.isValid())
108 {
109 this->getBuffersPool().unrefBuffer(bufferID);
110 bufferID = {};
111 }
112 if (autoCloseDescriptor)
113 {
114 SC_ASYNC_STREAMS_ASSERT_RELEASE(request.closeHandle());
115 }
116 SC_ASYNC_STREAMS_ASSERT_RELEASE(this->finishedDestroyingReadable());
117 request = {};
118 }
119
120 template <typename T_AsyncResult>
121 static void stopReadableCallback(T_AsyncResult& result)
122 {
123 SC_COMPILER_WARNING_PUSH_OFFSETOF;
124 Self& stream = SC_COMPILER_FIELD_OFFSET(Self, request, static_cast<AsyncRequestType&>(result.async));
125 stream.finalizeReadableDestruction();
126 SC_COMPILER_WARNING_POP_OFFSETOF;
127 }
128
129 private:
130 // clang-format off
131 static Function<void(AsyncResult&)>& getStopCallback() { static Function<void(AsyncResult&)> cb = &stopReadableCallback<AsyncResult>; return cb; }
132 // clang-format on
133
134 public:
135 template <typename DescriptorType>
136 Result init(AsyncBuffersPool& buffersPool, AsyncEventLoopType& loop, const DescriptorType& descriptor)
137 {
138 SC_TRY_MSG(not request.isCancelling(), "AsyncRequestReadableStream - Destroy in progress");
139 this->eventLoop = &loop;
140 SC_TRY(descriptor.get(this->request.handle, Result::Error("Missing descriptor")));
141 return AsyncReadableStream::init(buffersPool);
142 }
143};
144
145template <typename AsyncRequestType, typename AsyncEventLoopType>
147{
149
151 void setAutoCloseDescriptor(bool value) { autoCloseDescriptor = value; }
152
153 AsyncRequestType request;
154
155 protected:
158
159 AsyncEventLoopType* eventLoop = nullptr;
160 BufferViewID bufferID;
161 bool autoCloseDescriptor = false;
162
163 Function<void(BufferViewID)> callback;
164
165 virtual Result asyncWrite(BufferViewID newBufferID, Function<void(BufferViewID)> cb) override
166 {
167 bufferID = newBufferID;
168 SC_ASYNC_STREAMS_ASSERT_RELEASE(not callback.isValid());
169 callback = move(cb);
170 SC_TRY(this->getBuffersPool().getReadableData(bufferID, request.buffer));
171 request.callback.template bind<Self, &Self::afterWrite>(*this);
172 SC_TRY_MSG(eventLoop != nullptr, "AsyncRequestWritableStream eventLoop == nullptr");
173 const Result res = request.start(*eventLoop);
174 if (res)
175 {
176 this->getBuffersPool().refBuffer(bufferID);
177 }
178 return res;
179 }
180
181 virtual Result asyncDestroyWritable() override
182 {
183 if (request.isFree())
184 {
185 finalizeWritableDestruction();
186 return Result(true);
187 }
188 else
189 {
190 return request.stop(*eventLoop, &getStopCallback());
191 }
192 }
193
194 virtual bool canEndWritable() override { return request.isFree(); }
195
196 void afterWrite(typename AsyncRequestType::Result& result)
197 {
198 BufferViewID savedBufferID = bufferID;
199 this->getBuffersPool().unrefBuffer(bufferID);
200 bufferID = {};
201 auto cb = move(callback);
202 callback = {};
203 this->finishedWriting(savedBufferID, move(cb), result.isValid());
204 }
205
206 void finalizeWritableDestruction()
207 {
208 if (autoCloseDescriptor)
209 {
210 SC_ASYNC_STREAMS_ASSERT_RELEASE(request.closeHandle());
211 }
212 request = {};
214 }
215
216 template <typename T_AsyncResult>
217 static void stopWritableCallback(T_AsyncResult& result)
218 {
219 SC_COMPILER_WARNING_PUSH_OFFSETOF;
220 Self& stream = SC_COMPILER_FIELD_OFFSET(Self, request, static_cast<AsyncRequestType&>(result.async));
221 stream.finalizeWritableDestruction();
222 SC_COMPILER_WARNING_POP_OFFSETOF;
223 }
224
225 private:
226 // clang-format off
227 static Function<void(AsyncResult&)>& getStopCallback() { static Function<void(AsyncResult&)> cb = &stopWritableCallback<AsyncResult>; return cb; }
228 // clang-format on
229 public:
230 template <typename DescriptorType>
231 Result init(AsyncBuffersPool& buffersPool, AsyncEventLoopType& loop, const DescriptorType& descriptor)
232 {
233 this->eventLoop = &loop;
234 SC_TRY(descriptor.get(this->request.handle, Result::Error("Missing descriptor")));
235 return AsyncWritableStream::init(buffersPool);
236 }
237};
238
239// clang-format off
241template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncReadableFileStream : public AsyncRequestReadableStream<typename AsyncEventLoopType::FileRead, AsyncEventLoopType>{};
243template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncWritableFileStream : public AsyncRequestWritableStream<typename AsyncEventLoopType::FileWrite, AsyncEventLoopType>{};
245template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncReadableSocketStream : public AsyncRequestReadableStream<typename AsyncEventLoopType::SocketReceive, AsyncEventLoopType>{};
247template <typename AsyncEventLoopType> struct SC_ASYNC_STREAMS_EXPORT AsyncWritableSocketStream : public AsyncRequestWritableStream<typename AsyncEventLoopType::SocketSend, AsyncEventLoopType>{};
248// clang-format on
249
250} // namespace SC
Definition AsyncStreams.h:66
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:241
Uses an SC::AsyncSocketReceive to stream data from a socket.
Definition AsyncRequestStreams.h:245
Async source abstraction emitting data events in caller provided byte buffers.
Definition AsyncStreams.h:228
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:269
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:48
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:147
void setAutoCloseDescriptor(bool value)
Automatically closes descriptor during write stream finish event.
Definition AsyncRequestStreams.h:151
virtual Result asyncDestroyWritable() override
Function that a writable stream can re-implement to release its internal resources.
Definition AsyncRequestStreams.h:181
virtual bool canEndWritable() override
Allows keeping a writable in ENDING state until it has finished flushing all pending data.
Definition AsyncRequestStreams.h:194
AsyncBufferView::ID BufferViewID
AsyncFileRead / AsyncFileWrite / AsyncSocketReceive / AsyncSocketSend.
Definition AsyncRequestStreams.h:156
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:165
Uses an SC::AsyncFileWrite to stream data to a file.
Definition AsyncRequestStreams.h:243
Uses an SC::AsyncSocketSend to stream data to a socket.
Definition AsyncRequestStreams.h:247
Async destination abstraction where bytes can be written to.
Definition AsyncStreams.h:364
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.