opengris-scaler 1.12.7__cp310-cp310-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,272 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef __linux__
|
|
4
|
+
#include <sys/timerfd.h>
|
|
5
|
+
#include <unistd.h>
|
|
6
|
+
#endif // __linux__
|
|
7
|
+
#ifdef _WIN32
|
|
8
|
+
#include <windows.h>
|
|
9
|
+
#endif // _WIN32
|
|
10
|
+
|
|
11
|
+
#include <cassert>
|
|
12
|
+
#include <queue>
|
|
13
|
+
#include <set>
|
|
14
|
+
|
|
15
|
+
#include "scaler/io/ymq/configuration.h"
|
|
16
|
+
#include "scaler/io/ymq/error.h"
|
|
17
|
+
#include "scaler/io/ymq/timestamp.h"
|
|
18
|
+
|
|
19
|
+
namespace scaler {
|
|
20
|
+
namespace ymq {
|
|
21
|
+
|
|
22
|
+
#ifdef __linux__
|
|
23
|
+
inline int createTimerfd()
|
|
24
|
+
{
|
|
25
|
+
int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
|
26
|
+
if (timerfd >= 0) {
|
|
27
|
+
return timerfd;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const int myErrno = errno;
|
|
31
|
+
switch (myErrno) {
|
|
32
|
+
case EMFILE:
|
|
33
|
+
case ENFILE:
|
|
34
|
+
case ENODEV:
|
|
35
|
+
case ENOMEM:
|
|
36
|
+
case EPERM:
|
|
37
|
+
unrecoverableError({
|
|
38
|
+
Error::ErrorCode::ConfigurationError,
|
|
39
|
+
"Originated from",
|
|
40
|
+
__PRETTY_FUNCTION__,
|
|
41
|
+
"Errno is",
|
|
42
|
+
strerror(myErrno),
|
|
43
|
+
});
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case EINVAL:
|
|
47
|
+
case EBADF:
|
|
48
|
+
case EFAULT:
|
|
49
|
+
case ECANCELED:
|
|
50
|
+
default:
|
|
51
|
+
unrecoverableError({
|
|
52
|
+
Error::ErrorCode::CoreBug,
|
|
53
|
+
"Originated from",
|
|
54
|
+
__PRETTY_FUNCTION__,
|
|
55
|
+
"Errno is",
|
|
56
|
+
strerror(myErrno),
|
|
57
|
+
});
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class TimedQueue {
|
|
63
|
+
public:
|
|
64
|
+
using Callback = Configuration::TimedQueueCallback;
|
|
65
|
+
using Identifier = Configuration::ExecutionCancellationIdentifier;
|
|
66
|
+
using TimedFunc = std::tuple<Timestamp, Callback, Identifier>;
|
|
67
|
+
constexpr static auto cmp = [](const auto& x, const auto& y) { return std::get<0>(x) < std::get<0>(y); };
|
|
68
|
+
using PriorityQueue = std::priority_queue<TimedFunc, std::vector<TimedFunc>, decltype(cmp)>;
|
|
69
|
+
|
|
70
|
+
TimedQueue(): _timerFd(createTimerfd()), _currentId {} { assert(_timerFd); }
|
|
71
|
+
~TimedQueue()
|
|
72
|
+
{
|
|
73
|
+
if (_timerFd >= 0)
|
|
74
|
+
close(_timerFd);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Identifier push(Timestamp timestamp, Callback cb)
|
|
78
|
+
{
|
|
79
|
+
auto ts = convertToItimerspec(timestamp);
|
|
80
|
+
if (pq.empty() || timestamp < std::get<0>(pq.top())) {
|
|
81
|
+
int ret = timerfd_settime(_timerFd, 0, &ts, nullptr);
|
|
82
|
+
if (ret == -1) {
|
|
83
|
+
unrecoverableError({
|
|
84
|
+
Error::ErrorCode::CoreBug,
|
|
85
|
+
"Originated from",
|
|
86
|
+
__PRETTY_FUNCTION__,
|
|
87
|
+
"Errno is",
|
|
88
|
+
strerror(errno),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
pq.push({timestamp, std::move(cb), _currentId});
|
|
93
|
+
return _currentId++;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void cancelExecution(Identifier id) { _cancelledFunctions.insert(id); }
|
|
97
|
+
|
|
98
|
+
std::vector<Callback> dequeue()
|
|
99
|
+
{
|
|
100
|
+
uint64_t numItems;
|
|
101
|
+
ssize_t n = read(_timerFd, &numItems, sizeof numItems);
|
|
102
|
+
if (n != sizeof numItems) [[unlikely]] {
|
|
103
|
+
// This should never happen anyway
|
|
104
|
+
unrecoverableError({
|
|
105
|
+
Error::ErrorCode::CoreBug,
|
|
106
|
+
"Originated from",
|
|
107
|
+
__PRETTY_FUNCTION__,
|
|
108
|
+
"Errno is",
|
|
109
|
+
strerror(errno),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
std::vector<Callback> callbacks;
|
|
114
|
+
|
|
115
|
+
Timestamp now;
|
|
116
|
+
while (pq.size()) {
|
|
117
|
+
if (std::get<0>(pq.top()) < now) {
|
|
118
|
+
auto [ts, cb, id] = std::move(const_cast<PriorityQueue::reference>(pq.top()));
|
|
119
|
+
pq.pop();
|
|
120
|
+
auto cancelled = _cancelledFunctions.find(id);
|
|
121
|
+
if (cancelled != _cancelledFunctions.end()) {
|
|
122
|
+
_cancelledFunctions.erase(cancelled);
|
|
123
|
+
} else {
|
|
124
|
+
callbacks.emplace_back(std::move(cb));
|
|
125
|
+
}
|
|
126
|
+
} else
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!pq.empty()) {
|
|
131
|
+
auto nextTs = std::get<0>(pq.top());
|
|
132
|
+
auto ts = convertToItimerspec(nextTs);
|
|
133
|
+
int ret = timerfd_settime(_timerFd, 0, &ts, nullptr);
|
|
134
|
+
if (ret == -1) {
|
|
135
|
+
const int myErrno = errno;
|
|
136
|
+
switch (myErrno) {
|
|
137
|
+
case EMFILE:
|
|
138
|
+
case ENFILE:
|
|
139
|
+
case ENODEV:
|
|
140
|
+
case ENOMEM:
|
|
141
|
+
case EPERM:
|
|
142
|
+
unrecoverableError({
|
|
143
|
+
Error::ErrorCode::ConfigurationError,
|
|
144
|
+
"Originated from",
|
|
145
|
+
__PRETTY_FUNCTION__,
|
|
146
|
+
"Errno is",
|
|
147
|
+
strerror(myErrno),
|
|
148
|
+
});
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
case EINVAL:
|
|
152
|
+
case EBADF:
|
|
153
|
+
case EFAULT:
|
|
154
|
+
case ECANCELED:
|
|
155
|
+
default:
|
|
156
|
+
unrecoverableError({
|
|
157
|
+
Error::ErrorCode::CoreBug,
|
|
158
|
+
"Originated from",
|
|
159
|
+
__PRETTY_FUNCTION__,
|
|
160
|
+
"Errno is",
|
|
161
|
+
strerror(myErrno),
|
|
162
|
+
});
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return callbacks;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
int timingFd() const { return _timerFd; }
|
|
171
|
+
|
|
172
|
+
private:
|
|
173
|
+
int _timerFd;
|
|
174
|
+
Identifier _currentId;
|
|
175
|
+
PriorityQueue pq;
|
|
176
|
+
std::set<Identifier> _cancelledFunctions;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
#endif // __linux__
|
|
180
|
+
|
|
181
|
+
#ifdef _WIN32
|
|
182
|
+
class TimedQueue {
|
|
183
|
+
public:
|
|
184
|
+
using Callback = Configuration::TimedQueueCallback;
|
|
185
|
+
using Identifier = Configuration::ExecutionCancellationIdentifier;
|
|
186
|
+
using TimedFunc = std::tuple<Timestamp, Callback, Identifier>;
|
|
187
|
+
constexpr static auto cmp = [](const auto& x, const auto& y) { return std::get<0>(x) < std::get<0>(y); };
|
|
188
|
+
using PriorityQueue = std::priority_queue<TimedFunc, std::vector<TimedFunc>, decltype(cmp)>;
|
|
189
|
+
HANDLE _completionPort;
|
|
190
|
+
const size_t _key;
|
|
191
|
+
|
|
192
|
+
// TODO: Handle error for system calls
|
|
193
|
+
TimedQueue(HANDLE completionPort, size_t key)
|
|
194
|
+
: _completionPort(completionPort), _key(key), _timerFd(CreateWaitableTimer(NULL, 0, NULL)), _currentId {}
|
|
195
|
+
{
|
|
196
|
+
assert(_timerFd);
|
|
197
|
+
}
|
|
198
|
+
~TimedQueue()
|
|
199
|
+
{
|
|
200
|
+
if (_timerFd) {
|
|
201
|
+
CloseHandle(_timerFd);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
Identifier push(Timestamp timestamp, Callback cb)
|
|
206
|
+
{
|
|
207
|
+
auto ts = convertToLARGE_INTEGER(timestamp);
|
|
208
|
+
if (pq.empty() || timestamp < std::get<0>(pq.top())) {
|
|
209
|
+
SetWaitableTimer(
|
|
210
|
+
_timerFd,
|
|
211
|
+
(LARGE_INTEGER*)&ts,
|
|
212
|
+
0,
|
|
213
|
+
[](LPVOID thisPointer, DWORD, DWORD) {
|
|
214
|
+
auto* self = (TimedQueue*)thisPointer;
|
|
215
|
+
PostQueuedCompletionStatus(self->_completionPort, 0, (ULONG_PTR)(self->_timerFd), nullptr);
|
|
216
|
+
},
|
|
217
|
+
(LPVOID)this,
|
|
218
|
+
false);
|
|
219
|
+
}
|
|
220
|
+
pq.push({timestamp, std::move(cb), _currentId});
|
|
221
|
+
return _currentId++;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
void cancelExecution(Identifier id) { _cancelledFunctions.insert(id); }
|
|
225
|
+
|
|
226
|
+
std::vector<Callback> dequeue()
|
|
227
|
+
{
|
|
228
|
+
std::vector<Callback> callbacks;
|
|
229
|
+
|
|
230
|
+
Timestamp now;
|
|
231
|
+
while (pq.size()) {
|
|
232
|
+
if (std::get<0>(pq.top()) < now) {
|
|
233
|
+
auto [ts, cb, id] = std::move(const_cast<PriorityQueue::reference>(pq.top()));
|
|
234
|
+
pq.pop();
|
|
235
|
+
auto cancelled = _cancelledFunctions.find(id);
|
|
236
|
+
if (cancelled != _cancelledFunctions.end()) {
|
|
237
|
+
_cancelledFunctions.erase(cancelled);
|
|
238
|
+
} else {
|
|
239
|
+
callbacks.emplace_back(std::move(cb));
|
|
240
|
+
}
|
|
241
|
+
} else
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (!pq.empty()) {
|
|
246
|
+
auto nextTs = std::get<0>(pq.top());
|
|
247
|
+
auto ts = convertToLARGE_INTEGER(nextTs);
|
|
248
|
+
SetWaitableTimer(
|
|
249
|
+
_timerFd,
|
|
250
|
+
(LARGE_INTEGER*)&ts,
|
|
251
|
+
0,
|
|
252
|
+
[](LPVOID thisPointer, DWORD, DWORD) {
|
|
253
|
+
auto* self = (TimedQueue*)thisPointer;
|
|
254
|
+
PostQueuedCompletionStatus(self->_completionPort, 0, (ULONG_PTR)(self->_timerFd), nullptr);
|
|
255
|
+
},
|
|
256
|
+
(LPVOID)this,
|
|
257
|
+
false);
|
|
258
|
+
}
|
|
259
|
+
return callbacks;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private:
|
|
263
|
+
HANDLE _timerFd;
|
|
264
|
+
Identifier _currentId;
|
|
265
|
+
PriorityQueue pq;
|
|
266
|
+
std::set<Identifier> _cancelledFunctions;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
#endif // _WIN32
|
|
270
|
+
|
|
271
|
+
} // namespace ymq
|
|
272
|
+
} // namespace scaler
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef __linux__
|
|
4
|
+
#include <sys/time.h> // itimerspec
|
|
5
|
+
#endif // __linux__
|
|
6
|
+
#ifdef _WIN32
|
|
7
|
+
#include <windows.h>
|
|
8
|
+
#endif // _WIN32
|
|
9
|
+
|
|
10
|
+
#include <cassert>
|
|
11
|
+
#include <chrono>
|
|
12
|
+
#include <ostream>
|
|
13
|
+
#include <sstream> // stringify
|
|
14
|
+
|
|
15
|
+
namespace scaler {
|
|
16
|
+
namespace ymq {
|
|
17
|
+
|
|
18
|
+
// Simple timestamp utility
|
|
19
|
+
struct Timestamp {
|
|
20
|
+
std::chrono::time_point<std::chrono::system_clock> timestamp;
|
|
21
|
+
|
|
22
|
+
friend std::strong_ordering operator<=>(Timestamp x, Timestamp y) { return x.timestamp <=> y.timestamp; }
|
|
23
|
+
|
|
24
|
+
Timestamp(): timestamp(std::chrono::system_clock::now()) {}
|
|
25
|
+
Timestamp(std::chrono::time_point<std::chrono::system_clock> t) { timestamp = std::move(t); }
|
|
26
|
+
|
|
27
|
+
template <class Rep, class Period = std::ratio<1>>
|
|
28
|
+
Timestamp createTimestampByOffsetDuration(std::chrono::duration<Rep, Period> offset)
|
|
29
|
+
{
|
|
30
|
+
return {timestamp + offset};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// For possibly logging purposes
|
|
35
|
+
inline std::string stringifyTimestamp(Timestamp ts)
|
|
36
|
+
{
|
|
37
|
+
std::ostringstream oss;
|
|
38
|
+
const auto ts_point {std::chrono::floor<std::chrono::seconds>(ts.timestamp)};
|
|
39
|
+
const std::chrono::zoned_time z {std::chrono::current_zone(), ts_point};
|
|
40
|
+
oss << std::format("{0:%F %T%z}", z);
|
|
41
|
+
return oss.str();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
inline std::ostream& operator<<(std::ostream& os, const Timestamp& ts)
|
|
45
|
+
{
|
|
46
|
+
// Use the existing stringify function
|
|
47
|
+
os << stringifyTimestamp(ts);
|
|
48
|
+
return os;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#ifdef __linux__
|
|
52
|
+
// For timerfd
|
|
53
|
+
inline itimerspec convertToItimerspec(Timestamp ts)
|
|
54
|
+
{
|
|
55
|
+
using namespace std::chrono;
|
|
56
|
+
|
|
57
|
+
itimerspec timerspec {};
|
|
58
|
+
const auto duration = ts.timestamp - std::chrono::system_clock::now();
|
|
59
|
+
assert(duration.count() >= 0);
|
|
60
|
+
|
|
61
|
+
const auto secs = duration_cast<seconds>(duration);
|
|
62
|
+
const auto nanosecs = duration_cast<nanoseconds>(duration - secs);
|
|
63
|
+
timerspec.it_value.tv_sec = secs.count();
|
|
64
|
+
timerspec.it_value.tv_nsec = nanosecs.count();
|
|
65
|
+
|
|
66
|
+
return timerspec;
|
|
67
|
+
}
|
|
68
|
+
#endif // __linux__
|
|
69
|
+
#ifdef _WIN32
|
|
70
|
+
// For timerfd
|
|
71
|
+
inline LARGE_INTEGER convertToLARGE_INTEGER(Timestamp ts)
|
|
72
|
+
{
|
|
73
|
+
using namespace std::chrono;
|
|
74
|
+
const auto duration = ts.timestamp - std::chrono::system_clock::now();
|
|
75
|
+
assert(duration.count() >= 0);
|
|
76
|
+
const auto nanosecs = duration_cast<nanoseconds>(duration);
|
|
77
|
+
long long relativeHundredNanos = 1LL * nanosecs.count() / 100 * -1;
|
|
78
|
+
return *(LARGE_INTEGER*)&relativeHundredNanos;
|
|
79
|
+
}
|
|
80
|
+
#endif // _WIN32
|
|
81
|
+
|
|
82
|
+
} // namespace ymq
|
|
83
|
+
} // namespace scaler
|
|
84
|
+
|
|
85
|
+
template <>
|
|
86
|
+
struct std::formatter<scaler::ymq::Timestamp, char> {
|
|
87
|
+
template <class ParseContext>
|
|
88
|
+
constexpr ParseContext::iterator parse(ParseContext& ctx)
|
|
89
|
+
{
|
|
90
|
+
return ctx.begin();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
template <class FmtContext>
|
|
94
|
+
constexpr FmtContext::iterator format(scaler::ymq::Timestamp e, FmtContext& ctx) const
|
|
95
|
+
{
|
|
96
|
+
std::ostringstream out;
|
|
97
|
+
const auto ts {std::chrono::floor<std::chrono::seconds>(e.timestamp)};
|
|
98
|
+
const std::chrono::zoned_time z {std::chrono::current_zone(), ts};
|
|
99
|
+
out << std::format("{0:%F %T%z}", z);
|
|
100
|
+
return std::ranges::copy(std::move(out).str(), ctx.out()).out;
|
|
101
|
+
}
|
|
102
|
+
};
|
scaler/io/ymq/typedefs.h
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstdint>
|
|
4
|
+
|
|
5
|
+
namespace scaler {
|
|
6
|
+
namespace ymq {
|
|
7
|
+
|
|
8
|
+
// THIS FILE MUST NOT CONTAIN USER DEFINED TYPES
|
|
9
|
+
enum IOSocketType : uint8_t {
|
|
10
|
+
Uninit, // Not allowed from user code
|
|
11
|
+
Binder,
|
|
12
|
+
Connector,
|
|
13
|
+
Unicast,
|
|
14
|
+
Multicast,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
enum Ownership { Owned, Borrowed };
|
|
18
|
+
|
|
19
|
+
} // namespace ymq
|
|
20
|
+
} // namespace scaler
|
scaler/io/ymq/utils.h
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <cstring>
|
|
5
|
+
#include <format>
|
|
6
|
+
#include <string>
|
|
7
|
+
|
|
8
|
+
namespace scaler {
|
|
9
|
+
namespace ymq {
|
|
10
|
+
|
|
11
|
+
template <std::size_t N>
|
|
12
|
+
requires(N > 0)
|
|
13
|
+
consteval auto getFormatString()
|
|
14
|
+
{
|
|
15
|
+
std::string str = "{}";
|
|
16
|
+
for (size_t i = 1; i < N; ++i)
|
|
17
|
+
str += ": {}";
|
|
18
|
+
std::array<char, (N - 1) * 4 + 2> arr;
|
|
19
|
+
std::ranges::copy(str, arr.begin());
|
|
20
|
+
return arr;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// NOTE: It seems like this two lines cannot be placed in the constructor for unknown reason.
|
|
24
|
+
template <typename... Args>
|
|
25
|
+
constexpr std::string argsToString(Args&&... args)
|
|
26
|
+
{
|
|
27
|
+
static constexpr const auto str = getFormatString<sizeof...(Args)>();
|
|
28
|
+
|
|
29
|
+
std::string res = std::format(std::string_view {str}, std::forward<Args>(args)...);
|
|
30
|
+
return res;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
} // namespace ymq
|
|
34
|
+
} // namespace scaler
|
scaler/io/ymq/ymq.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# This file wraps the interface exported by the C implementation of the module
|
|
2
|
+
# and provides a more ergonomic interface supporting both asynchronous and synchronous execution
|
|
3
|
+
|
|
4
|
+
__all__ = ["IOSocket", "IOContext", "Message", "IOSocketType", "YMQException", "Bytes", "ErrorCode"]
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import concurrent.futures
|
|
8
|
+
from typing import Optional, Callable, TypeVar, Union
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from typing import ParamSpec, Concatenate # type: ignore[attr-defined]
|
|
12
|
+
except ImportError:
|
|
13
|
+
from typing_extensions import ParamSpec, Concatenate # type: ignore[assignment]
|
|
14
|
+
|
|
15
|
+
from scaler.io.ymq._ymq import BaseIOContext, BaseIOSocket, Bytes, ErrorCode, IOSocketType, Message, YMQException
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IOSocket:
|
|
19
|
+
_base: BaseIOSocket
|
|
20
|
+
|
|
21
|
+
def __init__(self, base: BaseIOSocket) -> None:
|
|
22
|
+
self._base = base
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def socket_type(self) -> IOSocketType:
|
|
26
|
+
return self._base.socket_type
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def identity(self) -> str:
|
|
30
|
+
return self._base.identity
|
|
31
|
+
|
|
32
|
+
async def bind(self, address: str) -> None:
|
|
33
|
+
"""Bind the socket to an address and listen for incoming connections"""
|
|
34
|
+
await call_async(self._base.bind, address)
|
|
35
|
+
|
|
36
|
+
def bind_sync(self, address: str, /, timeout: Optional[float] = None) -> None:
|
|
37
|
+
"""Bind the socket to an address and listen for incoming connections"""
|
|
38
|
+
call_sync(self._base.bind, address, timeout=timeout)
|
|
39
|
+
|
|
40
|
+
async def connect(self, address: str) -> None:
|
|
41
|
+
"""Connect to a remote socket"""
|
|
42
|
+
await call_async(self._base.connect, address)
|
|
43
|
+
|
|
44
|
+
def connect_sync(self, address: str, /, timeout: Optional[float] = None) -> None:
|
|
45
|
+
"""Connect to a remote socket"""
|
|
46
|
+
call_sync(self._base.connect, address, timeout=timeout)
|
|
47
|
+
|
|
48
|
+
async def send(self, message: Message) -> None:
|
|
49
|
+
"""Send a message to one of the socket's peers"""
|
|
50
|
+
await call_async(self._base.send, message)
|
|
51
|
+
|
|
52
|
+
def send_sync(self, message: Message, /, timeout: Optional[float] = None) -> None:
|
|
53
|
+
"""Send a message to one of the socket's peers"""
|
|
54
|
+
call_sync(self._base.send, message, timeout=timeout)
|
|
55
|
+
|
|
56
|
+
async def recv(self) -> Message:
|
|
57
|
+
"""Receive a message from one of the socket's peers"""
|
|
58
|
+
return await call_async(self._base.recv)
|
|
59
|
+
|
|
60
|
+
def recv_sync(self, /, timeout: Optional[float] = None) -> Message:
|
|
61
|
+
"""Receive a message from one of the socket's peers"""
|
|
62
|
+
return call_sync(self._base.recv, timeout=timeout)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class IOContext:
|
|
66
|
+
_base: BaseIOContext
|
|
67
|
+
|
|
68
|
+
def __init__(self, num_threads: int = 1) -> None:
|
|
69
|
+
self._base = BaseIOContext(num_threads)
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def num_threads(self) -> int:
|
|
73
|
+
return self._base.num_threads
|
|
74
|
+
|
|
75
|
+
async def createIOSocket(self, identity: str, socket_type: IOSocketType) -> IOSocket:
|
|
76
|
+
"""Create an io socket with an identity and socket type"""
|
|
77
|
+
return IOSocket(await call_async(self._base.createIOSocket, identity, socket_type))
|
|
78
|
+
|
|
79
|
+
def createIOSocket_sync(self, identity: str, socket_type: IOSocketType) -> IOSocket:
|
|
80
|
+
"""Create an io socket with an identity and socket type"""
|
|
81
|
+
return IOSocket(call_sync(self._base.createIOSocket, identity, socket_type))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
P = ParamSpec("P")
|
|
85
|
+
T = TypeVar("T")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def call_async(
|
|
89
|
+
func: Callable[Concatenate[Callable[[Union[T, Exception]], None], P], None], # type: ignore
|
|
90
|
+
*args: P.args, # type: ignore
|
|
91
|
+
**kwargs: P.kwargs, # type: ignore
|
|
92
|
+
) -> T:
|
|
93
|
+
future = asyncio.get_event_loop().create_future()
|
|
94
|
+
|
|
95
|
+
def callback(result: Union[T, Exception]):
|
|
96
|
+
if future.done():
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
loop = future.get_loop()
|
|
100
|
+
|
|
101
|
+
if isinstance(result, Exception):
|
|
102
|
+
loop.call_soon_threadsafe(future.set_exception, result)
|
|
103
|
+
else:
|
|
104
|
+
loop.call_soon_threadsafe(future.set_result, result)
|
|
105
|
+
|
|
106
|
+
func(callback, *args, **kwargs)
|
|
107
|
+
return await future
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# about the ignore directives: mypy cannot properly handle typing extension's ParamSpec and Concatenate in python <=3.9
|
|
111
|
+
# these type hints are correctly understood in Python 3.10+
|
|
112
|
+
def call_sync( # type: ignore[valid-type]
|
|
113
|
+
func: Callable[Concatenate[Callable[[Union[T, Exception]], None], P], None], # type: ignore
|
|
114
|
+
*args: P.args, # type: ignore
|
|
115
|
+
timeout: Optional[float] = None,
|
|
116
|
+
**kwargs: P.kwargs, # type: ignore
|
|
117
|
+
) -> T: # type: ignore
|
|
118
|
+
future: concurrent.futures.Future = concurrent.futures.Future()
|
|
119
|
+
|
|
120
|
+
def callback(result: Union[T, Exception]):
|
|
121
|
+
if future.done():
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
if isinstance(result, Exception):
|
|
125
|
+
future.set_exception(result)
|
|
126
|
+
else:
|
|
127
|
+
future.set_result(result)
|
|
128
|
+
|
|
129
|
+
func(callback, *args, **kwargs)
|
|
130
|
+
return future.result(timeout)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# capnp_objs ===========================================================================================================
|
|
2
|
+
# compile object storage schema capnp
|
|
3
|
+
set(CAPNPC_SRC_PREFIX "${PROJECT_SOURCE_DIR}/scaler/protocol/capnp" CACHE STRING "" FORCE)
|
|
4
|
+
set(CAPNPC_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/scaler/protocol/capnp" CACHE STRING "" FORCE)
|
|
5
|
+
file(MAKE_DIRECTORY "${CAPNPC_OUTPUT_DIR}")
|
|
6
|
+
capnp_generate_cpp(OBJECT_STORAGE_CAPNP_SRCS OBJECT_STORAGE_CAPNP_HEADERS "../protocol/capnp/object_storage.capnp")
|
|
7
|
+
|
|
8
|
+
add_library(capnp_objs OBJECT
|
|
9
|
+
${OBJECT_STORAGE_CAPNP_SRCS}
|
|
10
|
+
${OBJECT_STORAGE_CAPNP_HEADERS}
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# object_storage_server_objs ===========================================================================================
|
|
14
|
+
add_library(object_storage_server_objs OBJECT
|
|
15
|
+
io_helper.cpp
|
|
16
|
+
message.cpp
|
|
17
|
+
object_storage_server.cpp
|
|
18
|
+
object_manager.cpp
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/scaler/object_storage)
|
|
22
|
+
|
|
23
|
+
# object_storage_server python =========================================================================================
|
|
24
|
+
find_package(Python3 REQUIRED COMPONENTS Development.Module)
|
|
25
|
+
|
|
26
|
+
add_library(py_object_storage_server MODULE
|
|
27
|
+
pymod_object_storage_server.cpp
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
set_target_properties(py_object_storage_server PROPERTIES
|
|
31
|
+
PREFIX ""
|
|
32
|
+
OUTPUT_NAME "object_storage_server"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
target_link_libraries(py_object_storage_server PRIVATE
|
|
36
|
+
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wl,-Bstatic>
|
|
37
|
+
capnp_objs
|
|
38
|
+
ymq_objs
|
|
39
|
+
object_storage_server_objs
|
|
40
|
+
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wl,-Bdynamic>
|
|
41
|
+
CapnProto::capnp
|
|
42
|
+
CapnProto::kj
|
|
43
|
+
Python3::Module
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
install(
|
|
47
|
+
TARGETS py_object_storage_server
|
|
48
|
+
LIBRARY DESTINATION scaler/object_storage/
|
|
49
|
+
COMPONENT object_storage_server
|
|
50
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
namespace scaler {
|
|
4
|
+
namespace object_storage {
|
|
5
|
+
|
|
6
|
+
static constexpr size_t MEMORY_LIMIT_IN_BYTES = 6uz << 40; // 6 TB
|
|
7
|
+
static constexpr const char* DEFAULT_ADDR = "127.0.0.1";
|
|
8
|
+
static constexpr const char* DEFAULT_PORT = "55555";
|
|
9
|
+
|
|
10
|
+
}; // namespace object_storage
|
|
11
|
+
}; // namespace scaler
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <memory> // std::shared_ptr
|
|
4
|
+
|
|
5
|
+
#include "scaler/io/ymq/bytes.h"
|
|
6
|
+
|
|
7
|
+
namespace scaler {
|
|
8
|
+
namespace object_storage {
|
|
9
|
+
|
|
10
|
+
using ObjectPayload = Bytes;
|
|
11
|
+
using SharedObjectPayload = std::shared_ptr<ObjectPayload>;
|
|
12
|
+
|
|
13
|
+
}; // namespace object_storage
|
|
14
|
+
}; // namespace scaler
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#include "io_helper.h"
|
|
2
|
+
|
|
3
|
+
#include <netinet/in.h> // sockaddr_in
|
|
4
|
+
#include <sys/socket.h> // socket(2)
|
|
5
|
+
#include <unistd.h> // close(2)
|
|
6
|
+
|
|
7
|
+
#include <exception>
|
|
8
|
+
#include <iostream>
|
|
9
|
+
|
|
10
|
+
namespace scaler {
|
|
11
|
+
namespace object_storage {
|
|
12
|
+
|
|
13
|
+
int getAvailableTCPPort()
|
|
14
|
+
{
|
|
15
|
+
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
16
|
+
if (sockfd < 0) {
|
|
17
|
+
return -1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
sockaddr_in addr {};
|
|
21
|
+
addr.sin_family = AF_INET;
|
|
22
|
+
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
23
|
+
addr.sin_port = 0; // Let OS choose port
|
|
24
|
+
|
|
25
|
+
if (bind(sockfd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
|
|
26
|
+
close(sockfd);
|
|
27
|
+
throw std::runtime_error("bind() failed");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
socklen_t len = sizeof(addr);
|
|
31
|
+
if (getsockname(sockfd, reinterpret_cast<sockaddr*>(&addr), &len) < 0) {
|
|
32
|
+
close(sockfd);
|
|
33
|
+
throw std::runtime_error("getsockname() failed");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const int port = ntohs(addr.sin_port);
|
|
37
|
+
|
|
38
|
+
close(sockfd);
|
|
39
|
+
|
|
40
|
+
return port;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
}; // namespace object_storage
|
|
44
|
+
}; // namespace scaler
|