opengris-scaler 1.12.7__cp312-cp312-manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opengris-scaler might be problematic. Click here for more details.
- opengris_scaler-1.12.7.dist-info/METADATA +729 -0
- opengris_scaler-1.12.7.dist-info/RECORD +232 -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-b787335c.1.0.so +0 -0
- opengris_scaler.libs/libkj-1-094aa318.1.0.so +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,83 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import threading
|
|
3
|
+
from typing import Callable, Optional
|
|
4
|
+
|
|
5
|
+
import zmq
|
|
6
|
+
|
|
7
|
+
from scaler.io.mixins import SyncSubscriber
|
|
8
|
+
from scaler.io.utility import deserialize
|
|
9
|
+
from scaler.protocol.python.mixins import Message
|
|
10
|
+
from scaler.config.types.zmq import ZMQConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ZMQSyncSubscriber(SyncSubscriber, threading.Thread):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
address: ZMQConfig,
|
|
17
|
+
callback: Callable[[Message], None],
|
|
18
|
+
topic: bytes,
|
|
19
|
+
exit_callback: Optional[Callable[[], None]] = None,
|
|
20
|
+
stop_event: threading.Event = threading.Event(),
|
|
21
|
+
daemonic: bool = False,
|
|
22
|
+
timeout_seconds: int = -1,
|
|
23
|
+
):
|
|
24
|
+
threading.Thread.__init__(self)
|
|
25
|
+
|
|
26
|
+
self._stop_event = stop_event
|
|
27
|
+
self._address = address
|
|
28
|
+
self._callback = callback
|
|
29
|
+
self._exit_callback = exit_callback
|
|
30
|
+
self._topic = topic
|
|
31
|
+
self.daemon = bool(daemonic)
|
|
32
|
+
self._timeout_seconds = timeout_seconds
|
|
33
|
+
|
|
34
|
+
self._context: Optional[zmq.Context] = None
|
|
35
|
+
self._socket: Optional[zmq.Socket] = None
|
|
36
|
+
|
|
37
|
+
def __close(self):
|
|
38
|
+
self._socket.close()
|
|
39
|
+
|
|
40
|
+
def __stop_polling(self):
|
|
41
|
+
self._stop_event.set()
|
|
42
|
+
|
|
43
|
+
def destroy(self):
|
|
44
|
+
self.__stop_polling()
|
|
45
|
+
|
|
46
|
+
def run(self) -> None:
|
|
47
|
+
self.__initialize()
|
|
48
|
+
|
|
49
|
+
while not self._stop_event.is_set():
|
|
50
|
+
self.__routine_polling()
|
|
51
|
+
|
|
52
|
+
if self._exit_callback is not None:
|
|
53
|
+
self._exit_callback()
|
|
54
|
+
|
|
55
|
+
self.__close()
|
|
56
|
+
|
|
57
|
+
def __initialize(self):
|
|
58
|
+
self._context = zmq.Context.instance()
|
|
59
|
+
self._socket = self._context.socket(zmq.SUB)
|
|
60
|
+
self._socket.setsockopt(zmq.RCVHWM, 0)
|
|
61
|
+
|
|
62
|
+
if self._timeout_seconds == -1:
|
|
63
|
+
self._socket.setsockopt(zmq.RCVTIMEO, self._timeout_seconds)
|
|
64
|
+
else:
|
|
65
|
+
self._socket.setsockopt(zmq.RCVTIMEO, self._timeout_seconds * 1000)
|
|
66
|
+
|
|
67
|
+
self._socket.subscribe(self._topic)
|
|
68
|
+
self._socket.connect(self._address.to_address())
|
|
69
|
+
self._socket.connect(self._address.to_address())
|
|
70
|
+
|
|
71
|
+
def __routine_polling(self):
|
|
72
|
+
try:
|
|
73
|
+
self.__routine_receive(self._socket.recv(copy=False).bytes)
|
|
74
|
+
except zmq.Again:
|
|
75
|
+
raise TimeoutError(f"Cannot connect to {self._address.to_address()} in {self._timeout_seconds} seconds")
|
|
76
|
+
|
|
77
|
+
def __routine_receive(self, payload: bytes):
|
|
78
|
+
result: Optional[Message] = deserialize(payload)
|
|
79
|
+
if result is None:
|
|
80
|
+
logging.error(f"received unknown message: {payload!r}")
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
self._callback(result)
|
scaler/io/utility.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from scaler.config.defaults import CAPNP_DATA_SIZE_LIMIT, CAPNP_MESSAGE_SIZE_LIMIT
|
|
5
|
+
from scaler.protocol.capnp._python import _message # noqa
|
|
6
|
+
from scaler.protocol.python.message import PROTOCOL
|
|
7
|
+
from scaler.protocol.python.mixins import Message
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def deserialize(data: bytes) -> Optional[Message]:
|
|
11
|
+
with _message.Message.from_bytes(data, traversal_limit_in_words=CAPNP_MESSAGE_SIZE_LIMIT) as payload:
|
|
12
|
+
if not hasattr(payload, payload.which()):
|
|
13
|
+
logging.error(f"unknown message type: {payload.which()}")
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
message = getattr(payload, payload.which())
|
|
17
|
+
return PROTOCOL[payload.which()](message)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def serialize(message: Message) -> bytes:
|
|
21
|
+
payload = _message.Message(**{PROTOCOL.inverse[type(message)]: message.get_message()})
|
|
22
|
+
return payload.to_bytes()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def chunk_to_list_of_bytes(data: bytes) -> List[bytes]:
|
|
26
|
+
# TODO: change to list of memoryview when capnp can support memoryview
|
|
27
|
+
return [data[i : i + CAPNP_DATA_SIZE_LIMIT] for i in range(0, len(data), CAPNP_DATA_SIZE_LIMIT)]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def concat_list_of_bytes(data: List[bytes]) -> bytes:
|
|
31
|
+
return bytearray().join(data)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
add_library(ymq_objs OBJECT
|
|
2
|
+
bytes.h
|
|
3
|
+
common.h
|
|
4
|
+
configuration.h
|
|
5
|
+
|
|
6
|
+
epoll_context.h
|
|
7
|
+
epoll_context.cpp
|
|
8
|
+
|
|
9
|
+
iocp_context.h
|
|
10
|
+
iocp_context.cpp
|
|
11
|
+
|
|
12
|
+
event_loop.h
|
|
13
|
+
|
|
14
|
+
event_loop_thread.h
|
|
15
|
+
event_loop_thread.cpp
|
|
16
|
+
|
|
17
|
+
event_manager.h
|
|
18
|
+
# file_descriptor.h
|
|
19
|
+
|
|
20
|
+
message_connection.h
|
|
21
|
+
message_connection_tcp.h
|
|
22
|
+
message_connection_tcp.cpp
|
|
23
|
+
|
|
24
|
+
third_party/concurrentqueue.h
|
|
25
|
+
interruptive_concurrent_queue.h
|
|
26
|
+
|
|
27
|
+
typedefs.h
|
|
28
|
+
|
|
29
|
+
io_context.h
|
|
30
|
+
io_context.cpp
|
|
31
|
+
|
|
32
|
+
io_socket.h
|
|
33
|
+
io_socket.cpp
|
|
34
|
+
|
|
35
|
+
tcp_server.h
|
|
36
|
+
tcp_server.cpp
|
|
37
|
+
|
|
38
|
+
tcp_client.h
|
|
39
|
+
tcp_client.cpp
|
|
40
|
+
|
|
41
|
+
tcp_operations.h
|
|
42
|
+
|
|
43
|
+
timestamp.h
|
|
44
|
+
|
|
45
|
+
timed_queue.h
|
|
46
|
+
|
|
47
|
+
network_utils.h
|
|
48
|
+
|
|
49
|
+
error.h
|
|
50
|
+
|
|
51
|
+
logging.h
|
|
52
|
+
|
|
53
|
+
simple_interface.h
|
|
54
|
+
simple_interface.cpp
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/scaler/io/ymq)
|
|
58
|
+
|
|
59
|
+
if(LINUX)
|
|
60
|
+
# ymq python =======================================================================================================
|
|
61
|
+
find_package(Python3 COMPONENTS Development.Module REQUIRED)
|
|
62
|
+
|
|
63
|
+
add_library(py_ymq SHARED
|
|
64
|
+
pymod_ymq/bytes.h
|
|
65
|
+
pymod_ymq/exception.h
|
|
66
|
+
pymod_ymq/gil.h
|
|
67
|
+
pymod_ymq/message.h
|
|
68
|
+
pymod_ymq/io_context.h
|
|
69
|
+
pymod_ymq/io_socket.h
|
|
70
|
+
pymod_ymq/ymq.h
|
|
71
|
+
pymod_ymq/ymq.cpp
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# target_include_directories(py_ymq PRIVATE ${Python3_INCLUDE_DIRS})
|
|
75
|
+
target_link_libraries(py_ymq PRIVATE
|
|
76
|
+
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wl,-Bstatic> ymq_objs
|
|
77
|
+
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wl,-Bdynamic>
|
|
78
|
+
Python3::Module
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
set_target_properties(py_ymq PROPERTIES
|
|
82
|
+
PREFIX ""
|
|
83
|
+
OUTPUT_NAME "_ymq"
|
|
84
|
+
LINKER_LANGUAGE CXX
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
install(
|
|
88
|
+
TARGETS py_ymq
|
|
89
|
+
LIBRARY DESTINATION scaler/io/ymq
|
|
90
|
+
)
|
|
91
|
+
endif()
|
|
92
|
+
|
|
93
|
+
if(WIN32)
|
|
94
|
+
# ymq python =======================================================================================================
|
|
95
|
+
|
|
96
|
+
target_link_libraries(ymq_objs PRIVATE "ws2_32")
|
|
97
|
+
target_compile_definitions(ymq_objs PRIVATE _WINSOCKAPI_=) # Yes, trailing equal to guarantee empty def
|
|
98
|
+
endif()
|
|
File without changes
|
scaler/io/ymq/_ymq.pyi
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# NOTE: NOT IMPLEMENTATION, TYPE INFORMATION ONLY
|
|
2
|
+
# This file contains type stubs for the Ymq Python C Extension module
|
|
3
|
+
import sys
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
from typing import Callable, Optional, SupportsBytes, Union
|
|
6
|
+
|
|
7
|
+
if sys.version_info >= (3, 12):
|
|
8
|
+
from collections.abc import Buffer
|
|
9
|
+
else:
|
|
10
|
+
Buffer = object
|
|
11
|
+
|
|
12
|
+
class Bytes(Buffer):
|
|
13
|
+
data: bytes | None
|
|
14
|
+
len: int
|
|
15
|
+
|
|
16
|
+
def __init__(self, data: Buffer | None = None) -> None: ...
|
|
17
|
+
def __repr__(self) -> str: ...
|
|
18
|
+
def __len__(self) -> int: ...
|
|
19
|
+
|
|
20
|
+
# this type signature is not 100% accurate because it's implemented in C
|
|
21
|
+
# but this satisfies the type check and is good enough
|
|
22
|
+
def __buffer__(self, flags: int, /) -> memoryview: ...
|
|
23
|
+
|
|
24
|
+
class Message:
|
|
25
|
+
address: Bytes | None
|
|
26
|
+
payload: Bytes
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self, address: Bytes | bytes | SupportsBytes | None, payload: Bytes | bytes | SupportsBytes
|
|
30
|
+
) -> None: ...
|
|
31
|
+
def __repr__(self) -> str: ...
|
|
32
|
+
def __str__(self) -> str: ...
|
|
33
|
+
|
|
34
|
+
class IOSocketType(IntEnum):
|
|
35
|
+
Uninit = 0
|
|
36
|
+
Binder = 1
|
|
37
|
+
Connector = 2
|
|
38
|
+
Unicast = 3
|
|
39
|
+
Multicast = 4
|
|
40
|
+
|
|
41
|
+
class BaseIOContext:
|
|
42
|
+
num_threads: int
|
|
43
|
+
|
|
44
|
+
def __init__(self, num_threads: int = 1) -> None: ...
|
|
45
|
+
def __repr__(self) -> str: ...
|
|
46
|
+
def createIOSocket(
|
|
47
|
+
self, callback: Callable[[Union[BaseIOSocket, Exception]], None], identity: str, socket_type: IOSocketType
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Create an io socket with an identity and socket type"""
|
|
50
|
+
|
|
51
|
+
class BaseIOSocket:
|
|
52
|
+
identity: str
|
|
53
|
+
socket_type: IOSocketType
|
|
54
|
+
|
|
55
|
+
def __repr__(self) -> str: ...
|
|
56
|
+
def send(self, callback: Callable[[Optional[Exception]], None], message: Message) -> None:
|
|
57
|
+
"""Send a message to one of the socket's peers"""
|
|
58
|
+
|
|
59
|
+
def recv(self, callback: Callable[[Union[Message, Exception]], None]) -> None:
|
|
60
|
+
"""Receive a message from one of the socket's peers"""
|
|
61
|
+
|
|
62
|
+
def bind(self, callback: Callable[[Optional[Exception]], None], address: str) -> None:
|
|
63
|
+
"""Bind the socket to an address and listen for incoming connections"""
|
|
64
|
+
|
|
65
|
+
def connect(self, callback: Callable[[Optional[Exception]], None], address: str) -> None:
|
|
66
|
+
"""Connect to a remote socket"""
|
|
67
|
+
|
|
68
|
+
class ErrorCode(IntEnum):
|
|
69
|
+
Uninit = 0
|
|
70
|
+
InvalidPortFormat = 1
|
|
71
|
+
InvalidAddressFormat = 2
|
|
72
|
+
ConfigurationError = 3
|
|
73
|
+
SignalNotSupported = 4
|
|
74
|
+
CoreBug = 5
|
|
75
|
+
RepetetiveIOSocketIdentity = 6
|
|
76
|
+
RedundantIOSocketRefCount = 7
|
|
77
|
+
MultipleConnectToNotSupported = 8
|
|
78
|
+
MultipleBindToNotSupported = 9
|
|
79
|
+
InitialConnectFailedWithInProgress = 10
|
|
80
|
+
SendMessageRequestCouldNotComplete = 11
|
|
81
|
+
SetSockOptNonFatalFailure = 12
|
|
82
|
+
IPv6NotSupported = 13
|
|
83
|
+
RemoteEndDisconnectedOnSocketWithoutGuaranteedDelivery = 14
|
|
84
|
+
ConnectorSocketClosedByRemoteEnd = 15
|
|
85
|
+
IOSocketStopRequested = 16
|
|
86
|
+
BinderSendMessageWithNoAddress = 17
|
|
87
|
+
|
|
88
|
+
def explanation(self) -> str: ...
|
|
89
|
+
|
|
90
|
+
class YMQException(Exception):
|
|
91
|
+
code: ErrorCode
|
|
92
|
+
message: str
|
|
93
|
+
|
|
94
|
+
def __init__(self, /, code: ErrorCode, message: str) -> None: ...
|
|
95
|
+
def __repr__(self) -> str: ...
|
|
96
|
+
def __str__(self) -> str: ...
|
scaler/io/ymq/_ymq.so
ADDED
|
Binary file
|
scaler/io/ymq/bytes.h
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// C
|
|
4
|
+
#include <string.h> // memcmp
|
|
5
|
+
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <compare>
|
|
8
|
+
#include <cstddef>
|
|
9
|
+
#include <cstdint>
|
|
10
|
+
#include <cstring>
|
|
11
|
+
|
|
12
|
+
// C++
|
|
13
|
+
#include <optional>
|
|
14
|
+
#include <string>
|
|
15
|
+
|
|
16
|
+
// First-party
|
|
17
|
+
#include "scaler/io/ymq/common.h"
|
|
18
|
+
|
|
19
|
+
// TODO: This is not in the namespace
|
|
20
|
+
class Bytes {
|
|
21
|
+
uint8_t* _data;
|
|
22
|
+
size_t _len;
|
|
23
|
+
|
|
24
|
+
void free()
|
|
25
|
+
{
|
|
26
|
+
if (is_null())
|
|
27
|
+
return;
|
|
28
|
+
delete[] _data;
|
|
29
|
+
_data = nullptr;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
explicit Bytes(uint8_t* data, size_t len): _data(data), _len(len) {}
|
|
33
|
+
|
|
34
|
+
public:
|
|
35
|
+
Bytes(char* data, size_t len): _data(datadup((uint8_t*)data, len)), _len(len) {}
|
|
36
|
+
|
|
37
|
+
explicit Bytes(const std::string& s): _data(datadup((uint8_t*)s.data(), s.length())), _len(s.length()) {}
|
|
38
|
+
|
|
39
|
+
Bytes(): _data {}, _len {} {}
|
|
40
|
+
|
|
41
|
+
Bytes(const Bytes& other) noexcept
|
|
42
|
+
{
|
|
43
|
+
this->_data = datadup(other._data, other._len);
|
|
44
|
+
this->_len = other._len;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Bytes& operator=(const Bytes& other) noexcept
|
|
48
|
+
{
|
|
49
|
+
Bytes tmp(other);
|
|
50
|
+
swap(*this, tmp);
|
|
51
|
+
return *this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
friend void swap(Bytes& x, Bytes& y) noexcept
|
|
55
|
+
{
|
|
56
|
+
using std::swap;
|
|
57
|
+
swap(x._len, y._len);
|
|
58
|
+
swap(x._data, y._data);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Bytes(Bytes&& other) noexcept: _data(other._data), _len(other._len)
|
|
62
|
+
{
|
|
63
|
+
other._data = nullptr;
|
|
64
|
+
other._len = 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
friend std::strong_ordering operator<=>(const Bytes& x, const Bytes& y) noexcept
|
|
68
|
+
{
|
|
69
|
+
return std::lexicographical_compare_three_way(x._data, x._data + x._len, y._data, y._data + y._len);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// https://stackoverflow.com/questions/68221024/why-must-i-provide-operator-when-operator-is-enough
|
|
73
|
+
friend bool operator==(const Bytes& x, const Bytes& y) noexcept { return x <=> y == 0; }
|
|
74
|
+
|
|
75
|
+
Bytes& operator=(Bytes&& other) noexcept
|
|
76
|
+
{
|
|
77
|
+
if (this != &other) {
|
|
78
|
+
this->free(); // free current data
|
|
79
|
+
|
|
80
|
+
_data = other._data;
|
|
81
|
+
_len = other._len;
|
|
82
|
+
|
|
83
|
+
other._data = nullptr;
|
|
84
|
+
other._len = 0;
|
|
85
|
+
}
|
|
86
|
+
return *this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
~Bytes() { this->free(); }
|
|
90
|
+
|
|
91
|
+
[[nodiscard]] constexpr bool operator!() const noexcept { return is_null(); }
|
|
92
|
+
|
|
93
|
+
[[nodiscard]] constexpr bool is_null() const noexcept { return !this->_data; }
|
|
94
|
+
|
|
95
|
+
std::optional<std::string> as_string() const
|
|
96
|
+
{
|
|
97
|
+
if (is_null())
|
|
98
|
+
return std::nullopt;
|
|
99
|
+
|
|
100
|
+
return std::string((char*)_data, _len);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[[nodiscard("Allocated Bytes is not used, likely causing a memory leak")]]
|
|
104
|
+
static Bytes alloc(size_t len) noexcept
|
|
105
|
+
{
|
|
106
|
+
auto ptr = new uint8_t[len]; // we just assume the allocation will succeed
|
|
107
|
+
return Bytes {ptr, len};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[[nodiscard]] constexpr size_t len() const { return _len; }
|
|
111
|
+
[[nodiscard]] constexpr size_t size() const { return _len; }
|
|
112
|
+
[[nodiscard]] constexpr const uint8_t* data() const { return _data; }
|
|
113
|
+
[[nodiscard]] constexpr uint8_t* data() { return _data; }
|
|
114
|
+
};
|
scaler/io/ymq/common.h
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// C++
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
#include <cstdlib>
|
|
6
|
+
#include <cstring>
|
|
7
|
+
|
|
8
|
+
using Errno = int;
|
|
9
|
+
|
|
10
|
+
[[nodiscard("Memory is allocated but not used, likely causing a memory leak")]]
|
|
11
|
+
inline uint8_t* datadup(const uint8_t* data, size_t len) noexcept
|
|
12
|
+
{
|
|
13
|
+
uint8_t* dup = new uint8_t[len]; // we just assume allocation will succeed
|
|
14
|
+
std::memcpy(dup, data, len);
|
|
15
|
+
return dup;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
inline void serialize_u32(uint32_t x, uint8_t buffer[4])
|
|
19
|
+
{
|
|
20
|
+
buffer[0] = x & 0xFF;
|
|
21
|
+
buffer[1] = (x >> 8) & 0xFF;
|
|
22
|
+
buffer[2] = (x >> 16) & 0xFF;
|
|
23
|
+
buffer[3] = (x >> 24) & 0xFF;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
inline void deserialize_u32(const uint8_t buffer[4], uint32_t* x)
|
|
27
|
+
{
|
|
28
|
+
*x = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24;
|
|
29
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// C++
|
|
4
|
+
#include <expected>
|
|
5
|
+
#include <functional>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
// Because the devil says "You shall live with errors".
|
|
10
|
+
// ^-- The linker complains when the file is not here.
|
|
11
|
+
#include "scaler/io/ymq/error.h"
|
|
12
|
+
|
|
13
|
+
// Because the devil casts spells in plain English.
|
|
14
|
+
#ifdef _WIN32
|
|
15
|
+
#undef SendMessageCallback
|
|
16
|
+
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
|
17
|
+
#endif // _WIN32
|
|
18
|
+
|
|
19
|
+
namespace scaler {
|
|
20
|
+
namespace ymq {
|
|
21
|
+
|
|
22
|
+
class EpollContext;
|
|
23
|
+
class IocpContext;
|
|
24
|
+
class Message;
|
|
25
|
+
class IOSocket;
|
|
26
|
+
|
|
27
|
+
// Use feature-test macro to detect support for std::move_only_function.
|
|
28
|
+
// This works across GCC, Clang, and MSVC on all platforms.
|
|
29
|
+
#if defined(__cpp_lib_move_only_function) && __cpp_lib_move_only_function >= 202110L
|
|
30
|
+
template <typename T>
|
|
31
|
+
using MoveOnlyFunction = std::move_only_function<T>;
|
|
32
|
+
#else
|
|
33
|
+
template <typename T>
|
|
34
|
+
using MoveOnlyFunction = std::function<T>;
|
|
35
|
+
#endif
|
|
36
|
+
|
|
37
|
+
constexpr const uint64_t IOCP_SOCKET_CLOSED = 4;
|
|
38
|
+
constexpr const uint64_t LARGEST_PAYLOAD_SIZE = 6000'000'000'000; // 6TB
|
|
39
|
+
|
|
40
|
+
struct Configuration {
|
|
41
|
+
#ifdef __linux__
|
|
42
|
+
using PollingContext = EpollContext;
|
|
43
|
+
#endif // __linux__
|
|
44
|
+
#ifdef _WIN32
|
|
45
|
+
using PollingContext = IocpContext;
|
|
46
|
+
#endif // _WIN32
|
|
47
|
+
|
|
48
|
+
using IOSocketIdentity = std::string;
|
|
49
|
+
using SendMessageCallback = MoveOnlyFunction<void(std::expected<void, Error>)>;
|
|
50
|
+
using RecvMessageCallback = MoveOnlyFunction<void(std::pair<Message, Error>)>;
|
|
51
|
+
using ConnectReturnCallback = MoveOnlyFunction<void(std::expected<void, Error>)>;
|
|
52
|
+
using BindReturnCallback = MoveOnlyFunction<void(std::expected<void, Error>)>;
|
|
53
|
+
using CreateIOSocketCallback = MoveOnlyFunction<void(std::shared_ptr<IOSocket>)>;
|
|
54
|
+
using TimedQueueCallback = MoveOnlyFunction<void()>;
|
|
55
|
+
using ExecutionFunction = MoveOnlyFunction<void()>;
|
|
56
|
+
using ExecutionCancellationIdentifier = size_t;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
} // namespace ymq
|
|
60
|
+
} // namespace scaler
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#ifdef __linux__
|
|
2
|
+
|
|
3
|
+
#include "scaler/io/ymq/epoll_context.h"
|
|
4
|
+
|
|
5
|
+
#include <sys/epoll.h>
|
|
6
|
+
|
|
7
|
+
#include <cerrno>
|
|
8
|
+
#include <functional>
|
|
9
|
+
|
|
10
|
+
#include "scaler/io/ymq/error.h"
|
|
11
|
+
#include "scaler/io/ymq/event_manager.h"
|
|
12
|
+
|
|
13
|
+
namespace scaler {
|
|
14
|
+
namespace ymq {
|
|
15
|
+
|
|
16
|
+
void EpollContext::execPendingFunctions()
|
|
17
|
+
{
|
|
18
|
+
while (_delayedFunctions.size()) {
|
|
19
|
+
auto top = std::move(_delayedFunctions.front());
|
|
20
|
+
top();
|
|
21
|
+
_delayedFunctions.pop();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void EpollContext::loop()
|
|
26
|
+
{
|
|
27
|
+
std::array<epoll_event, _reventSize> events {};
|
|
28
|
+
int n = epoll_wait(_epfd, events.data(), _reventSize, -1);
|
|
29
|
+
if (n == -1) {
|
|
30
|
+
const int myErrno = errno;
|
|
31
|
+
switch (myErrno) {
|
|
32
|
+
case EINTR:
|
|
33
|
+
// unrecoverableError({
|
|
34
|
+
// Error::ErrorCode::SignalNotSupported,
|
|
35
|
+
// "Originated from",
|
|
36
|
+
// "epoll_wait(2)",
|
|
37
|
+
// "Errno is",
|
|
38
|
+
// strerror(errno),
|
|
39
|
+
// });
|
|
40
|
+
|
|
41
|
+
// todo: investigate better error handling
|
|
42
|
+
// the epoll thread is not expected to receive signals(?)
|
|
43
|
+
// but occasionally does (e.g. sigwinch) and we shouldn't stop the thread in that case
|
|
44
|
+
break;
|
|
45
|
+
case EBADF:
|
|
46
|
+
case EFAULT:
|
|
47
|
+
case EINVAL:
|
|
48
|
+
default:
|
|
49
|
+
unrecoverableError({
|
|
50
|
+
Error::ErrorCode::CoreBug,
|
|
51
|
+
"Originated from",
|
|
52
|
+
"epoll_wait(2)",
|
|
53
|
+
"Errno is",
|
|
54
|
+
strerror(errno),
|
|
55
|
+
"_epfd",
|
|
56
|
+
_epfd,
|
|
57
|
+
"_reventSize",
|
|
58
|
+
_reventSize,
|
|
59
|
+
});
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (auto it = events.begin(); it != events.begin() + n; ++it) {
|
|
65
|
+
epoll_event current_event = *it;
|
|
66
|
+
auto* event = (EventManager*)current_event.data.ptr;
|
|
67
|
+
if (event == (void*)_isInterruptiveFd) {
|
|
68
|
+
auto vec = _interruptiveFunctions.dequeue();
|
|
69
|
+
std::ranges::for_each(vec, [](auto&& x) { x(); });
|
|
70
|
+
} else if (event == (void*)_isTimingFd) {
|
|
71
|
+
auto vec = _timingFunctions.dequeue();
|
|
72
|
+
std::ranges::for_each(vec, [](auto& x) { x(); });
|
|
73
|
+
} else {
|
|
74
|
+
event->onEvents(current_event.events);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
execPendingFunctions();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void EpollContext::addFdToLoop(int fd, uint64_t events, EventManager* manager)
|
|
82
|
+
{
|
|
83
|
+
epoll_event event {};
|
|
84
|
+
event.events = (int)events & (EPOLLIN | EPOLLOUT | EPOLLET);
|
|
85
|
+
event.data.ptr = (void*)manager;
|
|
86
|
+
int res = epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &event);
|
|
87
|
+
if (res == 0) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (errno == EEXIST) {
|
|
92
|
+
if (epoll_ctl(_epfd, EPOLL_CTL_MOD, fd, &event) == 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const int myErrno = errno;
|
|
98
|
+
switch (myErrno) {
|
|
99
|
+
case ENOMEM:
|
|
100
|
+
case ENOSPC:
|
|
101
|
+
unrecoverableError({
|
|
102
|
+
Error::ErrorCode::ConfigurationError,
|
|
103
|
+
"Originated from",
|
|
104
|
+
"epoll_ctl(2)",
|
|
105
|
+
"Errno is",
|
|
106
|
+
strerror(errno),
|
|
107
|
+
"_epfd",
|
|
108
|
+
_epfd,
|
|
109
|
+
"fd",
|
|
110
|
+
fd,
|
|
111
|
+
"event.events",
|
|
112
|
+
(int)event.events,
|
|
113
|
+
});
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case EBADF:
|
|
117
|
+
case EPERM:
|
|
118
|
+
case EINVAL:
|
|
119
|
+
case ELOOP:
|
|
120
|
+
case ENOENT:
|
|
121
|
+
default:
|
|
122
|
+
unrecoverableError({
|
|
123
|
+
Error::ErrorCode::CoreBug,
|
|
124
|
+
"Originated from",
|
|
125
|
+
"epoll_ctl(2)",
|
|
126
|
+
"Errno is",
|
|
127
|
+
strerror(errno),
|
|
128
|
+
"_epfd",
|
|
129
|
+
_epfd,
|
|
130
|
+
"fd",
|
|
131
|
+
fd,
|
|
132
|
+
"event.events",
|
|
133
|
+
(int)event.events,
|
|
134
|
+
});
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
void EpollContext::removeFdFromLoop(int fd)
|
|
140
|
+
{
|
|
141
|
+
if (epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr) == 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const int myErrno = errno;
|
|
145
|
+
switch (myErrno) {
|
|
146
|
+
case ENOMEM:
|
|
147
|
+
case ENOSPC:
|
|
148
|
+
unrecoverableError({
|
|
149
|
+
Error::ErrorCode::ConfigurationError,
|
|
150
|
+
"Originated from",
|
|
151
|
+
__PRETTY_FUNCTION__,
|
|
152
|
+
"Errno is",
|
|
153
|
+
strerror(errno),
|
|
154
|
+
"_epfd",
|
|
155
|
+
_epfd,
|
|
156
|
+
"fd",
|
|
157
|
+
fd,
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
|
|
161
|
+
case EBADF:
|
|
162
|
+
case EPERM:
|
|
163
|
+
case EINVAL:
|
|
164
|
+
case ELOOP:
|
|
165
|
+
case ENOENT:
|
|
166
|
+
default:
|
|
167
|
+
unrecoverableError({
|
|
168
|
+
Error::ErrorCode::CoreBug,
|
|
169
|
+
"Originated from",
|
|
170
|
+
__PRETTY_FUNCTION__,
|
|
171
|
+
"Errno is",
|
|
172
|
+
strerror(errno),
|
|
173
|
+
"_epfd",
|
|
174
|
+
_epfd,
|
|
175
|
+
"fd",
|
|
176
|
+
fd,
|
|
177
|
+
});
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
} // namespace ymq
|
|
183
|
+
} // namespace scaler
|
|
184
|
+
|
|
185
|
+
#endif // __linux__
|