11using System ;
22using System . Collections . Concurrent ;
3+ using System . Diagnostics ;
34using System . Threading ;
45
56namespace Npgsql
67{
78 sealed class SingleThreadSynchronizationContext : SynchronizationContext , IDisposable
89 {
910 readonly BlockingCollection < CallbackAndState > _tasks = new BlockingCollection < CallbackAndState > ( ) ;
10- Thread ? _thread ;
11+ readonly object _lockObject = new object ( ) ;
12+ volatile Thread ? _thread ;
13+ bool _doingWork ;
1114
1215 const int ThreadStayAliveMs = 10000 ;
1316 readonly string _threadName ;
@@ -19,12 +22,16 @@ public override void Post(SendOrPostCallback callback, object? state)
1922 {
2023 _tasks . Add ( new CallbackAndState { Callback = callback , State = state } ) ;
2124
22- if ( _thread == null )
25+ lock ( _lockObject )
2326 {
24- lock ( this )
27+ if ( ! _doingWork )
2528 {
26- if ( _thread != null )
27- return ;
29+ // Either there is no thread, or the current thread is exiting
30+ // In which case, wait for it to complete
31+ var currentThread = _thread ;
32+ currentThread ? . Join ( ) ;
33+ Debug . Assert ( _thread is null ) ;
34+ _doingWork = true ;
2835 _thread = new Thread ( WorkLoop ) { Name = _threadName , IsBackground = true } ;
2936 _thread . Start ( ) ;
3037 }
@@ -34,12 +41,10 @@ public override void Post(SendOrPostCallback callback, object? state)
3441 public void Dispose ( )
3542 {
3643 _tasks . CompleteAdding ( ) ;
37- _tasks . Dispose ( ) ;
44+ var thread = _thread ;
45+ thread ? . Join ( ) ;
3846
39- lock ( this )
40- {
41- _thread ? . Join ( ) ;
42- }
47+ _tasks . Dispose ( ) ;
4348 }
4449
4550 void WorkLoop ( )
@@ -52,13 +57,40 @@ void WorkLoop()
5257 {
5358 var taken = _tasks . TryTake ( out var callbackAndState , ThreadStayAliveMs ) ;
5459 if ( ! taken )
55- return ;
56- callbackAndState . Callback ( callbackAndState . State ) ;
60+ {
61+ lock ( _lockObject )
62+ {
63+ if ( _tasks . Count == 0 )
64+ {
65+ _doingWork = false ;
66+ return ;
67+ }
68+ }
69+
70+ continue ;
71+ }
72+
73+ try
74+ {
75+ Debug . Assert ( _doingWork ) ;
76+ callbackAndState . Callback ( callbackAndState . State ) ;
77+ }
78+ catch ( Exception )
79+ {
80+ // No logging until 5.0
81+ }
5782 }
5883 }
84+ catch ( Exception )
85+ {
86+ // Here we attempt to catch any exception coming from BlockingCollection _tasks
87+ lock ( _lockObject )
88+ _doingWork = false ;
89+ }
5990 finally
6091 {
61- lock ( this ) { _thread = null ; }
92+ Debug . Assert ( ! _doingWork ) ;
93+ _thread = null ;
6294 }
6395 }
6496
0 commit comments