Skip to content

Commit c33920b

Browse files
committed
switch from semaphore to mutex
1 parent 365dc95 commit c33920b

File tree

4 files changed

+232
-176
lines changed

4 files changed

+232
-176
lines changed

Signal-Windows.Lib/LibUtils.cs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using libsignalservice.push;
1+
using libsignalservice;
2+
using libsignalservice.push;
3+
using Microsoft.Extensions.Logging;
24
using System;
35
using System.Collections.Generic;
46
using System.Linq;
@@ -58,24 +60,74 @@ await dispatcher.RunAsync(priority, () =>
5860

5961
public class LibUtils
6062
{
63+
private static readonly ILogger Logger = LibsignalLogging.CreateLogger<LibUtils>();
6164
public const string GlobalSemaphoreName = "SignalWindowsPrivateMessenger_Mutex";
6265
public static string URL = "https://textsecure-service.whispersystems.org";
6366
public static SignalServiceUrl[] ServiceUrls = new SignalServiceUrl[] { new SignalServiceUrl(URL, null) };
6467
public static bool MainPageActive = false;
6568
public static string USER_AGENT = "Signal-Windows";
6669
public static uint PREKEY_BATCH_SIZE = 100;
6770
public static bool WindowActive = false;
68-
public static Semaphore GlobalSemaphore;
71+
public static Mutex GlobalLock;
72+
private static SynchronizationContext GlobalLockContext;
6973

7074
internal static void Lock()
7175
{
72-
GlobalSemaphore = new Semaphore(1, 1, GlobalSemaphoreName, out bool b);
73-
GlobalSemaphore.WaitOne();
76+
Logger.LogTrace("System lock locking, sync context = {0}", SynchronizationContext.Current);
77+
GlobalLock = new Mutex(false, GlobalSemaphoreName, out bool createdNew);
78+
GlobalLockContext = SynchronizationContext.Current;
79+
try
80+
{
81+
GlobalLock.WaitOne();
82+
}
83+
catch (AbandonedMutexException e)
84+
{
85+
Logger.LogWarning("System lock was abandoned! {0}", e.Message);
86+
}
87+
Logger.LogTrace("System lock locked");
88+
}
89+
90+
public static bool Lock(int timeout)
91+
{
92+
GlobalLock = new Mutex(false, GlobalSemaphoreName, out bool createdNew);
93+
GlobalLockContext = SynchronizationContext.Current;
94+
Logger.LogTrace("System lock locking with timeout, sync context = {0}", SynchronizationContext.Current);
95+
bool success = false;
96+
try
97+
{
98+
success = GlobalLock.WaitOne(timeout);
99+
}
100+
catch(AbandonedMutexException e)
101+
{
102+
Logger.LogWarning("System lock was abandoned! {0}", e.Message);
103+
success = true;
104+
}
105+
Logger.LogTrace("System lock locked = {}", success);
106+
return success;
74107
}
75108

76-
internal static void Unlock()
109+
public static void Unlock()
77110
{
78-
GlobalSemaphore.Release();
111+
Logger.LogTrace("System lock releasing, sync context = {0}", SynchronizationContext.Current);
112+
try
113+
{
114+
if(GlobalLockContext != null)
115+
{
116+
GlobalLockContext.Post((a) =>
117+
{
118+
GlobalLock.ReleaseMutex();
119+
}, null);
120+
}
121+
else
122+
{
123+
GlobalLock.ReleaseMutex();
124+
}
125+
}
126+
catch(Exception e)
127+
{
128+
Logger.LogWarning("System lock failed to unlock! {0}\n{1}", e.Message, e.StackTrace);
129+
}
130+
Logger.LogTrace("System lock released");
79131
}
80132
}
81133
}

Signal-Windows.Lib/SignalLibHandle.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,11 @@ public void Release()
178178
IncomingMessagesTask?.Wait();
179179
OutgoingMessagesTask?.Wait();
180180
Instance = null;
181-
Logger.LogTrace("Release() releasing (global and local)");
181+
Logger.LogTrace("Release() releasing global)");
182182
LibUtils.Unlock();
183+
Logger.LogTrace("Release() releasing local)");
183184
SemaphoreSlim.Release();
185+
Logger.LogTrace("Release() released");
184186
}
185187

186188
public void BackgroundRelease()
Lines changed: 145 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,145 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Diagnostics;
4-
using System.Linq;
5-
using System.Text;
6-
using System.Threading;
7-
using System.Threading.Tasks;
8-
using libsignalservice;
9-
using Microsoft.Extensions.Logging;
10-
using Microsoft.Toolkit.Uwp.Notifications;
11-
using Signal_Windows.Lib;
12-
using Signal_Windows.Lib.Events;
13-
using Signal_Windows.Models;
14-
using Signal_Windows.Storage;
15-
using Windows.ApplicationModel.Background;
16-
using Windows.UI.Notifications;
17-
18-
namespace Signal_Windows.RC
19-
{
20-
public sealed class SignalBackgroundTask : IBackgroundTask
21-
{
22-
private const string TaskName = "SignalMessageBackgroundTask";
23-
private const string SemaphoreName = "Signal_Windows_Semaphore";
24-
25-
private readonly ILogger Logger = LibsignalLogging.CreateLogger<SignalBackgroundTask>();
26-
27-
private BackgroundTaskDeferral deferral;
28-
29-
private Semaphore semaphore;
30-
private DateTime taskStartTime;
31-
private DateTime taskEndTime;
32-
private SignalLibHandle handle;
33-
private ToastNotifier toastNotifier;
34-
35-
public async void Run(IBackgroundTaskInstance taskInstance)
36-
{
37-
taskStartTime = DateTime.Now;
38-
taskEndTime = taskStartTime + TimeSpan.FromSeconds(25);
39-
taskInstance.Canceled += TaskInstance_Canceled;
40-
deferral = taskInstance.GetDeferral();
41-
SignalLogging.SetupLogging(false);
42-
toastNotifier = ToastNotificationManager.CreateToastNotifier();
43-
Logger.LogInformation("Background task starting");
44-
bool appRunning = IsAppRunning();
45-
if (appRunning)
46-
{
47-
Logger.LogWarning("App is running, background task shutting down");
48-
deferral.Complete();
49-
return;
50-
}
51-
handle = new SignalLibHandle(true);
52-
handle.SignalMessageEvent += Handle_SignalMessageEvent;
53-
handle.BackgroundAcquire();
54-
await CheckTimer();
55-
Shutdown();
56-
deferral.Complete();
57-
}
58-
59-
private async Task CheckTimer()
60-
{
61-
Logger.LogInformation("Started listening for messages");
62-
while (true)
63-
{
64-
if (DateTime.Now >= taskEndTime)
65-
{
66-
return;
67-
}
68-
await Task.Delay(TimeSpan.FromMilliseconds(250));
69-
}
70-
}
71-
72-
private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
73-
{
74-
Logger.LogError($"Background task cancelled: {reason}");
75-
Shutdown();
76-
deferral.Complete();
77-
}
78-
79-
private bool IsAppRunning()
80-
{
81-
semaphore = null;
82-
try
83-
{
84-
semaphore = Semaphore.OpenExisting(LibUtils.GlobalSemaphoreName);
85-
}
86-
catch (WaitHandleCannotBeOpenedException)
87-
{
88-
semaphore = new Semaphore(1, 1, LibUtils.GlobalSemaphoreName);
89-
}
90-
91-
bool gotSignal = semaphore.WaitOne(TimeSpan.FromSeconds(5));
92-
return !gotSignal;
93-
}
94-
95-
private void Shutdown()
96-
{
97-
Logger.LogInformation("Background task shutting down");
98-
handle.BackgroundRelease();
99-
semaphore.Release();
100-
}
101-
102-
private void Handle_SignalMessageEvent(object sender, SignalMessageEventArgs e)
103-
{
104-
string notificationId = e.Message.ThreadId;
105-
ToastBindingGeneric toastBinding = new ToastBindingGeneric();
106-
107-
var notificationText = GetNotificationText(e.Message.Author.ThreadDisplayName, e.Message.Content.Content);
108-
foreach (var item in notificationText)
109-
{
110-
toastBinding.Children.Add(item);
111-
}
112-
113-
ToastContent toastContent = new ToastContent()
114-
{
115-
Launch = notificationId,
116-
Visual = new ToastVisual()
117-
{
118-
BindingGeneric = toastBinding
119-
},
120-
DisplayTimestamp = DateTimeOffset.FromUnixTimeMilliseconds(e.Message.ReceivedTimestamp)
121-
};
122-
123-
ToastNotification toastNotification = new ToastNotification(toastContent.GetXml());
124-
uint expiresIn = e.Message.ExpiresAt;
125-
if (expiresIn > 0)
126-
{
127-
toastNotification.ExpirationTime = DateTime.Now.Add(TimeSpan.FromSeconds(expiresIn));
128-
}
129-
toastNotification.Tag = notificationId;
130-
toastNotifier.Show(toastNotification);
131-
}
132-
133-
private IList<AdaptiveText> GetNotificationText(string authorName, string content)
134-
{
135-
List<AdaptiveText> text = new List<AdaptiveText>();
136-
AdaptiveText title = new AdaptiveText()
137-
{
138-
Text = authorName,
139-
HintMaxLines = 1
140-
};
141-
AdaptiveText messageText = new AdaptiveText()
142-
{
143-
Text = content,
144-
HintWrap = true
145-
};
146-
text.Add(title);
147-
text.Add(messageText);
148-
return text;
149-
}
150-
}
151-
}
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using libsignalservice;
9+
using Microsoft.Extensions.Logging;
10+
using Microsoft.Toolkit.Uwp.Notifications;
11+
using Signal_Windows.Lib;
12+
using Signal_Windows.Lib.Events;
13+
using Signal_Windows.Models;
14+
using Signal_Windows.Storage;
15+
using Windows.ApplicationModel.Background;
16+
using Windows.UI.Notifications;
17+
18+
namespace Signal_Windows.RC
19+
{
20+
public sealed class SignalBackgroundTask : IBackgroundTask
21+
{
22+
private const string TaskName = "SignalMessageBackgroundTask";
23+
private readonly ILogger Logger = LibsignalLogging.CreateLogger<SignalBackgroundTask>();
24+
private BackgroundTaskDeferral Deferral;
25+
private DateTime TaskStartTime;
26+
private DateTime TaskEndTime;
27+
private SignalLibHandle Handle;
28+
private ToastNotifier ToastNotifier;
29+
30+
public async void Run(IBackgroundTaskInstance taskInstance)
31+
{
32+
Logger.LogInformation("Background task starting");
33+
TaskStartTime = DateTime.Now;
34+
TaskEndTime = TaskStartTime + TimeSpan.FromSeconds(25);
35+
Deferral = taskInstance.GetDeferral();
36+
SignalLogging.SetupLogging(false);
37+
ToastNotifier = ToastNotificationManager.CreateToastNotifier();
38+
taskInstance.Canceled += OnCanceled;
39+
bool locked = LibUtils.Lock(5000);
40+
Logger.LogTrace("Locking global finished, locked = {0}", locked);
41+
if (!locked)
42+
{
43+
Logger.LogWarning("App is running, background task shutting down");
44+
Deferral.Complete();
45+
return;
46+
}
47+
try
48+
{
49+
Handle = new SignalLibHandle(true);
50+
Handle.SignalMessageEvent += Handle_SignalMessageEvent;
51+
Handle.BackgroundAcquire();
52+
await CheckTimer();
53+
}
54+
catch (Exception e)
55+
{
56+
Logger.LogError("Background task failed: {0}\n{1}", e.Message, e.StackTrace);
57+
}
58+
finally
59+
{
60+
LibUtils.Unlock();
61+
Deferral.Complete();
62+
}
63+
}
64+
65+
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
66+
{
67+
Logger.LogWarning("Background task received cancel request");
68+
try
69+
{
70+
Handle.BackgroundRelease();
71+
}
72+
catch(Exception e)
73+
{
74+
Logger.LogError("OnCanceled() failed : {0}\n{1}", e.Message, e.StackTrace);
75+
}
76+
finally
77+
{
78+
LibUtils.Unlock();
79+
Logger.LogWarning("Background task cancel handler finished");
80+
}
81+
}
82+
83+
private async Task CheckTimer()
84+
{
85+
Logger.LogInformation("Started listening for messages");
86+
while (true)
87+
{
88+
if (DateTime.Now >= TaskEndTime)
89+
{
90+
return;
91+
}
92+
await Task.Delay(TimeSpan.FromMilliseconds(250));
93+
}
94+
}
95+
96+
private void Handle_SignalMessageEvent(object sender, SignalMessageEventArgs e)
97+
{
98+
string notificationId = e.Message.ThreadId;
99+
ToastBindingGeneric toastBinding = new ToastBindingGeneric();
100+
101+
var notificationText = GetNotificationText(e.Message.Author.ThreadDisplayName, e.Message.Content.Content);
102+
foreach (var item in notificationText)
103+
{
104+
toastBinding.Children.Add(item);
105+
}
106+
107+
ToastContent toastContent = new ToastContent()
108+
{
109+
Launch = notificationId,
110+
Visual = new ToastVisual()
111+
{
112+
BindingGeneric = toastBinding
113+
},
114+
DisplayTimestamp = DateTimeOffset.FromUnixTimeMilliseconds(e.Message.ReceivedTimestamp)
115+
};
116+
117+
ToastNotification toastNotification = new ToastNotification(toastContent.GetXml());
118+
uint expiresIn = e.Message.ExpiresAt;
119+
if (expiresIn > 0)
120+
{
121+
toastNotification.ExpirationTime = DateTime.Now.Add(TimeSpan.FromSeconds(expiresIn));
122+
}
123+
toastNotification.Tag = notificationId;
124+
ToastNotifier.Show(toastNotification);
125+
}
126+
127+
private IList<AdaptiveText> GetNotificationText(string authorName, string content)
128+
{
129+
List<AdaptiveText> text = new List<AdaptiveText>();
130+
AdaptiveText title = new AdaptiveText()
131+
{
132+
Text = authorName,
133+
HintMaxLines = 1
134+
};
135+
AdaptiveText messageText = new AdaptiveText()
136+
{
137+
Text = content,
138+
HintWrap = true
139+
};
140+
text.Add(title);
141+
text.Add(messageText);
142+
return text;
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)