With the new Single Process Model of the Anniversary SDK syncing became much easier because background processing no longer runs in a different process. I am using the AsyncLock from Stephen Clearey's AsyncEx with it.
ORIGINAL ANSWER:
Statements like "Mutexes are thread-affine, so they don't work with async code" and bugs in my apps and tests had made me suspicious that we generally cannot use a named Mutex to synchronize resource access between an UWP app and its background tasks. All alternative solutions I saw only work in-process.
I came to the conclusion that mutexes do work fine in this scenario as long as .ReleaseMutex is coded on the same level as .WaitOne (e.g. not within an async method awaited after .WaitOne).
For coding convenience I encapsulated the mutex handling to allow Using statements:
'Usage to serialize access: Using New AppAndBackgroundMutex(TimeSpan.FromSeconds(5)) 'Access storage files 'Access ApplicationData settings 'Update Tiles End Using 'Usage to back out: Using New AppAndBackgroundMutex(TimeSpan.Zero) '... End Using Public NotInheritable Class AppAndBackgroundMutex : Implements IDisposable Private _mutex As Threading.Mutex Private _iOwnMutex As Boolean Sub New(waitTimeout As TimeSpan, Optional syncId As String = "SyncRates&Selections&Date") Const UniqePartOfMutexName = "<app specific GUID>" Try _mutex = New Threading.Mutex(False, UniqePartOfMutexName & syncId) _iOwnMutex = _mutex.WaitOne(waitTimeout) If Not _iOwnMutex Then Dim msg = ($"Unable to acquire mutex for app/background sync after waiting for {waitTimeout}.") If waitTimeout = TimeSpan.Zero Then 'Intentionally backing out Trace.Info(msg) Else Trace.Error(msg) End If Throw New MutexTimeoutException(msg) End If Catch ex As Threading.AbandonedMutexException Trace.Error("Abandoned Mutex detected! OS might have killed background task. Ignoring problem.") _iOwnMutex = True End Try End Sub 'Simple Dispose implementaion because class is sealed Public Sub Dispose() Implements IDisposable.Dispose If _iOwnMutex Then _mutex.ReleaseMutex() _ mutex.Dispose() End Sub End Class
Alternatively one could use a file lock to back out:
Try Dim file = Await ApplicationData.Current.LocalFolder.CreateFileAsync("__AppAndBackgroundSync.lock", CreationCollisionOption.OpenIfExists) Await file.OpenAsync(FileAccessMode.ReadWrite) '... Catch ex As UnauthorizedAccessException Throw New AppAndBackgroundConcurrencyViolationException() End Try