/// <summary> /// The worker thread entry point for polling the MailSlot. Threads will queue until a Mutex becomes /// available for a particular channel. /// </summary> /// <param name="state"></param> private void MailSlotChecker(object state) { MailSlotThreadInfo info = (MailSlotThreadInfo)state; bool isOwner = false; string mutextKey = string.Concat(mutexNetworkDispatcher, ".", info.ChannelName); using (Mutex mutex = new Mutex(true, mutextKey, out isOwner)) { // if doesn't own mutex then wait if (!isOwner) { try { mutex.WaitOne(); isOwner = true; } catch (ThreadInterruptedException) { } // shut down thread catch (AbandonedMutexException) { // This thread is now the owner isOwner = true; } } if (isOwner) { // enter message read loop ProcessMessages(info); // if this thread owns mutex then release it mutex.ReleaseMutex(); } } }
/// <summary> /// Helper method starts up a new listener thread for a given channel. /// </summary> /// <param name="channelName">The channel name.</param> /// <returns></returns> private MailSlotThreadInfo StartNewThread(string channelName) { // create and start the thread at low priority Thread thread = new Thread(new ParameterizedThreadStart(MailSlotChecker)); thread.Priority = ThreadPriority.Lowest; thread.IsBackground = true; MailSlotThreadInfo info = new MailSlotThreadInfo(channelName, thread); thread.Start(info); return(info); }
/// <summary> /// This helper method puts the thread into a read message /// loop. /// </summary> /// <param name="info"></param> private void ProcessMessages(MailSlotThreadInfo info) { int bytesToRead = 512, maxMessageSize = 0, messageCount = 0, readTimeout = 0; // for as long as thread is alive and the channel is registered then act as the MailSlot reader while (!disposed && activeThreads.ContainsKey(info.ChannelName)) { // if the channel mailslot is not open try to open it if (!info.HasValidFileHandle) { info.FileHandle = Native.CreateMailslot(string.Concat(mailSlotIdentifier, info.ChannelName), 0, Native.MAILSLOT_WAIT_FOREVER, IntPtr.Zero); } // if there is a valid read handle try to read messages if (info.HasValidFileHandle) { byte[] buffer = new byte[bytesToRead]; uint bytesRead = 0; // this blocks until a message is received, the message cannot be buffered with overlap structure // so the bytes array must be larger than the current item in order to read the complete message while (Native.ReadFile(info.FileHandle, buffer, (uint)bytesToRead, out bytesRead, IntPtr.Zero)) { ProcessMessage(buffer, bytesRead); // reset buffer size bytesToRead = 512; buffer = new byte[bytesToRead]; } int code = Marshal.GetLastWin32Error(); switch (code) { case Native.ERROR_INSUFFICIENT_BUFFER: // insufficent buffer size, we need to the increase buffer size to read the current item Native.GetMailslotInfo(info.FileHandle, ref maxMessageSize, ref bytesToRead, ref messageCount, ref readTimeout); break; case Native.ERROR_INVALID_HANDLE: // close handle if invalid if (info.HasValidFileHandle) { Native.CloseHandle(info.FileHandle); info.FileHandle = IntPtr.Zero; } break; case Native.ERROR_HANDLE_EOF: // read handle has been closed info.FileHandle = IntPtr.Zero; break; } } } }
/// <summary> /// Unregisters the current instance from the given channel. No more messages will be /// processed, and another process will be allowed to obtain the listener lock. /// </summary> /// <param name="channelName"></param> public void UnRegisterChannel(string channelName) { MailSlotThreadInfo info = null; if (activeThreads.TryGetValue(channelName, out info)) { // only lock if changing lock (lockObj) { // double check has not been modified before lock if (activeThreads.TryGetValue(channelName, out info)) { // removing form hash shuts down the thread loop activeThreads.Remove(channelName); } } if (info != null) { // close any read handles if (info.HasValidFileHandle) { Native.CloseHandle(info.FileHandle); } if (info.Thread.IsAlive) { // interrupt incase of asleep thread info.Thread.Interrupt(); } if (info.Thread.IsAlive) { // attempt to join thread if (!info.Thread.Join(500)) { // if no response within timeout, force abort //info.Thread.Abort(); //Do nothing } } } } }
/// <summary> /// Helper method starts up a new listener thread for a given channel. /// </summary> /// <param name="channelName">The channel name.</param> /// <returns></returns> private MailSlotThreadInfo StartNewThread(string channelName) { // create and start the thread at low priority Thread thread = new Thread(new ParameterizedThreadStart(MailSlotChecker)); thread.Priority = ThreadPriority.Lowest; thread.IsBackground = true; MailSlotThreadInfo info = new MailSlotThreadInfo(channelName, thread); thread.Start(info); return info; }