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.
- opengris_scaler-1.12.7.dist-info/METADATA +729 -0
- opengris_scaler-1.12.7.dist-info/RECORD +234 -0
- opengris_scaler-1.12.7.dist-info/WHEEL +5 -0
- opengris_scaler-1.12.7.dist-info/entry_points.txt +9 -0
- opengris_scaler-1.12.7.dist-info/licenses/LICENSE +201 -0
- opengris_scaler-1.12.7.dist-info/licenses/LICENSE.spdx +7 -0
- opengris_scaler-1.12.7.dist-info/licenses/NOTICE +8 -0
- opengris_scaler.libs/libcapnp-1-61c06778.1.0.so +0 -0
- opengris_scaler.libs/libgcc_s-2298274a.so.1 +0 -0
- opengris_scaler.libs/libkj-1-21b63b70.1.0.so +0 -0
- opengris_scaler.libs/libstdc++-08d5c7eb.so.6.0.33 +0 -0
- scaler/CMakeLists.txt +11 -0
- scaler/__init__.py +14 -0
- scaler/about.py +5 -0
- scaler/client/__init__.py +0 -0
- scaler/client/agent/__init__.py +0 -0
- scaler/client/agent/client_agent.py +210 -0
- scaler/client/agent/disconnect_manager.py +27 -0
- scaler/client/agent/future_manager.py +112 -0
- scaler/client/agent/heartbeat_manager.py +74 -0
- scaler/client/agent/mixins.py +89 -0
- scaler/client/agent/object_manager.py +98 -0
- scaler/client/agent/task_manager.py +64 -0
- scaler/client/client.py +635 -0
- scaler/client/future.py +252 -0
- scaler/client/object_buffer.py +129 -0
- scaler/client/object_reference.py +25 -0
- scaler/client/serializer/__init__.py +0 -0
- scaler/client/serializer/default.py +16 -0
- scaler/client/serializer/mixins.py +38 -0
- scaler/cluster/__init__.py +0 -0
- scaler/cluster/cluster.py +115 -0
- scaler/cluster/combo.py +148 -0
- scaler/cluster/object_storage_server.py +45 -0
- scaler/cluster/scheduler.py +83 -0
- scaler/config/__init__.py +0 -0
- scaler/config/defaults.py +87 -0
- scaler/config/loader.py +95 -0
- scaler/config/mixins.py +15 -0
- scaler/config/section/__init__.py +0 -0
- scaler/config/section/cluster.py +56 -0
- scaler/config/section/native_worker_adapter.py +44 -0
- scaler/config/section/object_storage_server.py +7 -0
- scaler/config/section/scheduler.py +53 -0
- scaler/config/section/symphony_worker_adapter.py +47 -0
- scaler/config/section/top.py +13 -0
- scaler/config/section/webui.py +16 -0
- scaler/config/types/__init__.py +0 -0
- scaler/config/types/object_storage_server.py +45 -0
- scaler/config/types/worker.py +57 -0
- scaler/config/types/zmq.py +79 -0
- scaler/entry_points/__init__.py +0 -0
- scaler/entry_points/cluster.py +133 -0
- scaler/entry_points/object_storage_server.py +41 -0
- scaler/entry_points/scheduler.py +135 -0
- scaler/entry_points/top.py +286 -0
- scaler/entry_points/webui.py +26 -0
- scaler/entry_points/worker_adapter_native.py +137 -0
- scaler/entry_points/worker_adapter_symphony.py +102 -0
- scaler/io/__init__.py +0 -0
- scaler/io/async_binder.py +85 -0
- scaler/io/async_connector.py +95 -0
- scaler/io/async_object_storage_connector.py +185 -0
- scaler/io/mixins.py +154 -0
- scaler/io/sync_connector.py +68 -0
- scaler/io/sync_object_storage_connector.py +185 -0
- scaler/io/sync_subscriber.py +83 -0
- scaler/io/utility.py +31 -0
- scaler/io/ymq/CMakeLists.txt +98 -0
- scaler/io/ymq/__init__.py +0 -0
- scaler/io/ymq/_ymq.pyi +96 -0
- scaler/io/ymq/_ymq.so +0 -0
- scaler/io/ymq/bytes.h +114 -0
- scaler/io/ymq/common.h +29 -0
- scaler/io/ymq/configuration.h +60 -0
- scaler/io/ymq/epoll_context.cpp +185 -0
- scaler/io/ymq/epoll_context.h +85 -0
- scaler/io/ymq/error.h +132 -0
- scaler/io/ymq/event_loop.h +55 -0
- scaler/io/ymq/event_loop_thread.cpp +64 -0
- scaler/io/ymq/event_loop_thread.h +46 -0
- scaler/io/ymq/event_manager.h +81 -0
- scaler/io/ymq/file_descriptor.h +203 -0
- scaler/io/ymq/interruptive_concurrent_queue.h +169 -0
- scaler/io/ymq/io_context.cpp +98 -0
- scaler/io/ymq/io_context.h +44 -0
- scaler/io/ymq/io_socket.cpp +299 -0
- scaler/io/ymq/io_socket.h +121 -0
- scaler/io/ymq/iocp_context.cpp +102 -0
- scaler/io/ymq/iocp_context.h +83 -0
- scaler/io/ymq/logging.h +163 -0
- scaler/io/ymq/message.h +15 -0
- scaler/io/ymq/message_connection.h +16 -0
- scaler/io/ymq/message_connection_tcp.cpp +672 -0
- scaler/io/ymq/message_connection_tcp.h +96 -0
- scaler/io/ymq/network_utils.h +179 -0
- scaler/io/ymq/pymod_ymq/bytes.h +113 -0
- scaler/io/ymq/pymod_ymq/exception.h +124 -0
- scaler/io/ymq/pymod_ymq/gil.h +15 -0
- scaler/io/ymq/pymod_ymq/io_context.h +166 -0
- scaler/io/ymq/pymod_ymq/io_socket.h +285 -0
- scaler/io/ymq/pymod_ymq/message.h +99 -0
- scaler/io/ymq/pymod_ymq/python.h +153 -0
- scaler/io/ymq/pymod_ymq/ymq.cpp +23 -0
- scaler/io/ymq/pymod_ymq/ymq.h +357 -0
- scaler/io/ymq/readme.md +114 -0
- scaler/io/ymq/simple_interface.cpp +80 -0
- scaler/io/ymq/simple_interface.h +24 -0
- scaler/io/ymq/tcp_client.cpp +367 -0
- scaler/io/ymq/tcp_client.h +75 -0
- scaler/io/ymq/tcp_operations.h +41 -0
- scaler/io/ymq/tcp_server.cpp +410 -0
- scaler/io/ymq/tcp_server.h +79 -0
- scaler/io/ymq/third_party/concurrentqueue.h +3747 -0
- scaler/io/ymq/timed_queue.h +272 -0
- scaler/io/ymq/timestamp.h +102 -0
- scaler/io/ymq/typedefs.h +20 -0
- scaler/io/ymq/utils.h +34 -0
- scaler/io/ymq/ymq.py +130 -0
- scaler/object_storage/CMakeLists.txt +50 -0
- scaler/object_storage/__init__.py +0 -0
- scaler/object_storage/constants.h +11 -0
- scaler/object_storage/defs.h +14 -0
- scaler/object_storage/io_helper.cpp +44 -0
- scaler/object_storage/io_helper.h +9 -0
- scaler/object_storage/message.cpp +56 -0
- scaler/object_storage/message.h +130 -0
- scaler/object_storage/object_manager.cpp +126 -0
- scaler/object_storage/object_manager.h +52 -0
- scaler/object_storage/object_storage_server.cpp +359 -0
- scaler/object_storage/object_storage_server.h +126 -0
- scaler/object_storage/object_storage_server.so +0 -0
- scaler/object_storage/pymod_object_storage_server.cpp +104 -0
- scaler/protocol/__init__.py +0 -0
- scaler/protocol/capnp/__init__.py +0 -0
- scaler/protocol/capnp/_python.py +6 -0
- scaler/protocol/capnp/common.capnp +63 -0
- scaler/protocol/capnp/message.capnp +216 -0
- scaler/protocol/capnp/object_storage.capnp +52 -0
- scaler/protocol/capnp/status.capnp +73 -0
- scaler/protocol/introduction.md +105 -0
- scaler/protocol/python/__init__.py +0 -0
- scaler/protocol/python/common.py +135 -0
- scaler/protocol/python/message.py +726 -0
- scaler/protocol/python/mixins.py +13 -0
- scaler/protocol/python/object_storage.py +118 -0
- scaler/protocol/python/status.py +279 -0
- scaler/protocol/worker.md +228 -0
- scaler/scheduler/__init__.py +0 -0
- scaler/scheduler/allocate_policy/__init__.py +0 -0
- scaler/scheduler/allocate_policy/allocate_policy.py +9 -0
- scaler/scheduler/allocate_policy/capability_allocate_policy.py +280 -0
- scaler/scheduler/allocate_policy/even_load_allocate_policy.py +159 -0
- scaler/scheduler/allocate_policy/mixins.py +55 -0
- scaler/scheduler/controllers/__init__.py +0 -0
- scaler/scheduler/controllers/balance_controller.py +65 -0
- scaler/scheduler/controllers/client_controller.py +131 -0
- scaler/scheduler/controllers/config_controller.py +31 -0
- scaler/scheduler/controllers/graph_controller.py +424 -0
- scaler/scheduler/controllers/information_controller.py +81 -0
- scaler/scheduler/controllers/mixins.py +201 -0
- scaler/scheduler/controllers/object_controller.py +147 -0
- scaler/scheduler/controllers/scaling_controller.py +86 -0
- scaler/scheduler/controllers/task_controller.py +373 -0
- scaler/scheduler/controllers/worker_controller.py +168 -0
- scaler/scheduler/object_usage/__init__.py +0 -0
- scaler/scheduler/object_usage/object_tracker.py +131 -0
- scaler/scheduler/scheduler.py +253 -0
- scaler/scheduler/task/__init__.py +0 -0
- scaler/scheduler/task/task_state_machine.py +92 -0
- scaler/scheduler/task/task_state_manager.py +61 -0
- scaler/ui/__init__.py +0 -0
- scaler/ui/constants.py +9 -0
- scaler/ui/live_display.py +118 -0
- scaler/ui/memory_window.py +146 -0
- scaler/ui/setting_page.py +47 -0
- scaler/ui/task_graph.py +370 -0
- scaler/ui/task_log.py +83 -0
- scaler/ui/utility.py +35 -0
- scaler/ui/webui.py +125 -0
- scaler/ui/worker_processors.py +85 -0
- scaler/utility/__init__.py +0 -0
- scaler/utility/debug.py +19 -0
- scaler/utility/event_list.py +63 -0
- scaler/utility/event_loop.py +58 -0
- scaler/utility/exceptions.py +42 -0
- scaler/utility/formatter.py +44 -0
- scaler/utility/graph/__init__.py +0 -0
- scaler/utility/graph/optimization.py +27 -0
- scaler/utility/graph/topological_sorter.py +11 -0
- scaler/utility/graph/topological_sorter_graphblas.py +174 -0
- scaler/utility/identifiers.py +105 -0
- scaler/utility/logging/__init__.py +0 -0
- scaler/utility/logging/decorators.py +25 -0
- scaler/utility/logging/scoped_logger.py +33 -0
- scaler/utility/logging/utility.py +183 -0
- scaler/utility/many_to_many_dict.py +123 -0
- scaler/utility/metadata/__init__.py +0 -0
- scaler/utility/metadata/profile_result.py +31 -0
- scaler/utility/metadata/task_flags.py +30 -0
- scaler/utility/mixins.py +13 -0
- scaler/utility/network_util.py +7 -0
- scaler/utility/one_to_many_dict.py +72 -0
- scaler/utility/queues/__init__.py +0 -0
- scaler/utility/queues/async_indexed_queue.py +37 -0
- scaler/utility/queues/async_priority_queue.py +70 -0
- scaler/utility/queues/async_sorted_priority_queue.py +45 -0
- scaler/utility/queues/indexed_queue.py +114 -0
- scaler/utility/serialization.py +9 -0
- scaler/version.txt +1 -0
- scaler/worker/__init__.py +0 -0
- scaler/worker/agent/__init__.py +0 -0
- scaler/worker/agent/heartbeat_manager.py +107 -0
- scaler/worker/agent/mixins.py +137 -0
- scaler/worker/agent/processor/__init__.py +0 -0
- scaler/worker/agent/processor/object_cache.py +107 -0
- scaler/worker/agent/processor/processor.py +279 -0
- scaler/worker/agent/processor/streaming_buffer.py +28 -0
- scaler/worker/agent/processor_holder.py +145 -0
- scaler/worker/agent/processor_manager.py +365 -0
- scaler/worker/agent/profiling_manager.py +109 -0
- scaler/worker/agent/task_manager.py +150 -0
- scaler/worker/agent/timeout_manager.py +19 -0
- scaler/worker/preload.py +84 -0
- scaler/worker/worker.py +264 -0
- scaler/worker_adapter/__init__.py +0 -0
- scaler/worker_adapter/native.py +154 -0
- scaler/worker_adapter/symphony/__init__.py +0 -0
- scaler/worker_adapter/symphony/callback.py +45 -0
- scaler/worker_adapter/symphony/heartbeat_manager.py +79 -0
- scaler/worker_adapter/symphony/message.py +24 -0
- scaler/worker_adapter/symphony/task_manager.py +288 -0
- scaler/worker_adapter/symphony/worker.py +205 -0
- 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
|