WidcommRfcommStream.cs :  » Business-Application » 32feet.NET » InTheHand » Net » Bluetooth » Widcomm » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Business Application » 32feet.NET 
32feet.NET » InTheHand » Net » Bluetooth » Widcomm » WidcommRfcommStream.cs
// 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
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.