The Labs \ Source Viewer \ SSCLI \ System.Net \ PooledStream

  1. //------------------------------------------------------------------------------
  2. // <copyright file="PooledStream.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Net
  16. {
  17.     using System;
  18.     using System.Net.Sockets;
  19.     using System.IO;
  20.     using System.Diagnostics;
  21.     using System.Security.Permissions;
  22.     using System.Threading;
  23.    
  24.     internal class PooledStream : Stream
  25.     {
  26.         // managed pooling lifetime controls
  27.         private bool m_CheckLifetime;
  28.         // true when the connection is only to live for a specific timespan
  29.         private TimeSpan m_Lifetime;
  30.         // the timespan the connection is to live for
  31.         private DateTime m_CreateTime;
  32.         // when the connection was created.
  33.         private bool m_ConnectionIsDoomed;
  34.         // true when the connection should no longer be used.
  35.         // managed pooling
  36.         private ConnectionPool m_ConnectionPool;
  37.         // the pooler that the connection came from
  38.         private WeakReference m_Owner;
  39.         // the owning object, when not in the pool.
  40.         private int m_PooledCount;
  41.         // the number of times this object has been pushed into the pool less the number of times it's been popped (0 == inPool)
  42.         // connection info
  43.         private bool m_Initalizing;
  44.         // true while we're creating the stream
  45.         private IPAddress m_ServerAddress;
  46.         // IP address of server we're connected to
  47.         private NetworkStream m_NetworkStream;
  48.         // internal stream for socket
  49.         private Socket m_AbortSocket;
  50.         // in abort scenarios, used to abort connect
  51.         private Socket m_AbortSocket6;
  52.         // in abort scenarios, used to abort connect
  53.         private bool m_JustConnected;
  54.        
  55.         internal PooledStream(object owner) : base()
  56.         {
  57.             // non-pooled constructor
  58.             m_Owner = new WeakReference(owner);
  59.             m_PooledCount = -1;
  60.             m_Initalizing = true;
  61.             m_NetworkStream = new NetworkStream();
  62.             m_CreateTime = DateTime.UtcNow;
  63.         }
  64.        
  65.         internal PooledStream(ConnectionPool connectionPool, TimeSpan lifetime, bool checkLifetime) : base()
  66.         {
  67.             // pooled constructor
  68.             m_ConnectionPool = connectionPool;
  69.             m_Lifetime = lifetime;
  70.             m_CheckLifetime = checkLifetime;
  71.             m_Initalizing = true;
  72.             m_NetworkStream = new NetworkStream();
  73.             m_CreateTime = DateTime.UtcNow;
  74.         }
  75.        
  76.        
  77.         internal bool JustConnected {
  78.             get {
  79.                 if (m_JustConnected) {
  80.                     m_JustConnected = false;
  81.                     return true;
  82.                 }
  83.                 return false;
  84.             }
  85.         }
  86.        
  87.         internal IPAddress ServerAddress {
  88.             get { return m_ServerAddress; }
  89.         }
  90.        
  91.         internal bool IsInitalizing {
  92.             get { return m_Initalizing; }
  93.         }
  94.        
  95.         internal bool CanBePooled {
  96.             get {
  97.                 if (m_Initalizing) {
  98.                     GlobalLog.Print("PooledStream#" + ValidationHelper.HashString(this) + "::CanBePooled " + "init: true");
  99.                     return true;
  100.                 }
  101.                 if (!m_NetworkStream.Connected) {
  102.                     GlobalLog.Print("PooledStream#" + ValidationHelper.HashString(this) + "::CanBePooled " + "not-connected: false");
  103.                     return false;
  104.                 }
  105.                
  106.                 WeakReference weakref = m_Owner;
  107.                 bool flag = (!m_ConnectionIsDoomed && ((null == weakref) || !weakref.IsAlive));
  108.                 GlobalLog.Print("PooledStream#" + ValidationHelper.HashString(this) + "::CanBePooled " + "flag: " + flag.ToString());
  109.                 return (flag);
  110.             }
  111.             set { m_ConnectionIsDoomed |= !value; }
  112.         }
  113.        
  114.         internal bool IsEmancipated {
  115.             get {
  116.                 WeakReference owner = m_Owner;
  117.                 bool value = (0 >= m_PooledCount) && (null == owner || !owner.IsAlive);
  118.                 return value;
  119.             }
  120.         }
  121.        
  122.         internal object Owner {
  123.             // We use a weak reference to the owning object so we can identify when
  124.             // it has been garbage collected without thowing exceptions.
  125.             get {
  126.                 WeakReference weakref = m_Owner;
  127.                 if ((null != weakref) && weakref.IsAlive) {
  128.                     return weakref.Target;
  129.                 }
  130.                 return null;
  131.             }
  132.             set {
  133.                 lock (this) {
  134.                     if (null != m_Owner) {
  135.                         m_Owner.Target = value;
  136.                     }
  137.                 }
  138.             }
  139.         }
  140.        
  141.         internal ConnectionPool Pool {
  142.             get { return m_ConnectionPool; }
  143.         }
  144.        
  145.         internal virtual ServicePoint ServicePoint {
  146.             get { return Pool.ServicePoint; }
  147.         }
  148.        
  149.         private GeneralAsyncDelegate m_AsyncCallback;
  150.        
  151.         internal bool Activate(object owningObject, GeneralAsyncDelegate asyncCallback)
  152.         {
  153.             return Activate(owningObject, asyncCallback != null, Timeout.Infinite, asyncCallback);
  154.         }
  155.        
  156.         protected bool Activate(object owningObject, bool async, int timeout, GeneralAsyncDelegate asyncCallback)
  157.         {
  158.             GlobalLog.Assert(owningObject == Owner || Owner == null, "PooledStream::Activate|Owner is not the same as expected.");
  159.             try {
  160.                 if (m_Initalizing) {
  161.                     IPAddress address = null;
  162.                     m_AsyncCallback = asyncCallback;
  163.                     Socket socket = ServicePoint.GetConnection(this, owningObject, async, out address, ref m_AbortSocket, ref m_AbortSocket6, timeout);
  164.                     if (socket != null) {
  165.                         m_NetworkStream.InitNetworkStream(socket, FileAccess.ReadWrite);
  166.                         m_ServerAddress = address;
  167.                         m_Initalizing = false;
  168.                         m_JustConnected = true;
  169.                         m_AbortSocket = null;
  170.                         m_AbortSocket6 = null;
  171.                         return true;
  172.                     }
  173.                     return false;
  174.                 }
  175.                 else if (async && asyncCallback != null) {
  176.                     asyncCallback(owningObject, this);
  177.                 }
  178.                 return true;
  179.             }
  180.             catch {
  181.                 m_Initalizing = false;
  182.                 throw;
  183.             }
  184.         }
  185.        
  186.         internal void Deactivate()
  187.         {
  188.             // Called when the connection is about to be placed back into the pool; this
  189.            
  190.             m_AsyncCallback = null;
  191.            
  192.             if (!m_ConnectionIsDoomed && m_CheckLifetime) {
  193.                 // check lifetime here - as a side effect it will doom connection if
  194.                 // it's lifetime has elapsed
  195.                 CheckLifetime();
  196.             }
  197.         }
  198.        
  199.         internal virtual void ConnectionCallback(object owningObject, Exception e, Socket socket, IPAddress address)
  200.         {
  201.             GlobalLog.Assert(owningObject == Owner || Owner == null, "PooledStream::ConnectionCallback|Owner is not the same as expected.");
  202.             object result = null;
  203.             if (e != null) {
  204.                 m_Initalizing = false;
  205.                 result = e;
  206.             }
  207.             else {
  208.                 try {
  209.                     m_NetworkStream.InitNetworkStream(socket, FileAccess.ReadWrite);
  210.                     result = this;
  211.                 }
  212.                 catch (Exception ex) {
  213.                     if (NclUtilities.IsFatal(ex))
  214.                         throw;
  215.                     result = ex;
  216.                 }
  217.                 catch {
  218.                     throw;
  219.                 }
  220.                 m_ServerAddress = address;
  221.                 m_Initalizing = false;
  222.                 m_JustConnected = true;
  223.             }
  224.             if (m_AsyncCallback != null) {
  225.                 m_AsyncCallback(owningObject, result);
  226.             }
  227.             m_AbortSocket = null;
  228.             m_AbortSocket6 = null;
  229.         }
  230.        
  231.         protected void CheckLifetime()
  232.         {
  233.             bool okay = !m_ConnectionIsDoomed;
  234.             if (okay) {
  235.                 // Returns whether or not this object's lifetime has had expired.
  236.                 // True means the object is still good, false if it has timed out.
  237.                
  238.                 // obtain current time
  239.                 DateTime utcNow = DateTime.UtcNow;
  240.                
  241.                 // obtain timespan
  242.                 TimeSpan timeSpan = utcNow.Subtract(m_CreateTime);
  243.                
  244.                 // compare timeSpan with lifetime, if equal or less,
  245.                 // designate this object to be killed
  246.                 m_ConnectionIsDoomed = (0 < TimeSpan.Compare(m_Lifetime, timeSpan));
  247.             }
  248.         }
  249.        
  250.         /// <devdoc>
  251.         /// <para>Updates the lifetime of the time for this stream to live</para>
  252.         /// </devdoc>
  253.         internal void UpdateLifetime()
  254.         {
  255.             int timeout = ServicePoint.ConnectionLeaseTimeout;
  256.             TimeSpan connectionLifetime;
  257.            
  258.             if (timeout == System.Threading.Timeout.Infinite) {
  259.                 connectionLifetime = TimeSpan.MaxValue;
  260.                 m_CheckLifetime = false;
  261.             }
  262.             else {
  263.                 connectionLifetime = new TimeSpan(0, 0, 0, 0, timeout);
  264.                 m_CheckLifetime = true;
  265.             }
  266.            
  267.             if (connectionLifetime != m_Lifetime) {
  268.                 m_Lifetime = connectionLifetime;
  269.             }
  270.         }
  271.        
  272.         internal void Destroy()
  273.         {
  274.             m_Owner = null;
  275.             m_ConnectionIsDoomed = true;
  276.             this.Close(0);
  277.         }
  278.        
  279.        
  280.         internal void PrePush(object expectedOwner)
  281.         {
  282.             lock (this) {
  283.                 //3 // The following tests are retail assertions of things we can't allow to happen.
  284.                 if (null == expectedOwner) {
  285.                     if (null != m_Owner && null != m_Owner.Target)
  286.                         throw new InternalException();
  287.                     // new unpooled object has an owner
  288.                 }
  289.                 else {
  290.                     if (null == m_Owner || m_Owner.Target != expectedOwner)
  291.                         throw new InternalException();
  292.                     // unpooled object has incorrect owner
  293.                 }
  294.                
  295.                 m_PooledCount++;
  296.                
  297.                 if (1 != m_PooledCount)
  298.                     throw new InternalException();
  299.                 // pushing object onto stack a second time
  300.                 if (null != m_Owner)
  301.                     m_Owner.Target = null;
  302.             }
  303.         }
  304.        
  305.         internal void PostPop(object newOwner)
  306.         {
  307.             GlobalLog.Assert(!IsEmancipated, "Pooled object not in pool.");
  308.             GlobalLog.Assert(CanBePooled, "Pooled object is not poolable.");
  309.            
  310.            
  311.             lock (this) {
  312.                 if (null == m_Owner)
  313.                     m_Owner = new WeakReference(newOwner);
  314.                 else {
  315.                     if (null != m_Owner.Target)
  316.                         throw new InternalException();
  317.                     // pooled connection already has an owner!
  318.                     m_Owner.Target = newOwner;
  319.                 }
  320.                
  321.                 m_PooledCount--;
  322.                
  323.                 if (null != Pool) {
  324.                     if (0 != m_PooledCount)
  325.                         throw new InternalException();
  326.                     // popping object off stack with multiple pooledCount
  327.                 }
  328.                 else {
  329.                     if (-1 != m_PooledCount)
  330.                         throw new InternalException();
  331.                     // popping object off stack with multiple pooledCount
  332.                 }
  333.             }
  334.         }
  335.        
  336.         /// <devdoc>
  337.         /// <para>True if we're using a TlsStream</para>
  338.         /// </devdoc>
  339.         protected bool UsingSecureStream {
  340.             get { return false; }
  341.         }
  342.        
  343.         /// <devdoc>
  344.         /// <para>Allows inherited objects to modify NetworkStream</para>
  345.         /// </devdoc>
  346.         internal NetworkStream NetworkStream {
  347.             get { return m_NetworkStream; }
  348.             set {
  349.                 m_Initalizing = false;
  350.                 m_NetworkStream = value;
  351.             }
  352.         }
  353.        
  354.         /// <devdoc>
  355.         /// <para>Gives the socket for internal use.</para>
  356.         /// </devdoc>
  357.         protected Socket Socket {
  358.             get { return m_NetworkStream.InternalSocket; }
  359.         }
  360.        
  361.         /// <devdoc>
  362.         /// <para>Indicates that data can be read from the stream.
  363.         /// </devdoc>
  364.         public override bool CanRead {
  365.             get { return m_NetworkStream.CanRead; }
  366.         }
  367.        
  368.         /// <devdoc>
  369.         /// <para>Indicates that the stream is seekable</para>
  370.         /// </devdoc>
  371.         public override bool CanSeek {
  372.             get { return m_NetworkStream.CanSeek; }
  373.         }
  374.        
  375.        
  376.         /// <devdoc>
  377.         /// <para>Indicates that the stream is writeable</para>
  378.         /// </devdoc>
  379.         public override bool CanWrite {
  380.             get { return m_NetworkStream.CanWrite; }
  381.         }
  382.        
  383.         /// <devdoc>
  384.         /// <para>Indicates whether we can timeout</para>
  385.         /// </devdoc>
  386.         public override bool CanTimeout {
  387.             get { return m_NetworkStream.CanTimeout; }
  388.         }
  389.        
  390.        
  391.         /// <devdoc>
  392.         /// <para>Set/Get ReadTimeout</para>
  393.         /// </devdoc>
  394.         public override int ReadTimeout {
  395.             get { return m_NetworkStream.ReadTimeout; }
  396.             set { m_NetworkStream.ReadTimeout = value; }
  397.         }
  398.        
  399.         /// <devdoc>
  400.         /// <para>Set/Get WriteTimeout</para>
  401.         /// </devdoc>
  402.         public override int WriteTimeout {
  403.             get { return m_NetworkStream.WriteTimeout; }
  404.             set { m_NetworkStream.WriteTimeout = value; }
  405.         }
  406.        
  407.         /// <devdoc>
  408.         /// <para>Indicates that the stream is writeable</para>
  409.         /// </devdoc>
  410.         public override long Length {
  411.             get { return m_NetworkStream.Length; }
  412.         }
  413.        
  414.         /// <devdoc>
  415.         /// <para>Gets or sets the position in the stream. Always throws <see cref='NotSupportedException'/>.</para>
  416.         /// </devdoc>
  417.         public override long Position {
  418.             get { return m_NetworkStream.Position; }
  419.            
  420.             set { m_NetworkStream.Position = value; }
  421.         }
  422.        
  423. /*
  424.         //                         
  425.         public bool DataAvailable {
  426.             get {
  427.                 return m_NetworkStream.DataAvailable;
  428.             }
  429.         }
  430.         */       
  431.        
  432.         /// <devdoc>
  433.         /// <para>Seeks a specific position in the stream.</para>
  434.         /// </devdoc>
  435.         public override long Seek(long offset, SeekOrigin origin)
  436.         {
  437.             return m_NetworkStream.Seek(offset, origin);
  438.         }
  439.        
  440.        
  441.         /// <devdoc>
  442.         /// <para> Reads data from the stream. </para>
  443.         /// </devdoc>
  444.         public override int Read(byte[] buffer, int offset, int size)
  445.         {
  446.             int result = m_NetworkStream.Read(buffer, offset, size);
  447.             GlobalLog.Dump(buffer, offset, result);
  448.             return result;
  449.         }
  450.        
  451.        
  452.         /// <devdoc>
  453.         /// <para>Writes data to the stream.</para>
  454.         /// </devdoc>
  455.         public override void Write(byte[] buffer, int offset, int size)
  456.         {
  457.             GlobalLog.Dump(buffer, offset, size);
  458.             m_NetworkStream.Write(buffer, offset, size);
  459.         }
  460.        
  461.         /// <devdoc>
  462.         /// <para>Writes multiple buffers at once</para>
  463.         /// </devdoc>
  464.         internal void MultipleWrite(BufferOffsetSize[] buffers)
  465.         {
  466.             m_NetworkStream.MultipleWrite(buffers);
  467.         }
  468.        
  469.         /// <devdoc>
  470.         /// <para>
  471.         /// Closes the stream, and then closes the underlying socket.
  472.         /// </para>
  473.         /// </devdoc>
  474.         protected override void Dispose(bool disposing)
  475.         {
  476.             try {
  477.                 if (disposing)
  478.                     CloseSocket();
  479.             }
  480.             finally {
  481.                 base.Dispose(disposing);
  482.             }
  483.         }
  484.        
  485.         internal void CloseSocket()
  486.         {
  487.             Socket socket = m_AbortSocket;
  488.             Socket socket6 = m_AbortSocket6;
  489.            
  490.             m_NetworkStream.Close();
  491.             if (socket != null) {
  492.                 socket.Close();
  493.             }
  494.             if (socket6 != null) {
  495.                 socket6.Close();
  496.             }
  497.         }
  498.        
  499.         public void Close(int timeout)
  500.         {
  501.             Socket socket = m_AbortSocket;
  502.             Socket socket6 = m_AbortSocket6;
  503.             m_NetworkStream.Close(timeout);
  504.             if (socket != null) {
  505.                 socket.Close(timeout);
  506.             }
  507.             if (socket6 != null) {
  508.                 socket6.Close(timeout);
  509.             }
  510.         }
  511.        
  512.         /// <devdoc>
  513.         /// <para>
  514.         /// Begins an asychronous read from a stream.
  515.         /// </para>
  516.         /// </devdoc>
  517.         [HostProtection(ExternalThreading = true)]
  518.         public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
  519.         {
  520.             return m_NetworkStream.BeginRead(buffer, offset, size, callback, state);
  521.         }
  522.        
  523.         internal virtual IAsyncResult UnsafeBeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
  524.         {
  525.             return m_NetworkStream.UnsafeBeginRead(buffer, offset, size, callback, state);
  526.         }
  527.        
  528.         /// <devdoc>
  529.         /// <para>
  530.         /// Handle the end of an asynchronous read.
  531.         /// </para>
  532.         /// </devdoc>
  533.         public override int EndRead(IAsyncResult asyncResult)
  534.         {
  535.             // only caller can recover the debug dump for the read result
  536.             return m_NetworkStream.EndRead(asyncResult);
  537.         }
  538.        
  539.         /// <devdoc>
  540.         /// <para>
  541.         /// Begins an asynchronous write to a stream.
  542.         /// </para>
  543.         /// </devdoc>
  544.         [HostProtection(ExternalThreading = true)]
  545.         public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
  546.         {
  547.             GlobalLog.Dump(buffer, offset, size);
  548.             return m_NetworkStream.BeginWrite(buffer, offset, size, callback, state);
  549.         }
  550.        
  551.         internal virtual IAsyncResult UnsafeBeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
  552.         {
  553.             GlobalLog.Dump(buffer, offset, size);
  554.             return m_NetworkStream.UnsafeBeginWrite(buffer, offset, size, callback, state);
  555.         }
  556.        
  557.        
  558.         /// <devdoc>
  559.         /// <para>
  560.         /// Handle the end of an asynchronous write.
  561.         /// </para>
  562.         /// </devdoc>
  563.         public override void EndWrite(IAsyncResult asyncResult)
  564.         {
  565.             m_NetworkStream.EndWrite(asyncResult);
  566.         }
  567.        
  568.         /// <devdoc>
  569.         /// <para>
  570.         /// Begins an asynchronous write to a stream.
  571.         /// </para>
  572.         /// </devdoc>
  573.         [HostProtection(ExternalThreading = true)]
  574.         internal IAsyncResult BeginMultipleWrite(BufferOffsetSize[] buffers, AsyncCallback callback, object state)
  575.         {
  576.             return m_NetworkStream.BeginMultipleWrite(buffers, callback, state);
  577.         }
  578.        
  579. /*
  580.         //                                   
  581.         internal IAsyncResult UnsafeBeginMultipleWrite(BufferOffsetSize[] buffers, AsyncCallback callback, object state) {
  582. #if TRAVE
  583.             for (int i = 0; i < buffers.Length; ++i)
  584.             {
  585.                 GlobalLog.Dump(buffers[i].Buffer, buffers[i].Offset, buffers[i].Size);
  586.             }
  587. #endif
  588.             return m_NetworkStream.UnsafeBeginMultipleWrite(buffers, callback, state);
  589.         }
  590.         */       
  591.        
  592.        
  593.         /// <devdoc>
  594.         /// <para>
  595.         /// Handle the end of an asynchronous write.
  596.         /// </para>
  597.         /// </devdoc>
  598.         internal void EndMultipleWrite(IAsyncResult asyncResult)
  599.         {
  600.             m_NetworkStream.EndMultipleWrite(asyncResult);
  601.         }
  602.        
  603.         /// <devdoc>
  604.         /// <para>
  605.         /// Flushes data from the stream.
  606.         /// </para>
  607.         /// </devdoc>
  608.         public override void Flush()
  609.         {
  610.             m_NetworkStream.Flush();
  611.         }
  612.        
  613.         /// <devdoc>
  614.         /// <para>Sets the length of the stream.</para>
  615.         /// </devdoc>
  616.         public override void SetLength(long value)
  617.         {
  618.             m_NetworkStream.SetLength(value);
  619.         }
  620.        
  621.         internal void SetSocketTimeoutOption(SocketShutdown mode, int timeout, bool silent)
  622.         {
  623.             m_NetworkStream.SetSocketTimeoutOption(mode, timeout, silent);
  624.         }
  625.        
  626.         internal bool Poll(int microSeconds, SelectMode mode)
  627.         {
  628.             return m_NetworkStream.Poll(microSeconds, mode);
  629.         }
  630.     }
  631. }
  632. // System.Net

Developer Fusion