// 32feet.NET - Personal Area Networking for .NET
//
// InTheHand.Net.Widcomm.WidcommSocketExceptions
//
// Copyright (c) 2008-2010 In The Hand Ltd, All rights reserved.
// Copyright (c) 2008-2010 Alan J. McFarlane, All rights reserved.
// This source code is licensed under the In The Hand Community License - see License.txt
//define TRACE_TO_FILE
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;
//
using WriteAsyncResultInTheHand.Net.AsyncNoResultInTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.BeginReadParameters;
using ReadAsyncResultInTheHand.Net.AsyncResultInTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.BeginReadParameters;
using System.Diagnostics.CodeAnalysis;
namespace InTheHand.Net.Bluetooth.Widcomm{
/*
* 1. Rename WidcommRfcommStream to WidcommRfcommStreamBase
* 2. Recompile including Tests!
* 3. Make WidcommRfcommStreamBase abstract, and add the concrete "sealed" version.
* 4. Compile and fix the references needed to the concrete class.
internal sealed class WidcommRfcommStream : WidcommRfcommStreamBase
{
internal WidcommRfcommStream(IRfcommPort port, IRfCommIf rfCommIf, WidcommBluetoothFactoryBase factory)
: base(port, rfCommIf, factory)
{
}
[Obsolete("factory!!")]
internal WidcommRfcommStream(IRfcommPort port, IRfCommIf rfCommIf)
: base(port, rfCommIf)
{
}
}
*/
//internal abstract class WidcommRfcommStreamBase : Stream
internal class WidcommRfcommStream : Stream
{
const string ObjectDisposedException_ObjectName = "Network";
internal const string WrappingIOExceptionMessage = "IOError on socket.";
//
WidcommBluetoothFactoryBase m_factory;
IRfcommPort m_port;
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Is used in the DEBUG build.")]
IRfcommPort m_origPort;//DEBUG
enum State
{
New,
//Connecting,
Connected,
PeerDidClose,
Closed
};
State m_state_;
// Socket.get_Connected reports false only after a (failed) user IO operation,
// so we should do the same. So m_state reports the live state, and this
// property reports the user-visible connected state.
bool m_connected;
object lockKey = new object();
BluetoothAddress m_remoteAddress;
// Connect
string m_passcodeToTry;
BluetoothAddress m_addressToConnect;
byte m_ocScn;
AsyncResultNoResult m_arConnect, m_arConnectCompleted;
WidcommRfcommInterface m_RfCommIf;
// Receive
int m_receivedDataFirstBlockOffset;
Queue<byte[]> m_receivedData = new Queue<byte[]>();
int m_amountInReadBuffers;
Queue<ReadAsyncResult> m_arReceiveList = new Queue<AsyncResult<int, BeginReadParameters>>();
// Write
Queue<WriteAsyncResult> m_arWriteQueue = new Queue<WriteAsyncResult>();
ManualResetEvent m_writeEmptied;
// Single threading
#if WIDCOMM_SINGLE_THREADING
WidcommPortSingleThreader _singleThreader;
#endif
//
#if DEBUG && ! PocketPC
string m_creationStackTrace;
#endif
//--------
[SuppressMessage("Microsoft.Usage", "CA1816:CallGCSuppressFinalizeCorrectly",
Justification = "If we don't create the native object, there's no need for finalization")]
internal WidcommRfcommStream(IRfcommPort port, IRfCommIf rfCommIf, WidcommBluetoothFactoryBase factory)
{
m_factory = factory;
#if DEBUG
#if TRACE_TO_FILE
//m_sbLogging = new StringBuilder();
//StringWriter wtr = new StringWriter(m_sbLogging, System.Globalization.CultureInfo.InvariantCulture);
TextWriter wtr = File.CreateText("RfcommStream.txt");
m_logWtr = TextWriter.Synchronized(wtr);
#else
m_log = new string[LogNumberOfLines];
#endif
#endif
#if DEBUG && ! PocketPC
m_creationStackTrace = Environment.StackTrace;
#endif
//----
#if WIDCOMM_SINGLE_THREADING
_singleThreader = factory.GetSingleThreader();
#endif
bool created = false;
try {
SetPort(port);
if (rfCommIf != null) {
m_RfCommIf = new WidcommRfcommInterface(rfCommIf);
rfCommIf.Create();
}
created = true;
} finally {
if (!created) { GC.SuppressFinalize(this); }
}
}
private void SetPort(IRfcommPort port)
{
if (this.m_port != null) {
this.m_origPort = this.m_port;
}
this.m_port = port;
port.SetParentStream(this);
DoPortCreate(port);
//
m_debugId = null; // force re-create
if (m_origPortId == null) {
m_origPortId = port.DebugId;
} else {
m_curPortId = port.DebugId;
}
}
private void DoPortCreate(IRfcommPort port)
{
#if !WIDCOMM_SINGLE_THREADING
port.Create();
#else
if (_singleThreader != null) {
WidcommPortSingleThreader.PortCreateCommand cmd = AddCommand(
new WidcommPortSingleThreader.PortCreateCommand(port));
cmd.WaitCompletion();
#if !NETCF
} else if (WidcommBtInterface.IsWidcommCallbackThread) {
ThreadStart dlgt = port.Create;
IAsyncResult ar = dlgt.BeginInvoke(null, null);
dlgt.EndInvoke(ar);
#endif
} else {
port.Create();
}
#endif
}
//----------------
#if DEBUG
#if TRACE_TO_FILE
readonly StringBuilder m_sbLogging;
readonly TextWriter m_logWtr;
#else
const int LogNumberOfLines = 64;
string[] m_log;
int m_logIdx;
#endif
#endif
string m_debugId, m_origPortId, m_curPortId;
[Conditional("DEBUG")]
void Log(string message)
{
WidcommUtils.Trace_WriteLine(message); // for PC command-line etc
#if DEBUG
#if TRACE_TO_FILE
m_logWtr.WriteLine(message); // for NETCF+debugger
#else
int idx0, idx;
idx = idx0 = Interlocked.Increment(ref m_logIdx);
uint ui = unchecked((uint)idx);
ui %= (uint)m_log.Length;
idx = checked((int)ui);
m_log[idx] = "" + idx0 + "-- " + message; // for NETCF+debugger
#endif
#endif
}
[Conditional("DEBUG")]
void LogFormat(string format, params object[] args)
{
Log(string.Format(System.Globalization.CultureInfo.InvariantCulture,
format, args));
}
internal string DebugId
{
get
{
if (m_debugId == null) {
string id = m_origPortId;
if (m_curPortId != null) {
id += "->" + m_curPortId;
}
m_debugId = id;
}
return m_debugId;
}
}
//----------------
private LingerOption m_lingerState = new LingerOption(true, 0);
internal LingerOption LingerState
{
get { return m_lingerState; }
set
{
if (!value.Enabled)
throw new ArgumentException("No support for non-Linger mode in Widcomm wrapper.");
m_lingerState = value;
}
}
//----------------
/// <summary>
/// Fails if state is not Connected.
/// </summary>
private void EnsureOpenForWrite()
{
EnsureOpenForRead();
if (m_state_ == State.Connected)
return;
//
if (m_state_ == State.PeerDidClose) { // Extra negative condition to EnsureOpenForRead
SetAsDisconnected();
throw new IOException(WrappingIOExceptionMessage,
WidcommSocketExceptions.ConnectionIsPeerClosed());
}
Debug.Fail("Unknown state: " + m_state_);
throw new InvalidOperationException("Not connected.");
}
/// <summary>
/// Fails if state is not Connected or PeerDidClose.
/// </summary>
private void EnsureOpenForRead()
{
if (m_state_ == State.Closed)
throw new IOException(WrappingIOExceptionMessage,
new ObjectDisposedException(ObjectDisposedException_ObjectName));
if (m_state_ == State.New)
throw new InvalidOperationException("Not connected.");
if (!m_connected)
throw new IOException(WrappingIOExceptionMessage,
WidcommSocketExceptions.ConnectionIsPeerClosed());
if (m_state_ == State.Connected || m_state_ == State.PeerDidClose)
return;
//
Debug.Fail("Unknown state: " + m_state_);
throw new InvalidOperationException("Not connected.");
}
/// <summary>
/// Used by Client, note from MSDN Socket.Connected:
/// "Gets a value that indicates whether a Socket is connected to a remote host as of the last Send or Receive operation."
/// </summary>
/// -
/// <remarks>
/// <para>From MSDN <see cref="P:System.Net.Sockets.Socket.Connected"/>:
/// "Gets a value that indicates whether a Socket is connected to a remote host as of the last Send or Receive operation."
/// From MSDN <see cref="P:System.Net.Sockets.TcpClient.Connected"/>:
/// "true if the Client socket was connected to a remote resource as of the most recent operation; otherwise, false."
/// </para>
/// </remarks>
internal bool Connected
{
get { return m_connected; }
}
internal bool LiveConnected
{
get { return m_state_ == State.Connected; }
}
void SetAsDisconnected()
{
Debug.Assert(m_connected, "already not m_connected");
SetAsDisconnectedFromDisposal();
}
void SetAsDisconnectedFromDisposal()
{
Debug.Assert(m_state_ == State.Closed || m_state_ == State.PeerDidClose, "m_state open!");
m_connected = false;
}
private void ImplicitPeerClose()
{
m_state_ = State.PeerDidClose;
}
protected override void Dispose(bool disposing)
{
LogFormat("Dispose({0})", disposing);
if (disposing) {
/*DEBUG*/
} else {
/*DEBUG*/
}
//
State m_prevState = m_state_;
ReadAsyncResult[] readsToAbort;
WriteAsyncResult[] writesToAbort;
AsyncResultNoResult connectToAbort = null;
Exception exToThrow = null;
bool dbgExpectEmptyWriteQueue = false;
try {
if (m_state_ != State.Closed) {
try {
m_state_ = State.Closed;
SetAsDisconnectedFromDisposal();
DisposeLinger(disposing, out exToThrow, out dbgExpectEmptyWriteQueue);
lock (lockKey) {
Log("Dispose: in lock");
if (m_prevState == State.PeerDidClose) { // Don't know whether its ok to call twice...
// COVERAGE
}
DoPortClose(disposing);
ClearAllReadRequests_inLock(out readsToAbort);
ClearAllWriteRequests_inLock(out writesToAbort);
if (m_arConnect != null) {
connectToAbort = m_arConnect;
m_arConnectCompleted = m_arConnect;
m_arConnect = null;
}
ManualResetEvent wee = m_writeEmptied;
if (disposing && wee != null) {
m_writeEmptied = null;
wee.Close();
}
}//lock
// Do we need to abort these when being Finalized? XXXXXXXXXXXXXXXXXX
// TODO SetAsC on same thread (only on user/finalizer thread though).
Abort(readsToAbort);
Abort(writesToAbort);
if (connectToAbort != null) {
connectToAbort.SetAsCompleted(new ObjectDisposedException(
ObjectDisposedException_ObjectName), false);
}
} finally {
if (m_RfCommIf != null)
m_RfCommIf.Dispose();
//
// Could there be a race of an event arriving just as we call Close?
// And thus the callback called when the object are being deleted.
// So do we need to delay etc before calling this??
DoPortDestroy(disposing);
}
}
} finally {
base.Dispose(disposing);
#if DEBUG
#if TRACE_TO_FILE
m_logWtr.Flush(); // let it just finalize -- in case we write after this
#endif
#endif
}
Debug.Assert(!dbgExpectEmptyWriteQueue || m_arWriteQueue.Count == 0,
"Told to expect no remaining Send data, but was some...");
if (exToThrow != null) {
throw exToThrow;
}
}
private void DoPortClose(bool disposing)
{
#if !WIDCOMM_SINGLE_THREADING
m_port.Close();
#else
if (_singleThreader != null) {
WidcommPortSingleThreader.PortCloseCommand cmd = AddCommand(
new WidcommPortSingleThreader.PortCloseCommand(m_port));
cmd.WaitCompletion();
} else {
m_port.Close();
}
#endif
}
private void DoPortDestroy(bool disposing)
{
#if !WIDCOMM_SINGLE_THREADING
m_port.Destroy();
#else
if (_singleThreader != null) {
ThreadStart dlgt = delegate { m_port.Destroy(); };
WidcommPortSingleThreader.MiscNoReturnCommand cmd = AddCommand(
new WidcommPortSingleThreader.MiscNoReturnCommand(dlgt));
cmd.WaitCompletion();
} else {
m_port.Destroy();
}
#endif
}
private void DisposeLinger(bool disposing, out Exception exToThrow, out bool dbgExpectEmptyWriteQueue)
{
if (disposing) {
if (!LingerState.Enabled) {
throw new NotSupportedException("Background non-linger Close not supported.");
} else {
if (LingerState.LingerTime == 0) {
// Hard close, so just proceed and abort the writes.
dbgExpectEmptyWriteQueue = true;
exToThrow = null;
} else {
// Wait for the linger-time to see if the writes are processed.
lock (lockKey) {
if (m_arWriteQueue.Count == 0) {
// NOP
} else {
m_writeEmptied = new ManualResetEvent(false);
}
}
if (m_writeEmptied == null) {
Debug.Assert(m_arWriteQueue.Count == 0);
exToThrow = null;
dbgExpectEmptyWriteQueue = true;
} else {
const int MillisPerSeconds = 1000;
int msTime = LingerState.LingerTime * MillisPerSeconds;
bool emptied = m_writeEmptied.WaitOne(msTime, false);
if (emptied) {
Debug.Assert(m_arWriteQueue.Count == 0);
exToThrow = null;
dbgExpectEmptyWriteQueue = true;
} else {
Debug.Assert(m_arWriteQueue.Count != 0);
exToThrow = new Exception("Linger time-out FIXME");
dbgExpectEmptyWriteQueue = false;
}
}
}
}
} else {
exToThrow = null;
dbgExpectEmptyWriteQueue = false;
}
}
~WidcommRfcommStream()
{
Dispose(false);
}
private static void MemoryBarrier()
{
#if ! PocketPC
Thread.MemoryBarrier();
#endif
}
internal IAsyncResult BeginConnect(BluetoothEndPoint bep, string pin,
AsyncCallback asyncCallback, Object state)
{
if (bep.Port == 0 || bep.Port == -1)
throw new ArgumentException("Channel Number must be set in the BluetoothEndPoint, i.e. SDP lookup done.", "bep");
if (bep.Port < BluetoothEndPoint.MinScn || bep.Port > BluetoothEndPoint.MaxScn)
throw new ArgumentOutOfRangeException("bep", "Channel Number must be in the range 1 to 30.");
byte scn = (byte)bep.Port;
m_passcodeToTry = pin;
//
lock (lockKey) {
if (m_state_ == State.Closed)
throw new ObjectDisposedException(ObjectDisposedException_ObjectName);
if (m_state_ != State.New) {
const int SocketError_IsConnected = 10056;
throw new SocketException(SocketError_IsConnected); // [Begin]Connect called twice
}
if (m_arConnect != null)
throw new InvalidOperationException("Another Connect operation is already in progress.");
Debug.Assert(m_arConnectCompleted == null);
AsyncResultNoResult ar = new AsyncResultNoResult(asyncCallback, state);
//
m_RfCommIf.SetScnForPeerServer(bep.Service, scn);
m_RfCommIf.SetSecurityLevelClient(BTM_SEC.NONE);
// Initiate the connect attempt, and then wait for the CONNECTED Event.
m_ocScn = scn;
m_addressToConnect = bep.Address;
PORT_RETURN_CODE ret = DoOpenClient(m_ocScn, WidcommUtils.FromBluetoothAddress(m_addressToConnect));
WidcommUtils.Trace_WriteLine("OpenClient ret: {0}=0x{0:X}", ret);
if (ret != PORT_RETURN_CODE.SUCCESS)
throw WidcommSocketExceptions.Create(ret, "OpenClient");
Debug.Assert(ar != null);
m_arConnect = ar;
return ar;
}
}
PORT_RETURN_CODE DoOpenClient(byte scn, byte[] address)
{
PORT_RETURN_CODE ret;
#if !WIDCOMM_SINGLE_THREADING
ret = m_port.OpenClient(scn, address);
#else
if (_singleThreader != null) {
WidcommPortSingleThreader.OpenClientCommand cmd = AddCommand(
new WidcommPortSingleThreader.OpenClientCommand(scn, address, m_port));
ret = cmd.WaitCompletion();
#if !NETCF
} else if (WidcommBtInterface.IsWidcommCallbackThread) {
Func<byte, byte[], PORT_RETURN_CODE> dlgt = m_port.OpenClient;
IAsyncResult ar = dlgt.BeginInvoke(scn, address, null, null);
ret = dlgt.EndInvoke(ar);
#endif
} else {
ret = m_port.OpenClient(scn, address);
}
#endif
return ret;
}
internal void EndConnect(IAsyncResult ar)
{
// (Can't lock here as that would block the callback methods).
if (ar != m_arConnect && ar != m_arConnectCompleted)
throw new InvalidOperationException("Unknown IAsyncResult.");
try {
((AsyncResultNoResult)ar).EndInvoke();
} finally {
MemoryBarrier();
Debug.Assert(m_arConnect == null);
m_arConnectCompleted = null;
}
}
//----
internal IAsyncResult BeginAccept(BluetoothEndPoint bep, string serviceName, AsyncCallback asyncCallback, Object state)
{
if (bep == null)
throw new ArgumentNullException("bep");
if (bep.Port == 0 || bep.Port == -1)
throw new ArgumentException("Channel Number must be set in the BluetoothEndPoint, i.e. SDP lookup done.", "bep");
if (bep.Port < BluetoothEndPoint.MinScn || bep.Port > BluetoothEndPoint.MaxScn)
throw new ArgumentOutOfRangeException("bep", "Channel Number must be in the range 1 to 30.");
byte scn = (byte)bep.Port;
//
lock (lockKey) {
if (m_state_ == State.Closed)
throw new ObjectDisposedException(ObjectDisposedException_ObjectName);
if (m_state_ != State.New) {
const int SocketError_IsConnected = 10056;
throw new SocketException(SocketError_IsConnected); // [Begin]Connect/Accept called twice
}
//
if (m_arConnect != null)
throw new InvalidOperationException("Another Connect operation is already in progress.");
Debug.Assert(m_arConnectCompleted == null);
AsyncResultNoResult ar = new AsyncResultNoResult(asyncCallback, state);
//
// Initiate the connect attempt, and then wait for the CONNECTED Event.
PORT_RETURN_CODE ret = DoOpenServer(scn);
WidcommUtils.Trace_WriteLine("OpenServer ret: {0}=0x{0:X}", ret);
if (ret != PORT_RETURN_CODE.SUCCESS)
throw WidcommSocketExceptions.Create(ret, "OpenServer");
Debug.Assert(ar != null);
m_arConnect = ar;
return ar;
}
}
private PORT_RETURN_CODE DoOpenServer(byte scn)
{
PORT_RETURN_CODE ret;
#if !WIDCOMM_SINGLE_THREADING
ret = m_port.OpenServer(scn);
#else
if (_singleThreader != null) {
WidcommPortSingleThreader.OpenServerCommand cmd = AddCommand(
new WidcommPortSingleThreader.OpenServerCommand(scn, m_port));
ret = cmd.WaitCompletion();
#if !NETCF
} else if (WidcommBtInterface.IsWidcommCallbackThread) {
Func<byte, PORT_RETURN_CODE> dlgt = m_port.OpenServer;
IAsyncResult ar = dlgt.BeginInvoke(scn, null, null);
ret = dlgt.EndInvoke(ar);
#endif
} else {
ret = m_port.OpenServer(scn);
}
#endif
return ret;
}
delegate TResult Func<T1, TResult>(T1 p1);
delegate TResult Func<T1, T2, TResult>(T1 p1, T2 p2);
internal void EndAccept(IAsyncResult ar)
{
// (Can't lock here as that would block the callback methods).
if (ar != m_arConnect && ar != m_arConnectCompleted)
throw new InvalidOperationException("Unknown IAsyncResult.");
try {
((AsyncResultNoResult)ar).EndInvoke();
} finally {
MemoryBarrier();
Debug.Assert(m_arConnect == null);
m_arConnectCompleted = null;
}
}
//----------------
internal BluetoothAddress RemoteAddress { get { return m_remoteAddress; } }
//----------------
public override bool CanRead { get { return Connected; } }
public override bool CanWrite { get { return Connected; } }
public override bool CanSeek { get { return false; } }
//----
PORT_EV PORT_EV_ModemSignal
= PORT_EV.CTS | PORT_EV.DSR | PORT_EV.RLSD | PORT_EV.BREAK | PORT_EV.ERR
| PORT_EV.RING | PORT_EV.CTSS | PORT_EV.DSRS | PORT_EV.RLSDS | PORT_EV.OVERRUN;
internal void HandlePortEvent(PORT_EV eventId, IRfcommPort port)
{
WidcommBtInterface.EntryIsWidcommThread();
LogFormat("{3}: {0} ({1}) port: {2}\r\n", eventId, m_state_, port, DebugId);
lock (lockKey) {
if (port != m_port) {
return;
}
}
int handledCount = 0;
//
if ((eventId & PORT_EV_ModemSignal) != 0) {
++handledCount;
; //NOP
}
if ((eventId & PORT_EV.CONNECTED) != 0) {
++handledCount;
HandleCONNECTED(eventId);
}
if ((eventId & PORT_EV.CONNECT_ERR) != 0) {
++handledCount;
HandleCONNECT_ERR(eventId);
}
if ((eventId & PORT_EV.TXCHAR) != 0) {
++handledCount;
;//NOP
}
if ((eventId & PORT_EV.TXEMPTY) != 0) {
++handledCount;
FreePendingWrites();
}
if ((eventId & PORT_EV.FCS) != 0) { // FlowControl On
++handledCount;
}
if (((eventId & PORT_EV.FC) != 0)
&& ((eventId & PORT_EV.FCS) == 0)) { // FlowControl Off
++handledCount;
; // NOP
}
//
if (handledCount == 0)
WidcommUtils.Trace_WriteLine(DebugId + ": " + "Unknown event: '{0}'=0x{0:X}", eventId);
WidcommBtInterface.ExitIsWidcommThread();
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private void HandleCONNECTED(PORT_EV eventId)
{
AsyncResultNoResult sacAr; // Call SetAsCompleted outside the lock.
lock (lockKey) {
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
"CONNECTED {0}; m_state: {1}; m_arConnect {2}, IsCompleted: {3}.",
DebugId, m_state_,
(m_arConnect == null) ? "(null)" : "(set)",
(m_arConnect == null) ? "n/a" : m_arConnect.IsCompleted.ToString()));
if (m_arConnect != null && !m_arConnect.IsCompleted) {
// Success!
m_state_ = State.Connected;
m_connected = true;
sacAr = m_arConnect;
m_arConnectCompleted = m_arConnect;
m_arConnect = null;
// Get the remote address
try {
bool connected = DoIsConnected(out m_remoteAddress);
Debug.Assert(connected || TestUtilities.IsUnderTestHarness(), "!CRfCommPort.IsConnected");
} catch (Exception ex) {
Debug.Assert(TestUtilities.IsUnderTestHarness(), DebugId + ": " + "m_port.IsConnected exception: " + ex);
}
} else {
Debug.Assert(m_state_ == State.Connected
|| m_state_ == State.Closed // Consumer called Close soon after connect.
,
DebugId + ": " +
eventId + ": m_state wanted: " + "Connected" + ", but was: " + m_state_
+ ((m_arConnect == null) ? " ar==(null)" : " ar==(non-null)")
);
sacAr = null;
}
}//lock
if (sacAr != null)
sacAr.SetAsCompleted(null, false);
}
bool DoIsConnected(out BluetoothAddress p_remote_bdaddr)
{
bool connected;
#if !WIDCOMM_SINGLE_THREADING
connected = m_port.IsConnected(out p_remote_bdaddr);
#else
if (_singleThreader != null) {
WidcommPortSingleThreader.Func<IsConnectedResult> dlgt = delegate {
IsConnectedResult rslt = new IsConnectedResult();
rslt._connected = m_port.IsConnected(out rslt._p_remote_bdaddr);
return rslt;
};
WidcommPortSingleThreader.MiscReturnCommand<IsConnectedResult> cmd = AddCommand(
new WidcommPortSingleThreader.MiscReturnCommand<IsConnectedResult>(dlgt));
IsConnectedResult ret = cmd.WaitCompletion();
connected = ret._connected;
p_remote_bdaddr = ret._p_remote_bdaddr;
} else {
connected = m_port.IsConnected(out p_remote_bdaddr);
}
#endif
return connected;
}
#if WIDCOMM_SINGLE_THREADING
class IsConnectedResult
{
public bool _connected;
public BluetoothAddress _p_remote_bdaddr;
}
#endif
private void HandleCONNECT_ERR(PORT_EV eventId)
{
AsyncResultNoResult sacAr; // Call SetAsCompleted outside the lock.
Exception sacEx;
ReadAsyncResult[] allRead = null;
WriteAsyncResult[] allWrite = null;
lock (lockKey) {
Debug.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
"CONNECT_ERR {0}, m_state: {1}, m_arConnect {2}",
DebugId, m_state_,
(m_arConnect == null) ? "(null)" : "(set)"));
if (m_arConnect != null) {
Debug.Assert(m_state_ == State.New, eventId + ": m_state wanted: " + "New" + ", but was: " + m_state_);
WidcommUtils.Trace_WriteLine("HandlePortEvent: connect failed.");
// It **could** be that the peer needs authentication/bonding,
// unfortunately there's no specific error returned in the CONNECT_ERR
// event, so we'll try a Bond and try OpenClient again if there's
// a Passcode provided. Three states are possible:
// 1. No Passcode, so we didn't retry.
// 2. Something failed, return that error to the consumer.
// 3. We are retrying, so expect CONNECT/CONNECT_ERR.
Exception bondEx;
bool retrying = TryBondingIf_inLock(out bondEx);
if (retrying) {
Debug.Assert(bondEx == null);
sacAr = null;
sacEx = null;
} else if (bondEx != null) {
// Report the new failure.
sacAr = m_arConnect;
sacEx = bondEx;
m_arConnectCompleted = m_arConnect;
m_arConnect = null;
} else {
// Report the original failure.
sacAr = m_arConnect;
sacEx = WidcommSocketExceptions.CreateConnectFailed("PortCONNECT_ERR");
m_arConnectCompleted = m_arConnect;
m_arConnect = null;
}
} else if (m_state_ == State.Closed) {
// On Win32, at least, we get CONNECT_ERR on calling Close
//Debug.Fail("Info: here be CONNECT_ERR after calling Close");
sacAr = null;
sacEx = null;
} else {
Debug.Assert(m_state_ == State.Connected
|| m_state_ == State.PeerDidClose, // We've seen CONNECT_ERR occur twice
DebugId + ": " +
eventId + ": m_state wanted: " + "Connected" + ", but was: " + m_state_);
WidcommUtils.Trace_WriteLine("HandlePortEvent: closed when open.");
CloseInternal(out allRead, out allWrite);
sacAr = null;
sacEx = null;
}
}//lock
if (sacAr != null)
sacAr.SetAsCompleted(sacEx, false);
AbortIf(allRead, allWrite);
}
/// <summary>
/// Used: 1. when we get CONNECT_ERR from the stack, and POSSIBLY 2. when we close the
/// stream to do consumer timeout (SO_RCVTIMEO/etc).
/// </summary>
/// <param name="allRead">Out: to call <see cref="M:InTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.AbortIf(System.Collections.Generic.IList{InTheHand.Net.AsyncResult{System.Int32,InTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.BeginReadParameters}}, System.Collections.Generic.IList{InTheHand.Net.AsyncNoResult{InTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.BeginReadParameters}})"/>
/// on.</param>
/// <param name="allWrite">Out: to call <see cref="M:InTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.AbortIf(System.Collections.Generic.IList{InTheHand.Net.AsyncResult{System.Int32,InTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.BeginReadParameters}}, System.Collections.Generic.IList{InTheHand.Net.AsyncNoResult{InTheHand.Net.Bluetooth.Widcomm.WidcommRfcommStream.BeginReadParameters}})"/>
/// on.</param>
private void CloseInternal(out ReadAsyncResult[] allRead, out WriteAsyncResult[] allWrite)
{
// For Listener an old port (previously accepted and closed by
// the peer) is re-used to accept a new connection even when other
// ports have been create'd/OpenServer'd, so close immmediately.
if (m_state_ != State.PeerDidClose) { // Don't know whether its ok to call twice...
// i.e. Debug.Assert(m_state_ == State.Connected);
DoPortClose(false);
}
// No information whether this is a hard or clean close! :-(
m_state_ = State.PeerDidClose;
if (GetPendingReceiveDataLength() == 0) {
// Must signal EoS to release any pending Reads for which no data will arrive.
ClearAllReadRequests_inLock(out allRead);
} else {
allRead = null;
}
ClearAllWriteRequests_inLock(out allWrite);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private bool TryBondingIf_inLock(out Exception error)
{
const bool Retrying = true;
const bool NotRetrying = false;
//
/*
* if(havePin)
* if(Bond success)
* if(OpenClient success)
* return Retrying;
* return NotRetrying;
*/
try { // Mustn't die on this thread, need to report all exceptions back!!
//
if (m_passcodeToTry != null) {
if (m_addressToConnect == null) {
Debug.Fail("In retry, have passcode, but looks like Server mode!!");
error = null;
return NotRetrying;
}
//
string passcodeToTry = m_passcodeToTry;
m_passcodeToTry = null;
Debug.Assert(m_addressToConnect != null, "m_addressToConnect != null");
bool didPair = Bond(m_addressToConnect, passcodeToTry);
if (didPair) {
Debug.Assert(m_arConnect != null);
Debug.Assert(!m_arConnect.IsCompleted, "!m_arConnect.IsCompleted");
//TODO Destroy old port!
SetPort(m_factory.GetWidcommRfcommPort());
PORT_RETURN_CODE ret = m_port.OpenClient(m_ocScn, WidcommUtils.FromBluetoothAddress(m_addressToConnect));
WidcommUtils.Trace_WriteLine("OpenClient/AB ret: {0}=0x{0:X}", ret);
if (ret == PORT_RETURN_CODE.SUCCESS) {
error = null;
return Retrying;
} else {
error = WidcommSocketExceptions.Create(ret, "OpenClient/AB");
return NotRetrying;
}
}
}
//
error = null;
} catch (Exception ex) {
error = ex;
}
return NotRetrying;
}
/// <summary>
/// Wrapper around CBtIf::Bond().
/// </summary>
/// <param name="device"><see cref="BluetoothAddress"/></param>
/// <param name="passcode"><see cref="T:System.String"/></param>
/// <returns><see langword="true"/> if pairing was completed.
/// <see langword="false"/> if were already paired, or pairing failed.
/// </returns>
private bool Bond(BluetoothAddress device, string passcode)
{
//bool pd = BluetoothSecurity.PairRequest(device, passcode);
BOND_RETURN_CODE rc = ((WidcommBluetoothSecurity)m_factory.DoGetBluetoothSecurity()).Bond_(device, passcode);
Log("Bond: " + rc);
switch (rc) {
case BOND_RETURN_CODE.SUCCESS:
return true;
case BOND_RETURN_CODE.FAIL:
// TODO ? BOND_RETURN_CODE.FAIL -> Report this in the exception?
return false;
case BOND_RETURN_CODE.ALREADY_BONDED:
// (BTW:"maintained for compatibility")
return false;
case BOND_RETURN_CODE.REPEATED_ATTEMPTS:
// What is this??
return false;
case BOND_RETURN_CODE.BAD_PARAMETER:
// TODO ? BOND_RETURN_CODE.BAD_PARAMETER -> Report this in the exception?
return false;
case BOND_RETURN_CODE.NO_BT_SERVER:
default:
return false;
}
}
//--------------------------------------------------------------
void ClearAllReadRequests_inLock(out ReadAsyncResult[] all)
{
all = m_arReceiveList.ToArray();
m_arReceiveList.Clear();
}
void Abort(IList<ReadAsyncResult> all)
{
foreach (ReadAsyncResult ar in all) {
ar.SetAsCompleted(0, false);
}
}
void ClearAllRequests_inLock(out ReadAsyncResult[] allRead, out WriteAsyncResult[] allWrite)
{
ClearAllReadRequests_inLock(out allRead);
ClearAllWriteRequests_inLock(out allWrite);
}
void AbortIf(IList<ReadAsyncResult> allRead, IList<WriteAsyncResult> allWrite)
{
if (allRead != null)
Abort(allRead);
if (allWrite != null)
Abort(allWrite);
}
//--------------------------------------------------------------
internal void HandlePortReceive(byte[] buffer, IRfcommPort port)
{
WidcommBtInterface.EntryIsWidcommThread();
Log("HandlePortReceive port: " + port.DebugId);
lock (lockKey) {
Log("HandlePortReceive: in lock");
if (port != m_port) {
return;
}
// Put it on the queue.
m_receivedData.Enqueue(buffer);
System.Diagnostics.Debug.Assert(m_amountInReadBuffers >= 0);
m_amountInReadBuffers += buffer.Length;
// TODO Check high-water mark and set SetFlowControl.
}//lock
// Free any waiting reads.
int readLen;
AsyncResult<int, BeginReadParameters> ar; // Call SetAsCompleted outside the lock.
while (true) {
lock (lockKey) {
if (m_arReceiveList.Count == 0 || GetPendingReceiveDataLength() == 0)
break;
ar = m_arReceiveList.Dequeue();
BeginReadParameters args = ar.BeginParameters;
readLen = ReturnSomeReceivedData_MustBeInLock(args.buffer, args.offset, args.count);
}//lock
ar.SetAsCompleted(readLen, AsyncResultCompletion.MakeAsync);
}//while
WidcommBtInterface.ExitIsWidcommThread();
}
private int GetPendingReceiveDataLength()
{
if (m_receivedData.Count == 0)
return 0;
else {
int firstLen, firstOffset;
FirstReceivedBlockInfo_(out firstLen, out firstOffset);
return firstLen;
}
}
private int ReturnSomeReceivedData_MustBeInLock(byte[] buffer, int offset, int count)
{
// Use the data; returning as much of the first block that will fit.
// Possible TO-DO: Or we could return data from many of the blocks to fill the buffer.
int firstLen, firstOffset;
FirstReceivedBlockInfo_(out firstLen, out firstOffset);
int returnedLen;
if (firstLen <= count) { // Use all of the first block and remove it.
byte[] data = m_receivedData.Dequeue();
Array.Copy(data, m_receivedDataFirstBlockOffset, buffer, offset, firstLen);
m_receivedDataFirstBlockOffset = 0;
Debug.Assert(firstLen != 0, "Mustn't return 0 except at closed.");
returnedLen = firstLen;
} else { // Use some of the first block.
byte[] data = m_receivedData.Peek();
int lenToRead = Math.Min(count, firstLen);
Array.Copy(data, firstOffset, buffer, offset, lenToRead);
m_receivedDataFirstBlockOffset += lenToRead;
Debug.Assert(m_receivedDataFirstBlockOffset <= data.Length, "Should not leave an empty block.");
Debug.Assert(lenToRead != 0 || count == 0, "Mustn't return 0 except at closed, or when requested.");
returnedLen = lenToRead;
}
m_amountInReadBuffers -= returnedLen;
System.Diagnostics.Debug.Assert(m_amountInReadBuffers >= 0, "m_amountInReadBuffers: " + m_amountInReadBuffers);
// TODO Check low-water mark and clear SetFlowControl
return returnedLen;
}
private void FirstReceivedBlockInfo_(out int length, out int offset)
{
Debug.Assert(m_receivedData.Count != 0);
byte[] data = m_receivedData.Peek();
offset = m_receivedDataFirstBlockOffset;
length = data.Length - offset;
Debug.Assert(length != 0, "Found empty block on receive list.");
}
public override int Read(byte[] buffer, int offset, int count)
{
// TODO Add parameter checking
EnsureOpenForRead();
IAsyncResult ar;
lock (lockKey) {
if (m_receivedData.Count != 0) {
return ReturnSomeReceivedData_MustBeInLock(buffer, offset, count);
} else {
// No pending data, due to closed? Otherwise wait.
if (m_state_ == State.PeerDidClose)
return 0;
ar = this.BeginRead(buffer, offset, count, null, null);
// We must exit the lock to allow new data to arrive!
}
}//lock
Debug.Assert(ar != null, "Should be here only after calling BeginRead");
if (!IsInfiniteTimeout(_readTimeout)) {
ApplyTimeout(ar, _readTimeout, m_arReceiveList);
}
return this.EndRead(ar);
}
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
// TODO Add parameter checking
EnsureOpenForRead();
AsyncResult<int, BeginReadParameters> sacAr; // Call SetAsCompleted outside the lock.
int readLen;
lock (lockKey) {
BeginReadParameters args = new BeginReadParameters(buffer, offset, count);
AsyncResult<int, BeginReadParameters> ar
= new AsyncResult<int, BeginReadParameters>(callback, state, args);
if (m_receivedData.Count == 0) {
switch (m_state_) {
case State.Connected:
// Wait for some more data then.
m_arReceiveList.Enqueue(ar);
return ar;
default: // Expect only "PeerDidClose", others blocked by EnsureOpen.
Debug.Assert(m_state_ == State.PeerDidClose, "Unexpected State: " + m_state_);
// So, we're at EoF -> Completed Synchronously
sacAr = ar;
readLen = 0;
break;
}
} else {
// Data available -> Completed Synchronously
readLen = ReturnSomeReceivedData_MustBeInLock(args.buffer, args.offset, args.count);
sacAr = ar;
}
}//lock
Debug.Assert(sacAr != null, "Only get here when want to call SetAsCompleted!");
sacAr.SetAsCompleted(readLen, true);
return sacAr;
}
public override int EndRead(IAsyncResult asyncResult)
{
// (Can't lock here as that would block the callback methods).
AsyncResult<int, BeginReadParameters> ar
= (AsyncResult<int, BeginReadParameters>)asyncResult;
int readLen = ar.EndInvoke();
Debug.Assert(!m_arReceiveList.Contains(ar), "Should have cleaned up outstanding IAsyncResult list");
return readLen;
}
internal sealed class BeginReadParameters
{
//Unused: public readonly int startTicks = Environment.TickCount;
public byte[] buffer;
public int offset;
public int count;
public BeginReadParameters(byte[] buffer, int offset, int count)
{
this.buffer = buffer;
this.offset = offset;
this.count = count;
}
}
public virtual bool DataAvailable { get { return AmountInReadBuffers > 0; } }
internal int AmountInReadBuffers
{
get
{
lock (lockKey) {
System.Diagnostics.Debug.Assert(m_amountInReadBuffers >= 0);
return m_amountInReadBuffers;
}
}
}
//--------
int _readTimeout = Timeout.Infinite;
int _writeTimeout = Timeout.Infinite;
public override bool CanTimeout { get { return true; } }
public override int ReadTimeout
{
get { return _readTimeout; }
set { _readTimeout = value; }
}
public override int WriteTimeout
{
get { return _writeTimeout; }
set { _writeTimeout = value; }
}
static bool IsInfiniteTimeout(int timeout)
{
if (timeout == Timeout.Infinite)
return true;
if (timeout == 0)
return true;
return false;
}
private void ApplyTimeout<TAsyncResult>(IAsyncResult ar, int timeout,
Queue<TAsyncResult> queue)
where TAsyncResult : AsyncResultNoResult
{
Debug.Assert(timeout != Timeout.Infinite, "Called with invalid timeout (-1).");
Debug.Assert(timeout != 0, "Called with invalid timeout (0).");
TAsyncResult sacAr = null;
ReadAsyncResult[] allRead = null;
WriteAsyncResult[] allWrite = null;
int watchdog = 0;
while (true) { // See comment in loop
Debug.Assert(sacAr == null);
bool signalled = ar.AsyncWaitHandle.WaitOne(timeout, false);
if (signalled) {
break;
} else {
lock (lockKey) {
TAsyncResult peekFirstAr = queue.Peek();
if (peekFirstAr == ar) {
sacAr = queue.Dequeue();
Debug.Assert(sacAr == ar, "How did a different AR get to the front of the queue?!?");
CloseInternal(out allRead, out allWrite);
break;
} else {
// There are waiting Reads in the queue ahead of us
// and we can't remove ours from behind them, so we
// just have to wait some more...
sacAr = null;
}
}//lock
}
++watchdog;
//if (watchdog > 10) {
// Debug.Fail("Looping in DoTimeout!");
// break;
//}
}//while
if (sacAr != null) { // Set completion with error.
const int WSAETIMEDOUT = 10060;
Exception ex0 = new SocketException(WSAETIMEDOUT);
sacAr.SetAsCompleted(new IOException(ex0.Message, ex0), true);
AbortIf(allRead, allWrite);
} else {
Debug.Assert(allRead == null, "inconsistent allRead");
Debug.Assert(allWrite == null, "inconsistent allWrite");
}
}
//--------------------------------------------------------------
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
private void FreePendingWrites()
{
// Release the async operations outside the lock.
Queue<WriteAsyncResult> sacArQueue = new Queue<WriteAsyncResult>();
WriteAsyncResult sacErroredLast = null;
Exception sacEx = null;
lock (lockKey) {
int loops = 0;
while (m_arWriteQueue.Count != 0) {
++loops;
WriteAsyncResult arPeek = m_arWriteQueue.Peek();
BeginReadParameters work = arPeek.BeginParameters;
int lenWritten;
try {
lenWritten = PortWrite(work.buffer, work.offset, work.count);
} catch (Exception ex) {
if (!TestUtilities.IsUnderTestHarness())
Debug.Fail("Write failed at FPW -- Not seen in reality");
ImplicitPeerClose();
SetAsDisconnected();
WriteAsyncResult ar = m_arWriteQueue.Dequeue();
sacErroredLast = ar;
sacEx = ex;
break;
}
if (lenWritten == work.count) {
WriteAsyncResult ar = m_arWriteQueue.Dequeue();
sacArQueue.Enqueue(ar);
} else {
Debug.Assert(lenWritten < work.count, "lenWritten: " + lenWritten + ", work.count: " + work.count);
work.count -= lenWritten;
work.offset += lenWritten;
break; // Need to wait for the next event!
}
}//while
if (m_arWriteQueue.Count == 0 && m_writeEmptied != null) {
m_writeEmptied.Set();
}
//
if (loops == 0) {
// DEBUG test-coverage
} else if (loops == 1) {
// DEBUG test-coverage
} else {
// DEBUG test-coverage
}
}//lock
while (sacArQueue.Count != 0) {
WriteAsyncResult ar = sacArQueue.Dequeue();
ar.SetAsCompleted(null, AsyncResultCompletion.MakeAsync);
}
Debug.Assert((sacErroredLast == null) == (sacEx == null));
if (sacErroredLast != null) {
sacErroredLast.SetAsCompleted(sacEx, AsyncResultCompletion.MakeAsync);
}
}
void ClearAllWriteRequests_inLock(out WriteAsyncResult[] all)
{
all = m_arWriteQueue.ToArray();
m_arWriteQueue.Clear();
}
void Abort(IList<WriteAsyncResult> all)
{
foreach (WriteAsyncResult ar in all) {
ar.SetAsCompleted(new IOException(WrappingIOExceptionMessage,
WidcommSocketExceptions.ConnectionIsPeerClosed()), false);
}
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
EnsureOpenForWrite();
WriteAsyncResult sacAr; // SetAsComplete outside the lock.
lock (lockKey) {
WriteAsyncResult ar;
if (m_arWriteQueue.Count == 0) { // Try to complete the send immediately.
bool success = false;
int lenWritten;
try {
lenWritten = PortWrite(buffer, offset, count);
success = true;
} finally {
if (!success) {
if (!TestUtilities.IsUnderTestHarness())
Debug.Fail("Write failed at BW -- Not seen in reality");
ImplicitPeerClose();
SetAsDisconnected();
}
}
if (lenWritten == count) { // -> CompletedSynchronously
ar = new WriteAsyncResult(callback, state, null);
sacAr = ar;
} else { // Queue the remainder...
BeginReadParameters work = new BeginReadParameters(
buffer, offset + lenWritten, count - lenWritten);
Debug.Assert(work.offset < buffer.Length, "work.offset: " + work.offset + ", buffer.Length: " + buffer.Length);
Debug.Assert(work.count > 0, "work.count: " + work.count);
ar = new WriteAsyncResult(callback, state, work);
m_arWriteQueue.Enqueue(ar);
return ar;
}
} else { //-----------------------
// Queue it all...
ar = new WriteAsyncResult(callback, state,
new BeginReadParameters(buffer, offset, count));
m_arWriteQueue.Enqueue(ar);
return ar;
}
}
// Fall throught from complete write.
sacAr.SetAsCompleted(null, true);
return sacAr;
}
public override void EndWrite(IAsyncResult asyncResult)
{
bool success = false;
try {
WriteAsyncResult ar2 = (WriteAsyncResult)asyncResult;
ar2.EndInvoke();
success = true;
} finally {
if (!success) {
Debug.Assert(!LiveConnected, "EndWrite on !success !LiveConnected already");
Debug.Assert(!LiveConnected || !Connected, "EndWrite on !success !Connected already");
}
}
}
public override void Write(byte[] buffer, int offset, int count)
{
IAsyncResult ar = BeginWrite(buffer, offset, count, null, null);
if (!IsInfiniteTimeout(_writeTimeout)) {
ApplyTimeout(ar, _writeTimeout, m_arWriteQueue);
}
EndWrite(ar);
}
private int PortWrite(byte[] buffer, int offset, int count)
{
int totalLenWritten = 0;
UInt16 lenAttemptedToWrite, lenWritten;
// Try in 16KB chunks
while (count > 0) {
PortWriteMax16kb(buffer, offset, count, out lenAttemptedToWrite, out lenWritten);
Debug.Assert(lenWritten <= lenAttemptedToWrite, "lenWritten<= lenToWrite ("
+ lenWritten + "," + lenAttemptedToWrite + ")");
Debug.Assert(lenWritten >= 0, "NOT +ve lenWritten: " + lenWritten);
count -= lenWritten;
offset += lenWritten;
Debug.Assert(count >= 0, "count: " + count);
Debug.Assert(offset <= buffer.Length, "offset: " + offset + ", buffer.Length: " + buffer.Length);
totalLenWritten += lenWritten;
//
if (lenAttemptedToWrite != lenWritten)
break; // port.Write accepted only part! The rest needs to be queued.
}
return totalLenWritten;
}
private void PortWriteMax16kb(byte[] buffer, int offset, int count, out UInt16 lenAttemptToWrite, out UInt16 lenWritten)
{
lenAttemptToWrite = (UInt16)Math.Min(count, UInt16.MaxValue);
byte[] data;
if (offset == 0 && lenAttemptToWrite == count && buffer.Length == lenAttemptToWrite) {
// perf optimisation
data = buffer;
} else {
data = new byte[lenAttemptToWrite];
Array.Copy(buffer, offset, data, 0, lenAttemptToWrite);
}
Log("m_port.Write");
PORT_RETURN_CODE ret = DoWrite(data, lenAttemptToWrite, out lenWritten);
if (ret != PORT_RETURN_CODE.SUCCESS)
throw new IOException(WrappingIOExceptionMessage,
WidcommSocketExceptions.Create(ret, "Write"));
WidcommUtils.Trace_WriteLine("m_port.Write: len in: {0}, len out: {1}", lenAttemptToWrite, lenWritten);
Debug.Assert(lenWritten <= lenAttemptToWrite, "lenWritten<= lenToWrite ("
+ lenWritten + "," + lenAttemptToWrite + ")");
}
PORT_RETURN_CODE DoWrite(byte[] p_data, UInt16 len_to_write, out UInt16 p_len_written)
{
PORT_RETURN_CODE ret;
#if !WIDCOMM_SINGLE_THREADING
ret = m_port.Write(p_data, len_to_write, out p_len_written);
#else
if (_singleThreader != null) {
WidcommPortSingleThreader.PortWriteCommand cmd = AddCommand(
new WidcommPortSingleThreader.PortWriteCommand(p_data, len_to_write, m_port));
ret = cmd.WaitCompletion(out p_len_written);
#if !NETCF
} else if (WidcommBtInterface.IsWidcommCallbackThread) {
// Must not call back into Widcomm from one of its threads.
FuncPortWrite dlgt = new FuncPortWrite(BackgroundWrite);
// How much slower than QueueUserWorkItem etc is this??
IAsyncResult ar = dlgt.BeginInvoke(p_data, len_to_write, out p_len_written,
Thread.CurrentThread, null, null);
ret = dlgt.EndInvoke(out p_len_written, ar);
#endif
} else {
ret = m_port.Write(p_data, len_to_write, out p_len_written);
}
#endif
return ret;
}
delegate PORT_RETURN_CODE FuncPortWrite(
byte[] p_data, UInt16 len_to_write, out UInt16 p_len_written,
Thread callerThread);
PORT_RETURN_CODE BackgroundWrite(byte[] p_data, UInt16 len_to_write, out UInt16 p_len_written,
Thread callerThread)
{
Debug.Assert(Thread.CurrentThread != callerThread, "Same thread!!! "
+ Thread.CurrentThread.ManagedThreadId + " =?= " + callerThread.ManagedThreadId);
PORT_RETURN_CODE ret = m_port.Write(p_data, len_to_write, out p_len_written);
return ret;
}
public override void Flush()
{
// TODO (((WidcommRfcommStream.Flush Need we do anything here?)))
// Can we do anything here? We're not a buffered stream so there is
// no need to flush. Any data in the write queue is there due to flow
// control, we can only send it when the stack signals that its ready.
}
//--------
public override long Length { get { throw NewNotSupportedException(); } }
public override long Position
{
get { throw NewNotSupportedException(); }
set { throw NewNotSupportedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw NewNotSupportedException();
}
public override void SetLength(long value)
{
throw NewNotSupportedException();
}
//--------
private static Exception NewNotSupportedException()
{
// no message, for NETCF
throw new NotSupportedException();
}
#region SingleThread Actions
#if WIDCOMM_SINGLE_THREADING
private T AddCommand<T>(T cmd)
where T : WidcommPortSingleThreader.StCommand
{
return _singleThreader.AddCommand(cmd);
}
#endif
#endregion
}//class
}
|