ImapClient is not thread safe

When I use several ImapClient instances in several instances of a class that uses a System.Timers.Timer to check a specific IMAP mailbox it runs into trouble. This code hangs itself somewhere in the Aspose.Email.dll when running in the thread created by the .NET Timer class:


try
{
using (var imapClient = CreateImapClient())
{
var imapFolderInfo = imapClient.ListFolder(Folder);
if (imapFolderInfo.TotalMessageCount > 0)
{
// Select the configured folder
imapClient.SelectFolder(Folder);

// Get a list of messages
var imapMessageInfoCollection = imapClient.ListMessages();

// Traverse the list and process the emails
foreach (var imapMessageInfo in imapMessageInfoCollection)
{
using (var mailMessage = imapClient.FetchMessage(imapMessageInfo.UniqueId))
{
if (TransferEmail(Name, mailMessage))
{
imapClient.DeleteMessage(imapMessageInfo.UniqueId);
}
}
}
}
imapClient.Disconnect();
}
}

Adding a lock fixes it:

try
{
lock (ImapClientLock)
{
using (var imapClient = CreateImapClient())
{
var imapFolderInfo = imapClient.ListFolder(Folder);
if (imapFolderInfo.TotalMessageCount > 0)
{
// Select the configured folder
imapClient.SelectFolder(Folder);

// Get a list of messages
var imapMessageInfoCollection = imapClient.ListMessages();

// Traverse the list and process the emails
foreach (var imapMessageInfo in imapMessageInfoCollection)
{
using (var mailMessage = imapClient.FetchMessage(imapMessageInfo.UniqueId))
{
if (TransferEmail(Name, mailMessage))
{
imapClient.DeleteMessage(imapMessageInfo.UniqueId);
}
}
}
}
imapClient.Disconnect();
}
}
}

When it hangs, it keeps producing one or more of these exceptions (shown in the debugger output window) per second:
A first chance exception of type ‘System.IO.IOException’ occurred in Aspose.Email.dll
A first chance exception of type ‘System.IO.IOException’ occurred in System.dll
A first chance exception of type ‘System.IO.IOException’ occurred in Aspose.Email.dll
A first chance exception of type ‘System.IO.IOException’ occurred in System.dll
A first chance exception of type ‘System.IO.IOException’ occurred in Aspose.Email.dll
A first chance exception of type ‘System.IO.IOException’ occurred in System.dll
A first chance exception of type ‘System.IO.IOException’ occurred in Aspose.Email.dll
A first chance exception of type ‘System.IO.IOException’ occurred in System.dll
A first chance exception of type ‘System.IO.IOException’ occurred in Aspose.Email.dll
A first chance exception of type ‘System.IO.IOException’ occurred in System.dll
A first chance exception of type ‘System.IO.IOException’ occurred in Aspose.Email.dll
A first chance exception of type ‘System.IO.IOException’ occurred in System.dll

But it never returns to my TimerElapsed handler.

Adding a lock like this fixes my problem, but introduces the problem of ImapClients waiting for each other to finish. Please advise.

Hi Eric,


Thank you for posting your inquiry.

With respect to the ImapClient class, the Public static (Shared in Visual Basic) members of this type are safe
for multi-threaded operations, however, instance members are not guaranteed to be
thread-safe. Can you please share your sample runnable code with us that we could use to investigate if there is some other issue underneath this problem? We shall look into this and assist you further.

Hi Muhammad,


I tried to reproduce the problem outside my application, but until now without success. I takes my application hours and thousands of mails before this threading race condition is produced, so it’s not easy to put it in a few lines of code. I will give it another try when I have more time.

kind regards,
Eric van Doorn

Hi Eric,


We have tried to reproduce this issue at our end by creating multiple threads and fetching messages via ImapClient from an Exchange and a Gmail server. Perhaps, the sample data is not that much due to which we are not able to reproduce the problem at our end. Please share your feedback with us once you have further information to share with us about this issue.

Hi Muhammad,


It seems my earlier conclusion that placing a lock would solve it was incorrect. PLacing a lock() around the usage of the ImapClient only makes it less likely that the problem occurs, but it still happens. So, the problem is the same: After a few hours of fetching and sending mail, my application hangs somewhere in a thread that was spawn by Aspose.Email.dll. I’m using a Lotus Notes v9 Imap server.

I have some extra debugging information that might help us solve this problem.

The first 2 chance exceptions that are thrown every second are:

1.
System.IO.IOException occurred
Message: A first chance exception of type ‘System.IO.IOException’ occurred in Aspose.Email.dll
Additional information: The handshake failed due to an unexpected packet format.

Aspose.Email.dll! . () Unknown
Aspose.Email.dll! . () Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() Unknown

2.
System.IO.IOException occurred
Message: A first chance exception of type ‘System.IO.IOException’ occurred in System.dll
Additional information: The handshake failed due to an unexpected packet format.

System.dll!System.Net.Security.SslState.ValidateCreateContext(bool isServer, string targetHost, System.Security.Authentication.SslProtocols enabledSslProtocols, System.Security.Cryptography.X509Certificates.X509Certificate serverCertificate, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, bool remoteCertRequired, bool checkCertRevocationStatus, bool checkCertName) Unknown
System.dll!System.Net.Security.SslStream.AuthenticateAsClient(string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation) Unknown
Aspose.Email.dll! ​.(object = null) Unknown
Aspose.Email.dll! ​.() Unknown
Aspose.Email.dll! ​.Read(byte[] = {byte[1024]}, int = 0, int = 1024) Unknown
Aspose.Email.dll! .Read(byte[] = {byte[1024]}, int = 0, int = 1024) Unknown
Aspose.Email.dll! .Read(byte[] = {byte[1024]}, int = 0, int = 1024) Unknown
Aspose.Email.dll! .() Unknown
Aspose.Email.dll! .(bool = false) Unknown
Aspose.Email.dll! ..() Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() Unknown


The stack trace of my waiting timer thread:

mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout, bool exitContext)
Aspose.Email.dll! . (System.IAsyncResult = { })
Aspose.Email.dll! . ()
Aspose.Email.dll! .(string = “10.130.10.37”, int = 143, Aspose.Email.SecurityOptions = SSLImplicit, int = 40000)
Aspose.Email.dll! . (string = “10.130.10.37”, int = 143)
Aspose.Email.dll! . ()
Aspose.Email.dll! . ()
Aspose.Email.dll! . (string = “Inbox”)
Aspose.Email.dll!Aspose.Email.Imap.ImapClient.ListFolder(string folderName = “Inbox”)
Tjip.Process.Core.dll!Tjip.Process.Core.Connectors.ImapConnector.ProcessWaitingMessages(object sender = {System.Timers.Timer}, System.Timers.ElapsedEventArgs e = {System.Timers.ElapsedEventArgs}) Line 148
System.dll!System.Timers.Timer.MyTimerCallback(object state)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.TimerQueueTimer.CallCallback()
mscorlib.dll!System.Threading.TimerQueueTimer.Fire()
mscorlib.dll!System.Threading.TimerQueue.FireNextTimers()

It could be a coincidence but another thread I see is:

Aspose.Email.dll! .(byte[] = {byte[1024]}, int = 0, int = 0, bool = false)
Aspose.Email.dll! .( = { }, byte[] = {byte[1024]}, int = 0, int = 1024)
Aspose.Email.dll! .( = { })
Aspose.Email.dll! .()
Aspose.Email.dll! .Dispose()
Aspose.Email.dll! .( = { }, out string = null)
Aspose.Email.dll! .( = { })
Aspose.Email.dll! . (object = { }, System.EventArgs = {System.EventArgs})
Aspose.Email.dll! .Close()
Aspose.Email.dll! . ()
Aspose.Email.dll!Aspose.Email.Mail.SmtpClient.(System.Collections.IEnumerator = {Aspose.Email.Mail.MailMessage.}, = { })
Aspose.Email.dll!Aspose.Email.Mail.SmtpClient..(object = null)
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)
mscorlib.dll!System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch()

I hope this information helps, because my program is unstable at the moment. BTW, I’m still using Aspose Email 5.4. So today I will update it to 5.6 and run the test again.

Kind regards,
Eric van Doorn

Hi,


Thank you for providing the additional information. You are requested to test the scenario with the latest library and let us know if same issue is faced again. We will analyze the issue and error log and provide assistance accordingly.

Hi Muhammad,


I found out that the problem can be simply reproduced by cutting off the network connection on the clientside and reconnect it after the first chance exceptions have arrived. Version 5.4 starts a never ending exception throwing thread and version 5.6 does this as wel, but throws other exceptions.

Luckily I also found out that setting the Timeout and ConnectionTimeout properties to a value like 30000ms fixes this problem, but only for version 5.6, because version 5.4 seems to totaly ignore these properties.

Version 5.6 did bring a new, smaller problem, but I will post an other message for that. So in a way, my problem is solved, but all this leaves me a bit worried about the regression problems I see in releases of this library.

Anyway, thanks for the help.

kind regards,
Eric van Doorn

Hi Eric,


The API version 5.4.0 had known issues with the the TimeOut property that were fixed in the lateral version and that is why the version 5.6.0 works fine for the TimeOut properties. We have tested this issue with the standard Gmail account having more than 2000 emails and were not able to face any such problem.

For your additional issue you have found, please feel free to report to us via a separate thread along with your sample code and server details. We shall look into it to assist you further.