First I want to say that I'm sorry if this is a bad question. I had a question about this issue earlier this morning and could fix some issues, but I'm still getting a deadlock and I'm struggling with this the whole morning.
I'm trying to request chats from a server. In UWP, I need to do this asynchronously. My approach causes a deadlock at about every 5th try I'm running the following code.
The first await happens in OnNavigatedTo
:
public async void OnNavigatedTo(NavigationParameters parameters)
{
IsLoadMoreItemsPossible = true;
if (_first)
{
_first = false;
await LoadDataTaskAsync();
}
}
Which calls LoadDataTaskAsync
:
private async Task LoadDataTaskAsync()
{
if (IsBusy)
{
return;
}
IsLoadMoreItemsPossible = false;
IsBusy = true;
IsFree = false;
if (!CommonUtils.isInternetAvailable())
{
// load from local
}
await _remoteDataManager.requestChats();
}
_remoteDataManager.requestChats()
gets the right method for the current platform:
public async Task requestChats()
{
await _websocketManager.requestChats();
}
requestChats
in my UWP WebsocketManager looks like this:
public async Task requestChats()
{
dataWriter.WriteString(WebsocketRequestFactory.Create(SocketEventsEnm.GET_CHATS));
await SendData(dataWriter);
}
And finally SendData
, where the exception happens:
private async Task SendData(DataWriter dataWriter)
{
try
{
_evaLogger.Info("Trying to send data...");
await dataWriter.StoreAsync(); // This is where the exception occurs
_evaLogger.Info("Data was sent");
}
catch (Exception e)
{
_evaLogger.Error(e.Message, e);
}
}
_evaLogger
writes into a logfile, which logs this when the deadlock happens:
12.12.2016 10:38:08 - Info: Websocket Opened
12.12.2016 10:38:08 - Info: Trying to send data...
12.12.2016 10:38:08 - Error: A method was called at an unexpected time. (Exception from HRESULT: 0x8000000E)
12.12.2016 10:38:09 - Info: Trying to send data...
12.12.2016 10:38:09 - Info: Data was sent
Sorry for the long question, I tried to only show the relevant code for the deadlock.
I tried Azhar Khorasany's suggestion in the comments to use semaphore while accessing the stream:
public class AsyncLock
{
public readonly AsyncSemaphore m_semaphore;
private readonly Task<Releaser> m_releaser;
public AsyncLock()
{
m_semaphore = new AsyncSemaphore(1);
m_releaser = Task.FromResult(new Releaser(this));
}
public Task<Releaser> LockAsync()
{
var wait = m_semaphore.WaitAsync();
return wait.IsCompleted ?
m_releaser :
wait.ContinueWith((_, state) => new Releaser((AsyncLock)state),
this, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
}
New requestChats
method:
private static readonly AsyncLock m_lock = new AsyncLock();
public async Task requestChats()
{
using (await m_lock.LockAsync())
{
using (DataWriter dataWriter = new DataWriter(messageWebsocket.OutputStream))
{
_evaLogger.Info("dataWriter initialized requestChats.");
dataWriter.WriteString(WebsocketRequestFactory.Create(SocketEventsEnm.GET_CHATS));
await SendData(dataWriter, "requestChats");
}
}
_evaLogger.Info("dataWriter disposed requestChats.");
}
Unfortunately, it didn't solve the deadlock issue:
13.12.2016 07:55:31 - Info: Websocket Opened
13.12.2016 07:55:32 - Info: dataWriter initialized requestChats.
13.12.2016 07:55:32 - Info: Trying to send data... requestChats
13.12.2016 07:55:32 - Error: A method was called at an unexpected time. (Exception from HRESULT: 0x8000000E)
13.12.2016 07:55:32 - Info: dataWriter disposed requestChats.
13.12.2016 07:55:32 - Info: Trying to send data... requestContacts
13.12.2016 07:55:32 - Info: dataWriter initialized requestContacts.
There are several potential problems in this code because it's clearly not written with parallel threads in mind. What your immediate problem is, as far as I can tell from your code, is that dataWriter
shared among the threads. Imagine that one thread may be executing dataWriter.WriteString(...)
while another thread is executing dataWriter.StoreAsync()
and you see the problem. You need to read up about parallel programming and have a full understanding of how you code executes. Having flags like IsBusy
may "work" in a one-thread-program it is bound to cause you headaches as soon as you have multiple threads.
Quick advice to your code:
Remove your global declaradion of dataWriter
and change the following code:
public async Task requestChats()
{
/* declare dataWriter here so it becomes an instance variable*/
dataWriter.WriteString(WebsocketRequestFactory.Create(SocketEventsEnm.GET_CHATS));
await SendData(dataWriter);
User contributions licensed under CC BY-SA 3.0