@@ -22,6 +22,12 @@ namespace Unityctl.Plugin.Editor.Ipc
2222 /// </summary>
2323 public sealed class IpcServer
2424 {
25+ private enum ShutdownMode
26+ {
27+ Graceful ,
28+ EditorQuit
29+ }
30+
2531 private const int MaxServerInstances = 4 ;
2632 private const int PipeBusyRetryDelayMs = 250 ;
2733 private const int ErrorPipeBusy = 231 ;
@@ -78,7 +84,7 @@ public void Start(string projectPath)
7884 if ( IsRunning && _pipeName == pipeName ) return ;
7985
8086 // Different project or not running — stop existing, start new
81- if ( IsRunning ) StopInternal ( ) ;
87+ if ( IsRunning ) StopInternal ( ShutdownMode . Graceful ) ;
8288
8389 _projectPath = projectPath ;
8490 _pipeName = pipeName ;
@@ -111,58 +117,79 @@ public void Stop()
111117 {
112118 lock ( _lock )
113119 {
114- StopInternal ( ) ;
120+ StopInternal ( ShutdownMode . Graceful ) ;
115121 }
116122 }
117123
118- private void StopInternal ( )
124+ private void StopForEditorQuit ( )
125+ {
126+ lock ( _lock )
127+ {
128+ StopInternal ( ShutdownMode . EditorQuit ) ;
129+ }
130+ }
131+
132+ private void StopInternal ( ShutdownMode shutdownMode )
119133 {
120134 if ( ! IsRunning ) return ;
121135
136+ var fastExit = shutdownMode == ShutdownMode . EditorQuit ;
122137 _stopping = true ;
123138 _shutdownCompletion . TrySetResult ( true ) ;
124139
140+ EditorApplication . update -= PumpMainThreadQueue ;
141+ AssemblyReloadEvents . beforeAssemblyReload -= OnBeforeAssemblyReload ;
142+ EditorApplication . quitting -= OnQuitting ;
143+
125144 // Signal watch session to stop
126145 if ( _watchActive )
127146 {
128- try
147+ if ( ! fastExit )
129148 {
130- var closeEnvelope = EventEnvelope . Create ( "_close" , "Shutdown" ) ;
131- _watchQueue . Enqueue ( closeEnvelope ) ;
149+ try
150+ {
151+ var closeEnvelope = EventEnvelope . Create ( "_close" , "Shutdown" ) ;
152+ _watchQueue . Enqueue ( closeEnvelope ) ;
153+ }
154+ catch { }
132155 }
133- catch { }
134156 _watchActive = false ;
135157 _watchEventSource ? . Unsubscribe ( ) ;
136158 _watchEventSource = null ;
137159 }
138160
139161 // Dispose current pipe to unblock WaitForConnection
140162 try { _listenPipe ? . Dispose ( ) ; } catch { }
163+ try { _watchPipe ? . Dispose ( ) ; } catch { }
141164
142165 foreach ( var activePipe in _activePipes . Values )
143166 {
144167 try { activePipe . Dispose ( ) ; } catch { }
145168 }
146169
147- if ( _listenThread != null && _listenThread . IsAlive )
148- _listenThread . Join ( 3000 ) ;
149-
150- // Wait for watch writer thread to finish
151- if ( _watchThread != null && _watchThread . IsAlive )
152- _watchThread . Join ( 2000 ) ;
170+ if ( ! fastExit )
171+ {
172+ if ( _listenThread != null && _listenThread . IsAlive )
173+ _listenThread . Join ( 3000 ) ;
153174
154- EditorApplication . update -= PumpMainThreadQueue ;
155- AssemblyReloadEvents . beforeAssemblyReload -= OnBeforeAssemblyReload ;
156- EditorApplication . quitting -= OnQuitting ;
175+ // Wait for watch writer thread to finish
176+ if ( _watchThread != null && _watchThread . IsAlive )
177+ _watchThread . Join ( 2000 ) ;
178+ }
157179
158180 // Cancel and drain remaining queued requests so listener threads do not block.
159181 while ( _mainThreadQueue . TryDequeue ( out var pending ) )
160182 {
161183 pending . WorkItem . Cancel ( ) ;
162184 }
163185
186+ _listenPipe = null ;
187+ _watchPipe = null ;
188+ _listenThread = null ;
189+ _watchThread = null ;
164190 IsRunning = false ;
165- Debug . Log ( "[unityctl] IPC server stopped" ) ;
191+ if ( ! fastExit )
192+ Debug . Log ( "[unityctl] IPC server stopped" ) ;
166193 }
167194
168195 private void OnBeforeAssemblyReload ( )
@@ -172,7 +199,7 @@ private void OnBeforeAssemblyReload()
172199
173200 private void OnQuitting ( )
174201 {
175- Stop ( ) ;
202+ StopForEditorQuit ( ) ;
176203 }
177204
178205 /// <summary>
0 commit comments