forked from NousResearch/hermes-agent
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathregistration_lifecycle.py
More file actions
128 lines (109 loc) · 4.3 KB
/
Copy pathregistration_lifecycle.py
File metadata and controls
128 lines (109 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""Ownership leases for replaceable runtime registrations.
The coordinator models registration *generations*, not just value identity.
That distinction matters when the same provider singleton is registered again
after an older ownership generation was unloaded.
"""
from __future__ import annotations
import threading
from collections.abc import Callable, Hashable
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Any
def same_registration(left: Any, right: Any) -> bool:
"""Compare opaque registry snapshots using identity only."""
if isinstance(left, tuple) and isinstance(right, tuple):
return len(left) == len(right) and all(
same_registration(a, b) for a, b in zip(left, right)
)
return left is right
@dataclass
class ReplacementLease:
"""One ownership generation in a replaceable registry slot."""
coordinator: "ReplacementCoordinator"
slot: Hashable
current: Any
previous: Any
restore: Callable[[Any], bool]
finalize: Callable[[], None] | None = None
predecessor: "ReplacementLease | None" = None
active: bool = field(default=True, init=False)
def dispose(self) -> None:
self.coordinator.dispose(self)
class ReplacementCoordinator:
"""Link and remove registration generations in arbitrary unload order."""
def __init__(self) -> None:
self._active: dict[Hashable, list[ReplacementLease]] = {}
self._lock = threading.RLock()
@contextmanager
def transaction(self):
"""Serialize a registry snapshot/write/acquire with lease disposal."""
with self._lock:
yield
def acquire(
self,
slot: Hashable,
*,
current: Any,
previous: Any,
restore: Callable[[Any], bool],
finalize: Callable[[], None] | None = None,
) -> ReplacementLease:
"""Attach a new live generation to the matching active predecessor."""
with self._lock:
leases = self._active.setdefault(slot, [])
predecessor = next(
(
lease
for lease in reversed(leases)
if lease.active and same_registration(lease.current, previous)
),
None,
)
lease = ReplacementLease(
coordinator=self,
slot=slot,
current=current,
previous=previous,
restore=restore,
finalize=finalize,
predecessor=predecessor,
)
leases.append(lease)
return lease
def dispose(self, lease: ReplacementLease) -> None:
"""Remove *lease*, restoring the nearest still-live predecessor."""
with self._lock:
if not lease.active:
return
leases = self._active.get(lease.slot, [])
latest = next(
(candidate for candidate in reversed(leases) if candidate.active),
None,
)
lease.active = False
# An older generation can share the exact same object identity as
# a newer one. Registry-level CAS cannot distinguish those leases,
# so only the latest live generation is allowed to mutate the slot.
try:
try:
if latest is lease:
replacement = lease.previous
predecessor = lease.predecessor
while predecessor is not None:
if predecessor.active:
replacement = predecessor.current
break
replacement = predecessor.previous
predecessor = predecessor.predecessor
lease.restore(replacement)
finally:
if lease.finalize is not None:
lease.finalize()
finally:
if leases:
self._active[lease.slot] = [
item for item in leases if item.active
]
if not self._active[lease.slot]:
self._active.pop(lease.slot, None)
replacement_coordinator = ReplacementCoordinator()