@@ -43,10 +43,10 @@ public interface ISignalLibHandle
43
43
List < SignalMessageContainer > GetMessages ( SignalConversation thread , int startIndex , int count ) ;
44
44
void SaveAndDispatchSignalConversation ( SignalConversation updatedConversation , SignalMessage updateMessage ) ;
45
45
void PurgeAccountData ( ) ;
46
- Task Acquire ( CoreDispatcher d , ISignalFrontend w ) ;
46
+ Task < bool > Acquire ( CoreDispatcher d , ISignalFrontend w ) ;
47
47
Task Reacquire ( ) ;
48
48
void Release ( ) ;
49
- void AddFrontend ( CoreDispatcher d , ISignalFrontend w ) ;
49
+ bool AddFrontend ( CoreDispatcher d , ISignalFrontend w ) ;
50
50
void RemoveFrontend ( CoreDispatcher d ) ;
51
51
52
52
// Background API
@@ -75,8 +75,11 @@ internal class SignalLibHandle : ISignalLibHandle
75
75
public SemaphoreSlim SemaphoreSlim = new SemaphoreSlim ( 1 , 1 ) ;
76
76
private bool Headless ;
77
77
private bool Running = false ;
78
+ private bool LikelyHasValidStore = false ;
78
79
private CancellationTokenSource CancelSource = new CancellationTokenSource ( ) ;
79
80
private Dictionary < CoreDispatcher , ISignalFrontend > Frames = new Dictionary < CoreDispatcher , ISignalFrontend > ( ) ;
81
+ private CoreDispatcher MainWindowDispatcher ;
82
+ private ISignalFrontend MainWindow ;
80
83
private Task IncomingMessagesTask ;
81
84
private Task OutgoingMessagesTask ;
82
85
internal OutgoingMessages OutgoingMessages ;
@@ -96,23 +99,31 @@ public SignalLibHandle(bool headless)
96
99
Instance = this ;
97
100
}
98
101
99
- public void AddFrontend ( CoreDispatcher d , ISignalFrontend w )
102
+ public bool AddFrontend ( CoreDispatcher d , ISignalFrontend w )
100
103
{
101
104
Logger . LogTrace ( "AddFrontend() locking" ) ;
102
105
SemaphoreSlim . Wait ( CancelSource . Token ) ;
103
- Logger . LogTrace ( "AddFrontend() locked" ) ;
104
- if ( Running )
106
+ try
105
107
{
106
- Logger . LogInformation ( "Registering frontend of dispatcher {0}" , w . GetHashCode ( ) ) ;
107
- Frames . Add ( d , w ) ;
108
- w . ReplaceConversationList ( GetConversations ( ) ) ;
108
+ Logger . LogTrace ( "AddFrontend() locked" ) ;
109
+ if ( Running && LikelyHasValidStore )
110
+ {
111
+ Logger . LogInformation ( "Registering frontend of dispatcher {0}" , w . GetHashCode ( ) ) ;
112
+ Frames . Add ( d , w ) ;
113
+ w . ReplaceConversationList ( GetConversations ( ) ) ;
114
+ return true ;
115
+ }
116
+ else
117
+ {
118
+ Logger . LogInformation ( "Ignoring AddFrontend call" ) ;
119
+ return false ;
120
+ }
109
121
}
110
- else
122
+ finally
111
123
{
112
- Logger . LogInformation ( "Ignoring AddFrontend call, release in progress" ) ;
124
+ SemaphoreSlim . Release ( ) ;
125
+ Logger . LogTrace ( "AddFrontend() released" ) ;
113
126
}
114
- SemaphoreSlim . Release ( ) ;
115
- Logger . LogTrace ( "AddFrontend() released" ) ;
116
127
}
117
128
118
129
public void RemoveFrontend ( CoreDispatcher d )
@@ -136,44 +147,60 @@ public void PurgeAccountData()
136
147
Logger . LogTrace ( "PurgeAccountData() released" ) ;
137
148
}
138
149
139
- public async Task Acquire ( CoreDispatcher d , ISignalFrontend w ) //TODO wrap trycatch dispatch auth failure
150
+ public async Task < bool > Acquire ( CoreDispatcher d , ISignalFrontend w ) //TODO wrap trycatch dispatch auth failure
140
151
{
141
152
Logger . LogTrace ( "Acquire() locking" ) ;
142
153
CancelSource = new CancellationTokenSource ( ) ;
143
154
SemaphoreSlim . Wait ( CancelSource . Token ) ;
144
- GlobalResetEvent = LibUtils . OpenResetEventSet ( ) ;
145
- LibUtils . Lock ( ) ;
146
- GlobalResetEvent . Reset ( ) ;
147
- var getConversationsTask = Task . Run ( ( ) =>
148
- {
149
- return GetConversations ( ) ; // we want to display the conversations asap!
150
- } ) ;
151
- Logger . LogDebug ( "Acquire() locked (global and local)" ) ;
152
- Instance = this ;
153
- Frames . Add ( d , w ) ;
154
- w . ReplaceConversationList ( await getConversationsTask ) ;
155
- var failTask = Task . Run ( ( ) =>
156
- {
157
- SignalDBContext . FailAllPendingMessages ( ) ; // TODO GetMessages needs to be protected by semaphoreslim as we fail defered
158
- } ) ;
159
- Store = await Task . Run ( ( ) =>
155
+ try
160
156
{
161
- return LibsignalDBContext . GetSignalStore ( ) ;
162
- } ) ;
163
- if ( Store == null )
157
+ GlobalResetEvent = LibUtils . OpenResetEventSet ( ) ;
158
+ LibUtils . Lock ( ) ;
159
+ GlobalResetEvent . Reset ( ) ;
160
+ MainWindowDispatcher = d ;
161
+ MainWindow = w ;
162
+ Logger . LogDebug ( "Acquire() locked (global and local)" ) ;
163
+ var getConversationsTask = Task . Run ( ( ) =>
164
+ {
165
+ return GetConversations ( ) ; // we want to display the conversations asap!
166
+ } ) ;
167
+ Instance = this ;
168
+ Frames . Add ( d , w ) ;
169
+ w . ReplaceConversationList ( await getConversationsTask ) ;
170
+ var failTask = Task . Run ( ( ) =>
171
+ {
172
+ SignalDBContext . FailAllPendingMessages ( ) ; // TODO GetMessages needs to be protected by semaphoreslim as we fail defered
173
+ } ) ;
174
+ Store = await Task . Run ( ( ) =>
175
+ {
176
+ return LibsignalDBContext . GetSignalStore ( ) ;
177
+ } ) ;
178
+ if ( Store == null )
179
+ {
180
+ return false ;
181
+ }
182
+ else
183
+ {
184
+ LikelyHasValidStore = true ;
185
+ }
186
+ var initNetwork = Task . Run ( ( ) =>
187
+ {
188
+ InitNetwork ( ) ;
189
+ } ) ;
190
+ var recoverDownloadsTask = Task . Run ( ( ) =>
191
+ {
192
+ RecoverDownloads ( ) . Wait ( ) ;
193
+ } ) ;
194
+ await failTask ; // has to complete before messages are loaded
195
+ await recoverDownloadsTask ;
196
+ Running = true ;
197
+ return true ;
198
+ }
199
+ finally
164
200
{
165
201
SemaphoreSlim . Release ( ) ;
166
- throw new Exception ( "Signal Store has not been setup yet. ") ;
202
+ Logger . LogTrace ( "Acquire() released ") ;
167
203
}
168
- await Task . Run ( ( ) =>
169
- {
170
- InitNetwork ( ) ;
171
- RecoverDownloads ( ) . Wait ( ) ;
172
- } ) ;
173
- await failTask ; // has to complete before messages are loaded
174
- Running = true ;
175
- Logger . LogTrace ( "Acquire() releasing" ) ;
176
- SemaphoreSlim . Release ( ) ;
177
204
}
178
205
179
206
public void BackgroundAcquire ( )
@@ -192,46 +219,65 @@ public async Task Reacquire()
192
219
Logger . LogTrace ( "Reacquire() locking" ) ;
193
220
CancelSource = new CancellationTokenSource ( ) ;
194
221
SemaphoreSlim . Wait ( CancelSource . Token ) ;
195
- GlobalResetEvent = LibUtils . OpenResetEventSet ( ) ;
196
- LibUtils . Lock ( ) ;
197
- GlobalResetEvent . Reset ( ) ;
198
- LibsignalDBContext . ClearSessionCache ( ) ;
199
- Instance = this ;
200
- await Task . Run ( ( ) =>
222
+ try
201
223
{
202
- List < Task > tasks = new List < Task > ( ) ;
203
- foreach ( var f in Frames )
224
+ GlobalResetEvent = LibUtils . OpenResetEventSet ( ) ;
225
+ LibUtils . Lock ( ) ;
226
+ GlobalResetEvent . Reset ( ) ;
227
+ LibsignalDBContext . ClearSessionCache ( ) ;
228
+ Instance = this ;
229
+ await Task . Run ( ( ) =>
204
230
{
205
- var conversations = GetConversations ( ) ;
206
- tasks . Add ( f . Key . RunTaskAsync ( ( ) =>
231
+ List < Task > tasks = new List < Task > ( ) ;
232
+ foreach ( var f in Frames )
207
233
{
208
- f . Value . ReplaceConversationList ( conversations ) ;
209
- } ) ) ;
234
+ var conversations = GetConversations ( ) ;
235
+ tasks . Add ( f . Key . RunTaskAsync ( ( ) =>
236
+ {
237
+ f . Value . ReplaceConversationList ( conversations ) ;
238
+ } ) ) ;
239
+ }
240
+ Task . WaitAll ( tasks . ToArray ( ) ) ;
241
+ RecoverDownloads ( ) . Wait ( ) ;
242
+ } ) ;
243
+ if ( LikelyHasValidStore )
244
+ {
245
+ var initNetworkTask = Task . Run ( ( ) =>
246
+ {
247
+ InitNetwork ( ) ;
248
+ } ) ;
210
249
}
211
- Task . WaitAll ( tasks . ToArray ( ) ) ;
212
- InitNetwork ( ) ;
213
- RecoverDownloads ( ) . Wait ( ) ;
214
- } ) ;
215
- Running = true ;
216
- Logger . LogTrace ( "Reacquire() releasing " ) ;
217
- SemaphoreSlim . Release ( ) ;
250
+ Running = true ;
251
+ }
252
+ finally
253
+ {
254
+ SemaphoreSlim . Release ( ) ;
255
+ Logger . LogTrace ( "Reacquire() released " ) ;
256
+ }
218
257
}
219
258
220
259
public void Release ( )
221
260
{
222
261
//TODO invalidate view information
223
262
Logger . LogTrace ( "Release() locking" ) ;
224
- SemaphoreSlim . Wait ( CancelSource . Token ) ;
225
- Running = false ;
226
- CancelSource . Cancel ( ) ;
227
- IncomingMessagesTask ? . Wait ( ) ;
228
- OutgoingMessagesTask ? . Wait ( ) ;
229
- Instance = null ;
230
- Logger . LogTrace ( "Release() releasing global)" ) ;
231
- LibUtils . Unlock ( ) ;
232
- Logger . LogTrace ( "Release() releasing local)" ) ;
233
- SemaphoreSlim . Release ( ) ;
234
- Logger . LogTrace ( "Release() released" ) ;
263
+ if ( Running )
264
+ {
265
+ SemaphoreSlim . Wait ( CancelSource . Token ) ;
266
+ Running = false ;
267
+ CancelSource . Cancel ( ) ;
268
+ IncomingMessagesTask ? . Wait ( ) ;
269
+ OutgoingMessagesTask ? . Wait ( ) ;
270
+ Instance = null ;
271
+ Logger . LogTrace ( "Release() releasing global)" ) ;
272
+ LibUtils . Unlock ( ) ;
273
+ Logger . LogTrace ( "Release() releasing local)" ) ;
274
+ SemaphoreSlim . Release ( ) ;
275
+ Logger . LogTrace ( "Release() released" ) ;
276
+ }
277
+ else
278
+ {
279
+ Logger . LogTrace ( "SignalLibHandle was already closed" ) ;
280
+ }
235
281
}
236
282
237
283
public void BackgroundRelease ( )
@@ -317,6 +363,26 @@ public void StartAttachmentDownload(SignalAttachment sa)
317
363
#endregion
318
364
319
365
#region internal api
366
+ internal void DispatchHandleAuthFailure ( )
367
+ {
368
+ List < Task > operations = new List < Task > ( ) ;
369
+ foreach ( var dispatcher in Frames . Keys )
370
+ {
371
+ operations . Add ( dispatcher . RunTaskAsync ( ( ) =>
372
+ {
373
+ try
374
+ {
375
+ Frames [ dispatcher ] . HandleAuthFailure ( ) ;
376
+ }
377
+ catch ( Exception e )
378
+ {
379
+ Logger . LogError ( "DispatchHandleAuthFailure failed: {0}\n {1}" , e . Message , e . StackTrace ) ;
380
+ }
381
+ } ) ) ;
382
+ }
383
+ Task . WaitAll ( operations . ToArray ( ) ) ;
384
+ }
385
+
320
386
internal void SaveAndDispatchSignalMessage ( SignalMessage message , SignalConversation conversation )
321
387
{
322
388
conversation . MessagesCount += 1 ;
@@ -464,14 +530,48 @@ private List<SignalConversation> GetConversations()
464
530
return conversations ;
465
531
}
466
532
533
+ /// <summary>
534
+ /// Initializes the websocket connection handling. Must not not be called on a UI thread. Must not be called on a task which holds the handle lock.
535
+ /// </summary>
467
536
private void InitNetwork ( )
468
537
{
469
- MessageReceiver = new SignalServiceMessageReceiver ( CancelSource . Token , LibUtils . ServiceUrls , new StaticCredentialsProvider ( Store . Username , Store . Password , Store . SignalingKey , ( int ) Store . DeviceId ) , LibUtils . USER_AGENT ) ;
470
- Pipe = MessageReceiver . createMessagePipe ( ) ;
471
- MessageSender = new SignalServiceMessageSender ( CancelSource . Token , LibUtils . ServiceUrls , Store . Username , Store . Password , ( int ) Store . DeviceId , new Store ( ) , Pipe , null , LibUtils . USER_AGENT ) ;
472
- IncomingMessagesTask = Task . Factory . StartNew ( ( ) => new IncomingMessages ( CancelSource . Token , Pipe , this ) . HandleIncomingMessages ( ) , TaskCreationOptions . LongRunning ) ;
473
- OutgoingMessages = new OutgoingMessages ( CancelSource . Token , MessageSender , this ) ;
474
- OutgoingMessagesTask = Task . Factory . StartNew ( ( ) => OutgoingMessages . HandleOutgoingMessages ( ) , TaskCreationOptions . LongRunning ) ;
538
+ try
539
+ {
540
+ MessageReceiver = new SignalServiceMessageReceiver ( CancelSource . Token , LibUtils . ServiceUrls , new StaticCredentialsProvider ( Store . Username , Store . Password , Store . SignalingKey , ( int ) Store . DeviceId ) , LibUtils . USER_AGENT ) ;
541
+ Pipe = MessageReceiver . createMessagePipe ( ) ;
542
+ MessageSender = new SignalServiceMessageSender ( CancelSource . Token , LibUtils . ServiceUrls , Store . Username , Store . Password , ( int ) Store . DeviceId , new Store ( ) , Pipe , null , LibUtils . USER_AGENT ) ;
543
+ IncomingMessagesTask = Task . Factory . StartNew ( ( ) => new IncomingMessages ( CancelSource . Token , Pipe , this ) . HandleIncomingMessages ( ) , TaskCreationOptions . LongRunning ) ;
544
+ OutgoingMessages = new OutgoingMessages ( CancelSource . Token , MessageSender , this ) ;
545
+ OutgoingMessagesTask = Task . Factory . StartNew ( ( ) => OutgoingMessages . HandleOutgoingMessages ( ) , TaskCreationOptions . LongRunning ) ;
546
+ }
547
+ catch ( Exception e )
548
+ {
549
+ Logger . LogError ( "InitNetwork failed: {0}\n {1}" , e . Message , e . StackTrace ) ;
550
+ HandleAuthFailure ( ) ;
551
+ }
552
+ }
553
+
554
+ /// <summary>
555
+ /// Dispatches the auth failure to all frontends and resets the frontend dict. Must not be called on a UI thread. Must not be called on a task which holds the handle lock.
556
+ /// </summary>
557
+ private void HandleAuthFailure ( )
558
+ {
559
+ Logger . LogTrace ( "HandleAuthFailure() locking" ) ;
560
+ SemaphoreSlim . Wait ( CancelSource . Token ) ;
561
+ try
562
+ {
563
+ LikelyHasValidStore = false ;
564
+ Running = false ;
565
+ CancelSource . Cancel ( ) ;
566
+ DispatchHandleAuthFailure ( ) ;
567
+ Frames . Clear ( ) ;
568
+ Frames . Add ( MainWindowDispatcher , MainWindow ) ;
569
+ }
570
+ finally
571
+ {
572
+ SemaphoreSlim . Release ( ) ;
573
+ Logger . LogTrace ( "HandleAuthFailure() released" ) ;
574
+ }
475
575
}
476
576
477
577
private void TryScheduleAttachmentDownload ( SignalAttachment attachment )
0 commit comments