opengris-scaler 1.12.7__cp313-cp313-musllinux_1_2_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 (234) hide show
  1. opengris_scaler-1.12.7.dist-info/METADATA +729 -0
  2. opengris_scaler-1.12.7.dist-info/RECORD +234 -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-61c06778.1.0.so +0 -0
  9. opengris_scaler.libs/libgcc_s-2298274a.so.1 +0 -0
  10. opengris_scaler.libs/libkj-1-21b63b70.1.0.so +0 -0
  11. opengris_scaler.libs/libstdc++-08d5c7eb.so.6.0.33 +0 -0
  12. scaler/CMakeLists.txt +11 -0
  13. scaler/__init__.py +14 -0
  14. scaler/about.py +5 -0
  15. scaler/client/__init__.py +0 -0
  16. scaler/client/agent/__init__.py +0 -0
  17. scaler/client/agent/client_agent.py +210 -0
  18. scaler/client/agent/disconnect_manager.py +27 -0
  19. scaler/client/agent/future_manager.py +112 -0
  20. scaler/client/agent/heartbeat_manager.py +74 -0
  21. scaler/client/agent/mixins.py +89 -0
  22. scaler/client/agent/object_manager.py +98 -0
  23. scaler/client/agent/task_manager.py +64 -0
  24. scaler/client/client.py +635 -0
  25. scaler/client/future.py +252 -0
  26. scaler/client/object_buffer.py +129 -0
  27. scaler/client/object_reference.py +25 -0
  28. scaler/client/serializer/__init__.py +0 -0
  29. scaler/client/serializer/default.py +16 -0
  30. scaler/client/serializer/mixins.py +38 -0
  31. scaler/cluster/__init__.py +0 -0
  32. scaler/cluster/cluster.py +115 -0
  33. scaler/cluster/combo.py +148 -0
  34. scaler/cluster/object_storage_server.py +45 -0
  35. scaler/cluster/scheduler.py +83 -0
  36. scaler/config/__init__.py +0 -0
  37. scaler/config/defaults.py +87 -0
  38. scaler/config/loader.py +95 -0
  39. scaler/config/mixins.py +15 -0
  40. scaler/config/section/__init__.py +0 -0
  41. scaler/config/section/cluster.py +56 -0
  42. scaler/config/section/native_worker_adapter.py +44 -0
  43. scaler/config/section/object_storage_server.py +7 -0
  44. scaler/config/section/scheduler.py +53 -0
  45. scaler/config/section/symphony_worker_adapter.py +47 -0
  46. scaler/config/section/top.py +13 -0
  47. scaler/config/section/webui.py +16 -0
  48. scaler/config/types/__init__.py +0 -0
  49. scaler/config/types/object_storage_server.py +45 -0
  50. scaler/config/types/worker.py +57 -0
  51. scaler/config/types/zmq.py +79 -0
  52. scaler/entry_points/__init__.py +0 -0
  53. scaler/entry_points/cluster.py +133 -0
  54. scaler/entry_points/object_storage_server.py +41 -0
  55. scaler/entry_points/scheduler.py +135 -0
  56. scaler/entry_points/top.py +286 -0
  57. scaler/entry_points/webui.py +26 -0
  58. scaler/entry_points/worker_adapter_native.py +137 -0
  59. scaler/entry_points/worker_adapter_symphony.py +102 -0
  60. scaler/io/__init__.py +0 -0
  61. scaler/io/async_binder.py +85 -0
  62. scaler/io/async_connector.py +95 -0
  63. scaler/io/async_object_storage_connector.py +185 -0
  64. scaler/io/mixins.py +154 -0
  65. scaler/io/sync_connector.py +68 -0
  66. scaler/io/sync_object_storage_connector.py +185 -0
  67. scaler/io/sync_subscriber.py +83 -0
  68. scaler/io/utility.py +31 -0
  69. scaler/io/ymq/CMakeLists.txt +98 -0
  70. scaler/io/ymq/__init__.py +0 -0
  71. scaler/io/ymq/_ymq.pyi +96 -0
  72. scaler/io/ymq/_ymq.so +0 -0
  73. scaler/io/ymq/bytes.h +114 -0
  74. scaler/io/ymq/common.h +29 -0
  75. scaler/io/ymq/configuration.h +60 -0
  76. scaler/io/ymq/epoll_context.cpp +185 -0
  77. scaler/io/ymq/epoll_context.h +85 -0
  78. scaler/io/ymq/error.h +132 -0
  79. scaler/io/ymq/event_loop.h +55 -0
  80. scaler/io/ymq/event_loop_thread.cpp +64 -0
  81. scaler/io/ymq/event_loop_thread.h +46 -0
  82. scaler/io/ymq/event_manager.h +81 -0
  83. scaler/io/ymq/file_descriptor.h +203 -0
  84. scaler/io/ymq/interruptive_concurrent_queue.h +169 -0
  85. scaler/io/ymq/io_context.cpp +98 -0
  86. scaler/io/ymq/io_context.h +44 -0
  87. scaler/io/ymq/io_socket.cpp +299 -0
  88. scaler/io/ymq/io_socket.h +121 -0
  89. scaler/io/ymq/iocp_context.cpp +102 -0
  90. scaler/io/ymq/iocp_context.h +83 -0
  91. scaler/io/ymq/logging.h +163 -0
  92. scaler/io/ymq/message.h +15 -0
  93. scaler/io/ymq/message_connection.h +16 -0
  94. scaler/io/ymq/message_connection_tcp.cpp +672 -0
  95. scaler/io/ymq/message_connection_tcp.h +96 -0
  96. scaler/io/ymq/network_utils.h +179 -0
  97. scaler/io/ymq/pymod_ymq/bytes.h +113 -0
  98. scaler/io/ymq/pymod_ymq/exception.h +124 -0
  99. scaler/io/ymq/pymod_ymq/gil.h +15 -0
  100. scaler/io/ymq/pymod_ymq/io_context.h +166 -0
  101. scaler/io/ymq/pymod_ymq/io_socket.h +285 -0
  102. scaler/io/ymq/pymod_ymq/message.h +99 -0
  103. scaler/io/ymq/pymod_ymq/python.h +153 -0
  104. scaler/io/ymq/pymod_ymq/ymq.cpp +23 -0
  105. scaler/io/ymq/pymod_ymq/ymq.h +357 -0
  106. scaler/io/ymq/readme.md +114 -0
  107. scaler/io/ymq/simple_interface.cpp +80 -0
  108. scaler/io/ymq/simple_interface.h +24 -0
  109. scaler/io/ymq/tcp_client.cpp +367 -0
  110. scaler/io/ymq/tcp_client.h +75 -0
  111. scaler/io/ymq/tcp_operations.h +41 -0
  112. scaler/io/ymq/tcp_server.cpp +410 -0
  113. scaler/io/ymq/tcp_server.h +79 -0
  114. scaler/io/ymq/third_party/concurrentqueue.h +3747 -0
  115. scaler/io/ymq/timed_queue.h +272 -0
  116. scaler/io/ymq/timestamp.h +102 -0
  117. scaler/io/ymq/typedefs.h +20 -0
  118. scaler/io/ymq/utils.h +34 -0
  119. scaler/io/ymq/ymq.py +130 -0
  120. scaler/object_storage/CMakeLists.txt +50 -0
  121. scaler/object_storage/__init__.py +0 -0
  122. scaler/object_storage/constants.h +11 -0
  123. scaler/object_storage/defs.h +14 -0
  124. scaler/object_storage/io_helper.cpp +44 -0
  125. scaler/object_storage/io_helper.h +9 -0
  126. scaler/object_storage/message.cpp +56 -0
  127. scaler/object_storage/message.h +130 -0
  128. scaler/object_storage/object_manager.cpp +126 -0
  129. scaler/object_storage/object_manager.h +52 -0
  130. scaler/object_storage/object_storage_server.cpp +359 -0
  131. scaler/object_storage/object_storage_server.h +126 -0
  132. scaler/object_storage/object_storage_server.so +0 -0
  133. scaler/object_storage/pymod_object_storage_server.cpp +104 -0
  134. scaler/protocol/__init__.py +0 -0
  135. scaler/protocol/capnp/__init__.py +0 -0
  136. scaler/protocol/capnp/_python.py +6 -0
  137. scaler/protocol/capnp/common.capnp +63 -0
  138. scaler/protocol/capnp/message.capnp +216 -0
  139. scaler/protocol/capnp/object_storage.capnp +52 -0
  140. scaler/protocol/capnp/status.capnp +73 -0
  141. scaler/protocol/introduction.md +105 -0
  142. scaler/protocol/python/__init__.py +0 -0
  143. scaler/protocol/python/common.py +135 -0
  144. scaler/protocol/python/message.py +726 -0
  145. scaler/protocol/python/mixins.py +13 -0
  146. scaler/protocol/python/object_storage.py +118 -0
  147. scaler/protocol/python/status.py +279 -0
  148. scaler/protocol/worker.md +228 -0
  149. scaler/scheduler/__init__.py +0 -0
  150. scaler/scheduler/allocate_policy/__init__.py +0 -0
  151. scaler/scheduler/allocate_policy/allocate_policy.py +9 -0
  152. scaler/scheduler/allocate_policy/capability_allocate_policy.py +280 -0
  153. scaler/scheduler/allocate_policy/even_load_allocate_policy.py +159 -0
  154. scaler/scheduler/allocate_policy/mixins.py +55 -0
  155. scaler/scheduler/controllers/__init__.py +0 -0
  156. scaler/scheduler/controllers/balance_controller.py +65 -0
  157. scaler/scheduler/controllers/client_controller.py +131 -0
  158. scaler/scheduler/controllers/config_controller.py +31 -0
  159. scaler/scheduler/controllers/graph_controller.py +424 -0
  160. scaler/scheduler/controllers/information_controller.py +81 -0
  161. scaler/scheduler/controllers/mixins.py +201 -0
  162. scaler/scheduler/controllers/object_controller.py +147 -0
  163. scaler/scheduler/controllers/scaling_controller.py +86 -0
  164. scaler/scheduler/controllers/task_controller.py +373 -0
  165. scaler/scheduler/controllers/worker_controller.py +168 -0
  166. scaler/scheduler/object_usage/__init__.py +0 -0
  167. scaler/scheduler/object_usage/object_tracker.py +131 -0
  168. scaler/scheduler/scheduler.py +253 -0
  169. scaler/scheduler/task/__init__.py +0 -0
  170. scaler/scheduler/task/task_state_machine.py +92 -0
  171. scaler/scheduler/task/task_state_manager.py +61 -0
  172. scaler/ui/__init__.py +0 -0
  173. scaler/ui/constants.py +9 -0
  174. scaler/ui/live_display.py +118 -0
  175. scaler/ui/memory_window.py +146 -0
  176. scaler/ui/setting_page.py +47 -0
  177. scaler/ui/task_graph.py +370 -0
  178. scaler/ui/task_log.py +83 -0
  179. scaler/ui/utility.py +35 -0
  180. scaler/ui/webui.py +125 -0
  181. scaler/ui/worker_processors.py +85 -0
  182. scaler/utility/__init__.py +0 -0
  183. scaler/utility/debug.py +19 -0
  184. scaler/utility/event_list.py +63 -0
  185. scaler/utility/event_loop.py +58 -0
  186. scaler/utility/exceptions.py +42 -0
  187. scaler/utility/formatter.py +44 -0
  188. scaler/utility/graph/__init__.py +0 -0
  189. scaler/utility/graph/optimization.py +27 -0
  190. scaler/utility/graph/topological_sorter.py +11 -0
  191. scaler/utility/graph/topological_sorter_graphblas.py +174 -0
  192. scaler/utility/identifiers.py +105 -0
  193. scaler/utility/logging/__init__.py +0 -0
  194. scaler/utility/logging/decorators.py +25 -0
  195. scaler/utility/logging/scoped_logger.py +33 -0
  196. scaler/utility/logging/utility.py +183 -0
  197. scaler/utility/many_to_many_dict.py +123 -0
  198. scaler/utility/metadata/__init__.py +0 -0
  199. scaler/utility/metadata/profile_result.py +31 -0
  200. scaler/utility/metadata/task_flags.py +30 -0
  201. scaler/utility/mixins.py +13 -0
  202. scaler/utility/network_util.py +7 -0
  203. scaler/utility/one_to_many_dict.py +72 -0
  204. scaler/utility/queues/__init__.py +0 -0
  205. scaler/utility/queues/async_indexed_queue.py +37 -0
  206. scaler/utility/queues/async_priority_queue.py +70 -0
  207. scaler/utility/queues/async_sorted_priority_queue.py +45 -0
  208. scaler/utility/queues/indexed_queue.py +114 -0
  209. scaler/utility/serialization.py +9 -0
  210. scaler/version.txt +1 -0
  211. scaler/worker/__init__.py +0 -0
  212. scaler/worker/agent/__init__.py +0 -0
  213. scaler/worker/agent/heartbeat_manager.py +107 -0
  214. scaler/worker/agent/mixins.py +137 -0
  215. scaler/worker/agent/processor/__init__.py +0 -0
  216. scaler/worker/agent/processor/object_cache.py +107 -0
  217. scaler/worker/agent/processor/processor.py +279 -0
  218. scaler/worker/agent/processor/streaming_buffer.py +28 -0
  219. scaler/worker/agent/processor_holder.py +145 -0
  220. scaler/worker/agent/processor_manager.py +365 -0
  221. scaler/worker/agent/profiling_manager.py +109 -0
  222. scaler/worker/agent/task_manager.py +150 -0
  223. scaler/worker/agent/timeout_manager.py +19 -0
  224. scaler/worker/preload.py +84 -0
  225. scaler/worker/worker.py +264 -0
  226. scaler/worker_adapter/__init__.py +0 -0
  227. scaler/worker_adapter/native.py +154 -0
  228. scaler/worker_adapter/symphony/__init__.py +0 -0
  229. scaler/worker_adapter/symphony/callback.py +45 -0
  230. scaler/worker_adapter/symphony/heartbeat_manager.py +79 -0
  231. scaler/worker_adapter/symphony/message.py +24 -0
  232. scaler/worker_adapter/symphony/task_manager.py +288 -0
  233. scaler/worker_adapter/symphony/worker.py +205 -0
  234. scaler/worker_adapter/symphony/worker_adapter.py +142 -0
@@ -0,0 +1,169 @@
1
+ #pragma once
2
+
3
+ #ifdef __linux__
4
+ #include <sys/eventfd.h>
5
+ #include <unistd.h>
6
+ #endif // __linux__
7
+ #ifdef _WIN32
8
+ // clang-format off
9
+ #include <windows.h>
10
+ #include <winsock2.h>
11
+ // clang-format on
12
+ #endif // _WIN32
13
+
14
+ // C++
15
+ #include <cstdlib>
16
+ #include <vector>
17
+
18
+ #include "scaler/io/ymq/error.h"
19
+ #include "third_party/concurrentqueue.h"
20
+
21
+ namespace scaler {
22
+ namespace ymq {
23
+
24
+ class EventManager;
25
+
26
+ #ifdef __linux__
27
+ template <typename T>
28
+ class InterruptiveConcurrentQueue {
29
+ int _eventFd;
30
+ moodycamel::ConcurrentQueue<T> _queue;
31
+
32
+ public:
33
+ InterruptiveConcurrentQueue(): _queue()
34
+ {
35
+ _eventFd = eventfd(0, EFD_NONBLOCK);
36
+ if (_eventFd == -1) {
37
+ const int myErrno = errno;
38
+ switch (myErrno) {
39
+ case ENFILE:
40
+ case ENODEV:
41
+ case ENOMEM:
42
+ case EMFILE:
43
+ unrecoverableError({
44
+ Error::ErrorCode::ConfigurationError,
45
+ "Originated from",
46
+ "eventfd(2)",
47
+ "Errno is",
48
+ strerror(myErrno),
49
+ });
50
+ break;
51
+
52
+ case EINVAL:
53
+ default:
54
+ unrecoverableError({
55
+ Error::ErrorCode::CoreBug,
56
+ "Originated from",
57
+ "eventfd(2)",
58
+ "Errno is",
59
+ strerror(myErrno),
60
+ "flags",
61
+ "EFD_NONBLOCK",
62
+ });
63
+ break;
64
+ }
65
+ }
66
+ }
67
+
68
+ int eventFd() const { return _eventFd; }
69
+
70
+ void enqueue(T item)
71
+ {
72
+ _queue.enqueue(std::move(item));
73
+
74
+ uint64_t u = 1;
75
+ if (::eventfd_write(_eventFd, u) < 0) {
76
+ unrecoverableError({
77
+ Error::ErrorCode::CoreBug,
78
+ "Originated from",
79
+ "eventfd_write(2)",
80
+ "Errno is",
81
+ strerror(errno),
82
+ });
83
+ }
84
+ }
85
+
86
+ // NOTE: this method will block until an item is available
87
+ std::vector<T> dequeue()
88
+ {
89
+ uint64_t u {};
90
+ if (::eventfd_read(_eventFd, &u) < 0) {
91
+ if (errno == EAGAIN) {
92
+ return {};
93
+ }
94
+
95
+ unrecoverableError({
96
+ Error::ErrorCode::CoreBug,
97
+ "Originated from",
98
+ "eventfd_read(2)",
99
+ "Errno is",
100
+ strerror(errno),
101
+ });
102
+ }
103
+
104
+ std::vector<T> vecT(u);
105
+ for (auto i = 0uz; i < u; ++i) {
106
+ while (!_queue.try_dequeue(vecT[i]))
107
+ ;
108
+ }
109
+ return vecT;
110
+ }
111
+
112
+ // unmovable, uncopyable
113
+ InterruptiveConcurrentQueue(const InterruptiveConcurrentQueue&) = delete;
114
+ InterruptiveConcurrentQueue& operator=(const InterruptiveConcurrentQueue&) = delete;
115
+ InterruptiveConcurrentQueue(InterruptiveConcurrentQueue&&) = delete;
116
+ InterruptiveConcurrentQueue& operator=(InterruptiveConcurrentQueue&&) = delete;
117
+
118
+ ~InterruptiveConcurrentQueue() { close(_eventFd); }
119
+ };
120
+ #endif // __linux__
121
+
122
+ #ifdef _WIN32
123
+ template <typename T>
124
+ class InterruptiveConcurrentQueue {
125
+ HANDLE _completionPort;
126
+ const size_t _key;
127
+ moodycamel::ConcurrentQueue<T> _queue;
128
+
129
+ public:
130
+ InterruptiveConcurrentQueue(HANDLE completionPort, size_t key): _queue(), _completionPort(completionPort), _key(key)
131
+ {
132
+ }
133
+
134
+ void enqueue(T item)
135
+ {
136
+ _queue.enqueue(std::move(item));
137
+ if (!PostQueuedCompletionStatus(_completionPort, 0, (ULONG_PTR)_key, nullptr)) {
138
+ unrecoverableError({
139
+ Error::ErrorCode::CoreBug,
140
+ "Originated from",
141
+ "PostQueuedCompletionStatus",
142
+ "Errno is",
143
+ GetLastError(),
144
+ });
145
+ }
146
+ }
147
+
148
+ // NOTE: this method will block until an item is available
149
+ // NOTE: On Windows, due to how the polling mechanism work, we can only do it 1 by 1.
150
+ // NOTE: On Windows, we don't have a guaranteed number of functions, unlike with eventfd.
151
+ std::vector<T> dequeue()
152
+ {
153
+ std::vector<T> vecT(1);
154
+ while (!_queue.try_dequeue(vecT[0]))
155
+ ;
156
+ return vecT;
157
+ }
158
+
159
+ // unmovable, uncopyable
160
+ InterruptiveConcurrentQueue(const InterruptiveConcurrentQueue&) = delete;
161
+ InterruptiveConcurrentQueue& operator=(const InterruptiveConcurrentQueue&) = delete;
162
+ InterruptiveConcurrentQueue(InterruptiveConcurrentQueue&&) = delete;
163
+ InterruptiveConcurrentQueue& operator=(InterruptiveConcurrentQueue&&) = delete;
164
+ };
165
+
166
+ #endif // _WIN32
167
+
168
+ } // namespace ymq
169
+ } // namespace scaler
@@ -0,0 +1,98 @@
1
+
2
+ #include "scaler/io/ymq/io_context.h"
3
+
4
+ #include <algorithm> // std::ranges::generate
5
+ #include <cassert> // assert
6
+ #include <future>
7
+ #include <memory> // std::make_shared
8
+ #ifdef _WIN32
9
+ // clang-format off
10
+ #include <winsock2.h>
11
+ #include <mswsock.h>
12
+ // clang-format on
13
+ #endif // _WIN32
14
+
15
+ #include "scaler/io/ymq/event_loop_thread.h"
16
+ #include "scaler/io/ymq/io_socket.h"
17
+ #include "scaler/io/ymq/typedefs.h"
18
+
19
+ namespace scaler {
20
+ namespace ymq {
21
+
22
+ IOContext::IOContext(size_t threadCount) noexcept: _threads(threadCount)
23
+ {
24
+ assert(threadCount > 0);
25
+ std::ranges::generate(_threads, std::make_shared<EventLoopThread>);
26
+ #ifdef _WIN32
27
+ WSADATA wsaData;
28
+ const int myErrno = WSAStartup(MAKEWORD(2, 2), &wsaData);
29
+ if (myErrno != 0) {
30
+ unrecoverableError({
31
+ Error::ErrorCode::ConfigurationError,
32
+ "Originated from",
33
+ "WSAStartup",
34
+ "Errno is",
35
+ strerror(myErrno),
36
+ });
37
+ }
38
+ #endif // _WIN32
39
+ }
40
+
41
+ void IOContext::createIOSocket(
42
+ Identity identity, IOSocketType socketType, CreateIOSocketCallback onIOSocketCreated) & noexcept
43
+ {
44
+ static std::atomic<size_t> threadsRoundRobin = 0;
45
+ auto& thread = _threads[threadsRoundRobin];
46
+ ++threadsRoundRobin;
47
+ threadsRoundRobin = threadsRoundRobin % _threads.size();
48
+ thread->createIOSocket(std::move(identity), socketType, std::move(onIOSocketCreated));
49
+ }
50
+
51
+ void IOContext::removeIOSocket(std::shared_ptr<IOSocket>& socket) noexcept
52
+ {
53
+ auto* rawSocket = socket.get();
54
+ socket.reset();
55
+
56
+ // FIXME: This is a tmp fix in order to keep the changes small.
57
+ // A better fix would be to put "id" in each shared_ptr<EventLoopThread> and query that
58
+ // id to get the index. This is needed so we shutdown the thread when it's not needed.
59
+ // The better fix should be implemented in the next PR.
60
+ int id = -1;
61
+ {
62
+ // NOTE: Keep the eventloop thread alive
63
+ auto eventLoopThread = rawSocket->_eventLoopThread;
64
+ std::promise<void> promise;
65
+ auto future = promise.get_future();
66
+ rawSocket->_eventLoopThread->_eventLoop.executeNow([&promise, rawSocket] {
67
+ rawSocket->_eventLoopThread->_eventLoop.executeLater([&promise, rawSocket] {
68
+ rawSocket->_eventLoopThread->removeIOSocket(rawSocket);
69
+ promise.set_value();
70
+ });
71
+ });
72
+ future.wait();
73
+ if (eventLoopThread->stopRequested()) {
74
+ auto it = std::ranges::find_if(_threads, [&](const auto& x) { return x.get() == eventLoopThread.get(); });
75
+ id = std::distance(_threads.begin(), it);
76
+ }
77
+ }
78
+ if (id != -1) {
79
+ assert(_threads[id].use_count() == 1);
80
+ _threads[id] = std::make_shared<EventLoopThread>();
81
+ }
82
+ }
83
+
84
+ void IOContext::requestIOSocketStop(std::shared_ptr<IOSocket> socket) noexcept
85
+ {
86
+ socket->_eventLoopThread->_eventLoop.executeNow(
87
+ [socket] { socket->_eventLoopThread->_eventLoop.executeLater([socket] { socket->requestStop(); }); });
88
+ }
89
+
90
+ IOContext::~IOContext() noexcept
91
+ {
92
+ #ifdef _WIN32
93
+ WSACleanup();
94
+ #endif // _WIN32
95
+ }
96
+
97
+ } // namespace ymq
98
+ } // namespace scaler
@@ -0,0 +1,44 @@
1
+ #pragma once
2
+
3
+ // C++
4
+ #include <memory>
5
+ #include <vector>
6
+
7
+ // First-party
8
+ #include "scaler/io/ymq/configuration.h"
9
+ #include "scaler/io/ymq/typedefs.h"
10
+
11
+ namespace scaler {
12
+ namespace ymq {
13
+
14
+ class IOSocket;
15
+ class EventLoopThread;
16
+
17
+ class IOContext {
18
+ public:
19
+ using Identity = Configuration::IOSocketIdentity;
20
+ using CreateIOSocketCallback = Configuration::CreateIOSocketCallback;
21
+
22
+ IOContext(size_t threadCount = 1) noexcept;
23
+ IOContext(const IOContext&) = delete;
24
+ IOContext& operator=(const IOContext&) = delete;
25
+ IOContext(IOContext&&) = delete;
26
+ IOContext& operator=(IOContext&&) = delete;
27
+ ~IOContext() noexcept;
28
+
29
+ void createIOSocket(
30
+ Identity identity, IOSocketType socketType, CreateIOSocketCallback onIOSocketCreated) & noexcept;
31
+
32
+ // After user called this method, no other call on the passed in IOSocket should be made.
33
+ void removeIOSocket(std::shared_ptr<IOSocket>& socket) noexcept;
34
+
35
+ void requestIOSocketStop(std::shared_ptr<IOSocket> socket) noexcept;
36
+
37
+ constexpr size_t numThreads() const noexcept { return _threads.size(); }
38
+
39
+ private:
40
+ std::vector<std::shared_ptr<EventLoopThread>> _threads;
41
+ };
42
+
43
+ } // namespace ymq
44
+ } // namespace scaler
@@ -0,0 +1,299 @@
1
+ #include "scaler/io/ymq/io_socket.h"
2
+
3
+ #include <algorithm>
4
+ #include <expected>
5
+ #include <memory>
6
+ #include <optional>
7
+ #include <ranges>
8
+ #include <utility>
9
+ #include <vector>
10
+
11
+ #include "scaler/io/ymq/error.h"
12
+ #include "scaler/io/ymq/event_loop_thread.h"
13
+ #include "scaler/io/ymq/event_manager.h"
14
+ #include "scaler/io/ymq/message_connection_tcp.h"
15
+ #include "scaler/io/ymq/network_utils.h"
16
+ #include "scaler/io/ymq/tcp_client.h"
17
+ #include "scaler/io/ymq/tcp_server.h"
18
+ #include "scaler/io/ymq/typedefs.h"
19
+
20
+ namespace scaler {
21
+ namespace ymq {
22
+
23
+ IOSocket::IOSocket(
24
+ std::shared_ptr<EventLoopThread> eventLoopThread, Identity identity, IOSocketType socketType) noexcept
25
+ : _eventLoopThread(eventLoopThread)
26
+ , _identity(std::move(identity))
27
+ , _socketType(std::move(socketType))
28
+ , _pendingRecvMessages(std::make_shared<std::queue<RecvMessageCallback>>())
29
+ , _stopped {false}
30
+ , _connectorDisconnected {false}
31
+ {
32
+ }
33
+
34
+ void IOSocket::sendMessage(Message message, SendMessageCallback onMessageSent) noexcept
35
+ {
36
+ _eventLoopThread->_eventLoop.executeNow(
37
+ [this, message = std::move(message), callback = std::move(onMessageSent)] mutable {
38
+ if (_stopped) {
39
+ callback(std::unexpected {Error::ErrorCode::IOSocketStopRequested});
40
+ return;
41
+ }
42
+ if (_connectorDisconnected) {
43
+ callback(std::unexpected {Error::ErrorCode::ConnectorSocketClosedByRemoteEnd});
44
+ return;
45
+ }
46
+ if (!message.address.data() && this->socketType() == IOSocketType::Binder) {
47
+ callback(std::unexpected {Error::ErrorCode::BinderSendMessageWithNoAddress});
48
+ }
49
+
50
+ MessageConnectionTCP* conn = nullptr;
51
+
52
+ std::string address = std::string((char*)message.address.data(), message.address.len());
53
+ if (this->socketType() == IOSocketType::Connector) {
54
+ address = "";
55
+ } else if (this->socketType() == IOSocketType::Multicast) {
56
+ callback({}); // SUCCESS
57
+ for (const auto& [addr, conn]: _identityToConnection) {
58
+ // TODO: Currently doing N copies of the messages. Find a place to
59
+ // store this message and pass in reference.
60
+ if (addr.starts_with(address))
61
+ conn->sendMessage(message, [](auto) {});
62
+ }
63
+ return;
64
+ }
65
+
66
+ if (this->_identityToConnection.contains(address)) {
67
+ conn = this->_identityToConnection[address].get();
68
+ } else {
69
+ const auto it = std::ranges::find(
70
+ _unestablishedConnection, address, &MessageConnectionTCP::_remoteIOSocketIdentity);
71
+ if (it != _unestablishedConnection.end()) {
72
+ conn = it->get();
73
+ } else {
74
+ onConnectionCreated(address);
75
+ conn = _unestablishedConnection.back().get();
76
+ }
77
+ }
78
+ conn->sendMessage(std::move(message), std::move(callback));
79
+ });
80
+ }
81
+
82
+ void IOSocket::recvMessage(RecvMessageCallback onRecvMessage) noexcept
83
+ {
84
+ _eventLoopThread->_eventLoop.executeNow([this, callback = std::move(onRecvMessage)] mutable {
85
+ if (_stopped) {
86
+ callback({{}, Error::ErrorCode::IOSocketStopRequested});
87
+ return;
88
+ }
89
+
90
+ if (_connectorDisconnected) {
91
+ callback({{}, Error::ErrorCode::ConnectorSocketClosedByRemoteEnd});
92
+ return;
93
+ }
94
+
95
+ this->_pendingRecvMessages->emplace(std::move(callback));
96
+ if (_pendingRecvMessages->size() == 1) {
97
+ for (const auto& [fd, conn]: _identityToConnection) {
98
+ if (conn->recvMessage())
99
+ return;
100
+ }
101
+ }
102
+ });
103
+ }
104
+
105
+ void IOSocket::connectTo(sockaddr addr, ConnectReturnCallback onConnectReturn, size_t maxRetryTimes) noexcept
106
+ {
107
+ _eventLoopThread->_eventLoop.executeNow(
108
+ [this, addr = std::move(addr), callback = std::move(onConnectReturn), maxRetryTimes] mutable {
109
+ if (_tcpClient) {
110
+ unrecoverableError({
111
+ Error::ErrorCode::MultipleConnectToNotSupported,
112
+ "Originated from",
113
+ "IOSocket::connectTo",
114
+ });
115
+ }
116
+
117
+ _tcpClient.emplace(_eventLoopThread, this->identity(), std::move(addr), std::move(callback), maxRetryTimes);
118
+ _tcpClient->onCreated();
119
+ });
120
+ }
121
+
122
+ void IOSocket::connectTo(
123
+ std::string networkAddress, ConnectReturnCallback onConnectReturn, size_t maxRetryTimes) noexcept
124
+ {
125
+ auto res = stringToSockaddr(std::move(networkAddress));
126
+ connectTo(std::move(res.value()), std::move(onConnectReturn), maxRetryTimes);
127
+ }
128
+
129
+ void IOSocket::bindTo(std::string networkAddress, BindReturnCallback onBindReturn) noexcept
130
+ {
131
+ _eventLoopThread->_eventLoop.executeNow(
132
+ [this, networkAddress = std::move(networkAddress), callback = std::move(onBindReturn)] mutable {
133
+ if (_tcpServer) {
134
+ callback(std::unexpected {Error::ErrorCode::MultipleBindToNotSupported});
135
+ return;
136
+ }
137
+ auto res = stringToSockaddr(std::move(networkAddress));
138
+ assert(res);
139
+
140
+ _tcpServer.emplace(_eventLoopThread, this->identity(), std::move(res.value()), std::move(callback));
141
+ _tcpServer->onCreated();
142
+ });
143
+ }
144
+
145
+ void IOSocket::closeConnection(Identity remoteSocketIdentity) noexcept
146
+ {
147
+ _eventLoopThread->_eventLoop.executeNow([this, remoteIdentity = std::move(remoteSocketIdentity)] {
148
+ _eventLoopThread->_eventLoop.executeLater([this, remoteIdentity = std::move(remoteIdentity)] {
149
+ if (_stopped) {
150
+ return;
151
+ }
152
+ // _identityToConnection.erase(remoteIdentity);
153
+ if (_identityToConnection.contains(remoteIdentity))
154
+ _identityToConnection[remoteIdentity]->disconnect();
155
+ });
156
+ });
157
+ }
158
+
159
+ // TODO: The function should be separated into onConnectionAborted, onConnectionDisconnected,
160
+ // and probably onConnectionAbortedBeforeEstablished(?)
161
+ void IOSocket::onConnectionDisconnected(MessageConnectionTCP* conn, bool keepInBook) noexcept
162
+ {
163
+ if (!conn->_remoteIOSocketIdentity) {
164
+ return;
165
+ }
166
+
167
+ auto connIt = this->_identityToConnection.find(*conn->_remoteIOSocketIdentity);
168
+
169
+ _unestablishedConnection.push_back(std::move(connIt->second));
170
+ this->_identityToConnection.erase(connIt);
171
+ auto& connPtr = _unestablishedConnection.back();
172
+
173
+ if (!keepInBook) {
174
+ if (IOSocketType::Connector == this->_socketType) {
175
+ _connectorDisconnected = true;
176
+ while (this->_pendingRecvMessages->size()) {
177
+ auto top = std::move(this->_pendingRecvMessages->front());
178
+ top({Message {}, {Error::ErrorCode::ConnectorSocketClosedByRemoteEnd}});
179
+ this->_pendingRecvMessages->pop();
180
+ }
181
+ }
182
+ _unestablishedConnection.pop_back();
183
+ return;
184
+ }
185
+
186
+ if (socketType() == IOSocketType::Unicast || socketType() == IOSocketType::Multicast) {
187
+ auto destructWriteOp = std::move(connPtr->_writeOperations);
188
+ connPtr->_writeOperations.clear();
189
+ while (_pendingRecvMessages->size()) {
190
+ _pendingRecvMessages->front()(
191
+ {{}, Error::ErrorCode::RemoteEndDisconnectedOnSocketWithoutGuaranteedDelivery});
192
+ _pendingRecvMessages->pop();
193
+ }
194
+ auto destructReadOp = std::move(connPtr->_receivedReadOperations);
195
+ }
196
+
197
+ if (connPtr->_responsibleForRetry) {
198
+ connectTo(connPtr->_remoteAddr, [](auto) {}); // as the user callback is one-shot
199
+ }
200
+ }
201
+
202
+ // FIXME: This algorithm runs in O(n) complexity. To reduce the complexity of this algorithm
203
+ // to O(lg n), one has to restructure how connections are placed. We would have three lists:
204
+ // - _unconnectedConnections that holds connections with identity but not fd
205
+ // - _unestablishedConnections that holds connections with fd but not identity
206
+ // - _connectingConnections that holds connections with fd and identity
207
+ // And this three lists shall be lookedup in above order based on this rule:
208
+ // - look up in _unestablishedConnections and move this connection to _connectingConnections
209
+ // - look up _unconnectedConnections to find if there's a connection with the same identity
210
+ // if so, merge it to this connection that currently resides in _connectingConnections
211
+ // Similar thing for disconnection as well.
212
+ void IOSocket::onConnectionIdentityReceived(MessageConnectionTCP* conn) noexcept
213
+ {
214
+ auto& s = conn->_remoteIOSocketIdentity;
215
+ if (socketType() == IOSocketType::Connector) {
216
+ s = "";
217
+ }
218
+
219
+ auto thisConn = std::find_if(_unestablishedConnection.begin(), _unestablishedConnection.end(), [&](const auto& x) {
220
+ return x.get() == conn;
221
+ });
222
+ _identityToConnection[*s] = std::move(*thisConn);
223
+ _unestablishedConnection.erase(thisConn);
224
+
225
+ auto rge = _unestablishedConnection //
226
+ | std::views::filter([](const auto& x) { return x->_remoteIOSocketIdentity != std::nullopt; }) //
227
+ | std::views::filter([&s](const auto& x) { return *x->_remoteIOSocketIdentity == *s; }) //
228
+ | std::views::take(1);
229
+ if (rge.empty()) {
230
+ return;
231
+ }
232
+
233
+ auto& targetConn = _identityToConnection[*s];
234
+
235
+ auto c = _unestablishedConnection.begin() + (_unestablishedConnection.size() - rge.begin().count());
236
+
237
+ while ((*c)->_writeOperations.size()) {
238
+ targetConn->_writeOperations.emplace_back(std::move((*c)->_writeOperations.front()));
239
+ (*c)->_writeOperations.pop_front();
240
+ }
241
+
242
+ targetConn->_pendingRecvMessageCallbacks = std::move((*c)->_pendingRecvMessageCallbacks);
243
+
244
+ assert(targetConn->_receivedReadOperations.empty());
245
+ targetConn->_receivedReadOperations = std::move((*c)->_receivedReadOperations);
246
+
247
+ _unestablishedConnection.erase(c);
248
+ }
249
+
250
+ void IOSocket::onConnectionCreated(std::string remoteIOSocketIdentity) noexcept
251
+ {
252
+ _unestablishedConnection.push_back(
253
+ std::make_unique<MessageConnectionTCP>(
254
+ _eventLoopThread, this->identity(), std::move(remoteIOSocketIdentity), _pendingRecvMessages));
255
+ _unestablishedConnection.back()->onCreated();
256
+ }
257
+
258
+ void IOSocket::onConnectionCreated(int fd, sockaddr localAddr, sockaddr remoteAddr, bool responsibleForRetry) noexcept
259
+ {
260
+ _unestablishedConnection.push_back(
261
+ std::make_unique<MessageConnectionTCP>(
262
+ _eventLoopThread,
263
+ fd,
264
+ std::move(localAddr),
265
+ std::move(remoteAddr),
266
+ this->identity(),
267
+ responsibleForRetry,
268
+ _pendingRecvMessages));
269
+ _unestablishedConnection.back()->onCreated();
270
+ }
271
+
272
+ void IOSocket::removeConnectedTcpClient() noexcept
273
+ {
274
+ if (this->_tcpClient && this->_tcpClient->_connected) {
275
+ this->_tcpClient.reset();
276
+ }
277
+ }
278
+
279
+ void IOSocket::requestStop() noexcept
280
+ {
281
+ _stopped = true;
282
+ while (_pendingRecvMessages->size()) {
283
+ auto readOp = std::move(_pendingRecvMessages->front());
284
+ _pendingRecvMessages->pop();
285
+ readOp({{}, Error::ErrorCode::IOSocketStopRequested});
286
+ }
287
+ }
288
+
289
+ IOSocket::~IOSocket() noexcept
290
+ {
291
+ while (_pendingRecvMessages->size()) {
292
+ auto readOp = std::move(_pendingRecvMessages->front());
293
+ _pendingRecvMessages->pop();
294
+ readOp({{}, Error::ErrorCode::IOSocketStopRequested});
295
+ }
296
+ }
297
+
298
+ } // namespace ymq
299
+ } // namespace scaler