• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mobalazs / rotor-framework / 18919729013

29 Oct 2025 07:21PM UTC coverage: 85.379% (-0.1%) from 85.479%
18919729013

push

github

mobalazs
fix: update debug setting in bsconfig to enable debugging

1781 of 2086 relevant lines covered (85.38%)

1.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.59
/src/source/RotorFrameworkTask.bs
1
'''''''''
2
' ▗▄▄▖  ▗▄▖▗▄▄▄▖▗▄▖ ▗▄▄▖     ▗▄▄▄▖▗▄▄▖  ▗▄▖ ▗▖  ▗▖▗▄▄▄▖▗▖ ▗▖ ▗▄▖ ▗▄▄▖ ▗▖ ▗▖
3
' ▐▌ ▐▌▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌    ▐▌   ▐▌ ▐▌▐▌ ▐▌▐▛▚▞▜▌▐▌   ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌▗▞▘
4
' ▐▛▀▚▖▐▌ ▐▌ █ ▐▌ ▐▌▐▛▀▚▖    ▐▛▀▀▘▐▛▀▚▖▐▛▀▜▌▐▌  ▐▌▐▛▀▀▘▐▌ ▐▌▐▌ ▐▌▐▛▀▚▖▐▛▚▖
5
' ▐▌ ▐▌▝▚▄▞▘ █ ▝▚▄▞▘▐▌ ▐▌    ▐▌   ▐▌ ▐▌▐▌ ▐▌▐▌  ▐▌▐▙▄▄▖▐▙█▟▌▝▚▄▞▘▐▌ ▐▌▐▌ ▐▌
6
' Rotor Framework™               © 2025 Balázs Molnár. All rights reserved.
7
' Version 0.2.7
8
'''''''''
9

10
' constants
11
import "engine/Constants.bs"
12

13
' engine
14
import "engine/providers/DispatcherProvider.bs"
15
import "engine/providers/Dispatcher.bs"
16

17
' base classes
18
import "base/DispatcherCreator.bs"
19
import "base/DispatcherExternal.bs"
20
import "base/BaseReducer.bs"
21
import "base/BaseModel.bs"
22
import "base/BaseStack.bs"
23

24
' utils
25
import "utils/GeneralUtils.bs"
26
import "utils/NodeUtils.bs"
27
import "utils/ArrayUtils.bs"
28

29
namespace Rotor
30
    ' =====================================================================
31
    ' FrameworkTask - Task thread version of Rotor Framework for MVI
32
    '
33
    ' Task thread version of the Rotor Framework that enables cross-thread MVI
34
    ' (Model-View-Intent) architecture. This class manages state and dispatchers
35
    ' on a separate task thread, allowing heavy computations and state management
36
    ' to run off the render thread for better performance.
37
    '
38
    ' Configuration:
39
    '   - tasks (array, optional): List of additional task node names to synchronize with.
40
    '                             Allows multiple task threads to communicate and share
41
    '                             dispatchers across different threads.
42
    '
43
    ' USAGE NOTES:
44
    ' The FrameworkTask must be instantiated in the task's init() function and the sync()
45
    ' method MUST be called at the end of your task function to establish the message loop.
46
    '
47
    ' IMPORTANT: The sync() method creates an infinite loop that handles cross-thread
48
    ' communication and dispatcher synchronization. This call should be the LAST statement
49
    ' in your task function, after all dispatcher initialization.
50
    '
51
    ' Example:
52
    '   File: MyTask.task.bs
53
    '   import "pkg:/source/RotorFrameworkTask.bs"
54
    '   import "pkg:/source/MyDispatcher.bs"
55
    '
56
    '   sub init()
57
    '       m.top.functionName = "task"
58
    '       m.appFw = new Rotor.FrameworkTask({
59
    '           tasks: ["AnotherTask"]
60
    '       })
61
    '   end sub
62
    '
63
    '   sub task()
64
    '       m.fooDispatcher = createFooDispatcher()
65
    '       m.barDispatcher = createBarDispatcher()
66
    '       m.appFw.sync()
67
    '   end sub
68
    ' =====================================================================
69
    class FrameworkTask
70

71
        name = "Rotor Framework"
72
        version = "0.2.7"
73

74
        config = {
75
            tasks: invalid, ' optional
76
            debug: {
77
            }
78
        }
79

80
        threadType = Rotor.Const.ThreadType.TASK
81

82
        keepAlive = true
83

84
        ' helper vars
85
        taskNode as object
86
        dispatcherProvider as object
87
        port as object
88

89
        ' ---------------------------------------------------------------------
90
        ' new - Initializes the FrameworkTask instance
91
        '
92
        ' Sets up the task thread dispatcher provider, message port, and
93
        ' global framework helper for cross-thread communication.
94
        '
95
        ' @param {object} config - Configuration object (see class documentation)
96
        '
97
        sub new(config = {} as object)
98

99
            Rotor.Utils.deepExtendAA(m.config, config)
1✔
100

101
            globalScope = GetGlobalAA()
1✔
102
            globalScope.rotor_framework_helper = { ' this give to dispatcher instance the possibility to self-register
1✔
103
                threadType: m.threadType,
104
                frameworkInstance: m
105
            }
106
            m.taskNode = globalScope.top
1✔
107

108
            m.dispatcherProvider = new Rotor.DispatcherProvider(m.threadType)
1✔
109

110
            m.taskNode.addField("rotorSync", "assocarray", true)
1✔
111
            m.port = CreateObject("roMessagePort")
1✔
112
            m.taskNode.observeFieldScoped("rotorSync", m.port)
1✔
113

114
        end sub
115

116
        ' =====================================================================
117
        ' PUBLIC API
118
        ' =====================================================================
119

120
        ' ---------------------------------------------------------------------
121
        ' getDispatcher - Gets dispatcher facade by ID
122
        '
123
        ' @param {string} dispatcherId - Dispatcher identifier
124
        ' @returns {object} Dispatcher facade instance
125
        '
126
        public function getDispatcher(dispatcherId as string) as object
127
            return m.dispatcherProvider.getFacade(dispatcherId, GetGlobalAA())
×
128
        end function
129

130
        ' ---------------------------------------------------------------------
131
        ' sync - Starts the message loop for cross-thread communication
132
        '
133
        ' IMPORTANT: This method creates an infinite loop that handles:
134
        '   - Intent dispatching from render thread
135
        '   - External dispatcher registration
136
        '   - State change notifications
137
        '   - Async reducer callbacks
138
        '
139
        ' This method MUST be the last call in your task function, as it
140
        ' blocks execution until the framework is destroyed.
141
        '
142
        sub sync()
143
            m.notifySyncStatus(Rotor.Const.ThreadSyncType.TASK_SYNCING)
1✔
144

145
            keepAlive = true
1✔
146

147
            while true and keepAlive = true
1✔
148
                msg = wait(0, m.port)
1✔
149
                if msg <> invalid
3✔
150
                    msgType = type(msg)
1✔
151
                    if msgType = "roSGNodeEvent"
3✔
152
                        fieldId = msg.getField()
1✔
153

154
                        if fieldId = "rotorSync"
3✔
155

156
                            sync = msg.getData() ' @type:AA
1✔
157

158
                            if sync.type = Rotor.Const.ThreadSyncType.DISPATCH
2✔
159

160

161
                                dispatcherId = sync.payload.dispatcherId
1✔
162
                                intent = sync.payload.intent
1✔
163
                                dispatcherInstance = m.dispatcherProvider.stack.LookupCI(dispatcherId)
1✔
164

165
                                ' taskIntent = Rotor.Utils.deepCopy(intent)
166
                                dispatcherInstance.dispatch(intent)
1✔
167

168
                            else if sync.type = Rotor.Const.ThreadSyncType.REGISTER_EXTERNAL_DISPATCHER
2✔
169

170
                                for each item in sync.externalDispatcherList
×
171
                                    m.dispatcherProvider.registerExternalDispatchers(item.dispatcherId, item.externalTaskNode)
×
172
                                end for
173

174
                                m.notifySyncStatus(Rotor.Const.ThreadSyncType.TASK_SYNCED)
×
175

176
                            else if sync.type = Rotor.Const.ThreadSyncType.DESTROY
3✔
177

178
                                keepAlive = false
1✔
179

180
                            end if
181
                        else
182

×
183
                            data = msg.getData()
×
184
                            extraInfo = msg.GetInfo() ' Info AA passed during observeFieldScoped
×
185

186
                            if extraInfo?.asyncReducerCallbackId <> invalid and m.dispatcherProvider.get(extraInfo?.asyncReducerCallbackId) <> invalid
×
187
                                ' Catch by dispatcherId
188
                                m.dispatcherProvider.get(extraInfo?.asyncReducerCallbackId).asyncReducerCallback(msg)
×
189
                            else
×
190
                                dispatcherId = fieldId
×
191
                                dispatcherInstance = m.dispatcherProvider.get(dispatcherId)
×
192
                                dispatcherInstance.notifyListeners(data)
×
193
                            end if
194

195
                        end if
196
                    end if
197
                end if
198
            end while
199
            m.destroy()
1✔
200
        end sub
201

202
        ' =====================================================================
203
        ' INTERNAL METHODS
204
        ' =====================================================================
205

206
        ' ---------------------------------------------------------------------
207
        ' notifySyncStatus - Notifies render thread of sync status
208
        '
209
        ' Sends sync status message to render thread via rotorSync field.
210
        '
211
        ' @param {string} status - Sync status type (TASK_SYNCING or TASK_SYNCED)
212
        '
213
        sub notifySyncStatus(status as string)
214

215
            payload = {
1✔
216
                type: status,
217
                taskNode: m.taskNode
218
            }
219

220
            if status = Rotor.Const.ThreadSyncType.TASK_SYNCING
3✔
221
                payload.append({
1✔
222
                    dispatcherIds: m.dispatcherProvider.stack.Keys(),
223
                    tasks: m.config.tasks
224
                })
225
            end if
226

227
            m.taskNode.rootNode.setField("rotorSync", payload)
1✔
228

229
        end sub
230

231
        ' ---------------------------------------------------------------------
232
        ' addObserver - Adds field observer to task thread message port
233
        '
234
        ' @param {string} fieldId - Field name to observe
235
        ' @param {object} node - SceneGraph node to observe
236
        '
237
        sub addObserver(fieldId as string, node)
238
            node.observeFieldScoped(fieldId, m.port)
×
239
        end sub
240

241
        ' ---------------------------------------------------------------------
242
        ' removeObserver - Removes field observer from node
243
        '
244
        ' @param {string} fieldId - Field name to stop observing
245
        ' @param {object} node - SceneGraph node to unobserve
246
        '
247
        sub removeObserver(fieldId as string, node)
248
            node.unobserveFieldScoped(fieldId)
×
249
        end sub
250

251
        ' =====================================================================
252
        ' CLEANUP
253
        ' =====================================================================
254

255
        ' ---------------------------------------------------------------------
256
        ' destroy - Cleans up task thread resources
257
        '
258
        ' Destroys dispatcher provider and clears global framework helper.
259
        '
260
        public sub destroy()
261
            m.dispatcherProvider.destroy()
1✔
262

263
            globalScope = GetGlobalAA()
1✔
264
            globalScope.rotor_framework_helper = {
1✔
265
                frameworkInstance: invalid
266
            }
267

268
            m.taskNode.rootNode = invalid
1✔
269
            m.taskNode = invalid
1✔
270
        end sub
271

272
    end class
273

274
end namespace
275

276

277

278

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc