Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Async.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4
5#include "../Async/Internal/IntrusiveDoubleLinkedList.h"
6#include "../File/File.h"
7#include "../FileSystem/FileSystem.h"
8#include "../Foundation/Function.h"
9#include "../Foundation/OpaqueObject.h"
10#include "../Socket/Socket.h"
11#include "../Threading/Atomic.h"
12#include "../Threading/ThreadPool.h"
13#if SC_COMPILER_MSVC
14#pragma warning(push)
15#pragma warning(disable : 4251)
16#endif
17namespace SC
18{
19struct ThreadPool;
20struct ThreadPoolTask;
21struct EventObject;
22} // namespace SC
47
50namespace SC
51{
52struct AsyncEventLoop;
53struct AsyncResult;
54struct AsyncSequence;
55struct AsyncTaskSequence;
56
57namespace detail
58{
59struct AsyncWinOverlapped;
60struct AsyncWinOverlappedDefinition
61{
62 static constexpr int Windows = sizeof(void*) * 4 + sizeof(uint64_t);
63 static constexpr size_t Alignment = alignof(void*);
64
65 using Object = AsyncWinOverlapped;
66};
67using WinOverlappedOpaque = OpaqueObject<AsyncWinOverlappedDefinition>;
68
69struct AsyncWinWaitDefinition
70{
71 using Handle = FileDescriptor::Handle; // fd
72 static constexpr Handle Invalid = FileDescriptor::Invalid; // invalid fd
73
74 static Result releaseHandle(Handle& waitHandle);
75};
76struct SC_COMPILER_EXPORT WinWaitHandle : public UniqueHandle<AsyncWinWaitDefinition>
77{
78};
79} // namespace detail
80
119struct SC_COMPILER_EXPORT AsyncRequest
120{
121 AsyncRequest* next = nullptr;
122 AsyncRequest* prev = nullptr;
123
124 void setDebugName(const char* newDebugName);
125
127 void executeOn(AsyncSequence& sequence);
128
133
136
138 enum class Type : uint8_t
139 {
140 LoopTimeout,
141 LoopWakeUp,
142 LoopWork,
143 ProcessExit,
144 SocketAccept,
145 SocketConnect,
146 SocketSend,
147 SocketSendTo,
148 SocketReceive,
149 SocketReceiveFrom,
150 FileRead,
151 FileWrite,
152 FilePoll,
153 FileSystemOperation,
154 };
155
158 AsyncRequest(Type type) : state(State::Free), type(type), flags(0), unused(0), userFlags(0) {}
159
166 Result stop(AsyncEventLoop& eventLoop, Function<void(AsyncResult&)>* afterStopped = nullptr);
167
169 [[nodiscard]] bool isFree() const;
170
172 [[nodiscard]] bool isCancelling() const;
173
175 [[nodiscard]] bool isActive() const;
176
178 [[nodiscard]] Type getType() const { return type; }
179
182
184 void setUserFlags(uint16_t externalFlags) { userFlags = externalFlags; }
185
187 uint16_t getUserFlags() const { return userFlags; }
188
190 [[nodiscard]] Function<void(AsyncResult&)>* getCloseCallback() { return closeCallback; }
191
192 [[nodiscard]] const Function<void(AsyncResult&)>* getCloseCallback() const { return closeCallback; }
193
194 protected:
195 Result checkState();
196
197 void queueSubmission(AsyncEventLoop& eventLoop);
198
199 AsyncSequence* sequence = nullptr;
200
201 AsyncTaskSequence* getTask();
202
203 private:
204 Function<void(AsyncResult&)>* closeCallback = nullptr;
205
206 friend struct AsyncEventLoop;
207 friend struct AsyncResult;
208
209 void markAsFree();
210
211 [[nodiscard]] static const char* TypeToString(Type type);
212 enum class State : uint8_t
213 {
214 Free, // not in any queue, this can be started with an async.start(...)
215 Setup, // when in submission queue waiting to be setup (after an async.start(...))
216 Submitting, // when in submission queue waiting to be activated or re-activated
217 Active, // when monitored by OS syscall or in activeLoopWakeUps / activeTimeouts queues
218 Reactivate, // when flagged for reactivation inside the callback (after a result.reactivateRequest(true))
219 Cancelling, // when in cancellation queue waiting for a cancelAsync (on active async)
220 };
221
222#if SC_ASYNC_ENABLE_LOG
223 const char* debugName = "None";
224#endif
225 State state; // 1 byte
226 Type type; // 1 byte
227 int16_t flags; // 2 bytes
228
229 uint16_t unused; // 2 bytes
230 uint16_t userFlags; // 2 bytes
231};
232
237struct SC_COMPILER_EXPORT AsyncSequence
238{
239 AsyncSequence* next = nullptr;
240 AsyncSequence* prev = nullptr;
241
242 bool clearSequenceOnCancel = true;
243 bool clearSequenceOnError = true;
244 private:
245 friend struct AsyncEventLoop;
246 bool runningAsync = false; // true if an async from this sequence is being run
247 bool tracked = false;
248
249 IntrusiveDoubleLinkedList<AsyncRequest> submissions;
250};
251
253struct SC_COMPILER_EXPORT AsyncCompletionData{};
254
257struct SC_COMPILER_EXPORT AsyncResult
258{
260 AsyncResult(AsyncEventLoop& eventLoop, AsyncRequest& request, SC::Result& res, bool* hasBeenReactivated = nullptr)
261 : eventLoop(eventLoop), async(request), hasBeenReactivated(hasBeenReactivated), returnCode(res)
262 {}
263
266 void reactivateRequest(bool shouldBeReactivated);
267
269 [[nodiscard]] const SC::Result& isValid() const { return returnCode; }
270
271 AsyncEventLoop& eventLoop;
272 AsyncRequest& async;
273
274 protected:
275 friend struct AsyncEventLoop;
276
277 bool shouldCallCallback = true;
278 bool* hasBeenReactivated = nullptr;
279
280 SC::Result& returnCode;
281};
282
286template <typename T, typename C>
288{
289 T& getAsync() { return static_cast<T&>(AsyncResult::async); }
290 const T& getAsync() const { return static_cast<const T&>(AsyncResult::async); }
291
293
294 C completionData;
295 int32_t eventIndex = 0;
296};
297
302struct SC_COMPILER_EXPORT AsyncLoopTimeout : public AsyncRequest
303{
304 AsyncLoopTimeout() : AsyncRequest(Type::LoopTimeout) {}
305
308 using AsyncRequest::start;
309
311 SC::Result start(AsyncEventLoop& eventLoop, TimeMs relativeTimeout);
313
315
317 TimeMs getExpirationTime() const { return expirationTime; }
318
319 private:
320 SC::Result validate(AsyncEventLoop&);
321 friend struct AsyncEventLoop;
322 TimeMs expirationTime;
323};
324
337struct SC_COMPILER_EXPORT AsyncLoopWakeUp : public AsyncRequest
338{
339 AsyncLoopWakeUp() : AsyncRequest(Type::LoopWakeUp) {}
340
343 using AsyncRequest::start;
344
346 SC::Result start(AsyncEventLoop& eventLoop, EventObject& eventObject);
347
350
352 EventObject* eventObject = nullptr;
353
354 private:
355 friend struct AsyncEventLoop;
356 SC::Result validate(AsyncEventLoop&);
357
358 Atomic<bool> pending = false;
359};
360
365struct SC_COMPILER_EXPORT AsyncProcessExit : public AsyncRequest
366{
367 AsyncProcessExit() : AsyncRequest(Type::ProcessExit) {}
368
370 {
371 int exitStatus;
372 };
373
374 struct Result : public AsyncResultOf<AsyncProcessExit, CompletionData>
375 {
376 using AsyncResultOf<AsyncProcessExit, CompletionData>::AsyncResultOf;
377
378 SC::Result get(int& status)
379 {
380 status = completionData.exitStatus;
381 return returnCode;
382 }
383 };
384 using AsyncRequest::start;
385
389 SC::Result start(AsyncEventLoop& eventLoop, FileDescriptor::Handle process);
390
392
393 private:
394 friend struct AsyncEventLoop;
395 SC::Result validate(AsyncEventLoop&);
396
397 FileDescriptor::Handle handle = FileDescriptor::Invalid;
398#if SC_PLATFORM_WINDOWS
400 detail::WinWaitHandle waitHandle;
401 AsyncEventLoop* eventLoop = nullptr;
402#elif SC_PLATFORM_LINUX
403 FileDescriptor pidFd;
404#endif
405};
406
407struct AsyncSocketAccept;
408namespace detail
409{
412struct SC_COMPILER_EXPORT AsyncSocketAcceptData
413{
414#if SC_PLATFORM_WINDOWS
415 void (*pAcceptEx)() = nullptr;
417 SocketDescriptor clientSocket;
418 uint8_t acceptBuffer[288] = {0};
419#elif SC_PLATFORM_LINUX
420 AlignedStorage<28> sockAddrHandle;
421 uint32_t sockAddrLen;
422#endif
423};
424
426struct SC_COMPILER_EXPORT AsyncSocketAcceptBase : public AsyncRequest
427{
428 AsyncSocketAcceptBase() : AsyncRequest(Type::SocketAccept) {}
429
430 struct CompletionData : public AsyncCompletionData
431 {
432 SocketDescriptor acceptedClient;
433 };
434
435 struct Result : public AsyncResultOf<AsyncSocketAccept, CompletionData>
436 {
437 using AsyncResultOf<AsyncSocketAccept, CompletionData>::AsyncResultOf;
438
439 SC::Result moveTo(SocketDescriptor& client)
440 {
441 SC_TRY(returnCode);
442 return client.assign(move(completionData.acceptedClient));
443 }
444 };
445 using AsyncRequest::start;
446
448 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& socketDescriptor, AsyncSocketAcceptData& data);
449 SC::Result validate(AsyncEventLoop&);
450
451 Function<void(Result&)> callback;
452 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
453 SocketFlags::AddressFamily addressFamily = SocketFlags::AddressFamilyIPV4;
454 AsyncSocketAcceptData* acceptData = nullptr;
455};
456
457} // namespace detail
458
468struct SC_COMPILER_EXPORT AsyncSocketAccept : public detail::AsyncSocketAcceptBase
469{
470 AsyncSocketAccept() { AsyncSocketAcceptBase::acceptData = &data; }
471 using AsyncSocketAcceptBase::start;
472
474 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& socketDescriptor);
475
476 private:
477 detail::AsyncSocketAcceptData data;
478};
479
488struct SC_COMPILER_EXPORT AsyncSocketConnect : public AsyncRequest
489{
490 AsyncSocketConnect() : AsyncRequest(Type::SocketConnect) {}
491
494 using AsyncRequest::start;
495
497 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, SocketIPAddress address);
498
500
501 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
502 SocketIPAddress ipAddress;
503
504 private:
505 friend struct AsyncEventLoop;
506 SC::Result validate(AsyncEventLoop&);
507
508#if SC_PLATFORM_WINDOWS
509 void (*pConnectEx)() = nullptr;
511#endif
512};
513
522struct SC_COMPILER_EXPORT AsyncSocketSend : public AsyncRequest
523{
524 AsyncSocketSend() : AsyncRequest(Type::SocketSend) {}
526 {
527 size_t numBytes = 0;
528 };
530 using AsyncRequest::start;
531
534
537
539
540 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
541
544 bool singleBuffer = true;
545
546 protected:
547 AsyncSocketSend(Type type) : AsyncRequest(type) {}
548 friend struct AsyncEventLoop;
549 SC::Result validate(AsyncEventLoop&);
550
551 size_t totalBytesWritten = 0;
552#if SC_PLATFORM_WINDOWS
554#endif
555};
556
566struct SC_COMPILER_EXPORT AsyncSocketSendTo : public AsyncSocketSend
567{
568 AsyncSocketSendTo() : AsyncSocketSend(Type::SocketSendTo) {}
569
570 SocketIPAddress address;
571
572 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, SocketIPAddress ipAddress,
573 Span<const char> data);
574
575 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, SocketIPAddress ipAddress,
576 Span<Span<const char>> data);
577
578 private:
579 using AsyncSocketSend::start;
580 friend struct AsyncEventLoop;
581 SC::Result validate(AsyncEventLoop&);
582#if SC_PLATFORM_LINUX
583 AlignedStorage<56> typeErasedMsgHdr;
584#endif
585};
586
598struct SC_COMPILER_EXPORT AsyncSocketReceive : public AsyncRequest
599{
600 AsyncSocketReceive() : AsyncRequest(Type::SocketReceive) {}
601
603 {
604 size_t numBytes = 0;
605 bool disconnected = false;
606 };
607
608 struct Result : public AsyncResultOf<AsyncSocketReceive, CompletionData>
609 {
610 using AsyncResultOf<AsyncSocketReceive, CompletionData>::AsyncResultOf;
611
616 {
617 SC_TRY(getAsync().buffer.sliceStartLength(0, completionData.numBytes, outData));
618 return returnCode;
619 }
620
621 SocketIPAddress getSourceAddress() const;
622 };
623 using AsyncRequest::start;
624
626 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, Span<char> data);
627
629
631 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
632
633 protected:
635 friend struct AsyncEventLoop;
636 SC::Result validate(AsyncEventLoop&);
637#if SC_PLATFORM_WINDOWS
639#endif
640};
641
651struct SC_COMPILER_EXPORT AsyncSocketReceiveFrom : public AsyncSocketReceive
652{
653 AsyncSocketReceiveFrom() : AsyncSocketReceive(Type::SocketReceiveFrom) {}
654 using AsyncSocketReceive::start;
655
656 private:
657 SocketIPAddress address;
658 friend struct AsyncSocketReceive;
659 friend struct AsyncEventLoop;
660#if SC_PLATFORM_LINUX
661 AlignedStorage<56> typeErasedMsgHdr;
662#endif
663};
664
686struct SC_COMPILER_EXPORT AsyncFileRead : public AsyncRequest
687{
688 AsyncFileRead() : AsyncRequest(Type::FileRead) { handle = FileDescriptor::Invalid; }
689
691 {
692 size_t numBytes = 0;
693 bool endOfFile = false;
694 };
695
696 struct Result : public AsyncResultOf<AsyncFileRead, CompletionData>
697 {
698 using AsyncResultOf<AsyncFileRead, CompletionData>::AsyncResultOf;
699
700 SC::Result get(Span<char>& data)
701 {
702 SC_TRY(getAsync().buffer.sliceStartLength(0, completionData.numBytes, data));
703 return returnCode;
704 }
705 };
706 using AsyncRequest::start;
707
708 Function<void(Result&)> callback;
710 FileDescriptor::Handle handle;
712
714 uint64_t getOffset() const { return offset; }
715
718 void setOffset(uint64_t fileOffset)
719 {
720 useOffset = true;
721 offset = fileOffset;
722 }
723
724 private:
725 friend struct AsyncEventLoop;
726 SC::Result validate(AsyncEventLoop&);
727
728 bool useOffset = false;
729 bool endedSync = false;
730
731 uint64_t offset = 0;
732#if SC_PLATFORM_WINDOWS
733 uint64_t readCursor = 0;
735#endif
736};
737
755struct SC_COMPILER_EXPORT AsyncFileWrite : public AsyncRequest
756{
757 AsyncFileWrite() : AsyncRequest(Type::FileWrite) { handle = FileDescriptor::Invalid; }
758
760 {
761 size_t numBytes = 0;
762 };
763
764 struct Result : public AsyncResultOf<AsyncFileWrite, CompletionData>
765 {
766 using AsyncResultOf<AsyncFileWrite, CompletionData>::AsyncResultOf;
767
768 SC::Result get(size_t& writtenSizeInBytes)
769 {
770 writtenSizeInBytes = completionData.numBytes;
771 return returnCode;
772 }
773 };
774
775 using AsyncRequest::start;
776
779
782
784
785 FileDescriptor::Handle handle;
787
790 bool singleBuffer = true;
791
793 uint64_t getOffset() const { return offset; }
794
797 void setOffset(uint64_t fileOffset)
798 {
799 useOffset = true;
800 offset = fileOffset;
801 }
802
803 private:
804 friend struct AsyncEventLoop;
805 SC::Result validate(AsyncEventLoop&);
806
807#if SC_PLATFORM_WINDOWS
808 bool endedSync = false;
809#else
810 bool isWatchable = false;
811#endif
812 bool useOffset = false;
813 uint64_t offset = 0xffffffffffffffff;
814
815 size_t totalBytesWritten = 0;
816#if SC_PLATFORM_WINDOWS
818#endif
819};
820
825struct SC_COMPILER_EXPORT AsyncFilePoll : public AsyncRequest
826{
827 AsyncFilePoll() : AsyncRequest(Type::FilePoll) {}
828
831
833 SC::Result start(AsyncEventLoop& eventLoop, FileDescriptor::Handle fileDescriptor);
834
835#if SC_PLATFORM_WINDOWS
836 [[nodiscard]] void* getOverlappedPtr();
837#endif
838
839 Function<void(Result&)> callback;
840
841 private:
842 friend struct AsyncEventLoop;
843 SC::Result validate(AsyncEventLoop&);
844
845 FileDescriptor::Handle handle = FileDescriptor::Invalid;
846#if SC_PLATFORM_WINDOWS
848#endif
849};
850
851// forward declared because it must be defined after AsyncTaskSequence
852struct AsyncLoopWork;
854
856{
857 FileDescriptor::Handle handle = FileDescriptor::Invalid; // for open
858
859 int code = 0; // for open/close
860 size_t numBytes = 0; // for read
861};
862
863namespace detail
864{
865// A simple hand-made variant of all completion types
866struct SC_COMPILER_EXPORT AsyncCompletionVariant
867{
868 AsyncCompletionVariant() {}
869 ~AsyncCompletionVariant() { destroy(); }
870
871 AsyncCompletionVariant(const AsyncCompletionVariant&) = delete;
872 AsyncCompletionVariant(AsyncCompletionVariant&&) = delete;
873 AsyncCompletionVariant& operator=(const AsyncCompletionVariant&) = delete;
874 AsyncCompletionVariant& operator=(AsyncCompletionVariant&&) = delete;
875
876 bool inited = false;
877
878 AsyncRequest::Type type;
879 union
880 {
881 AsyncCompletionData completionDataLoopWork; // Defined after AsyncCompletionVariant / AsyncTaskSequence
882 AsyncLoopTimeout::CompletionData completionDataLoopTimeout;
883 AsyncLoopWakeUp::CompletionData completionDataLoopWakeUp;
884 AsyncProcessExit::CompletionData completionDataProcessExit;
885 AsyncSocketAccept::CompletionData completionDataSocketAccept;
886 AsyncSocketConnect::CompletionData completionDataSocketConnect;
887 AsyncSocketSend::CompletionData completionDataSocketSend;
888 AsyncSocketSendTo::CompletionData completionDataSocketSendTo;
889 AsyncSocketReceive::CompletionData completionDataSocketReceive;
890 AsyncSocketReceiveFrom::CompletionData completionDataSocketReceiveFrom;
891 AsyncFileRead::CompletionData completionDataFileRead;
892 AsyncFileWrite::CompletionData completionDataFileWrite;
893 AsyncFilePoll::CompletionData completionDataFilePoll;
894
895 AsyncFileSystemOperationCompletionData completionDataFileSystemOperation;
896 };
897
898 auto& getCompletion(AsyncLoopWork&) { return completionDataLoopWork; }
899 auto& getCompletion(AsyncLoopTimeout&) { return completionDataLoopTimeout; }
900 auto& getCompletion(AsyncLoopWakeUp&) { return completionDataLoopWakeUp; }
901 auto& getCompletion(AsyncProcessExit&) { return completionDataProcessExit; }
902 auto& getCompletion(AsyncSocketAccept&) { return completionDataSocketAccept; }
903 auto& getCompletion(AsyncSocketConnect&) { return completionDataSocketConnect; }
904 auto& getCompletion(AsyncSocketSend&) { return completionDataSocketSend; }
905 auto& getCompletion(AsyncSocketReceive&) { return completionDataSocketReceive; }
906 auto& getCompletion(AsyncFileRead&) { return completionDataFileRead; }
907 auto& getCompletion(AsyncFileWrite&) { return completionDataFileWrite; }
908 auto& getCompletion(AsyncFilePoll&) { return completionDataFilePoll; }
909 auto& getCompletion(AsyncFileSystemOperation&) { return completionDataFileSystemOperation; }
910
911 template <typename T>
912 auto& construct(T& t)
913 {
914 destroy();
915 placementNew(getCompletion(t));
916 inited = true;
917 type = t.getType();
918 return getCompletion(t);
919 }
920 void destroy();
921};
922} // namespace detail
923
927struct SC_COMPILER_EXPORT AsyncTaskSequence : public AsyncSequence
928{
929 protected:
930 ThreadPoolTask task;
931 ThreadPool* threadPool = nullptr;
932
933 friend struct AsyncEventLoop;
934 friend struct AsyncRequest;
935
936 detail::AsyncCompletionVariant completion;
937
938 SC::Result returnCode = SC::Result(true);
939};
940
946struct SC_COMPILER_EXPORT AsyncLoopWork : public AsyncRequest
947{
948 AsyncLoopWork() : AsyncRequest(Type::LoopWork) {}
949
952
956
957 Function<SC::Result()> work;
959
960 private:
961 friend struct AsyncEventLoop;
962 SC::Result validate(AsyncEventLoop&);
964};
965
999struct SC_COMPILER_EXPORT AsyncFileSystemOperation : public AsyncRequest
1000{
1001 AsyncFileSystemOperation() : AsyncRequest(Type::FileSystemOperation) {}
1002 ~AsyncFileSystemOperation() { destroy(); }
1003#ifdef CopyFile
1004#undef CopyFile
1005#endif
1006#ifdef RemoveDirectory
1007#undef RemoveDirectory
1008#endif
1009 enum class Operation
1010 {
1011 None = 0,
1012 Open,
1013 Close,
1014 Read,
1015 Write,
1016 CopyFile,
1017 CopyDirectory,
1018 Rename,
1019 RemoveDirectory,
1020 RemoveFile,
1021 };
1022
1025
1028
1030
1037
1042 SC::Result close(AsyncEventLoop& eventLoop, FileDescriptor::Handle handle);
1043
1050 SC::Result read(AsyncEventLoop& eventLoop, FileDescriptor::Handle handle, Span<char> buffer, uint64_t offset);
1051
1058 SC::Result write(AsyncEventLoop& eventLoop, FileDescriptor::Handle handle, Span<const char> buffer,
1059 uint64_t offset);
1060
1067 SC::Result copyFile(AsyncEventLoop& eventLoop, StringSpan path, StringSpan destinationPath,
1069
1078
1085
1092
1098
1099 private:
1100 friend struct AsyncEventLoop;
1101 Operation operation = Operation::None;
1102 AsyncLoopWork loopWork;
1103 CompletionData completionData;
1104
1105 void onOperationCompleted(AsyncLoopWork::Result& res);
1106
1107 struct FileDescriptorData
1108 {
1109 FileDescriptor::Handle handle;
1110 };
1111
1112 struct OpenData
1113 {
1114 StringSpan path;
1115 FileOpen mode;
1116 };
1117
1118 struct ReadData
1119 {
1120 FileDescriptor::Handle handle;
1121 Span<char> buffer;
1122 uint64_t offset;
1123 };
1124
1125 struct WriteData
1126 {
1127 FileDescriptor::Handle handle;
1128 Span<const char> buffer;
1129 uint64_t offset;
1130 };
1131
1132 struct CopyFileData
1133 {
1134 StringSpan path;
1135 StringSpan destinationPath;
1136 FileSystemCopyFlags copyFlags;
1137 };
1138
1139 using CopyDirectoryData = CopyFileData;
1140
1141 using CloseData = FileDescriptorData;
1142
1143 struct RenameData
1144 {
1145 StringSpan path;
1146 StringSpan newPath;
1147 };
1148
1149 struct RemoveData
1150 {
1151 StringSpan path;
1152 };
1153
1154 union
1155 {
1156 OpenData openData;
1157 CloseData closeData;
1158 ReadData readData;
1159 WriteData writeData;
1160 CopyFileData copyFileData;
1161 CopyDirectoryData copyDirectoryData;
1162 RenameData renameData;
1163 RemoveData removeData;
1164 };
1165
1166 void destroy();
1167
1168 SC::Result start(AsyncEventLoop& eventLoop, FileDescriptor::Handle fileDescriptor);
1169 SC::Result validate(AsyncEventLoop&);
1170};
1171
1175struct SC_COMPILER_EXPORT AsyncKernelEvents
1176{
1178
1179 private:
1180 int numberOfEvents = 0;
1181 friend struct AsyncEventLoop;
1182};
1183
1185struct SC_COMPILER_EXPORT AsyncEventLoopListeners
1186{
1187 Function<void(AsyncEventLoop&)> beforeBlockingPoll;
1188 Function<void(AsyncEventLoop&)> afterBlockingPoll;
1189};
1190
1198{
1200 struct Options
1201 {
1202 enum class ApiType : uint8_t
1203 {
1204 Automatic = 0,
1207 };
1209
1211 };
1212
1214
1217
1220
1224
1227
1229 [[nodiscard]] bool isInitialized() const;
1230
1237
1248
1255
1261
1281
1288
1292
1295
1298
1301
1304
1307
1310
1313
1316
1318 [[nodiscard]] TimeMs getLoopTime() const;
1319
1321 [[nodiscard]] int getNumberOfActiveRequests() const;
1322
1324 [[nodiscard]] int getNumberOfSubmittedRequests() const;
1325
1329
1332
1335
1337 void enumerateRequests(Function<void(AsyncRequest&)> enumerationCallback);
1338
1342
1344 [[nodiscard]] static bool isExcludedFromActiveCount(const AsyncRequest& async);
1345
1348 [[nodiscard]] static bool tryLoadingLiburing();
1349
1352
1353 struct Internal;
1354
1355 public:
1357 {
1358 static constexpr int Windows = 520;
1359 static constexpr int Apple = 512;
1360 static constexpr int Linux = 720;
1361 static constexpr int Default = Linux;
1362
1363 static constexpr size_t Alignment = 8;
1364
1365 using Object = Internal;
1366 };
1367
1369
1370 private:
1371 InternalOpaque internalOpaque;
1372 Internal& internal;
1373
1374 friend struct AsyncRequest;
1375 friend struct AsyncFileWrite;
1376 friend struct AsyncFileRead;
1377 friend struct AsyncFileSystemOperation;
1378 friend struct AsyncResult;
1379};
1380
1384struct SC_COMPILER_EXPORT AsyncEventLoopMonitor
1385{
1387
1391
1394
1402
1408
1409 private:
1410#if SC_COMPILER_MSVC
1411#pragma warning(push)
1412#pragma warning(disable : 4324) // useless warning on 32 bit... (structure was padded due to __declspec(align()))
1413#endif
1414 alignas(uint64_t) uint8_t eventsMemory[8 * 1024]; // 8 Kb of kernel events
1415#if SC_COMPILER_MSVC
1416#pragma warning(pop)
1417#endif
1418
1419 AsyncKernelEvents asyncKernelEvents;
1420 AsyncEventLoop* eventLoop = nullptr;
1421 AsyncLoopWakeUp eventLoopWakeUp;
1422
1423 Thread eventLoopThread;
1424 EventObject eventObjectEnterBlockingMode;
1425 EventObject eventObjectExitBlockingMode;
1426
1427 Atomic<bool> finished = false;
1428 Atomic<bool> needsWakeUp = true;
1429
1430 bool wakeUpHasBeenCalled = false;
1431
1432 Result monitoringLoopThread(Thread& thread);
1433};
1434
1435} // namespace SC
1437#if SC_COMPILER_MSVC
1438#pragma warning(pop)
1439#endif
unsigned short uint16_t
Platform independent (2) bytes unsigned int.
Definition PrimitiveTypes.h:37
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition Compiler.h:264
unsigned char uint8_t
Platform independent (1) byte unsigned int.
Definition PrimitiveTypes.h:36
unsigned long long uint64_t
Platform independent (8) bytes unsigned int.
Definition PrimitiveTypes.h:42
short int16_t
Platform independent (2) bytes signed int.
Definition PrimitiveTypes.h:45
int int32_t
Platform independent (4) bytes signed int.
Definition PrimitiveTypes.h:46
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition Result.h:48
A buffer of bytes with given alignment.
Definition AlignedStorage.h:29
Empty base struct for all AsyncRequest-derived CompletionData (internal) structs.
Definition Async.h:253
Allow library user to provide callbacks signaling different phases of async event loop cycle.
Definition Async.h:1186
Monitors Async I/O events from a background thread using a blocking kernel function (no CPU usage on ...
Definition Async.h:1385
Function< void(void)> onNewEventsAvailable
Informs to call dispatchCompletions on GUI Event Loop.
Definition Async.h:1386
Result startMonitoring()
Queue all async requests submissions and start monitoring loop events on a background thread.
Result close()
Stop monitoring the AsyncEventLoop, disposing all resources.
Result stopMonitoringAndDispatchCompletions()
Stops monitoring events on the background thread and dispatches callbacks for completed requests.
Result create(AsyncEventLoop &eventLoop)
Create the monitoring thread for an AsyncEventLoop.
Options given to AsyncEventLoop::create.
Definition Async.h:1201
ApiType apiType
Criteria to choose Async IO API.
Definition Async.h:1208
ApiType
Definition Async.h:1203
@ Automatic
Platform specific backend chooses the best API.
@ ForceUseEpoll
(Linux only) Tries to use epoll
@ ForceUseIOURing
(Linux only) Tries to use io_uring (failing if it's not found on the system)
Asynchronous I/O (files, sockets, timers, processes, fs events, threads wake-up) (see Async) AsyncEve...
Definition Async.h:1198
Result associateExternallyCreatedFileDescriptor(FileDescriptor &outDescriptor)
Associates a previously created File Descriptor with the eventLoop.
Result wakeUpFromExternalThread()
Wake up the event loop from a thread different than the one where run() is called (and potentially bl...
Result runNoWait()
Process active requests if any, dispatching their completions, or returns immediately without blockin...
static Result removeAllAssociationsFor(SocketDescriptor &outDescriptor)
Removes association of a TCP Socket with any event loop.
void updateTime()
Updates loop time to "now".
static bool isExcludedFromActiveCount(const AsyncRequest &async)
Checks if excludeFromActiveCount() has been called on the given request.
Result associateExternallyCreatedSocket(SocketDescriptor &outDescriptor)
Associates a previously created TCP / UDP socket with the eventLoop.
Result blockingPoll(AsyncKernelEvents &kernelEvents)
Blocks until at least one event happens, ensuring forward progress, without executing completions.
void clearSequence(AsyncSequence &sequence)
Clears the sequence.
int getNumberOfSubmittedRequests() const
Obtain the total number of submitted requests.
Result submitRequests(AsyncKernelEvents &kernelEvents)
Submits all queued async requests.
void enumerateRequests(Function< void(AsyncRequest &)> enumerationCallback)
Enumerates all requests objects associated with this loop.
TimeMs getLoopTime() const
Get Loop time (monotonic)
Result start(AsyncRequest &async)
Queues an async request request that has been correctly setup.
AsyncLoopTimeout * findEarliestLoopTimeout() const
Returns the next AsyncLoopTimeout that will be executed (shortest relativeTimeout)
void setListeners(AsyncEventLoopListeners *listeners)
Sets reference to listeners that will signal different events in loop lifetime.
Result dispatchCompletions(AsyncKernelEvents &kernelEvents)
Invokes completions for the AsyncKernelEvents collected by a call to AsyncEventLoop::blockingPoll.
void interrupt()
Interrupts the event loop even if it has active request on it.
Result wakeUpFromExternalThread(AsyncLoopWakeUp &wakeUp)
Wake up the event loop from a thread different than the one where run() is called (and potentially bl...
bool isInitialized() const
Returns true if create has been already called (successfully)
Result create(Options options=Options())
Creates the event loop kernel object.
static bool tryLoadingLiburing()
Check if liburing is loadable (only on Linux)
Result createAsyncTCPSocket(SocketFlags::AddressFamily family, SocketDescriptor &outDescriptor)
Creates an async TCP (IPV4 / IPV6) socket registered with the eventLoop.
Result close()
Closes the event loop kernel object.
Result createAsyncUDPSocket(SocketFlags::AddressFamily family, SocketDescriptor &outDescriptor)
Creates an async UCP (IPV4 / IPV6) socket registered with the eventLoop.
static Result removeAllAssociationsFor(FileDescriptor &outDescriptor)
Removes association of a File Descriptor with any event loop.
Result runOnce()
Blocks until at least one request proceeds, ensuring forward progress, dispatching all completions.
void excludeFromActiveCount(AsyncRequest &async)
Excludes the request from active handles count (to avoid it keeping event loop alive)
Result run()
Blocks until there are no more active queued requests, dispatching all completions.
void includeInActiveCount(AsyncRequest &async)
Reverses the effect of excludeFromActiveCount for the request.
int getNumberOfActiveRequests() const
Obtain the total number of active requests.
Starts an handle polling operation.
Definition Async.h:826
SC::Result start(AsyncEventLoop &eventLoop, FileDescriptor::Handle fileDescriptor)
Starts a file descriptor poll operation, monitoring its readiness with appropriate OS API.
Definition Async.h:691
Definition Async.h:697
Starts a file read operation, reading bytes from a file (or pipe).
Definition Async.h:687
FileDescriptor::Handle handle
The writeable span of memory where to data will be written.
Definition Async.h:710
Span< char > buffer
Callback called when some data has been read from the file into the buffer.
Definition Async.h:709
void setOffset(uint64_t fileOffset)
Sets the offset in bytes at which start reading.
Definition Async.h:718
uint64_t getOffset() const
The file/pipe descriptor handle to read data from.
Definition Async.h:714
Starts an asynchronous file system operation (open, close, read, write, sendFile, stat,...
Definition Async.h:1000
SC::Result copyDirectory(AsyncEventLoop &eventLoop, StringSpan path, StringSpan destinationPath, FileSystemCopyFlags copyFlags=FileSystemCopyFlags())
Copies a directory from one location to another.
SC::Result removeEmptyDirectory(AsyncEventLoop &eventLoop, StringSpan path)
Removes a directory asynchronously.
SC::Result rename(AsyncEventLoop &eventLoop, StringSpan path, StringSpan newPath)
Renames a file.
SC::Result removeFile(AsyncEventLoop &eventLoop, StringSpan path)
Removes a file asynchronously.
SC::Result setThreadPool(ThreadPool &threadPool)
Sets the thread pool to use for the operation.
SC::Result read(AsyncEventLoop &eventLoop, FileDescriptor::Handle handle, Span< char > buffer, uint64_t offset)
Reads data from a file descriptor at a given offset.
SC::Result write(AsyncEventLoop &eventLoop, FileDescriptor::Handle handle, Span< const char > buffer, uint64_t offset)
Writes data to a file descriptor at a given offset.
SC::Result close(AsyncEventLoop &eventLoop, FileDescriptor::Handle handle)
Closes a file descriptor asynchronously.
Function< void(Result &)> callback
Called after the operation is completed, on the event loop thread.
Definition Async.h:1029
SC::Result copyFile(AsyncEventLoop &eventLoop, StringSpan path, StringSpan destinationPath, FileSystemCopyFlags copyFlags=FileSystemCopyFlags())
Copies a file from one location to another.
SC::Result open(AsyncEventLoop &eventLoop, StringSpan path, FileOpen mode)
Opens a file asynchronously and returns its corresponding file descriptor.
Definition Async.h:765
Starts a file write operation, writing bytes to a file (or pipe).
Definition Async.h:756
uint64_t getOffset() const
Returns the last offset set with AsyncFileWrite::setOffset.
Definition Async.h:793
FileDescriptor::Handle handle
The file/pipe descriptor to write data to.
Definition Async.h:785
SC::Result start(AsyncEventLoop &eventLoop, Span< const char > data)
Sets async request members and calls AsyncEventLoop::start.
void setOffset(uint64_t fileOffset)
Sets the offset in bytes at which start writing.
Definition Async.h:797
Function< void(Result &)> callback
Callback called when descriptor is ready to be written with more data.
Definition Async.h:783
Span< Span< const char > > buffers
The read-only spans of memory where to read the data from.
Definition Async.h:789
SC::Result start(AsyncEventLoop &eventLoop, Span< Span< const char > > data)
Sets async request members and calls AsyncEventLoop::start.
Span< const char > buffer
The read-only span of memory where to read the data from.
Definition Async.h:788
Allows user to supply a block of memory that will store kernel I/O events retrieved from AsyncEventLo...
Definition Async.h:1176
Span< uint8_t > eventsMemory
User supplied block of memory used to store kernel I/O events.
Definition Async.h:1177
Starts a Timeout that is invoked only once after expiration (relative) time has passed.
Definition Async.h:303
TimeMs getExpirationTime() const
Gets computed absolute expiration time that determines when this timeout get executed.
Definition Async.h:317
SC::Result start(AsyncEventLoop &eventLoop, TimeMs relativeTimeout)
Sets async request members and calls AsyncEventLoop::start.
TimeMs relativeTimeout
First timer expiration (relative) time in milliseconds.
Definition Async.h:314
Function< void(Result &)> callback
Called after given expiration time since AsyncLoopTimeout::start has passed.
Definition Async.h:312
Starts a wake-up operation, allowing threads to execute callbacks on loop thread.
Definition Async.h:338
SC::Result start(AsyncEventLoop &eventLoop, EventObject &eventObject)
Sets async request members and calls AsyncEventLoop::start.
Function< void(Result &)> callback
Callback called by SC::AsyncEventLoop::run after SC::AsyncLoopWakeUp::wakeUp.
Definition Async.h:351
SC::Result wakeUp(AsyncEventLoop &eventLoop)
Wakes up event loop, scheduling AsyncLoopWakeUp::callback on next AsyncEventLoop::run (or its variati...
Executes work in a thread pool and then invokes a callback on the event loop thread.
Definition Async.h:947
Function< void(Result &)> callback
Called to execute the work in a background threadpool thread.
Definition Async.h:958
SC::Result setThreadPool(ThreadPool &threadPool)
Sets the ThreadPool that will supply the thread to run the async work on.
Definition Async.h:375
Starts monitoring a process, notifying about its termination.
Definition Async.h:366
SC::Result start(AsyncEventLoop &eventLoop, FileDescriptor::Handle process)
Sets async request members and calls AsyncEventLoop::start.
Function< void(Result &)> callback
Called when process has exited.
Definition Async.h:391
Base class for all async requests, holding state and type.
Definition Async.h:120
bool isCancelling() const
Returns true if this request is being cancelled.
AsyncRequest(Type type)
Constructs a free async request of given type.
Definition Async.h:158
Result start(AsyncEventLoop &eventLoop)
Shortcut for AsyncEventLoop::start.
uint16_t getUserFlags() const
Gets user flags, holding some meaningful data for the caller.
Definition Async.h:187
Function< void(AsyncResult &)> * getCloseCallback()
Returns currently set close callback (if any) passed to AsyncRequest::stop.
Definition Async.h:190
bool isActive() const
Returns true if this request is active or being reactivated.
bool isFree() const
Returns true if this request is free.
void disableThreadPool()
Disables the thread-pool usage for this request.
Type getType() const
Returns request type.
Definition Async.h:178
void setUserFlags(uint16_t externalFlags)
Sets user flags, holding some meaningful data for the caller.
Definition Async.h:184
Result executeOn(AsyncTaskSequence &task, ThreadPool &pool)
Adds the request to be executed on a specific AsyncTaskSequence.
Result stop(AsyncEventLoop &eventLoop, Function< void(AsyncResult &)> *afterStopped=nullptr)
Ask to stop current async operation.
void executeOn(AsyncSequence &sequence)
Adds the request to be executed on a specific AsyncSequence.
Type
Type of async request.
Definition Async.h:139
Helper holding CompletionData for a specific AsyncRequest-derived class.
Definition Async.h:288
Base class for all async results (argument of completion callbacks).
Definition Async.h:258
const SC::Result & isValid() const
Check if the returnCode of this result is valid.
Definition Async.h:269
AsyncResult(AsyncEventLoop &eventLoop, AsyncRequest &request, SC::Result &res, bool *hasBeenReactivated=nullptr)
Constructs an async result from a request and a result.
Definition Async.h:260
void reactivateRequest(bool shouldBeReactivated)
Ask the event loop to re-activate this request after it was already completed.
Execute AsyncRequests serially, by submitting the next one after the previous one is completed.
Definition Async.h:238
Starts a socket accept operation, obtaining a new socket from a listening socket.
Definition Async.h:469
SC::Result start(AsyncEventLoop &eventLoop, const SocketDescriptor &socketDescriptor)
Sets async request members and calls AsyncEventLoop::start.
Starts a socket connect operation, connecting to a remote endpoint.
Definition Async.h:489
Function< void(Result &)> callback
Called after socket is finally connected to endpoint.
Definition Async.h:499
SC::Result start(AsyncEventLoop &eventLoop, const SocketDescriptor &descriptor, SocketIPAddress address)
Sets async request members and calls AsyncEventLoop::start.
Starts an unconnected socket receive from operation, receiving bytes from a remote endpoint.
Definition Async.h:652
Definition Async.h:609
SC::Result get(Span< char > &outData)
Get a Span of the actually read data.
Definition Async.h:615
Starts a socket receive operation, receiving bytes from a remote endpoint.
Definition Async.h:599
AsyncSocketReceive(Type type)
The Socket Descriptor handle to read data from.
Definition Async.h:634
SC::Result start(AsyncEventLoop &eventLoop, const SocketDescriptor &descriptor, Span< char > data)
Sets async request members and calls AsyncEventLoop::start.
Span< char > buffer
The writeable span of memory where to data will be written.
Definition Async.h:630
Function< void(Result &)> callback
Called after data has been received.
Definition Async.h:628
Starts an unconnected socket send to operation, sending bytes to a remote endpoint.
Definition Async.h:567
Starts a socket send operation, sending bytes to a remote endpoint.
Definition Async.h:523
Function< void(Result &)> callback
Called when socket is ready to send more data.
Definition Async.h:538
SC::Result start(AsyncEventLoop &eventLoop, const SocketDescriptor &descriptor, Span< const char > data)
Sets async request members and calls AsyncEventLoop::start.
Span< Span< const char > > buffers
Spans of bytes to send (singleBuffer == false)
Definition Async.h:543
Span< const char > buffer
Span of bytes to send (singleBuffer == true)
Definition Async.h:542
SC::Result start(AsyncEventLoop &eventLoop, const SocketDescriptor &descriptor, Span< Span< const char > > data)
Sets async request members and calls AsyncEventLoop::start.
An AsyncSequence using a SC::ThreadPool to execute one or more SC::AsyncRequest in a background threa...
Definition Async.h:928
Atomic variables (only for int and bool for now).
Definition Atomic.h:41
An automatically reset event object to synchronize two threads.
Definition Threading.h:237
[UniqueHandleDeclaration2Snippet]
Definition File.h:78
Options used to open a file descriptor.
Definition File.h:49
A structure to describe copy flags.
Definition FileSystem.h:25
Wraps function pointers, member functions and lambdas without ever allocating.
Definition Function.h:19
Hides implementation details from public headers (static PIMPL).
Definition OpaqueObject.h:31
An ascii string used as boolean result. SC_TRY macro forwards errors to caller.
Definition Result.h:12
Low-level OS socket handle.
Definition Socket.h:153
AddressFamily
Sets the address family of an IP Address (IPv4 or IPV6)
Definition Socket.h:60
Native representation of an IP Address.
Definition Socket.h:100
View over a contiguous sequence of items (pointer + size in elements).
Definition Span.h:29
An read-only view over a string (to avoid including Strings library when parsing is not needed).
Definition StringSpan.h:37
A small task containing a function to execute that can be queued in the thread pool.
Definition ThreadPool.h:19
Simple thread pool that executes tasks in a fixed number of worker threads.
Definition ThreadPool.h:41
A native OS thread.
Definition Threading.h:121
A vocabulary type representing a time interval in milliseconds since epoch.
Definition PrimitiveTypes.h:63