opengris-scaler 1.12.7__cp310-cp310-manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opengris-scaler might be problematic. Click here for more details.

Files changed (232) hide show
  1. opengris_scaler-1.12.7.dist-info/METADATA +729 -0
  2. opengris_scaler-1.12.7.dist-info/RECORD +232 -0
  3. opengris_scaler-1.12.7.dist-info/WHEEL +5 -0
  4. opengris_scaler-1.12.7.dist-info/entry_points.txt +9 -0
  5. opengris_scaler-1.12.7.dist-info/licenses/LICENSE +201 -0
  6. opengris_scaler-1.12.7.dist-info/licenses/LICENSE.spdx +7 -0
  7. opengris_scaler-1.12.7.dist-info/licenses/NOTICE +8 -0
  8. opengris_scaler.libs/libcapnp-1-b787335c.1.0.so +0 -0
  9. opengris_scaler.libs/libkj-1-094aa318.1.0.so +0 -0
  10. scaler/CMakeLists.txt +11 -0
  11. scaler/__init__.py +14 -0
  12. scaler/about.py +5 -0
  13. scaler/client/__init__.py +0 -0
  14. scaler/client/agent/__init__.py +0 -0
  15. scaler/client/agent/client_agent.py +210 -0
  16. scaler/client/agent/disconnect_manager.py +27 -0
  17. scaler/client/agent/future_manager.py +112 -0
  18. scaler/client/agent/heartbeat_manager.py +74 -0
  19. scaler/client/agent/mixins.py +89 -0
  20. scaler/client/agent/object_manager.py +98 -0
  21. scaler/client/agent/task_manager.py +64 -0
  22. scaler/client/client.py +635 -0
  23. scaler/client/future.py +252 -0
  24. scaler/client/object_buffer.py +129 -0
  25. scaler/client/object_reference.py +25 -0
  26. scaler/client/serializer/__init__.py +0 -0
  27. scaler/client/serializer/default.py +16 -0
  28. scaler/client/serializer/mixins.py +38 -0
  29. scaler/cluster/__init__.py +0 -0
  30. scaler/cluster/cluster.py +115 -0
  31. scaler/cluster/combo.py +148 -0
  32. scaler/cluster/object_storage_server.py +45 -0
  33. scaler/cluster/scheduler.py +83 -0
  34. scaler/config/__init__.py +0 -0
  35. scaler/config/defaults.py +87 -0
  36. scaler/config/loader.py +95 -0
  37. scaler/config/mixins.py +15 -0
  38. scaler/config/section/__init__.py +0 -0
  39. scaler/config/section/cluster.py +56 -0
  40. scaler/config/section/native_worker_adapter.py +44 -0
  41. scaler/config/section/object_storage_server.py +7 -0
  42. scaler/config/section/scheduler.py +53 -0
  43. scaler/config/section/symphony_worker_adapter.py +47 -0
  44. scaler/config/section/top.py +13 -0
  45. scaler/config/section/webui.py +16 -0
  46. scaler/config/types/__init__.py +0 -0
  47. scaler/config/types/object_storage_server.py +45 -0
  48. scaler/config/types/worker.py +57 -0
  49. scaler/config/types/zmq.py +79 -0
  50. scaler/entry_points/__init__.py +0 -0
  51. scaler/entry_points/cluster.py +133 -0
  52. scaler/entry_points/object_storage_server.py +41 -0
  53. scaler/entry_points/scheduler.py +135 -0
  54. scaler/entry_points/top.py +286 -0
  55. scaler/entry_points/webui.py +26 -0
  56. scaler/entry_points/worker_adapter_native.py +137 -0
  57. scaler/entry_points/worker_adapter_symphony.py +102 -0
  58. scaler/io/__init__.py +0 -0
  59. scaler/io/async_binder.py +85 -0
  60. scaler/io/async_connector.py +95 -0
  61. scaler/io/async_object_storage_connector.py +185 -0
  62. scaler/io/mixins.py +154 -0
  63. scaler/io/sync_connector.py +68 -0
  64. scaler/io/sync_object_storage_connector.py +185 -0
  65. scaler/io/sync_subscriber.py +83 -0
  66. scaler/io/utility.py +31 -0
  67. scaler/io/ymq/CMakeLists.txt +98 -0
  68. scaler/io/ymq/__init__.py +0 -0
  69. scaler/io/ymq/_ymq.pyi +96 -0
  70. scaler/io/ymq/_ymq.so +0 -0
  71. scaler/io/ymq/bytes.h +114 -0
  72. scaler/io/ymq/common.h +29 -0
  73. scaler/io/ymq/configuration.h +60 -0
  74. scaler/io/ymq/epoll_context.cpp +185 -0
  75. scaler/io/ymq/epoll_context.h +85 -0
  76. scaler/io/ymq/error.h +132 -0
  77. scaler/io/ymq/event_loop.h +55 -0
  78. scaler/io/ymq/event_loop_thread.cpp +64 -0
  79. scaler/io/ymq/event_loop_thread.h +46 -0
  80. scaler/io/ymq/event_manager.h +81 -0
  81. scaler/io/ymq/file_descriptor.h +203 -0
  82. scaler/io/ymq/interruptive_concurrent_queue.h +169 -0
  83. scaler/io/ymq/io_context.cpp +98 -0
  84. scaler/io/ymq/io_context.h +44 -0
  85. scaler/io/ymq/io_socket.cpp +299 -0
  86. scaler/io/ymq/io_socket.h +121 -0
  87. scaler/io/ymq/iocp_context.cpp +102 -0
  88. scaler/io/ymq/iocp_context.h +83 -0
  89. scaler/io/ymq/logging.h +163 -0
  90. scaler/io/ymq/message.h +15 -0
  91. scaler/io/ymq/message_connection.h +16 -0
  92. scaler/io/ymq/message_connection_tcp.cpp +672 -0
  93. scaler/io/ymq/message_connection_tcp.h +96 -0
  94. scaler/io/ymq/network_utils.h +179 -0
  95. scaler/io/ymq/pymod_ymq/bytes.h +113 -0
  96. scaler/io/ymq/pymod_ymq/exception.h +124 -0
  97. scaler/io/ymq/pymod_ymq/gil.h +15 -0
  98. scaler/io/ymq/pymod_ymq/io_context.h +166 -0
  99. scaler/io/ymq/pymod_ymq/io_socket.h +285 -0
  100. scaler/io/ymq/pymod_ymq/message.h +99 -0
  101. scaler/io/ymq/pymod_ymq/python.h +153 -0
  102. scaler/io/ymq/pymod_ymq/ymq.cpp +23 -0
  103. scaler/io/ymq/pymod_ymq/ymq.h +357 -0
  104. scaler/io/ymq/readme.md +114 -0
  105. scaler/io/ymq/simple_interface.cpp +80 -0
  106. scaler/io/ymq/simple_interface.h +24 -0
  107. scaler/io/ymq/tcp_client.cpp +367 -0
  108. scaler/io/ymq/tcp_client.h +75 -0
  109. scaler/io/ymq/tcp_operations.h +41 -0
  110. scaler/io/ymq/tcp_server.cpp +410 -0
  111. scaler/io/ymq/tcp_server.h +79 -0
  112. scaler/io/ymq/third_party/concurrentqueue.h +3747 -0
  113. scaler/io/ymq/timed_queue.h +272 -0
  114. scaler/io/ymq/timestamp.h +102 -0
  115. scaler/io/ymq/typedefs.h +20 -0
  116. scaler/io/ymq/utils.h +34 -0
  117. scaler/io/ymq/ymq.py +130 -0
  118. scaler/object_storage/CMakeLists.txt +50 -0
  119. scaler/object_storage/__init__.py +0 -0
  120. scaler/object_storage/constants.h +11 -0
  121. scaler/object_storage/defs.h +14 -0
  122. scaler/object_storage/io_helper.cpp +44 -0
  123. scaler/object_storage/io_helper.h +9 -0
  124. scaler/object_storage/message.cpp +56 -0
  125. scaler/object_storage/message.h +130 -0
  126. scaler/object_storage/object_manager.cpp +126 -0
  127. scaler/object_storage/object_manager.h +52 -0
  128. scaler/object_storage/object_storage_server.cpp +359 -0
  129. scaler/object_storage/object_storage_server.h +126 -0
  130. scaler/object_storage/object_storage_server.so +0 -0
  131. scaler/object_storage/pymod_object_storage_server.cpp +104 -0
  132. scaler/protocol/__init__.py +0 -0
  133. scaler/protocol/capnp/__init__.py +0 -0
  134. scaler/protocol/capnp/_python.py +6 -0
  135. scaler/protocol/capnp/common.capnp +63 -0
  136. scaler/protocol/capnp/message.capnp +216 -0
  137. scaler/protocol/capnp/object_storage.capnp +52 -0
  138. scaler/protocol/capnp/status.capnp +73 -0
  139. scaler/protocol/introduction.md +105 -0
  140. scaler/protocol/python/__init__.py +0 -0
  141. scaler/protocol/python/common.py +135 -0
  142. scaler/protocol/python/message.py +726 -0
  143. scaler/protocol/python/mixins.py +13 -0
  144. scaler/protocol/python/object_storage.py +118 -0
  145. scaler/protocol/python/status.py +279 -0
  146. scaler/protocol/worker.md +228 -0
  147. scaler/scheduler/__init__.py +0 -0
  148. scaler/scheduler/allocate_policy/__init__.py +0 -0
  149. scaler/scheduler/allocate_policy/allocate_policy.py +9 -0
  150. scaler/scheduler/allocate_policy/capability_allocate_policy.py +280 -0
  151. scaler/scheduler/allocate_policy/even_load_allocate_policy.py +159 -0
  152. scaler/scheduler/allocate_policy/mixins.py +55 -0
  153. scaler/scheduler/controllers/__init__.py +0 -0
  154. scaler/scheduler/controllers/balance_controller.py +65 -0
  155. scaler/scheduler/controllers/client_controller.py +131 -0
  156. scaler/scheduler/controllers/config_controller.py +31 -0
  157. scaler/scheduler/controllers/graph_controller.py +424 -0
  158. scaler/scheduler/controllers/information_controller.py +81 -0
  159. scaler/scheduler/controllers/mixins.py +201 -0
  160. scaler/scheduler/controllers/object_controller.py +147 -0
  161. scaler/scheduler/controllers/scaling_controller.py +86 -0
  162. scaler/scheduler/controllers/task_controller.py +373 -0
  163. scaler/scheduler/controllers/worker_controller.py +168 -0
  164. scaler/scheduler/object_usage/__init__.py +0 -0
  165. scaler/scheduler/object_usage/object_tracker.py +131 -0
  166. scaler/scheduler/scheduler.py +253 -0
  167. scaler/scheduler/task/__init__.py +0 -0
  168. scaler/scheduler/task/task_state_machine.py +92 -0
  169. scaler/scheduler/task/task_state_manager.py +61 -0
  170. scaler/ui/__init__.py +0 -0
  171. scaler/ui/constants.py +9 -0
  172. scaler/ui/live_display.py +118 -0
  173. scaler/ui/memory_window.py +146 -0
  174. scaler/ui/setting_page.py +47 -0
  175. scaler/ui/task_graph.py +370 -0
  176. scaler/ui/task_log.py +83 -0
  177. scaler/ui/utility.py +35 -0
  178. scaler/ui/webui.py +125 -0
  179. scaler/ui/worker_processors.py +85 -0
  180. scaler/utility/__init__.py +0 -0
  181. scaler/utility/debug.py +19 -0
  182. scaler/utility/event_list.py +63 -0
  183. scaler/utility/event_loop.py +58 -0
  184. scaler/utility/exceptions.py +42 -0
  185. scaler/utility/formatter.py +44 -0
  186. scaler/utility/graph/__init__.py +0 -0
  187. scaler/utility/graph/optimization.py +27 -0
  188. scaler/utility/graph/topological_sorter.py +11 -0
  189. scaler/utility/graph/topological_sorter_graphblas.py +174 -0
  190. scaler/utility/identifiers.py +105 -0
  191. scaler/utility/logging/__init__.py +0 -0
  192. scaler/utility/logging/decorators.py +25 -0
  193. scaler/utility/logging/scoped_logger.py +33 -0
  194. scaler/utility/logging/utility.py +183 -0
  195. scaler/utility/many_to_many_dict.py +123 -0
  196. scaler/utility/metadata/__init__.py +0 -0
  197. scaler/utility/metadata/profile_result.py +31 -0
  198. scaler/utility/metadata/task_flags.py +30 -0
  199. scaler/utility/mixins.py +13 -0
  200. scaler/utility/network_util.py +7 -0
  201. scaler/utility/one_to_many_dict.py +72 -0
  202. scaler/utility/queues/__init__.py +0 -0
  203. scaler/utility/queues/async_indexed_queue.py +37 -0
  204. scaler/utility/queues/async_priority_queue.py +70 -0
  205. scaler/utility/queues/async_sorted_priority_queue.py +45 -0
  206. scaler/utility/queues/indexed_queue.py +114 -0
  207. scaler/utility/serialization.py +9 -0
  208. scaler/version.txt +1 -0
  209. scaler/worker/__init__.py +0 -0
  210. scaler/worker/agent/__init__.py +0 -0
  211. scaler/worker/agent/heartbeat_manager.py +107 -0
  212. scaler/worker/agent/mixins.py +137 -0
  213. scaler/worker/agent/processor/__init__.py +0 -0
  214. scaler/worker/agent/processor/object_cache.py +107 -0
  215. scaler/worker/agent/processor/processor.py +279 -0
  216. scaler/worker/agent/processor/streaming_buffer.py +28 -0
  217. scaler/worker/agent/processor_holder.py +145 -0
  218. scaler/worker/agent/processor_manager.py +365 -0
  219. scaler/worker/agent/profiling_manager.py +109 -0
  220. scaler/worker/agent/task_manager.py +150 -0
  221. scaler/worker/agent/timeout_manager.py +19 -0
  222. scaler/worker/preload.py +84 -0
  223. scaler/worker/worker.py +264 -0
  224. scaler/worker_adapter/__init__.py +0 -0
  225. scaler/worker_adapter/native.py +154 -0
  226. scaler/worker_adapter/symphony/__init__.py +0 -0
  227. scaler/worker_adapter/symphony/callback.py +45 -0
  228. scaler/worker_adapter/symphony/heartbeat_manager.py +79 -0
  229. scaler/worker_adapter/symphony/message.py +24 -0
  230. scaler/worker_adapter/symphony/task_manager.py +288 -0
  231. scaler/worker_adapter/symphony/worker.py +205 -0
  232. scaler/worker_adapter/symphony/worker_adapter.py +142 -0
@@ -0,0 +1,85 @@
1
+ #pragma once
2
+ #ifdef __linux__
3
+
4
+ // System
5
+ #include <sys/epoll.h>
6
+
7
+ // C++
8
+ #include <functional>
9
+ #include <queue>
10
+
11
+ #include "scaler/io/ymq/configuration.h"
12
+ #include "scaler/io/ymq/timed_queue.h"
13
+
14
+ // First-party
15
+ #include "scaler/io/ymq/file_descriptor.h"
16
+ #include "scaler/io/ymq/interruptive_concurrent_queue.h"
17
+ #include "scaler/io/ymq/timestamp.h"
18
+
19
+ namespace scaler {
20
+ namespace ymq {
21
+
22
+ class EventManager;
23
+
24
+ // In the constructor, the epoll context should register eventfd/timerfd from
25
+ // This way, the queues need not know about the event manager. We don't use callbacks.
26
+ class EpollContext {
27
+ public:
28
+ using Function = Configuration::ExecutionFunction;
29
+ using DelayedFunctionQueue = std::queue<Function>;
30
+ using Identifier = Configuration::ExecutionCancellationIdentifier;
31
+
32
+ EpollContext()
33
+ {
34
+ _epfd = epoll_create1(0);
35
+ epoll_event event {};
36
+
37
+ event.events = EPOLLIN | EPOLLET;
38
+ event.data.u64 = _isInterruptiveFd;
39
+ epoll_ctl(_epfd, EPOLL_CTL_ADD, _interruptiveFunctions.eventFd(), &event);
40
+
41
+ event = {};
42
+ event.events = EPOLLIN | EPOLLET;
43
+ event.data.u64 = _isTimingFd;
44
+ epoll_ctl(_epfd, EPOLL_CTL_ADD, _timingFunctions.timingFd(), &event);
45
+ }
46
+
47
+ ~EpollContext()
48
+ {
49
+ epoll_ctl(_epfd, EPOLL_CTL_DEL, _interruptiveFunctions.eventFd(), nullptr);
50
+ epoll_ctl(_epfd, EPOLL_CTL_DEL, _timingFunctions.timingFd(), nullptr);
51
+
52
+ close(_epfd);
53
+ }
54
+
55
+ void loop();
56
+
57
+ void addFdToLoop(int fd, uint64_t events, EventManager* manager);
58
+ void removeFdFromLoop(int fd);
59
+
60
+ // NOTE: Thread-safe method to communicate with the event loop thread
61
+ void executeNow(Function func) { _interruptiveFunctions.enqueue(std::move(func)); }
62
+ // WARN: NOT thread-safe. Thread safety is guaranteed by executeNow.
63
+ void executeLater(Function func) { _delayedFunctions.emplace(std::move(func)); }
64
+ // WARN: NOT thread-safe. Thread safety is guaranteed by executeNow.
65
+ Identifier executeAt(Timestamp timestamp, Function callback)
66
+ {
67
+ return _timingFunctions.push(timestamp, std::move(callback));
68
+ }
69
+ void cancelExecution(Identifier identifier) { _timingFunctions.cancelExecution(identifier); }
70
+
71
+ private:
72
+ void execPendingFunctions();
73
+ int _epfd;
74
+ TimedQueue _timingFunctions;
75
+ DelayedFunctionQueue _delayedFunctions;
76
+ InterruptiveConcurrentQueue<Function> _interruptiveFunctions;
77
+ constexpr static const size_t _isInterruptiveFd = 0;
78
+ constexpr static const size_t _isTimingFd = 1;
79
+ constexpr static const size_t _reventSize = 1024;
80
+ };
81
+
82
+ } // namespace ymq
83
+ } // namespace scaler
84
+
85
+ #endif // __linux__
scaler/io/ymq/error.h ADDED
@@ -0,0 +1,132 @@
1
+ #pragma once
2
+
3
+ #include <algorithm>
4
+ #include <exception> // std::terminate
5
+ #include <format>
6
+ #include <functional>
7
+ #include <print>
8
+ #include <string>
9
+
10
+ #include "scaler/io/ymq/timestamp.h"
11
+ #include "scaler/io/ymq/utils.h"
12
+
13
+ namespace scaler {
14
+ namespace ymq {
15
+
16
+ struct Error: public std::exception {
17
+ enum struct ErrorCode {
18
+ Uninit,
19
+ InvalidPortFormat,
20
+ InvalidAddressFormat,
21
+ ConfigurationError,
22
+ SignalNotSupported,
23
+ CoreBug,
24
+ RepetetiveIOSocketIdentity,
25
+ RedundantIOSocketRefCount,
26
+ MultipleConnectToNotSupported,
27
+ MultipleBindToNotSupported,
28
+ InitialConnectFailedWithInProgress,
29
+ SendMessageRequestCouldNotComplete,
30
+ SetSockOptNonFatalFailure,
31
+ IPv6NotSupported,
32
+ RemoteEndDisconnectedOnSocketWithoutGuaranteedDelivery,
33
+ ConnectorSocketClosedByRemoteEnd,
34
+ IOSocketStopRequested,
35
+ BinderSendMessageWithNoAddress,
36
+ };
37
+
38
+ // NOTE:
39
+ // Format:
40
+ // [Timestamp, ": ", Error Explanation, ": ", Other]
41
+ // For user calls errors:
42
+ // Other := ["Originated from", Function Name, items...]
43
+ // For system calls errors:
44
+ // Other := ["Originated from", Function Name, "Errno is", strerror(errno), items...]
45
+ template <typename... Args>
46
+ constexpr Error(ErrorCode e, Args&&... args) noexcept
47
+ : _errorCode(e), _logMsg(argsToString(Timestamp {}, convertErrorToExplanation(e), std::forward<Args>(args)...))
48
+ {
49
+ }
50
+
51
+ Error() noexcept: _errorCode(ErrorCode::Uninit) {}
52
+
53
+ static inline constexpr std::string_view convertErrorToExplanation(ErrorCode e) noexcept
54
+ {
55
+ switch (e) {
56
+ case ErrorCode::Uninit: return "";
57
+ case ErrorCode::InvalidPortFormat: return "Invalid port format, example input \"tcp://127.0.0.1:2345\"";
58
+ case ErrorCode::InvalidAddressFormat:
59
+ return "Invalid address format, example input \"tcp://127.0.0.1:2345\"";
60
+ case ErrorCode::ConfigurationError:
61
+ return "An error generated by system call that's likely due to mis-configuration";
62
+ case ErrorCode::SignalNotSupported:
63
+ return "A function call was interrupted by signal, but signal handling is not supported";
64
+ case ErrorCode::CoreBug: return "Likely a bug within the library";
65
+ case ErrorCode::RepetetiveIOSocketIdentity:
66
+ return "It is NOT allowed to create two IOSocket with the same identity";
67
+ case ErrorCode::RedundantIOSocketRefCount:
68
+ return "It is NOT allowed to hold IOSocket shared_ptr after you try to remove it";
69
+ case ErrorCode::MultipleConnectToNotSupported:
70
+ return "Connect to remote end without the previous such request successfully completed or exceeds "
71
+ "retry limit is currently NOT supported";
72
+ case ErrorCode::MultipleBindToNotSupported:
73
+ return "Bind to multiple IP addresses is currently not supported";
74
+ case ErrorCode::InitialConnectFailedWithInProgress:
75
+ return "The first connect(2) call to an socket failed with EINPROGRESS, expected";
76
+ case ErrorCode::SendMessageRequestCouldNotComplete:
77
+ return "sendMessage request could not complete. Possibly because the underlying connection has been "
78
+ "destructed because it exceeds maximum retry limit";
79
+ case ErrorCode::SetSockOptNonFatalFailure: return "sendsockopt(3) gets unfatal failure";
80
+ case ErrorCode::IPv6NotSupported: return "IPv6 is currently not supported";
81
+ case ErrorCode::RemoteEndDisconnectedOnSocketWithoutGuaranteedDelivery:
82
+ return "You are using IOSocket::Unicast or IOSocket::Multicast, which do not support guaranteed "
83
+ "message delivery, and the connection(s) disconnects";
84
+ case ErrorCode::ConnectorSocketClosedByRemoteEnd:
85
+ return "You have an IOSocket with Connector type but the only connection is closed by remote end";
86
+ case ErrorCode::IOSocketStopRequested: return "Current IOSocket is requested to stop by another thread";
87
+ case ErrorCode::BinderSendMessageWithNoAddress:
88
+ return "You call sendMessage with a Binder IOSocket but failed to provide an address";
89
+ }
90
+ fprintf(stderr, "Unrecognized ErrorCode value, program exits\n");
91
+ std::exit(1);
92
+ }
93
+
94
+ constexpr const char* what() const noexcept override { return _logMsg.c_str(); }
95
+
96
+ ErrorCode _errorCode;
97
+ std::string _logMsg;
98
+ };
99
+
100
+ } // namespace ymq
101
+ } // namespace scaler
102
+
103
+ template <>
104
+ struct std::formatter<scaler::ymq::Error, char> {
105
+ template <class ParseContext>
106
+ constexpr ParseContext::iterator parse(ParseContext& ctx) noexcept
107
+ {
108
+ return ctx.begin();
109
+ }
110
+
111
+ template <class FmtContext>
112
+ constexpr FmtContext::iterator format(scaler::ymq::Error e, FmtContext& ctx) const noexcept
113
+ {
114
+ return std::ranges::copy(e._logMsg, ctx.out()).out;
115
+ }
116
+ };
117
+
118
+ using UnrecoverableErrorFunctionHookPtr = std::function<void(scaler::ymq::Error)>;
119
+
120
+ [[noreturn]] inline void defaultUnrecoverableError(scaler::ymq::Error e) noexcept
121
+ {
122
+ std::print(stderr, "{}\n", e);
123
+ std::exit(1);
124
+ }
125
+
126
+ inline UnrecoverableErrorFunctionHookPtr unrecoverableErrorFunctionHookPtr = defaultUnrecoverableError;
127
+
128
+ [[noreturn]] inline void unrecoverableError(scaler::ymq::Error e) noexcept
129
+ {
130
+ unrecoverableErrorFunctionHookPtr(std::move(e));
131
+ std::exit(1);
132
+ }
@@ -0,0 +1,55 @@
1
+ #pragma once
2
+
3
+ // C++
4
+ #include <concepts>
5
+ #include <cstdint> // uint64_t
6
+ #include <functional>
7
+
8
+ // First-party
9
+ #include "scaler/io/ymq/configuration.h"
10
+ #include "scaler/io/ymq/epoll_context.h"
11
+ #include "scaler/io/ymq/iocp_context.h"
12
+
13
+ namespace scaler {
14
+ namespace ymq {
15
+
16
+ struct Timestamp;
17
+ class EventManager;
18
+
19
+ template <class Backend>
20
+ concept EventLoopBackend = requires(Backend backend, Backend::Function f) {
21
+ { backend.executeNow(std::move(f)) } -> std::same_as<void>;
22
+ { backend.executeLater(std::move(f)) } -> std::same_as<void>;
23
+ { backend.executeAt(Timestamp {}, std::move(f)) } -> std::integral;
24
+ { backend.cancelExecution(0) } -> std::same_as<void>;
25
+
26
+ backend.addFdToLoop(int {}, uint64_t {}, (EventManager*)nullptr);
27
+ { backend.removeFdFromLoop(int {}) } -> std::same_as<void>;
28
+ };
29
+
30
+ template <EventLoopBackend Backend = EpollContext>
31
+ class EventLoop {
32
+ Backend backend;
33
+
34
+ public:
35
+ using Function = Backend::Function;
36
+ using Identifier = Backend::Identifier;
37
+
38
+ void loop() { backend.loop(); }
39
+
40
+ void executeNow(Function func) { backend.executeNow(std::move(func)); }
41
+ void executeLater(Function func) { backend.executeLater(std::move(func)); }
42
+
43
+ Identifier executeAt(Timestamp timestamp, Function func) { return backend.executeAt(timestamp, std::move(func)); }
44
+ void cancelExecution(Identifier identifier) { backend.cancelExecution(identifier); }
45
+
46
+ auto addFdToLoop(int fd, uint64_t events, EventManager* manager)
47
+ {
48
+ return backend.addFdToLoop(fd, events, manager);
49
+ }
50
+
51
+ void removeFdFromLoop(int fd) { backend.removeFdFromLoop(fd); }
52
+ };
53
+
54
+ } // namespace ymq
55
+ } // namespace scaler
@@ -0,0 +1,64 @@
1
+
2
+ #include "scaler/io/ymq/event_loop_thread.h"
3
+
4
+ #include <cassert>
5
+ #include <memory>
6
+
7
+ #include "scaler/io/ymq/error.h"
8
+ #include "scaler/io/ymq/event_manager.h"
9
+ #include "scaler/io/ymq/io_socket.h"
10
+
11
+ namespace scaler {
12
+ namespace ymq {
13
+
14
+ void EventLoopThread::createIOSocket(std::string identity, IOSocketType socketType, CreateIOSocketCallback callback)
15
+ {
16
+ if (thread.get_id() == std::thread::id()) {
17
+ thread = std::jthread([this](std::stop_token token) {
18
+ while (!token.stop_requested()) {
19
+ this->_eventLoop.loop();
20
+ }
21
+ });
22
+ }
23
+
24
+ _eventLoop.executeNow([this, callback = std::move(callback), identity = std::move(identity), socketType] mutable {
25
+ auto [iterator, inserted] = _identityToIOSocket.try_emplace(
26
+ identity, std::make_shared<IOSocket>(shared_from_this(), identity, socketType));
27
+
28
+ if (!inserted) {
29
+ unrecoverableError({
30
+ Error::ErrorCode::RepetetiveIOSocketIdentity,
31
+ "Originated from (in EventLoopThread::createIOSocket)",
32
+ __PRETTY_FUNCTION__,
33
+ "Your input identity",
34
+ identity,
35
+ });
36
+ }
37
+
38
+ auto ptr = iterator->second;
39
+
40
+ callback(ptr);
41
+ });
42
+ }
43
+
44
+ void EventLoopThread::removeIOSocket(IOSocket* target)
45
+ {
46
+ auto useCount = _identityToIOSocket[target->identity()].use_count();
47
+ if (useCount != 1) {
48
+ unrecoverableError({
49
+ Error::ErrorCode::RedundantIOSocketRefCount,
50
+ "Originated from",
51
+ __PRETTY_FUNCTION__,
52
+ "use_count",
53
+ useCount,
54
+ });
55
+ }
56
+
57
+ _identityToIOSocket.erase(target->identity());
58
+ if (_identityToIOSocket.empty()) {
59
+ thread.request_stop();
60
+ }
61
+ }
62
+
63
+ } // namespace ymq
64
+ } // namespace scaler
@@ -0,0 +1,46 @@
1
+ #pragma once
2
+
3
+ #include <cstdint>
4
+ #include <functional>
5
+ #include <map>
6
+ #include <memory>
7
+ #include <thread>
8
+
9
+ #include "scaler/io/ymq/configuration.h"
10
+ #include "scaler/io/ymq/event_loop.h"
11
+ #include "scaler/io/ymq/typedefs.h"
12
+
13
+ namespace scaler {
14
+ namespace ymq {
15
+
16
+ class IOSocket;
17
+
18
+ class EventLoopThread: public std::enable_shared_from_this<EventLoopThread> {
19
+ public:
20
+ std::map<std::string, std::shared_ptr<IOSocket>> _identityToIOSocket;
21
+ using PollingContext = Configuration::PollingContext;
22
+ using CreateIOSocketCallback = Configuration::CreateIOSocketCallback;
23
+ EventLoop<PollingContext> _eventLoop;
24
+ // Why not make the class a friend class of IOContext?
25
+ // Because the removeIOSocket method is a bit trickier than addIOSocket,
26
+ // the IOSocket that is being removed will first remove every
27
+ // MessageConnectionTCP managed by it from the EventLoop, before it removes
28
+ // it self from ioSockets. return eventLoop.executeNow(createIOSocket());
29
+ void createIOSocket(std::string identity, IOSocketType socketType, CreateIOSocketCallback callback);
30
+
31
+ void removeIOSocket(IOSocket* target);
32
+ // EventLoop<PollingContext>& getEventLoop();
33
+ // IOSocket* getIOSocketByIdentity(size_t identity);
34
+
35
+ bool stopRequested() { return thread.get_stop_source().stop_requested(); }
36
+
37
+ EventLoopThread(const EventLoopThread&) = delete;
38
+ EventLoopThread& operator=(const EventLoopThread&) = delete;
39
+ EventLoopThread() = default;
40
+
41
+ private:
42
+ std::jthread thread;
43
+ };
44
+
45
+ } // namespace ymq
46
+ } // namespace scaler
@@ -0,0 +1,81 @@
1
+ #pragma once
2
+
3
+ // C++
4
+ #ifdef __linux__
5
+ #include <sys/epoll.h>
6
+ #endif // __linux__
7
+ #ifdef _WIN32
8
+ #include <windows.h>
9
+ #endif // _WIN32
10
+
11
+ #include <concepts>
12
+ #include <cstdint> // uint64_t
13
+ #include <functional>
14
+ #include <memory>
15
+
16
+ // First-party
17
+ #include "scaler/io/ymq/configuration.h"
18
+
19
+ namespace scaler {
20
+ namespace ymq {
21
+
22
+ class EventLoopThread;
23
+
24
+ // TODO: Add the _fd back
25
+ #ifdef _WIN32
26
+ class EventManager: public OVERLAPPED {
27
+ #endif // _WIN32
28
+ #ifdef __linux__
29
+ class EventManager {
30
+ #endif // __linux
31
+ // FileDescriptor _fd;
32
+
33
+ public:
34
+ void onEvents(uint64_t events)
35
+ {
36
+ #ifdef __linux__
37
+ if constexpr (std::same_as<Configuration::PollingContext, EpollContext>) {
38
+ int realEvents = (int)events;
39
+ if ((realEvents & EPOLLHUP) && !(realEvents & EPOLLIN)) {
40
+ onClose();
41
+ }
42
+ if (realEvents & (EPOLLERR | EPOLLHUP)) {
43
+ onError();
44
+ }
45
+ if (realEvents & (EPOLLIN | EPOLLRDHUP)) {
46
+ onRead();
47
+ }
48
+ if (realEvents & EPOLLOUT) {
49
+ onWrite();
50
+ }
51
+ }
52
+ #endif // __linux__
53
+ #ifdef _WIN32
54
+ if constexpr (std::same_as<Configuration::PollingContext, IocpContext>) {
55
+ onRead();
56
+ onWrite();
57
+ if (events & IOCP_SOCKET_CLOSED) {
58
+ onClose();
59
+ }
60
+ }
61
+ #endif // _WIN32
62
+ }
63
+
64
+ // User that registered them should have everything they need
65
+ // In the future, we might add more onXX() methods, for now these are all we need.
66
+ using OnEventCallback = std::function<void()>;
67
+ OnEventCallback onRead;
68
+ OnEventCallback onWrite;
69
+ OnEventCallback onClose;
70
+ OnEventCallback onError;
71
+ // EventManager(): _fd {} {}
72
+ EventManager()
73
+ {
74
+ #ifdef _WIN32
75
+ ZeroMemory(this, sizeof(*this));
76
+ #endif // _WIN32
77
+ };
78
+ };
79
+
80
+ } // namespace ymq
81
+ } // namespace scaler
@@ -0,0 +1,203 @@
1
+ #pragma once
2
+
3
+ // System
4
+ #include <sys/epoll.h>
5
+ #include <sys/eventfd.h>
6
+ #include <sys/socket.h>
7
+ #include <sys/timerfd.h>
8
+ #include <unistd.h>
9
+
10
+ // C
11
+ #include <cerrno>
12
+
13
+ // C++
14
+ #include <expected>
15
+ #include <optional>
16
+
17
+ // First-party
18
+ #include "scaler/io/ymq/common.h"
19
+
20
+ class FileDescriptor {
21
+ int fd;
22
+
23
+ FileDescriptor(int fd): fd(fd) {}
24
+
25
+ public:
26
+ ~FileDescriptor() noexcept(false)
27
+ {
28
+ if (auto code = close(fd) < 0)
29
+ throw std::system_error(errno, std::system_category(), "Failed to close file descriptor");
30
+
31
+ this->fd = -1;
32
+ }
33
+
34
+ FileDescriptor(): fd(-1) {}
35
+
36
+ // move-only
37
+ FileDescriptor(const FileDescriptor&) = delete;
38
+ FileDescriptor& operator=(const FileDescriptor&) = delete;
39
+ FileDescriptor(FileDescriptor&& other) noexcept: fd(other.fd)
40
+ {
41
+ other.fd = -1; // prevent double close
42
+ }
43
+ FileDescriptor& operator=(FileDescriptor&& other) noexcept
44
+ {
45
+ if (this != &other) {
46
+ if (fd >= 0) {
47
+ close(fd); // close current fd
48
+ }
49
+ fd = other.fd;
50
+ other.fd = -1; // prevent double close
51
+ }
52
+ return *this;
53
+ }
54
+
55
+ static std::expected<FileDescriptor, Errno> socket(int domain, int type, int protocol)
56
+ {
57
+ if (int fd = ::socket(domain, type, protocol) < 0) {
58
+ return std::unexpected {errno};
59
+ } else {
60
+ return FileDescriptor(fd);
61
+ }
62
+ }
63
+
64
+ static std::expected<FileDescriptor, Errno> eventfd(int initval, int flags)
65
+ {
66
+ if (int fd = ::eventfd(initval, flags) < 0) {
67
+ return std::unexpected {errno};
68
+ } else {
69
+ return FileDescriptor(fd);
70
+ }
71
+ }
72
+
73
+ static std::expected<FileDescriptor, Errno> timerfd(clockid_t clock, int flags)
74
+ {
75
+ if (int fd = ::timerfd_create(clock, flags) < 0) {
76
+ return std::unexpected {errno};
77
+ } else {
78
+ return FileDescriptor(fd);
79
+ }
80
+ }
81
+
82
+ static std::expected<FileDescriptor, Errno> epollfd()
83
+ {
84
+ if (int fd = ::epoll_create1(0) < 0) {
85
+ return std::unexpected {errno};
86
+ } else {
87
+ return FileDescriptor(fd);
88
+ }
89
+ }
90
+
91
+ std::optional<Errno> listen(int backlog)
92
+ {
93
+ if (::listen(fd, backlog) < 0) {
94
+ return errno;
95
+ } else {
96
+ return std::nullopt;
97
+ }
98
+ }
99
+
100
+ std::expected<FileDescriptor, Errno> accept(sockaddr& addr, socklen_t& addrlen)
101
+ {
102
+ if (auto fd2 = ::accept(fd, &addr, &addrlen) < 0) {
103
+ return std::unexpected {errno};
104
+ } else {
105
+ return FileDescriptor(fd2);
106
+ }
107
+ }
108
+
109
+ std::optional<Errno> connect(const sockaddr& addr, socklen_t addrlen)
110
+ {
111
+ if (::connect(fd, &addr, addrlen) < 0) {
112
+ return errno;
113
+ } else {
114
+ return std::nullopt;
115
+ }
116
+ }
117
+
118
+ std::optional<Errno> bind(const sockaddr& addr, socklen_t addrlen)
119
+ {
120
+ if (::bind(fd, &addr, addrlen) < 0) {
121
+ return errno;
122
+ } else {
123
+ return std::nullopt;
124
+ }
125
+ }
126
+
127
+ std::expected<ssize_t, Errno> read(void* buf, size_t count)
128
+ {
129
+ ssize_t n = ::read(fd, buf, count);
130
+ if (n < 0) {
131
+ return std::unexpected {errno};
132
+ } else {
133
+ return n;
134
+ }
135
+ }
136
+
137
+ std::expected<ssize_t, Errno> write(const void* buf, size_t count)
138
+ {
139
+ ssize_t n = ::write(fd, buf, count);
140
+ if (n < 0) {
141
+ return std::unexpected {errno};
142
+ } else {
143
+ return n;
144
+ }
145
+ }
146
+
147
+ std::optional<Errno> eventfd_signal()
148
+ {
149
+ uint64_t u = 1;
150
+ if (::eventfd_write(fd, u) < 0) {
151
+ return errno;
152
+ } else {
153
+ return std::nullopt;
154
+ }
155
+ }
156
+
157
+ std::optional<Errno> eventfd_wait()
158
+ {
159
+ uint64_t u;
160
+ if (::eventfd_read(fd, &u) < 0) {
161
+ return errno;
162
+ } else {
163
+ return std::nullopt;
164
+ }
165
+ }
166
+
167
+ std::optional<Errno> timerfd_settime(const itimerspec& new_value, itimerspec* old_value = nullptr)
168
+ {
169
+ if (::timerfd_settime(fd, 0, &new_value, old_value) < 0) {
170
+ return errno;
171
+ } else {
172
+ return std::nullopt;
173
+ }
174
+ }
175
+
176
+ std::optional<Errno> timerfd_wait()
177
+ {
178
+ uint64_t u;
179
+ if (::read(fd, &u, sizeof(u)) < 0) {
180
+ return errno;
181
+ } else {
182
+ return std::nullopt;
183
+ }
184
+ }
185
+
186
+ std::optional<Errno> epoll_ctl(int op, FileDescriptor& other, epoll_event* event)
187
+ {
188
+ if (::epoll_ctl(fd, op, other.fd, event) < 0) {
189
+ return errno;
190
+ } else {
191
+ return std::nullopt;
192
+ }
193
+ }
194
+
195
+ std::expected<int, Errno> epoll_wait(epoll_event* events, int maxevents, int timeout)
196
+ {
197
+ if (auto n = ::epoll_wait(fd, events, maxevents, timeout) < 0) {
198
+ return errno;
199
+ } else {
200
+ return n;
201
+ }
202
+ }
203
+ };