// 32feet.NET - Personal Area Networking for .NET
//
// InTheHand.Net.Widcomm.WidcommSocketExceptions
//
// Copyright (c) 2008-2009 In The Hand Ltd, All rights reserved.
// Copyright (c) 2008-2009 Alan J. McFarlane, All rights reserved.
// This source code is licensed under the In The Hand Community License - see License.txt
#if WIDCOMM_SINGLE_THREADING
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace InTheHand.Net.Bluetooth.Widcomm{
class WidcommPortSingleThreader : IDisposable
{
Queue<StCommand> m_actions = new Queue<StCommand>();
Thread m_thread;
public WidcommPortSingleThreader()
{
Thread t = new Thread(SingleThreader);
t.Name = "32feetWidcommST";
#if !NETCF //SetApartmentState
t.SetApartmentState(ApartmentState.STA);
#endif
t.IsBackground = true;
m_thread = t;
m_thread.Start();
}
#if NETCF
ManualResetEvent _netcfEvent = new ManualResetEvent(false);
#endif
internal T AddCommand<T>(T command) where T : StCommand
{
if (WidcommBtInterface.IsWidcommSingleThread
|| Thread.CurrentThread == m_thread) {
// Yikes the special thread is trying to offload some work!!
Debug.Fail("Widcomm main thread calling itself!?!");
throw new NotSupportedException("Internal error -- Widcomm main thread calling itself!?!");
}
lock (m_actions) {
m_actions.Enqueue(command);
#if NETCF
_netcfEvent.Set();
#else
Monitor.Pulse(m_actions);
#endif
}
return command;
}
void SingleThreader()
{
WidcommBtInterface.IsWidcommSingleThread = true;
#if !NETCF
while (true) {
lock (m_actions) {
while (m_actions.Count == 0) {
Monitor.Wait(m_actions);
}
StCommand cmd = m_actions.Dequeue();
if (cmd is ExitCommand) {
break;
}
cmd.Action();
// Clear the command, otherwise its port reference keeps the port,
// which keeps the WRCStream, thus its finalizer isn't called
// which means this thread says alive, which keeps the command...
cmd = null;
}
}//while
#else
while (true) {
int count;
lock (m_actions) {
count = m_actions.Count;
}// Like Wait() must leave the lock to let the writer in!
if (count == 0) {
_netcfEvent.WaitOne();
}
lock (m_actions) {
_netcfEvent.Reset();
count = m_actions.Count;
if (count == 0) {
Debug.Fail("Somebody else is reading from the queue!!!");
continue;
}
StCommand cmd = m_actions.Dequeue();
if (cmd is ExitCommand) {
break;
}
cmd.Action();
// Clear the command, otherwise its port reference keeps the port,
// which keeps the WRCStream, thus its finalizer isn't called
// which means this thread says alive, which keeps the command...
cmd = null;
}
}//while
#endif
}
internal abstract class StCommand
{
// TODO Pool ManualResetEvent instances in WidcommPortSingleThreader.
ManualResetEvent _complete = new ManualResetEvent(false);
Exception _error;
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "We rethrow it on the caller.")]
internal void Action()
{
try {
ActionCore();
} catch (Exception ex) {
_error = ex;
} finally {
_complete.Set();
}
}
protected abstract void ActionCore();
public Exception Error { get { return _error; } }
internal void WaitCompletion()
{
_complete.WaitOne();
_complete.Close();
if (Error != null)
throw new System.Reflection.TargetInvocationException(Error);
}
}
internal sealed class ExitCommand : StCommand
{
protected override void ActionCore()
{ //NOP
}
}
internal abstract class PortCommand : StCommand
{
IRfcommPort _port;
protected IRfcommPort Port { get { return _port; } }
public PortCommand(IRfcommPort port)
{
_port = port;
}
}
internal sealed class PortWriteCommand : PortCommand
{
byte[] _data;
ushort _lenToWrite, _lenWritten;
PORT_RETURN_CODE _result;
public PortWriteCommand(byte[] data, ushort lenToWrite, IRfcommPort port)
: base(port)
{
_data = data;
_lenToWrite = lenToWrite;
}
protected override void ActionCore()
{
_result = Port.Write(_data, _lenToWrite, out _lenWritten);
}
private new void WaitCompletion()
{
throw new NotSupportedException("Use WaitCompletion(out ushort lenWritten).");
}
internal PORT_RETURN_CODE WaitCompletion(out ushort lenWritten)
{
base.WaitCompletion();
lenWritten = _lenWritten;
return _result;
}
}
internal sealed class PortCreateCommand : PortCommand
{
public PortCreateCommand(IRfcommPort port)
: base(port)
{
}
protected override void ActionCore()
{
Port.Create();
}
}
internal sealed class OpenServerCommand : PortCommand
{
byte _scn;
PORT_RETURN_CODE _result;
public OpenServerCommand(byte scn, IRfcommPort port)
: base(port)
{
_scn = scn;
}
public new PORT_RETURN_CODE WaitCompletion()
{
base.WaitCompletion();
return _result;
}
protected override void ActionCore()
{
_result = Port.OpenServer(_scn);
}
}
internal sealed class OpenClientCommand : PortCommand
{
byte _scn;
byte[] _address;
PORT_RETURN_CODE _result;
public OpenClientCommand(byte scn, byte[] address, IRfcommPort port)
: base(port)
{
_address = address;
_scn = scn;
}
public new PORT_RETURN_CODE WaitCompletion()
{
base.WaitCompletion();
return _result;
}
protected override void ActionCore()
{
_result = Port.OpenClient(_scn, _address);
}
}
internal sealed class PortCloseCommand : PortCommand
{
public PortCloseCommand(IRfcommPort port)
: base(port)
{
}
protected override void ActionCore()
{
Port.Close();
}
}
internal sealed class MiscNoReturnCommand : StCommand
{
ThreadStart _dlgt;
public MiscNoReturnCommand(ThreadStart dlgt)
{
_dlgt = dlgt;
}
protected override void ActionCore()
{
_dlgt();
}
}
internal delegate TResult Func<TResult>();
internal sealed class MiscReturnCommand<TResult> : StCommand
{
Func<TResult> _dlgt;
TResult _result;
public MiscReturnCommand(Func<TResult> dlgt)
{
_dlgt = dlgt;
}
protected override void ActionCore()
{
_result = _dlgt();
}
public new TResult WaitCompletion()
{
base.WaitCompletion();
return _result;
}
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
// We can safely do this as Queue is not finalizable and our reference
// keeps it alive. The only danger could be if some blocked thread is
// holding the lock BUT THAT'S ONLY US!
AddCommand(new ExitCommand());
}
#endregion
}
}
#endif
|