Merge pull request #1624 from esdrubal/getprocesstimes
[mono.git] / mcs / class / System / System.Net / ServicePoint.cs
index 12895c441dcb8ceeb8cfbca93b096aa9f3efbf2a..2a2c1cb048fa6aa63336ee442c7902bb971fa39c 100644 (file)
@@ -31,6 +31,8 @@
 //
 
 using System;
+using System.Collections.Generic;
+using System.Diagnostics;
 using System.Collections;
 using System.Net.Sockets;
 using System.Security.Cryptography.X509Certificates;
@@ -50,26 +52,26 @@ namespace System.Net
                X509Certificate clientCertificate;
                IPHostEntry host;
                bool usesProxy;
-               Hashtable groups;
+               Dictionary<string,WebConnectionGroup> groups;
                bool sendContinue = true;
                bool useConnect;
-               object locker = new object ();
                object hostE = new object ();
                bool useNagle;
                BindIPEndPoint endPointCallback = null;
                bool tcp_keepalive;
                int tcp_keepalive_time;
                int tcp_keepalive_interval;
-               
+               Timer idleTimer;
+
                // Constructors
 
                internal ServicePoint (Uri uri, int connectionLimit, int maxIdleTime)
                {
                        this.uri = uri;  
                        this.connectionLimit = connectionLimit;
-                       this.maxIdleTime = maxIdleTime;                 
+                       this.maxIdleTime = maxIdleTime; 
                        this.currentConnections = 0;
-                       this.idleSince = DateTime.Now;
+                       this.idleSince = DateTime.UtcNow;
                }
                
                // Properties
@@ -130,20 +132,21 @@ namespace System.Net
 
                public DateTime IdleSince {
                        get {
-                               return idleSince;
-                       }
-                       internal set {
-                               lock (locker)
-                                       idleSince = value;
+                               return idleSince.ToLocalTime ();
                        }
                }
-               
+
                public int MaxIdleTime {
                        get { return maxIdleTime; }
                        set { 
                                if (value < Timeout.Infinite || value > Int32.MaxValue)
                                        throw new ArgumentOutOfRangeException ();
-                               this.maxIdleTime = value; 
+
+                               lock (this) {
+                                       maxIdleTime = value;
+                                       if (idleTimer != null)
+                                               idleTimer.Change (maxIdleTime, maxIdleTime);
+                               }
                        }
                }
                
@@ -237,21 +240,103 @@ namespace System.Net
                        set { useConnect = value; }
                }
 
-               internal bool AvailableForRecycling {
-                       get { 
-                               return CurrentConnections == 0
-                                   && maxIdleTime != Timeout.Infinite
-                                   && DateTime.Now >= IdleSince.AddMilliseconds (maxIdleTime);
-                       }
+               WebConnectionGroup GetConnectionGroup (string name)
+               {
+                       if (name == null)
+                               name = "";
+
+                       /*
+                        * Optimization:
+                        * 
+                        * In the vast majority of cases, we only have one single WebConnectionGroup per ServicePoint, so we
+                        * don't need to allocate a dictionary.
+                        * 
+                        */
+
+                       WebConnectionGroup group;
+                       if (groups != null && groups.TryGetValue (name, out group))
+                               return group;
+
+                       group = new WebConnectionGroup (this, name);
+                       group.ConnectionClosed += (s, e) => currentConnections--;
+
+                       if (groups == null)
+                               groups = new Dictionary<string, WebConnectionGroup> ();
+                       groups.Add (name, group);
+
+                       return group;
                }
 
-               internal Hashtable Groups {
-                       get {
-                               if (groups == null)
-                                       groups = new Hashtable ();
+               void RemoveConnectionGroup (WebConnectionGroup group)
+               {
+                       if (groups == null || groups.Count == 0)
+                               throw new InvalidOperationException ();
+
+                       groups.Remove (group.Name);
+               }
+
+               bool CheckAvailableForRecycling (out DateTime outIdleSince)
+               {
+                       outIdleSince = DateTime.MinValue;
 
-                               return groups;
+                       TimeSpan idleTimeSpan;
+                       List<WebConnectionGroup> groupList = null, removeList = null;
+                       lock (this) {
+                               if (groups == null || groups.Count == 0) {
+                                       idleSince = DateTime.MinValue;
+                                       return true;
+                               }
+
+                               idleTimeSpan = TimeSpan.FromMilliseconds (maxIdleTime);
+
+                               /*
+                                * WebConnectionGroup.TryRecycle() must run outside the lock, so we need to
+                                * copy the group dictionary if it exists.
+                                * 
+                                * In most cases, we only have a single connection group, so we can simply store
+                                * that in a local variable instead of copying a collection.
+                                * 
+                                */
+
+                               groupList = new List<WebConnectionGroup> (groups.Values);
+                       }
+
+                       foreach (var group in groupList) {
+                               if (!group.TryRecycle (idleTimeSpan, ref outIdleSince))
+                                       continue;
+                               if (removeList == null)
+                                       removeList = new List<WebConnectionGroup> ();
+                               removeList.Add (group);
                        }
+
+                       lock (this) {
+                               idleSince = outIdleSince;
+
+                               if (removeList != null) {
+                                       foreach (var group in removeList)
+                                               if (groups.ContainsKey (group.Name))
+                                                       RemoveConnectionGroup (group);
+                               }
+
+                               if (groups != null && groups.Count == 0)
+                                       groups = null;
+
+                               if (groups == null) {
+                                       if (idleTimer != null) {
+                                               idleTimer.Dispose ();
+                                               idleTimer = null;
+                                       }
+                                       return true;
+                               }
+
+                               return false;
+                       }
+               }
+
+               void IdleTimerCallback (object obj)
+               {
+                       DateTime dummy;
+                       CheckAvailableForRecycling (out dummy);
                }
 
                internal IPHostEntry HostEntry
@@ -297,39 +382,30 @@ namespace System.Net
                        protocolVersion = version;
                }
 
-#if !TARGET_JVM
-               WebConnectionGroup GetConnectionGroup (string name)
-               {
-                       if (name == null)
-                               name = "";
-
-                       WebConnectionGroup group = Groups [name] as WebConnectionGroup;
-                       if (group != null)
-                               return group;
-
-                       group = new WebConnectionGroup (this, name);
-                       Groups [name] = group;
-                       return group;
-               }
-
                internal EventHandler SendRequest (HttpWebRequest request, string groupName)
                {
                        WebConnection cnc;
                        
-                       lock (locker) {
+                       lock (this) {
+                               bool created;
                                WebConnectionGroup cncGroup = GetConnectionGroup (groupName);
-                               cnc = cncGroup.GetConnection (request);
+                               cnc = cncGroup.GetConnection (request, out created);
+                               if (created) {
+                                       ++currentConnections;
+                                       if (idleTimer == null)
+                                               idleTimer = new Timer (IdleTimerCallback, null, maxIdleTime, maxIdleTime);
+                               }
                        }
                        
                        return cnc.SendRequest (request);
                }
-#endif
                public bool CloseConnectionGroup (string connectionGroupName)
                {
-                       lock (locker) {
+                       lock (this) {
                                WebConnectionGroup cncGroup = GetConnectionGroup (connectionGroupName);
                                if (cncGroup != null) {
                                        cncGroup.Close ();
+                                       RemoveConnectionGroup (cncGroup);
                                        return true;
                                }
                        }
@@ -337,23 +413,6 @@ namespace System.Net
                        return false;
                }
 
-               internal void IncrementConnection ()
-               {
-                       lock (locker) {
-                               currentConnections++;
-                               idleSince = DateTime.Now.AddMilliseconds (1000000);
-                       }
-               }
-
-               internal void DecrementConnection ()
-               {
-                       lock (locker) {
-                               currentConnections--;
-                               if (currentConnections == 0)
-                                       idleSince = DateTime.Now;
-                       }
-               }
-
                internal void SetCertificates (X509Certificate client, X509Certificate server) 
                {
                        certificate = server;