Forum Xamarin.Android
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

How to force continuation to execute on original thread?

JordanWeber-FlinkJordanWeber-Flink USUniversity ✭✭
edited September 2017 in Xamarin.Android

Hi,

I have a method that interacts with a file system acting as a data store. Because of this, I have a ConcurrentCollection of ReaderWriterLockSlims - one for each path. The idea is to enter a read lock, read the file, and then exit the read lock. This is because the same lock could be used during a write on a separate thread.

The problem I'm having is that after awaiting the actual bytes being streamed from disk, the continuation sometimes happens on a different thread than I started on. Since the original thread holds the read lock, attempting to release it (on a different thread in the continuation code) throws an exception.

None of this is on the UI thread. The call from UI thread invokes execution with Task.Run to offload the file read to a threadpool thread. Also this implementation works flawlessly on iOS.

Code:

WriteLog(string.Format("Entering Read Lock for path '{0}' on thread [{1}]", filePath,
                       Thread.CurrentThread.ManagedThreadId));

fileLock.EnterReadLock();
try
{
    var file = new File(filePath);
    var stream = new FileInputStream(file);

    data = new byte[file.Length()];
    await stream.ReadAsync(data);

    stream.Close();
}
catch (Exception readEx)
{
    WriteLog(string.Format("Encountered exception in ReadFileAsync for path '{0}' on thread [{1}]: {2}",
                           filePath, Thread.CurrentThread.ManagedThreadId, readEx));
    throw readEx;
}
finally
{
    WriteLog(string.Format("Exiting Read Lock for path '{0}' on thread [{1}]. IsReadLockHeld: {2}", filePath,
                       Thread.CurrentThread.ManagedThreadId, fileLock.IsReadLockHeld));

    fileLock.ExitReadLock();
}

Sample output:

    09-01 15:15:51.697: I/mono-stdout(8631): [LW4ANDROID][9/1/2017 15:15:51.696] : [GDFileSystemHelper] : Entering Read Lock for path '/JSON/fbmobile.jabber' on thread [7]
    09-01 15:15:51.738: I/mono-stdout(8631): [LW4ANDROID][9/1/2017 15:15:51.738] : [GDFileSystemHelper] : Exiting Read Lock for path '/JSON/fbmobile.jabber' on thread [5]. IsReadLockHeld: False
    09-01 15:15:51.775: I/mono-stdout(8631): [LW4ANDROID][9/1/2017 15:15:51.775] : [GDFileSystemHelper] : ERROR in ReadFileAsync: System.Threading.SynchronizationLockException: The read lock is being released without being held.[NEWLINE]  at System.Threading.ReaderWriterLockSlim.ExitReadLock () [0x0002c] in <b5bd9d990a0b4733885e90ca5ec6c0fb>:0 [NEWLINE]  at LW4.Android.GoodDynamics.GDFileSystemHelper+<ReadFileAsync>d__6.MoveNext () [0x001e7] 

My question is, how do I ensure the continuation occurs on the same threadpool thread which initially entered the readlock prior to awaiting the stream read? I have to be on the same thread in order to successfully release the lock.

Thanks!

Sign In or Register to comment.