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
14namespace SC
15{
16struct ThreadPool;
17struct ThreadPoolTask;
18struct EventObject;
19} // namespace SC
44
47namespace SC
48{
49struct AsyncEventLoop;
50struct AsyncResult;
51struct AsyncSequence;
52struct AsyncTaskSequence;
53
54namespace detail
55{
56struct AsyncWinOverlapped;
57struct AsyncWinOverlappedDefinition
58{
59 static constexpr int Windows = sizeof(void*) * 4 + sizeof(uint64_t);
60 static constexpr size_t Alignment = alignof(void*);
61
62 using Object = AsyncWinOverlapped;
63};
64using WinOverlappedOpaque = OpaqueObject<AsyncWinOverlappedDefinition>;
65
66struct AsyncWinWaitDefinition
67{
68 using Handle = FileDescriptor::Handle; // fd
69 static constexpr Handle Invalid = FileDescriptor::Invalid; // invalid fd
70
71 static Result releaseHandle(Handle& waitHandle);
72};
73struct SC_COMPILER_EXPORT WinWaitHandle : public UniqueHandle<AsyncWinWaitDefinition>
74{
75};
76} // namespace detail
77
116struct SC_COMPILER_EXPORT AsyncRequest
117{
118 AsyncRequest* next = nullptr;
119 AsyncRequest* prev = nullptr;
120
121 void setDebugName(const char* newDebugName);
122
124 void executeOn(AsyncSequence& sequence);
125
130
133
135 enum class Type : uint8_t
136 {
137 LoopTimeout,
138 LoopWakeUp,
139 LoopWork,
140 ProcessExit,
141 SocketAccept,
142 SocketConnect,
143 SocketSend,
144 SocketSendTo,
145 SocketReceive,
146 SocketReceiveFrom,
147 FileRead,
148 FileWrite,
149 FilePoll,
150 FileSystemOperation,
151 };
152
155 AsyncRequest(Type type) : state(State::Free), type(type), flags(0), unused(0), userFlags(0) {}
156
163 Result stop(AsyncEventLoop& eventLoop, Function<void(AsyncResult&)>* afterStopped = nullptr);
164
166 [[nodiscard]] bool isFree() const;
167
169 [[nodiscard]] bool isCancelling() const;
170
172 [[nodiscard]] bool isActive() const;
173
175 [[nodiscard]] Type getType() const { return type; }
176
179
181 void setUserFlags(uint16_t externalFlags) { userFlags = externalFlags; }
182
184 uint16_t getUserFlags() const { return userFlags; }
185
187 [[nodiscard]] Function<void(AsyncResult&)>* getCloseCallback() { return closeCallback; }
188
189 [[nodiscard]] const Function<void(AsyncResult&)>* getCloseCallback() const { return closeCallback; }
190
191 protected:
192 Result checkState();
193
194 void queueSubmission(AsyncEventLoop& eventLoop);
195
196 AsyncSequence* sequence = nullptr;
197
198 AsyncTaskSequence* getTask();
199
200 private:
201 Function<void(AsyncResult&)>* closeCallback = nullptr;
202
203 friend struct AsyncEventLoop;
204 friend struct AsyncResult;
205
206 void markAsFree();
207
208 [[nodiscard]] static const char* TypeToString(Type type);
209 enum class State : uint8_t
210 {
211 Free, // not in any queue, this can be started with an async.start(...)
212 Setup, // when in submission queue waiting to be setup (after an async.start(...))
213 Submitting, // when in submission queue waiting to be activated or re-activated
214 Active, // when monitored by OS syscall or in activeLoopWakeUps / activeTimeouts queues
215 Reactivate, // when flagged for reactivation inside the callback (after a result.reactivateRequest(true))
216 Cancelling, // when in cancellation queue waiting for a cancelAsync (on active async)
217 };
218
219#if SC_ASYNC_ENABLE_LOG
220 const char* debugName = "None";
221#endif
222 State state; // 1 byte
223 Type type; // 1 byte
224 int16_t flags; // 2 bytes
225
226 uint16_t unused; // 2 bytes
227 uint16_t userFlags; // 2 bytes
228};
229
234struct SC_COMPILER_EXPORT AsyncSequence
235{
236 AsyncSequence* next = nullptr;
237 AsyncSequence* prev = nullptr;
238
239 bool clearSequenceOnCancel = true;
240 bool clearSequenceOnError = true;
241 private:
242 friend struct AsyncEventLoop;
243 bool runningAsync = false; // true if an async from this sequence is being run
244 bool tracked = false;
245
246 IntrusiveDoubleLinkedList<AsyncRequest> submissions;
247};
248
250struct SC_COMPILER_EXPORT AsyncCompletionData{};
251
254struct SC_COMPILER_EXPORT AsyncResult
255{
257 AsyncResult(AsyncEventLoop& eventLoop, AsyncRequest& request, SC::Result& res, bool* hasBeenReactivated = nullptr)
258 : eventLoop(eventLoop), async(request), hasBeenReactivated(hasBeenReactivated), returnCode(res)
259 {}
260
263 void reactivateRequest(bool shouldBeReactivated);
264
266 [[nodiscard]] const SC::Result& isValid() const { return returnCode; }
267
268 AsyncEventLoop& eventLoop;
269 AsyncRequest& async;
270
271 protected:
272 friend struct AsyncEventLoop;
273
274 bool shouldCallCallback = true;
275 bool* hasBeenReactivated = nullptr;
276
277 SC::Result& returnCode;
278};
279
283template <typename T, typename C>
285{
286 T& getAsync() { return static_cast<T&>(AsyncResult::async); }
287 const T& getAsync() const { return static_cast<const T&>(AsyncResult::async); }
288
290
291 C completionData;
292 int32_t eventIndex = 0;
293};
294
299struct SC_COMPILER_EXPORT AsyncLoopTimeout : public AsyncRequest
300{
301 AsyncLoopTimeout() : AsyncRequest(Type::LoopTimeout) {}
302
305 using AsyncRequest::start;
306
308 SC::Result start(AsyncEventLoop& eventLoop, TimeMs relativeTimeout);
310
312
314 TimeMs getExpirationTime() const { return expirationTime; }
315
316 private:
317 SC::Result validate(AsyncEventLoop&);
318 friend struct AsyncEventLoop;
319 TimeMs expirationTime;
320};
321
334struct SC_COMPILER_EXPORT AsyncLoopWakeUp : public AsyncRequest
335{
336 AsyncLoopWakeUp() : AsyncRequest(Type::LoopWakeUp) {}
337
340 using AsyncRequest::start;
341
343 SC::Result start(AsyncEventLoop& eventLoop, EventObject& eventObject);
344
347
349 EventObject* eventObject = nullptr;
350
351 private:
352 friend struct AsyncEventLoop;
353 SC::Result validate(AsyncEventLoop&);
354
355 Atomic<bool> pending = false;
356};
357
362struct SC_COMPILER_EXPORT AsyncProcessExit : public AsyncRequest
363{
364 AsyncProcessExit() : AsyncRequest(Type::ProcessExit) {}
365
367 {
368 int exitStatus;
369 };
370
371 struct Result : public AsyncResultOf<AsyncProcessExit, CompletionData>
372 {
373 using AsyncResultOf<AsyncProcessExit, CompletionData>::AsyncResultOf;
374
375 SC::Result get(int& status)
376 {
377 status = completionData.exitStatus;
378 return returnCode;
379 }
380 };
381 using AsyncRequest::start;
382
386 SC::Result start(AsyncEventLoop& eventLoop, FileDescriptor::Handle process);
387
389
390 private:
391 friend struct AsyncEventLoop;
392 SC::Result validate(AsyncEventLoop&);
393
394 FileDescriptor::Handle handle = FileDescriptor::Invalid;
395#if SC_PLATFORM_WINDOWS
397 detail::WinWaitHandle waitHandle;
398 AsyncEventLoop* eventLoop = nullptr;
399#elif SC_PLATFORM_LINUX
400 FileDescriptor pidFd;
401#endif
402};
403
404struct AsyncSocketAccept;
405namespace detail
406{
409struct SC_COMPILER_EXPORT AsyncSocketAcceptData
410{
411#if SC_PLATFORM_WINDOWS
412 void (*pAcceptEx)() = nullptr;
414 SocketDescriptor clientSocket;
415 uint8_t acceptBuffer[288] = {0};
416#elif SC_PLATFORM_LINUX
417 AlignedStorage<28> sockAddrHandle;
418 uint32_t sockAddrLen;
419#endif
420};
421
423struct SC_COMPILER_EXPORT AsyncSocketAcceptBase : public AsyncRequest
424{
425 AsyncSocketAcceptBase() : AsyncRequest(Type::SocketAccept) {}
426
427 struct CompletionData : public AsyncCompletionData
428 {
429 SocketDescriptor acceptedClient;
430 };
431
432 struct Result : public AsyncResultOf<AsyncSocketAccept, CompletionData>
433 {
434 using AsyncResultOf<AsyncSocketAccept, CompletionData>::AsyncResultOf;
435
436 SC::Result moveTo(SocketDescriptor& client)
437 {
438 SC_TRY(returnCode);
439 return client.assign(move(completionData.acceptedClient));
440 }
441 };
442 using AsyncRequest::start;
443
445 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& socketDescriptor, AsyncSocketAcceptData& data);
446 SC::Result validate(AsyncEventLoop&);
447
448 Function<void(Result&)> callback;
449 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
450 SocketFlags::AddressFamily addressFamily = SocketFlags::AddressFamilyIPV4;
451 AsyncSocketAcceptData* acceptData = nullptr;
452};
453
454} // namespace detail
455
465struct SC_COMPILER_EXPORT AsyncSocketAccept : public detail::AsyncSocketAcceptBase
466{
467 AsyncSocketAccept() { AsyncSocketAcceptBase::acceptData = &data; }
468 using AsyncSocketAcceptBase::start;
469
471 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& socketDescriptor);
472
473 private:
474 detail::AsyncSocketAcceptData data;
475};
476
485struct SC_COMPILER_EXPORT AsyncSocketConnect : public AsyncRequest
486{
487 AsyncSocketConnect() : AsyncRequest(Type::SocketConnect) {}
488
491 using AsyncRequest::start;
492
494 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, SocketIPAddress address);
495
497
498 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
499 SocketIPAddress ipAddress;
500
501 private:
502 friend struct AsyncEventLoop;
503 SC::Result validate(AsyncEventLoop&);
504
505#if SC_PLATFORM_WINDOWS
506 void (*pConnectEx)() = nullptr;
508#endif
509};
510
519struct SC_COMPILER_EXPORT AsyncSocketSend : public AsyncRequest
520{
521 AsyncSocketSend() : AsyncRequest(Type::SocketSend) {}
523 {
524 size_t numBytes = 0;
525 };
527 using AsyncRequest::start;
528
531
534
536
537 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
538
541 bool singleBuffer = true;
542
543 protected:
544 AsyncSocketSend(Type type) : AsyncRequest(type) {}
545 friend struct AsyncEventLoop;
546 SC::Result validate(AsyncEventLoop&);
547
548 size_t totalBytesWritten = 0;
549#if SC_PLATFORM_WINDOWS
551#endif
552};
553
563struct SC_COMPILER_EXPORT AsyncSocketSendTo : public AsyncSocketSend
564{
565 AsyncSocketSendTo() : AsyncSocketSend(Type::SocketSendTo) {}
566
567 SocketIPAddress address;
568
569 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, SocketIPAddress ipAddress,
570 Span<const char> data);
571
572 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, SocketIPAddress ipAddress,
573 Span<Span<const char>> data);
574
575 private:
576 using AsyncSocketSend::start;
577 friend struct AsyncEventLoop;
578 SC::Result validate(AsyncEventLoop&);
579#if SC_PLATFORM_LINUX
580 AlignedStorage<56> typeErasedMsgHdr;
581#endif
582};
583
595struct SC_COMPILER_EXPORT AsyncSocketReceive : public AsyncRequest
596{
597 AsyncSocketReceive() : AsyncRequest(Type::SocketReceive) {}
598
600 {
601 size_t numBytes = 0;
602 bool disconnected = false;
603 };
604
605 struct Result : public AsyncResultOf<AsyncSocketReceive, CompletionData>
606 {
607 using AsyncResultOf<AsyncSocketReceive, CompletionData>::AsyncResultOf;
608
613 {
614 SC_TRY(getAsync().buffer.sliceStartLength(0, completionData.numBytes, outData));
615 return returnCode;
616 }
617
618 SocketIPAddress getSourceAddress() const;
619 };
620 using AsyncRequest::start;
621
623 SC::Result start(AsyncEventLoop& eventLoop, const SocketDescriptor& descriptor, Span<char> data);
624
626
628 SocketDescriptor::Handle handle = SocketDescriptor::Invalid;
629
630 protected:
632 friend struct AsyncEventLoop;
633 SC::Result validate(AsyncEventLoop&);
634#if SC_PLATFORM_WINDOWS
636#endif
637};
638
648struct SC_COMPILER_EXPORT AsyncSocketReceiveFrom : public AsyncSocketReceive
649{
650 AsyncSocketReceiveFrom() : AsyncSocketReceive(Type::SocketReceiveFrom) {}
651 using AsyncSocketReceive::start;
652
653 private:
654 SocketIPAddress address;
655 friend struct AsyncSocketReceive;
656 friend struct AsyncEventLoop;
657#if SC_PLATFORM_LINUX
658 AlignedStorage<56> typeErasedMsgHdr;
659#endif
660};
661
683struct SC_COMPILER_EXPORT AsyncFileRead : public AsyncRequest
684{
685 AsyncFileRead() : AsyncRequest(Type::FileRead) { handle = FileDescriptor::Invalid; }
686
688 {
689 size_t numBytes = 0;
690 bool endOfFile = false;
691 };
692
693 struct Result : public AsyncResultOf<AsyncFileRead, CompletionData>
694 {
695 using AsyncResultOf<AsyncFileRead, CompletionData>::AsyncResultOf;
696
697 SC::Result get(Span<char>& data)
698 {
699 SC_TRY(getAsync().buffer.sliceStartLength(0, completionData.numBytes, data));
700 return returnCode;
701 }
702 };
703 using AsyncRequest::start;
704
705 Function<void(Result&)> callback;
707 FileDescriptor::Handle handle;
709
711 uint64_t getOffset() const { return offset; }
712
715 void setOffset(uint64_t fileOffset)
716 {
717 useOffset = true;
718 offset = fileOffset;
719 }
720
721 private:
722 friend struct AsyncEventLoop;
723 SC::Result validate(AsyncEventLoop&);
724
725 bool useOffset = false;
726 bool endedSync = false;
727
728 uint64_t offset = 0;
729#if SC_PLATFORM_WINDOWS
730 uint64_t readCursor = 0;
732#endif
733};
734
752struct SC_COMPILER_EXPORT AsyncFileWrite : public AsyncRequest
753{
754 AsyncFileWrite() : AsyncRequest(Type::FileWrite) { handle = FileDescriptor::Invalid; }
755
757 {
758 size_t numBytes = 0;
759 };
760
761 struct Result : public AsyncResultOf<AsyncFileWrite, CompletionData>
762 {
763 using AsyncResultOf<AsyncFileWrite, CompletionData>::AsyncResultOf;
764
765 SC::Result get(size_t& writtenSizeInBytes)
766 {
767 writtenSizeInBytes = completionData.numBytes;
768 return returnCode;
769 }
770 };
771
772 using AsyncRequest::start;
773
776
779
781
782 FileDescriptor::Handle handle;
784
787 bool singleBuffer = true;
788
790 uint64_t getOffset() const { return offset; }
791
794 void setOffset(uint64_t fileOffset)
795 {
796 useOffset = true;
797 offset = fileOffset;
798 }
799
800 private:
801 friend struct AsyncEventLoop;
802 SC::Result validate(AsyncEventLoop&);
803
804#if SC_PLATFORM_WINDOWS
805 bool endedSync = false;
806#else
807 bool isWatchable = false;
808#endif
809 bool useOffset = false;
810 uint64_t offset = 0xffffffffffffffff;
811
812 size_t totalBytesWritten = 0;
813#if SC_PLATFORM_WINDOWS
815#endif
816};
817
822struct SC_COMPILER_EXPORT AsyncFilePoll : public AsyncRequest
823{
824 AsyncFilePoll() : AsyncRequest(Type::FilePoll) {}
825
828
830 SC::Result start(AsyncEventLoop& eventLoop, FileDescriptor::Handle fileDescriptor);
831
832#if SC_PLATFORM_WINDOWS
833 [[nodiscard]] void* getOverlappedPtr();
834#endif
835
836 Function<void(Result&)> callback;
837
838 private:
839 friend struct AsyncEventLoop;
840 SC::Result validate(AsyncEventLoop&);
841
842 FileDescriptor::Handle handle = FileDescriptor::Invalid;
843#if SC_PLATFORM_WINDOWS
845#endif
846};
847
848// forward declared because it must be defined after AsyncTaskSequence
849struct AsyncLoopWork;
851
853{
854 FileDescriptor::Handle handle = FileDescriptor::Invalid; // for open
855
856 int code = 0; // for open/close
857 size_t numBytes = 0; // for read
858};
859
860namespace detail
861{
862// A simple hand-made variant of all completion types
863struct SC_COMPILER_EXPORT AsyncCompletionVariant
864{
865 AsyncCompletionVariant() {}
866 ~AsyncCompletionVariant() { destroy(); }
867
868 AsyncCompletionVariant(const AsyncCompletionVariant&) = delete;
869 AsyncCompletionVariant(AsyncCompletionVariant&&) = delete;
870 AsyncCompletionVariant& operator=(const AsyncCompletionVariant&) = delete;
871 AsyncCompletionVariant& operator=(AsyncCompletionVariant&&) = delete;
872
873 bool inited = false;
874
875 AsyncRequest::Type type;
876 union
877 {
878 AsyncCompletionData completionDataLoopWork; // Defined after AsyncCompletionVariant / AsyncTaskSequence
879 AsyncLoopTimeout::CompletionData completionDataLoopTimeout;
880 AsyncLoopWakeUp::CompletionData completionDataLoopWakeUp;
881 AsyncProcessExit::CompletionData completionDataProcessExit;
882 AsyncSocketAccept::CompletionData completionDataSocketAccept;
883 AsyncSocketConnect::CompletionData completionDataSocketConnect;
884 AsyncSocketSend::CompletionData completionDataSocketSend;
885 AsyncSocketSendTo::CompletionData completionDataSocketSendTo;
886 AsyncSocketReceive::CompletionData completionDataSocketReceive;
887 AsyncSocketReceiveFrom::CompletionData completionDataSocketReceiveFrom;
888 AsyncFileRead::CompletionData completionDataFileRead;
889 AsyncFileWrite::CompletionData completionDataFileWrite;
890 AsyncFilePoll::CompletionData completionDataFilePoll;
891
892 AsyncFileSystemOperationCompletionData completionDataFileSystemOperation;
893 };
894
895 auto& getCompletion(AsyncLoopWork&) { return completionDataLoopWork; }
896 auto& getCompletion(AsyncLoopTimeout&) { return completionDataLoopTimeout; }
897 auto& getCompletion(AsyncLoopWakeUp&) { return completionDataLoopWakeUp; }
898 auto& getCompletion(AsyncProcessExit&) { return completionDataProcessExit; }
899 auto& getCompletion(AsyncSocketAccept&) { return completionDataSocketAccept; }
900 auto& getCompletion(AsyncSocketConnect&) { return completionDataSocketConnect; }
901 auto& getCompletion(AsyncSocketSend&) { return completionDataSocketSend; }
902 auto& getCompletion(AsyncSocketReceive&) { return completionDataSocketReceive; }
903 auto& getCompletion(AsyncFileRead&) { return completionDataFileRead; }
904 auto& getCompletion(AsyncFileWrite&) { return completionDataFileWrite; }
905 auto& getCompletion(AsyncFilePoll&) { return completionDataFilePoll; }
906 auto& getCompletion(AsyncFileSystemOperation&) { return completionDataFileSystemOperation; }
907
908 template <typename T>
909 auto& construct(T& t)
910 {
911 destroy();
912 placementNew(getCompletion(t));
913 inited = true;
914 type = t.getType();
915 return getCompletion(t);
916 }
917 void destroy();
918};
919} // namespace detail
920
924struct SC_COMPILER_EXPORT AsyncTaskSequence : public AsyncSequence
925{
926 protected:
927 ThreadPoolTask task;
928 ThreadPool* threadPool = nullptr;
929
930 friend struct AsyncEventLoop;
931 friend struct AsyncRequest;
932
933 detail::AsyncCompletionVariant completion;
934
935 SC::Result returnCode = SC::Result(true);
936};
937
943struct SC_COMPILER_EXPORT AsyncLoopWork : public AsyncRequest
944{
945 AsyncLoopWork() : AsyncRequest(Type::LoopWork) {}
946
949
953
954 Function<SC::Result()> work;
956
957 private:
958 friend struct AsyncEventLoop;
959 SC::Result validate(AsyncEventLoop&);
961};
962
996struct SC_COMPILER_EXPORT AsyncFileSystemOperation : public AsyncRequest
997{
998 AsyncFileSystemOperation() : AsyncRequest(Type::FileSystemOperation) {}
999 ~AsyncFileSystemOperation() { destroy(); }
1000#ifdef CopyFile
1001#undef CopyFile
1002#endif
1003#ifdef RemoveDirectory
1004#undef RemoveDirectory
1005#endif
1006 enum class Operation
1007 {
1008 None = 0,
1009 Open,
1010 Close,
1011 Read,
1012 Write,
1013 CopyFile,
1014 CopyDirectory,
1015 Rename,
1016 RemoveDirectory,
1017 RemoveFile,
1018 };
1019
1022
1025
1027
1034
1039 SC::Result close(AsyncEventLoop& eventLoop, FileDescriptor::Handle handle);
1040
1047 SC::Result read(AsyncEventLoop& eventLoop, FileDescriptor::Handle handle, Span<char> buffer, uint64_t offset);
1048
1055 SC::Result write(AsyncEventLoop& eventLoop, FileDescriptor::Handle handle, Span<const char> buffer,
1056 uint64_t offset);
1057
1064 SC::Result copyFile(AsyncEventLoop& eventLoop, StringSpan path, StringSpan destinationPath,
1066
1075
1082
1089
1095
1096 private:
1097 friend struct AsyncEventLoop;
1098 Operation operation = Operation::None;
1099 AsyncLoopWork loopWork;
1100 CompletionData completionData;
1101
1102 void onOperationCompleted(AsyncLoopWork::Result& res);
1103
1104 struct FileDescriptorData
1105 {
1106 FileDescriptor::Handle handle;
1107 };
1108
1109 struct OpenData
1110 {
1111 StringSpan path;
1112 FileOpen mode;
1113 };
1114
1115 struct ReadData
1116 {
1117 FileDescriptor::Handle handle;
1118 Span<char> buffer;
1119 uint64_t offset;
1120 };
1121
1122 struct WriteData
1123 {
1124 FileDescriptor::Handle handle;
1125 Span<const char> buffer;
1126 uint64_t offset;
1127 };
1128
1129 struct CopyFileData
1130 {
1131 StringSpan path;
1132 StringSpan destinationPath;
1133 FileSystemCopyFlags copyFlags;
1134 };
1135
1136 using CopyDirectoryData = CopyFileData;
1137
1138 using CloseData = FileDescriptorData;
1139
1140 struct RenameData
1141 {
1142 StringSpan path;
1143 StringSpan newPath;
1144 };
1145
1146 struct RemoveData
1147 {
1148 StringSpan path;
1149 };
1150
1151 union
1152 {
1153 OpenData openData;
1154 CloseData closeData;
1155 ReadData readData;
1156 WriteData writeData;
1157 CopyFileData copyFileData;
1158 CopyDirectoryData copyDirectoryData;
1159 RenameData renameData;
1160 RemoveData removeData;
1161 };
1162
1163 void destroy();
1164
1165 SC::Result start(AsyncEventLoop& eventLoop, FileDescriptor::Handle fileDescriptor);
1166 SC::Result validate(AsyncEventLoop&);
1167};
1168
1172struct SC_COMPILER_EXPORT AsyncKernelEvents
1173{
1175
1176 private:
1177 int numberOfEvents = 0;
1178 friend struct AsyncEventLoop;
1179};
1180
1182struct SC_COMPILER_EXPORT AsyncEventLoopListeners
1183{
1184 Function<void(AsyncEventLoop&)> beforeBlockingPoll;
1185 Function<void(AsyncEventLoop&)> afterBlockingPoll;
1186};
1187
1194struct SC_COMPILER_EXPORT AsyncEventLoop
1195{
1197 struct Options
1198 {
1199 enum class ApiType : uint8_t
1200 {
1201 Automatic = 0,
1202 ForceUseIOURing,
1203 ForceUseEpoll,
1204 };
1206
1207 Options() { apiType = ApiType::Automatic; }
1208 };
1209
1211
1212 AsyncEventLoop(const AsyncEventLoop&) = delete;
1213 AsyncEventLoop(AsyncEventLoop&&) = delete;
1214 AsyncEventLoop& operator=(AsyncEventLoop&&) = delete;
1215 AsyncEventLoop& operator=(const AsyncEventLoop&) = delete;
1216
1219
1222
1226
1229
1231 [[nodiscard]] bool isInitialized() const;
1232
1234 [[nodiscard]] bool needsThreadPoolForFileOperations() const;
1235
1242
1253
1260
1266
1286
1293
1297
1300
1303
1306
1309
1312
1315
1318
1321
1323 [[nodiscard]] TimeMs getLoopTime() const;
1324
1326 [[nodiscard]] int getNumberOfActiveRequests() const;
1327
1329 [[nodiscard]] int getNumberOfSubmittedRequests() const;
1330
1334
1337
1340
1342 void enumerateRequests(Function<void(AsyncRequest&)> enumerationCallback);
1343
1347
1349 [[nodiscard]] static bool isExcludedFromActiveCount(const AsyncRequest& async);
1350
1353 [[nodiscard]] static bool tryLoadingLiburing();
1354
1357
1358 struct Internal;
1359
1360 public:
1361 struct SC_COMPILER_EXPORT InternalDefinition
1362 {
1363 static constexpr int Windows = 520;
1364 static constexpr int Apple = 512;
1365 static constexpr int Linux = 720;
1366 static constexpr int Default = Linux;
1367
1368 static constexpr size_t Alignment = 8;
1369
1370 using Object = Internal;
1371 };
1372
1374
1375 private:
1376 InternalOpaque internalOpaque;
1377 Internal& internal;
1378
1379 friend struct AsyncRequest;
1380 friend struct AsyncFileWrite;
1381 friend struct AsyncFileRead;
1382 friend struct AsyncFileSystemOperation;
1383 friend struct AsyncResult;
1384};
1385
1389struct SC_COMPILER_EXPORT AsyncEventLoopMonitor
1390{
1392
1396
1399
1407
1413
1414 private:
1415#if SC_COMPILER_MSVC
1416#pragma warning(push)
1417#pragma warning(disable : 4324) // useless warning on 32 bit... (structure was padded due to __declspec(align()))
1418#endif
1419 alignas(uint64_t) uint8_t eventsMemory[8 * 1024]; // 8 Kb of kernel events
1420#if SC_COMPILER_MSVC
1421#pragma warning(pop)
1422#endif
1423
1424 AsyncKernelEvents asyncKernelEvents;
1425 AsyncEventLoop* eventLoop = nullptr;
1426 AsyncLoopWakeUp eventLoopWakeUp;
1427
1428 Thread eventLoopThread;
1429 EventObject eventObjectEnterBlockingMode;
1430 EventObject eventObjectExitBlockingMode;
1431
1432 Atomic<bool> finished = false;
1433 Atomic<bool> needsWakeUp = true;
1434
1435 bool wakeUpHasBeenCalled = false;
1436
1437 Result monitoringLoopThread(Thread& thread);
1438};
1439
1440} // namespace SC
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:49
struct SC_COMPILER_EXPORT Function
Wraps function pointers, member functions and lambdas without ever allocating.
Definition Function.h:19
A buffer of bytes with given alignment.
Definition AlignedStorage.h:29
Empty base struct for all AsyncRequest-derived CompletionData (internal) structs.
Definition Async.h:250
Allow library user to provide callbacks signaling different phases of async event loop cycle.
Definition Async.h:1183
Monitors Async I/O events from a background thread using a blocking kernel function (no CPU usage on ...
Definition Async.h:1390
Function< void(void)> onNewEventsAvailable
Informs to call dispatchCompletions on GUI Event Loop.
Definition Async.h:1391
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:1198
ApiType apiType
Criteria to choose Async IO API.
Definition Async.h:1205
ApiType
Definition Async.h:1200
Asynchronous I/O (files, sockets, timers, processes, fs events, threads wake-up) (see Async) AsyncEve...
Definition Async.h:1195
bool needsThreadPoolForFileOperations() const
Returns true if backend needs a thread pool for non-blocking fs operations (anything but io_uring bas...
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:823
SC::Result start(AsyncEventLoop &eventLoop, FileDescriptor::Handle fileDescriptor)
Starts a file descriptor poll operation, monitoring its readiness with appropriate OS API.
Definition Async.h:688
Definition Async.h:694
Starts a file read operation, reading bytes from a file (or pipe).
Definition Async.h:684
FileDescriptor::Handle handle
The writeable span of memory where to data will be written.
Definition Async.h:707
Span< char > buffer
Callback called when some data has been read from the file into the buffer.
Definition Async.h:706
void setOffset(uint64_t fileOffset)
Sets the offset in bytes at which start reading.
Definition Async.h:715
uint64_t getOffset() const
The file/pipe descriptor handle to read data from.
Definition Async.h:711
Starts an asynchronous file system operation (open, close, read, write, sendFile, stat,...
Definition Async.h:997
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:1026
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:762
Starts a file write operation, writing bytes to a file (or pipe).
Definition Async.h:753
uint64_t getOffset() const
Returns the last offset set with AsyncFileWrite::setOffset.
Definition Async.h:790
FileDescriptor::Handle handle
The file/pipe descriptor to write data to.
Definition Async.h:782
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:794
Function< void(Result &)> callback
Callback called when descriptor is ready to be written with more data.
Definition Async.h:780
Span< Span< const char > > buffers
The read-only spans of memory where to read the data from.
Definition Async.h:786
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:785
Allows user to supply a block of memory that will store kernel I/O events retrieved from AsyncEventLo...
Definition Async.h:1173
Span< uint8_t > eventsMemory
User supplied block of memory used to store kernel I/O events.
Definition Async.h:1174
Starts a Timeout that is invoked only once after expiration (relative) time has passed.
Definition Async.h:300
TimeMs getExpirationTime() const
Gets computed absolute expiration time that determines when this timeout get executed.
Definition Async.h:314
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:311
Function< void(Result &)> callback
Called after given expiration time since AsyncLoopTimeout::start has passed.
Definition Async.h:309
Starts a wake-up operation, allowing threads to execute callbacks on loop thread.
Definition Async.h:335
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:348
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:944
Function< void(Result &)> callback
Called to execute the work in a background threadpool thread.
Definition Async.h:955
SC::Result setThreadPool(ThreadPool &threadPool)
Sets the ThreadPool that will supply the thread to run the async work on.
Definition Async.h:372
Starts monitoring a process, notifying about its termination.
Definition Async.h:363
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:388
Base class for all async requests, holding state and type.
Definition Async.h:117
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:155
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:184
Function< void(AsyncResult &)> * getCloseCallback()
Returns currently set close callback (if any) passed to AsyncRequest::stop.
Definition Async.h:187
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:175
void setUserFlags(uint16_t externalFlags)
Sets user flags, holding some meaningful data for the caller.
Definition Async.h:181
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:136
Helper holding CompletionData for a specific AsyncRequest-derived class.
Definition Async.h:285
Base class for all async results (argument of completion callbacks).
Definition Async.h:255
const SC::Result & isValid() const
Check if the returnCode of this result is valid.
Definition Async.h:266
AsyncResult(AsyncEventLoop &eventLoop, AsyncRequest &request, SC::Result &res, bool *hasBeenReactivated=nullptr)
Constructs an async result from a request and a result.
Definition Async.h:257
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:235
Starts a socket accept operation, obtaining a new socket from a listening socket.
Definition Async.h:466
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:486
Function< void(Result &)> callback
Called after socket is finally connected to endpoint.
Definition Async.h:496
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:649
Definition Async.h:606
SC::Result get(Span< char > &outData)
Get a Span of the actually read data.
Definition Async.h:612
Starts a socket receive operation, receiving bytes from a remote endpoint.
Definition Async.h:596
AsyncSocketReceive(Type type)
The Socket Descriptor handle to read data from.
Definition Async.h:631
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:627
Function< void(Result &)> callback
Called after data has been received.
Definition Async.h:625
Starts an unconnected socket send to operation, sending bytes to a remote endpoint.
Definition Async.h:564
Starts a socket send operation, sending bytes to a remote endpoint.
Definition Async.h:520
Function< void(Result &)> callback
Called when socket is ready to send more data.
Definition Async.h:535
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:540
Span< const char > buffer
Span of bytes to send (singleBuffer == true)
Definition Async.h:539
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:925
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:229
[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
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:13
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:16
Simple thread pool that executes tasks in a fixed number of worker threads.
Definition ThreadPool.h:38
A native OS thread.
Definition Threading.h:113
A vocabulary type representing a time interval in milliseconds since epoch.
Definition PrimitiveTypes.h:63