@@ -94,8 +94,13 @@ func (m *StateManager) StartSession() error {
9494func (m * StateManager ) StopSession () error {
9595
9696 m .mu .Lock ()
97+
9798 shutdownMgr := m .shutdownMgr
9899 wasPending := m .PendingStart
100+
101+ // Log what we're destroying
102+ m .logControllersRelease (shutdownMgr )
103+
99104 m .state = StateLoaded
100105 m .PendingStart = false
101106 m .mu .Unlock ()
@@ -104,50 +109,90 @@ func (m *StateManager) StopSession() error {
104109 return errNoActiveSession
105110 }
106111
112+ ctx := logger .BackgroundCtx
113+ if shutdownMgr != nil {
114+ ctx = * shutdownMgr .Context ()
115+ }
116+
107117 if wasPending {
108- logger .Info (* shutdownMgr . Context () , logger .BLE , "stop requested, canceling pending session setup..." )
118+ logger .Info (ctx , logger .APP , "stop requested, canceling pending session setup..." )
109119 } else {
110- logger .Info (* shutdownMgr . Context () , logger .BLE , "stop requested, canceling active session..." )
120+ logger .Info (ctx , logger .APP , "stop requested, canceling active session..." )
111121 }
112122
113- // Trigger shutdown (cancels ctx, waits wg—like Ctrl+C)
114123 fmt .Fprint (os .Stdout , "\r " ) // Clear the ^C character from the terminal line
115124
125+ // Stop the shutdown manager
116126 if shutdownMgr != nil {
117127 shutdownMgr .Shutdown ()
118128 }
119129
120- // Clear resources under lock
130+ m .clearResources ()
131+ m .stopBLEScan (ctx )
132+
133+ if wasPending {
134+ logger .Info (ctx , logger .APP , "stopped pending session startup" )
135+ } else {
136+ logger .Info (ctx , logger .APP , "session stopped" )
137+ }
138+
139+ return nil
140+ }
141+
142+ // logControllersRelease logs the release of controller objects
143+ func (m * StateManager ) logControllersRelease (shutdownMgr * services.ShutdownManager ) {
144+
145+ if m .controllers == nil || shutdownMgr == nil {
146+ return
147+ }
148+
149+ ctx := * shutdownMgr .Context ()
150+
151+ if m .controllers .bleController != nil {
152+ logger .Info (ctx , logger .BLE , fmt .Sprintf ("releasing BLE controller object (id:%04d)" , m .controllers .bleController .InstanceID ))
153+ }
154+ if m .controllers .speedController != nil {
155+ logger .Info (ctx , logger .SPEED , fmt .Sprintf ("releasing speed controller object (id:%04d)" , m .controllers .speedController .InstanceID ))
156+ }
157+ if m .controllers .videoPlayer != nil {
158+ logger .Info (ctx , logger .VIDEO , fmt .Sprintf ("releasing video controller object (id:%04d)" , m .controllers .videoPlayer .InstanceID ))
159+ }
160+
161+ }
162+
163+ // clearResources clears the session resources
164+ func (m * StateManager ) clearResources () {
165+
121166 m .mu .Lock ()
167+ defer m .mu .Unlock ()
168+
122169 m .controllers = nil
123170 m .shutdownMgr = nil
124171 m .activeConfig = nil
125- m .mu .Unlock ()
172+
173+ logger .Debug (logger .BackgroundCtx , logger .APP , "controllers and shutdown manager objects released" )
174+
175+ }
176+
177+ // stopBLEScan stops the BLE scan
178+ func (m * StateManager ) stopBLEScan (ctx context.Context ) {
126179
127180 // Stop any ongoing scan under mutex
128181 ble .AdapterMu .Lock ()
129182 defer ble .AdapterMu .Unlock ()
130183
131184 if err := bluetooth .DefaultAdapter .StopScan (); err != nil {
132- logger .Warn (* shutdownMgr .Context (), logger .BLE , fmt .Sprintf ("failed to stop current BLE scan: %v" , err ))
133- } else {
134- logger .Info (* shutdownMgr .Context (), logger .BLE , "BLE scan stopped" )
135- }
136-
137- if wasPending {
138- logger .Info (* shutdownMgr .Context (), logger .APP , "stopped pending session startup" )
185+ logger .Warn (ctx , logger .BLE , fmt .Sprintf ("failed to stop current BLE scan: %v" , err ))
139186 } else {
140- logger .Info (* shutdownMgr . Context () , logger .APP , "session stopped" )
187+ logger .Info (ctx , logger .BLE , "BLE scan stopped" )
141188 }
142189
143- return nil
144190}
145191
146192// BatteryLevel returns the current battery level from the BLE controller
147193func (m * StateManager ) BatteryLevel () byte {
148194
149- m .mu .RLock ()
150- defer m .mu .RUnlock ()
195+ defer m .readLock ()()
151196
152197 if m .controllers != nil && m .controllers .bleController != nil {
153198 return m .controllers .bleController .BatteryLevelLast ()
@@ -159,8 +204,7 @@ func (m *StateManager) BatteryLevel() byte {
159204// CurrentSpeed returns the current smoothed speed from the speed controller
160205func (m * StateManager ) CurrentSpeed () (float64 , string ) {
161206
162- m .mu .RLock ()
163- defer m .mu .RUnlock ()
207+ defer m .readLock ()()
164208
165209 // Use ActiveConfig here to ensure we return the units of the active running session
166210 cfg := m .activeConfig
@@ -179,8 +223,7 @@ func (m *StateManager) CurrentSpeed() (float64, string) {
179223// VideoTimeRemaining returns the formatted time remaining string (HH:MM:SS)
180224func (m * StateManager ) VideoTimeRemaining () string {
181225
182- m .mu .RLock ()
183- defer m .mu .RUnlock ()
226+ defer m .readLock ()()
184227
185228 noTime := "--:--:--"
186229
@@ -199,8 +242,7 @@ func (m *StateManager) VideoTimeRemaining() string {
199242// VideoPlaybackRate returns the current video playback multiplier (e.g. 1.0x)
200243func (m * StateManager ) VideoPlaybackRate () float64 {
201244
202- m .mu .RLock ()
203- defer m .mu .RUnlock ()
245+ defer m .readLock ()()
204246
205247 if m .controllers == nil || m .controllers .videoPlayer == nil {
206248 return 0.0
@@ -216,28 +258,32 @@ func (m *StateManager) initializeControllers() (*controllers, error) {
216258 cfg := m .activeConfig
217259 m .mu .RUnlock ()
218260
261+ logger .Debug (logger .BackgroundCtx , logger .APP , "creating and initializing controllers..." )
262+
219263 if cfg == nil {
220264 return nil , errNoActiveConfig
221265 }
222266
223- logger .Debug (logger .BackgroundCtx , logger .APP , "creating speed controller..." )
267+ logger .Debug (logger .BackgroundCtx , logger .APP , "creating new speed controller..." )
224268
225269 speedController := speed .NewSpeedController (cfg .Speed .SmoothingWindow )
226270
227- logger .Debug (logger .BackgroundCtx , logger .APP , "creating video controller..." )
271+ logger .Debug (logger .BackgroundCtx , logger .APP , "creating new video controller..." )
228272
229273 videoPlayer , err := video .NewPlaybackController (cfg .Video , cfg .Speed )
230274 if err != nil {
231275 return nil , fmt .Errorf ("failed to create video controller: %w" , err )
232276 }
233277
234- logger .Debug (logger .BackgroundCtx , logger .APP , "creating BLE controller..." )
278+ logger .Debug (logger .BackgroundCtx , logger .APP , "creating new BLE controller..." )
235279
236280 bleController , err := ble .NewBLEController (cfg .BLE , cfg .Speed )
237281 if err != nil {
238282 return nil , fmt .Errorf ("failed to create BLE controller: %w" , err )
239283 }
240284
285+ logger .Debug (logger .BackgroundCtx , logger .APP , "all controllers created and initialized" )
286+
241287 return & controllers {
242288 speedController : speedController ,
243289 videoPlayer : videoPlayer ,
@@ -310,6 +356,29 @@ func (m *StateManager) startServices(ctrl *controllers, shutdownMgr *services.Sh
310356
311357}
312358
359+ // cleanupStartFailure handles cleaning manager state when session startup fails
360+ func (m * StateManager ) cleanupStartFailure (shutdownMgr * services.ShutdownManager ) {
361+
362+ logger .Debug (logger .BackgroundCtx , logger .APP , "resetting controllers and session state..." )
363+
364+ m .mu .Lock ()
365+ m .PendingStart = false
366+ m .state = StateLoaded
367+ m .controllers = nil
368+ m .shutdownMgr = nil
369+ m .activeConfig = nil
370+
371+ m .mu .Unlock ()
372+
373+ // ensure the shutdown manager... uh... shuts down
374+ if shutdownMgr != nil {
375+ shutdownMgr .Shutdown ()
376+ }
377+
378+ logger .Debug (logger .BackgroundCtx , logger .APP , "controllers and session state reset complete" )
379+
380+ }
381+
313382// runService helper to launch a service with standard error handling and logging
314383func (m * StateManager ) runService (shutdownMgr * services.ShutdownManager , service string , action func (context.Context ) error ) {
315384
0 commit comments