@quenty/signal 7.6.0 → 7.7.0
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.
- package/CHANGELOG.md +16 -0
- package/package.json +3 -3
- package/src/Shared/GoodSignal.lua +63 -43
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [7.7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/signal@7.6.0...@quenty/signal@7.7.0) (2024-10-04)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* GoodSignal now uses the connection memory category instead of the original items memory category, resulting in even more accurate tracking ([c18353d](https://github.com/Quenty/NevermoreEngine/commit/c18353d61a4b4966ad4025c3b7e58b895dcb16a8))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Performance Improvements
|
|
15
|
+
|
|
16
|
+
* Connection clears references, and avoids storing _connected and _next, which reduces memory usage of signal ([8738269](https://github.com/Quenty/NevermoreEngine/commit/8738269c457b8075b89dd18e7371a103413879d6))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
# [7.6.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/signal@7.5.0...@quenty/signal@7.6.0) (2024-09-25)
|
|
7
23
|
|
|
8
24
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/signal",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.7.0",
|
|
4
4
|
"description": "A simple signal implementation for Roblox",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@quenty/loader": "^10.
|
|
33
|
+
"@quenty/loader": "^10.6.0"
|
|
34
34
|
},
|
|
35
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "035abfa088c854a73e1c65b350267eaa17669646"
|
|
36
36
|
}
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
]=]
|
|
45
45
|
|
|
46
46
|
-- The currently idle thread to run the next handler on
|
|
47
|
-
local
|
|
47
|
+
local weakFreeRunnerThreadLookup = setmetatable({}, {__mode = "kv"})
|
|
48
48
|
|
|
49
49
|
-- Function which acquires the currently idle handler runner thread, runs the
|
|
50
50
|
-- function fn on it, and then releases the thread, returning it to being the
|
|
@@ -52,13 +52,11 @@ local freeRunnerThreadLookup = {}
|
|
|
52
52
|
-- If there was a currently idle runner thread already, that's okay, that old
|
|
53
53
|
-- one will just get thrown and eventually GCed.
|
|
54
54
|
local function acquireRunnerThreadAndCallEventHandler(memoryCategory, fn, ...)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
local acquiredRunnerThread = freeRunnerThreadLookup[memoryCategory]
|
|
58
|
-
freeRunnerThreadLookup[memoryCategory] = nil
|
|
55
|
+
local acquiredRunnerThread = weakFreeRunnerThreadLookup[memoryCategory]
|
|
56
|
+
weakFreeRunnerThreadLookup[memoryCategory] = nil
|
|
59
57
|
fn(...)
|
|
60
58
|
-- The handler finished running, this runner thread is free again.
|
|
61
|
-
|
|
59
|
+
weakFreeRunnerThreadLookup[memoryCategory] = acquiredRunnerThread
|
|
62
60
|
end
|
|
63
61
|
|
|
64
62
|
-- Coroutine runner that we create coroutines of. The coroutine can be
|
|
@@ -83,40 +81,56 @@ end
|
|
|
83
81
|
|
|
84
82
|
-- Connection class
|
|
85
83
|
local Connection = {}
|
|
84
|
+
Connection.ClassName = "Connection"
|
|
86
85
|
Connection.__index = Connection
|
|
87
86
|
|
|
88
87
|
function Connection.new(signal, fn)
|
|
89
88
|
return setmetatable({
|
|
90
|
-
|
|
89
|
+
-- selene: allow(incorrect_standard_library_use)
|
|
90
|
+
_memoryCategory = debug.getmemorycategory(),
|
|
91
91
|
_signal = signal,
|
|
92
92
|
_fn = fn,
|
|
93
|
-
_next = false,
|
|
94
93
|
}, Connection)
|
|
95
94
|
end
|
|
96
95
|
|
|
96
|
+
function Connection:IsConnected()
|
|
97
|
+
return rawget(self, "_signal") ~= nil
|
|
98
|
+
end
|
|
99
|
+
|
|
97
100
|
function Connection:Disconnect()
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
--
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
local signal = rawget(self, "_signal")
|
|
102
|
+
if not signal then
|
|
103
|
+
return
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
-- Unhook the node. Originally the good signal would not clear this signal and
|
|
107
|
+
-- rely upon GC. However, this means that connections would keep themselves and other
|
|
108
|
+
-- disconnected nodes in the chain alive, keeping the function closure alive, and in return
|
|
109
|
+
-- keeping the signal alive. This means a `Maid` could keep full object trees alive if a
|
|
110
|
+
-- connection was made to them.
|
|
111
|
+
|
|
112
|
+
local ourNext = rawget(self, "_next")
|
|
113
|
+
|
|
114
|
+
if signal._handlerListHead == self then
|
|
115
|
+
signal._handlerListHead = ourNext or false
|
|
106
116
|
else
|
|
107
|
-
local prev =
|
|
108
|
-
while prev and prev
|
|
109
|
-
prev = prev
|
|
117
|
+
local prev = signal._handlerListHead
|
|
118
|
+
while prev and rawget(prev, "_next") ~= self do
|
|
119
|
+
prev = rawget(prev, "_next")
|
|
110
120
|
end
|
|
111
121
|
if prev then
|
|
112
|
-
prev
|
|
122
|
+
rawset(prev, "_next", ourNext)
|
|
113
123
|
end
|
|
114
124
|
end
|
|
125
|
+
|
|
126
|
+
-- Clear all member variables that aren't _next so keeping a connection
|
|
127
|
+
-- indexed allows for GC of other components
|
|
128
|
+
table.clear(self)
|
|
115
129
|
end
|
|
116
130
|
|
|
117
131
|
Connection.Destroy = Connection.Disconnect
|
|
118
132
|
|
|
119
|
-
-- Make
|
|
133
|
+
-- Make signal strict
|
|
120
134
|
setmetatable(Connection, {
|
|
121
135
|
__index = function(_, key)
|
|
122
136
|
error(string.format("Attempt to get Connection::%s (not a valid member)", tostring(key)), 2)
|
|
@@ -161,7 +175,7 @@ end
|
|
|
161
175
|
function Signal:Connect(fn)
|
|
162
176
|
local connection = Connection.new(self, fn)
|
|
163
177
|
if self._handlerListHead then
|
|
164
|
-
connection
|
|
178
|
+
rawset(connection, "_next", self._handlerListHead)
|
|
165
179
|
self._handlerListHead = connection
|
|
166
180
|
else
|
|
167
181
|
self._handlerListHead = connection
|
|
@@ -194,20 +208,26 @@ end
|
|
|
194
208
|
@param ... T -- Variable arguments to pass to handler
|
|
195
209
|
]=]
|
|
196
210
|
function Signal:Fire(...)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
local connection = self._handlerListHead
|
|
212
|
+
while connection do
|
|
213
|
+
-- capture our next node, which could after this be cleared or disconnected.
|
|
214
|
+
-- any connections occuring during fire will be added to the _handerListHead and not be fired
|
|
215
|
+
-- in this round. Any disconnections in the chain will still work here.
|
|
216
|
+
local nextNode = rawget(connection, "_next")
|
|
217
|
+
|
|
218
|
+
if rawget(connection, "_signal") ~= nil then -- isConnected
|
|
219
|
+
local memoryCategory = connection._memoryCategory
|
|
220
|
+
|
|
221
|
+
-- Get the freeRunnerThread to the first yield
|
|
222
|
+
if not weakFreeRunnerThreadLookup[memoryCategory] then
|
|
223
|
+
weakFreeRunnerThreadLookup[memoryCategory] = coroutine.create(runEventHandlerInFreeThread)
|
|
224
|
+
coroutine.resume(weakFreeRunnerThreadLookup[memoryCategory], memoryCategory)
|
|
207
225
|
end
|
|
208
|
-
|
|
226
|
+
|
|
227
|
+
task.spawn(weakFreeRunnerThreadLookup[memoryCategory], memoryCategory, connection._fn, ...)
|
|
209
228
|
end
|
|
210
|
-
|
|
229
|
+
|
|
230
|
+
connection = nextNode
|
|
211
231
|
end
|
|
212
232
|
end
|
|
213
233
|
|
|
@@ -224,11 +244,13 @@ end
|
|
|
224
244
|
]=]
|
|
225
245
|
function Signal:Wait()
|
|
226
246
|
local waitingCoroutine = coroutine.running()
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
247
|
+
|
|
248
|
+
local connection
|
|
249
|
+
connection = self:Connect(function(...)
|
|
250
|
+
connection:Disconnect()
|
|
230
251
|
task.spawn(waitingCoroutine, ...)
|
|
231
252
|
end)
|
|
253
|
+
|
|
232
254
|
return coroutine.yield()
|
|
233
255
|
end
|
|
234
256
|
|
|
@@ -244,14 +266,12 @@ end
|
|
|
244
266
|
@return RBXScriptConnection
|
|
245
267
|
]=]
|
|
246
268
|
function Signal:Once(fn)
|
|
247
|
-
local
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
cn:Disconnect()
|
|
251
|
-
end
|
|
269
|
+
local connection
|
|
270
|
+
connection = self:Connect(function(...)
|
|
271
|
+
connection:Disconnect()
|
|
252
272
|
fn(...)
|
|
253
273
|
end)
|
|
254
|
-
return
|
|
274
|
+
return connection
|
|
255
275
|
end
|
|
256
276
|
|
|
257
277
|
--[=[
|