@quenty/signal 7.8.1 → 7.9.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 CHANGED
@@ -3,6 +3,17 @@
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.9.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/signal@7.8.1...@quenty/signal@7.9.0) (2024-11-06)
7
+
8
+
9
+ ### Performance Improvements
10
+
11
+ * Better perf check on category ([594d5bb](https://github.com/Quenty/NevermoreEngine/commit/594d5bb8f56817b501f7c47053615d6cc3f8e313))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [7.8.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/signal@7.8.0...@quenty/signal@7.8.1) (2024-11-04)
7
18
 
8
19
  **Note:** Version bump only for package @quenty/signal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/signal",
3
- "version": "7.8.1",
3
+ "version": "7.9.0",
4
4
  "description": "A simple signal implementation for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -32,5 +32,5 @@
32
32
  "dependencies": {
33
33
  "@quenty/loader": "^10.7.1"
34
34
  },
35
- "gitHead": "01c43a0ddd3c5e0cb2d9027313dbfa9852eedef1"
35
+ "gitHead": "00e6f71716216dd6ecbc8505ad898a1ab7f72756"
36
36
  }
@@ -0,0 +1,66 @@
1
+ --[=[
2
+ Utility methods to fire an event in a free thread, reusing threads
3
+
4
+ @class EventHandlerUtils
5
+ ]=]
6
+
7
+ local require = require(script.Parent.loader).load(script)
8
+
9
+ local EventHandlerUtils = {}
10
+
11
+ -- The currently idle thread to run the next handler on
12
+ local freeThreads = setmetatable({}, {__mode = "kv"})
13
+
14
+ -- Function which acquires the currently idle handler runner thread, runs the
15
+ -- function fn on it, and then releases the thread, returning it to being the
16
+ -- currently idle one.
17
+ -- If there was a currently idle runner thread already, that's okay, that old
18
+ -- one will just get thrown and eventually GCed.
19
+ function EventHandlerUtils._fireEvent(memoryCategory, fn, ...)
20
+ local acquiredRunnerThread = freeThreads[memoryCategory]
21
+ freeThreads[memoryCategory] = nil
22
+ fn(...)
23
+ -- The handler finished running, this runner thread is free again.
24
+ freeThreads[memoryCategory] = acquiredRunnerThread
25
+ end
26
+
27
+ -- Coroutine runner that we create coroutines of. The coroutine can be
28
+ -- repeatedly resumed with functions to run followed by the argument to run
29
+ -- them with.
30
+ function EventHandlerUtils._initializeThread(memoryCategory)
31
+ if memoryCategory == "" then
32
+ debug.setmemorycategory("signal_unknown")
33
+ else
34
+ debug.setmemorycategory(memoryCategory)
35
+ end
36
+
37
+ -- Note: We cannot use the initial set of arguments passed to
38
+ -- initializeThread for a call to the handler, because those
39
+ -- arguments would stay on the stack for the duration of the thread's
40
+ -- existence, temporarily leaking references. Without access to raw bytecode
41
+ -- there's no way for us to clear the "..." references from the stack.
42
+ while true do
43
+ EventHandlerUtils._fireEvent(coroutine.yield())
44
+ end
45
+ end
46
+
47
+ --[=[
48
+ Safely fires an event in the given memory category we're in
49
+
50
+ @param memoryCategory string
51
+ @param callback any
52
+ @param ... any
53
+ ]=]
54
+ function EventHandlerUtils.fire(memoryCategory, callback, ...)
55
+ assert(type(memoryCategory) == "string", "Bad memoryCategory")
56
+ assert(type(callback) == "function", "Bad callback")
57
+
58
+ if not freeThreads[memoryCategory] then
59
+ freeThreads[memoryCategory] = coroutine.create(EventHandlerUtils._initializeThread)
60
+ coroutine.resume(freeThreads[memoryCategory], memoryCategory)
61
+ end
62
+
63
+ task.spawn(freeThreads[memoryCategory], memoryCategory, callback, ...)
64
+ end
65
+
66
+ return EventHandlerUtils
@@ -43,41 +43,9 @@
43
43
  @class GoodSignal
44
44
  ]=]
45
45
 
46
- -- The currently idle thread to run the next handler on
47
- local weakFreeRunnerThreadLookup = setmetatable({}, {__mode = "kv"})
48
-
49
- -- Function which acquires the currently idle handler runner thread, runs the
50
- -- function fn on it, and then releases the thread, returning it to being the
51
- -- currently idle one.
52
- -- If there was a currently idle runner thread already, that's okay, that old
53
- -- one will just get thrown and eventually GCed.
54
- local function acquireRunnerThreadAndCallEventHandler(memoryCategory, fn, ...)
55
- local acquiredRunnerThread = weakFreeRunnerThreadLookup[memoryCategory]
56
- weakFreeRunnerThreadLookup[memoryCategory] = nil
57
- fn(...)
58
- -- The handler finished running, this runner thread is free again.
59
- weakFreeRunnerThreadLookup[memoryCategory] = acquiredRunnerThread
60
- end
61
-
62
- -- Coroutine runner that we create coroutines of. The coroutine can be
63
- -- repeatedly resumed with functions to run followed by the argument to run
64
- -- them with.
65
- local function runEventHandlerInFreeThread(memoryCategory)
66
- if #memoryCategory == 0 then
67
- debug.setmemorycategory("signal_unknown")
68
- else
69
- debug.setmemorycategory(memoryCategory)
70
- end
46
+ local require = require(script.Parent.loader).load(script)
71
47
 
72
- -- Note: We cannot use the initial set of arguments passed to
73
- -- runEventHandlerInFreeThread for a call to the handler, because those
74
- -- arguments would stay on the stack for the duration of the thread's
75
- -- existence, temporarily leaking references. Without access to raw bytecode
76
- -- there's no way for us to clear the "..." references from the stack.
77
- while true do
78
- acquireRunnerThreadAndCallEventHandler(coroutine.yield())
79
- end
80
- end
48
+ local EventHandlerUtils = require("EventHandlerUtils")
81
49
 
82
50
  -- Connection class
83
51
  local Connection = {}
@@ -216,15 +184,7 @@ function Signal:Fire(...)
216
184
  local nextNode = rawget(connection, "_next")
217
185
 
218
186
  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)
225
- end
226
-
227
- task.spawn(weakFreeRunnerThreadLookup[memoryCategory], memoryCategory, connection._fn, ...)
187
+ EventHandlerUtils.fire(connection._memoryCategory, connection._fn, ...)
228
188
  end
229
189
 
230
190
  connection = nextNode