lucenenet-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mhern...@apache.org
Subject [2/9] git commit: adding PurgeableThreadLocal with tests and default purge strategy.
Date Wed, 20 Aug 2014 02:32:06 GMT
adding PurgeableThreadLocal with tests and default purge strategy.


Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/52e11309
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/52e11309
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/52e11309

Branch: refs/heads/pcl
Commit: 52e113094b13584fe6b93b77ab1438d47e3c894b
Parents: cb748dd
Author: Michael Herndon <mherndon@michaelherndon.com>
Authored: Sun Aug 17 14:48:22 2014 -0400
Committer: Michael Herndon <mherndon@michaelherndon.com>
Committed: Sun Aug 17 14:48:22 2014 -0400

----------------------------------------------------------------------
 src/Lucene.Net.Core/Lucene.Net.Core.csproj      |   8 ++
 .../Support/AtomicReferenceArray.cs             |  35 +++--
 src/Lucene.Net.Core/Util/IPurgeStrategy.cs      |  31 ++++
 src/Lucene.Net.Core/Util/PclPurgeStrategy.cs    | 144 +++++++++++++++++++
 src/Lucene.Net.Core/Util/WeakIdentityMap.cs     |  25 +++-
 .../Lucene.Net.Core.Tests.csproj                |   1 +
 .../Util/TestPurgableThreadLocal.cs             |  50 +++++++
 .../Util/TestWeakIdentityMap.cs                 |   2 +-
 8 files changed, 280 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/src/Lucene.Net.Core/Lucene.Net.Core.csproj
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Core/Lucene.Net.Core.csproj b/src/Lucene.Net.Core/Lucene.Net.Core.csproj
index 0d2e02a..903c74d 100644
--- a/src/Lucene.Net.Core/Lucene.Net.Core.csproj
+++ b/src/Lucene.Net.Core/Lucene.Net.Core.csproj
@@ -56,6 +56,7 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
+    <Compile Include="Index\IIndexDocument.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Check.cs" />
     <Compile Include="Support\AtomicReferenceArray.cs" />
@@ -82,6 +83,8 @@
     <Compile Include="Util\IAttribute.cs" />
     <Compile Include="Util\IBits.cs" />
     <Compile Include="Util\InPlaceMergeSorter.cs" />
+    <Compile Include="Util\IPurgeStrategy.cs" />
+    <Compile Include="Util\PclPurgeStrategy.cs" />
     <Compile Include="Util\RamUsageEstimator.cs" />
     <Compile Include="Util\SetOnce.cs" />
     <Compile Include="Util\Sorter.cs" />
@@ -90,6 +93,7 @@
     <Compile Include="Util\UnicodeUtil.cs" />
     <Compile Include="Util\Version.cs" />
     <Compile Include="Util\WeakIdentityMap.cs" />
+    <Compile Include="Util\PurgableThreadLocal.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
@@ -99,6 +103,10 @@
       <HintPath>..\..\packages\Microsoft.Framework.ConfigurationModel\1.0.0-alpha4-10193\lib\portable-net451+win81+wpa81\Microsoft.Framework.ConfigurationModel.dll</HintPath>
     </Reference>
   </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Analysis\" />
+    <Folder Include="Document\" />
+  </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets"
/>
   <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')"
/>
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/src/Lucene.Net.Core/Support/AtomicReferenceArray.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Core/Support/AtomicReferenceArray.cs b/src/Lucene.Net.Core/Support/AtomicReferenceArray.cs
index 519a038..c829ee2 100644
--- a/src/Lucene.Net.Core/Support/AtomicReferenceArray.cs
+++ b/src/Lucene.Net.Core/Support/AtomicReferenceArray.cs
@@ -1,9 +1,20 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 
 namespace Lucene.Net.Support
 {
@@ -14,7 +25,7 @@ namespace Lucene.Net.Support
     public class AtomicReferenceArray<T>
     {
         // ReSharper disable once StaticFieldInGenericType
-        private static readonly object SyncLock = new object();
+        private static readonly object s_syncLock = new object();
         private readonly T[] array;
 
 
@@ -60,6 +71,10 @@ namespace Lucene.Net.Support
             set { this.Set(index, value);}
         }
 
+        /// <summary>
+        /// Gets the length.
+        /// </summary>
+        /// <value>The length.</value>
         public int Length
         {
             get { return this.array.Length; }
@@ -75,7 +90,7 @@ namespace Lucene.Net.Support
         /// <returns><c>true</c> if the index was updated, <c>false</c>
otherwise.</returns>
         public bool CompareAndSet(int index, T expected, T value)
         {
-            lock (SyncLock)
+            lock (s_syncLock)
             {
                 var currentValue = this.array[index];
                 if (!expected.Equals(currentValue)) 
@@ -114,7 +129,7 @@ namespace Lucene.Net.Support
         {
             //Check.InRangeOfLength(0, this.Length, index);
 
-            lock (SyncLock)
+            lock (s_syncLock)
             {
                 var currentValue = this.array[index];
                 this.array[index] = value;
@@ -135,7 +150,7 @@ namespace Lucene.Net.Support
         {
           
 
-            lock (SyncLock)
+            lock (s_syncLock)
             {
                 array[index] = value;
             }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/src/Lucene.Net.Core/Util/IPurgeStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Core/Util/IPurgeStrategy.cs b/src/Lucene.Net.Core/Util/IPurgeStrategy.cs
new file mode 100644
index 0000000..de646bb
--- /dev/null
+++ b/src/Lucene.Net.Core/Util/IPurgeStrategy.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Lucene.Net.Util
+{
+
+
+    /// <summary>
+    ///  Contract for a strategy that removes dead object references created by 
+    /// <see cref="PurgeableThreadLocal{T}"/>
+    /// </summary>
+    public interface IPurgeStrategy : IDisposable
+    {
+        /// <summary>
+        /// Adds the value asynchronously.
+        /// </summary>
+        /// <param name="value">The value.</param>
+        /// <returns><see cref="Task"/></returns>
+        Task AddAsync(object value);
+
+        /// <summary>
+        /// Purges the object references asynchronously.
+        /// </summary>
+        /// <returns><see cref="Task"/></returns>
+        Task PurgeAsync();
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/src/Lucene.Net.Core/Util/PclPurgeStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Core/Util/PclPurgeStrategy.cs b/src/Lucene.Net.Core/Util/PclPurgeStrategy.cs
new file mode 100644
index 0000000..2ad9425
--- /dev/null
+++ b/src/Lucene.Net.Core/Util/PclPurgeStrategy.cs
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Lucene.Net.Util
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Threading;
+    using System.Threading.Tasks;
+
+    /// <summary>
+    /// Default <see cref="IPurgeStrategy"/> for <see cref="PurgeableThreadLocal{T}"/>
that 
+    /// uses <see cref="GC"/> and <see cref="WeakReference"/>s to determine which
objects
+    /// should be purged.
+    /// </summary>
+    public class PclPurgeStrategy : IPurgeStrategy
+    {
+        private readonly List<object> hardReferences = new List<object>();
+        private readonly List<WeakReference> weakReferences = new List<WeakReference>();

+        private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
+
+        // Increase this to decrease frequency of purging in get:
+        private const int PurgeMultiplier = 20;
+
+        // On each get or set we decrement this; when it hits 0 we
+        // purge.  After purge, we set this to
+        // PURGE_MULTIPLIER * stillAliveCount.  this keeps
+        // amortized cost of purging linear.
+        private int countUntilPurge = PurgeMultiplier;
+
+
+        /// <summary>
+        /// add as an asynchronous operation.
+        /// </summary>
+        /// <param name="value">The value.</param>
+        /// <returns><see cref="Task" /></returns>
+        public async Task AddAsync(object value)
+        {
+            await this.semaphoreSlim.WaitAsync();
+
+            try
+            {
+                this.hardReferences.Add(value);
+                this.weakReferences.Add(new WeakReference(value));
+            }
+            finally
+            {
+                this.semaphoreSlim.Release();
+            }
+        }
+
+        /// <summary>
+        /// Purges the object references asynchronously.
+        /// </summary>
+        /// <returns><see cref="Task" /></returns>
+        public async Task PurgeAsync()
+        {
+            if (Interlocked.Decrement(ref this.countUntilPurge) != 0) 
+                return;
+
+            var sem = new SemaphoreSlim(1);
+            await this.semaphoreSlim.WaitAsync();
+            try
+            {
+                var stillAliveCount = 0;
+                
+                this.hardReferences.Clear();
+
+                GC.Collect();
+
+                await Task.Delay(1000);
+
+                foreach (var reference in this.weakReferences)
+                {
+                    if (!reference.IsAlive) 
+                        continue;
+
+                    this.hardReferences.Add(reference);
+                    stillAliveCount++;
+                }
+
+                var nextCount = (1 + stillAliveCount)*PurgeMultiplier;
+                if (nextCount <= 0)
+                {
+                    // defensive: int overflow!
+                    nextCount = 1000000;
+                }
+
+                Interlocked.Exchange(ref this.countUntilPurge, nextCount);
+
+            }
+            finally
+            {
+                this.semaphoreSlim.Release();
+            }
+        }
+
+        /// <summary>
+        /// Performs application-defined tasks associated with freeing, releasing, or resetting
unmanaged resources.
+        /// </summary>
+        public void Dispose()
+        {
+            GC.SuppressFinalize(this);
+            this.Dispose(true);
+        }
+
+        /// <summary>
+        /// Releases unmanaged and - optionally - managed resources.
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed
and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!disposing)
+                return;
+
+            this.semaphoreSlim.Dispose();
+            this.hardReferences.Clear();
+            this.weakReferences.Clear();
+        }
+
+        /// <summary>
+        /// Finalizes an instance of the <see cref="PclPurgeStrategy"/> class.
+        /// </summary>
+        ~PclPurgeStrategy()
+        {
+            this.Dispose(false);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/src/Lucene.Net.Core/Util/WeakIdentityMap.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Core/Util/WeakIdentityMap.cs b/src/Lucene.Net.Core/Util/WeakIdentityMap.cs
index f3916aa..cfa456e 100644
--- a/src/Lucene.Net.Core/Util/WeakIdentityMap.cs
+++ b/src/Lucene.Net.Core/Util/WeakIdentityMap.cs
@@ -75,7 +75,8 @@ namespace Lucene.Net.Util
         }
 
         /// <summary>
-        /// Returns <c>True</c> when this map contains no key-value mappings.
</summary>
+        /// Returns <c>True</c> when this map contains no key-value mappings.

+        /// </summary>
         public bool Empty
         {
             get
@@ -84,6 +85,10 @@ namespace Lucene.Net.Util
             }
         }
 
+        /// <summary>
+        /// Gets the keys.
+        /// </summary>
+        /// <value>The keys.</value>
         public IEnumerable<TKey> Keys
         {
             // .NET port: using this method which mimics IDictionary instead of KeyIterator()
@@ -104,6 +109,10 @@ namespace Lucene.Net.Util
             }
         }
 
+        /// <summary>
+        /// Gets the values.
+        /// </summary>
+        /// <value>The values.</value>
         public IEnumerable<TValue> Values
         {
             get
@@ -123,7 +132,7 @@ namespace Lucene.Net.Util
         }
 
         /// <summary>
-        /// Creates a new {@code WeakIdentityMap} based on a <seealso cref="ConcurrentHashMap"/>.
</summary>
+        /// Initializes a new <see cref="WeakIdentityMap{TKey,TValue}"/> based on a
<seealso cref="ConcurrentHashMap"/>. </summary>
         /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans
up the reference queue on every read operation</a>. </param>
         public static WeakIdentityMap<TKey, TValue> NewConcurrentHashMap(bool reapOnRead)
         {
@@ -131,7 +140,7 @@ namespace Lucene.Net.Util
         }
 
         /// <summary>
-        /// Creates a new {@code WeakIdentityMap} based on a non-synchronized <seealso
cref="HashMap"/>.
+        /// Initializes a new <see cref="WeakIdentityMap{TKey,TValue}"/> based on a
non-synchronized <seealso cref="HashMap"/>.
         /// The map <a href="#reapInfo">cleans up the reference queue on every read
operation</a>.
         /// </summary>
         public static WeakIdentityMap<TKey, TValue> NewHashMap()
@@ -140,7 +149,7 @@ namespace Lucene.Net.Util
         }
 
         /// <summary>
-        /// Creates a new {@code WeakIdentityMap} based on a non-synchronized <seealso
cref="HashMap"/>. </summary>
+        /// Initializes a new <see cref="WeakIdentityMap{TKey,TValue}"/> based on a
non-synchronized <seealso cref="HashMap"/>. </summary>
         /// <param name="reapOnRead"> controls if the map <a href="#reapInfo">cleans
up the reference queue on every read operation</a>. </param>
         public static WeakIdentityMap<TKey, TValue> NewHashMap(bool reapOnRead)
         {
@@ -178,7 +187,13 @@ namespace Lucene.Net.Util
             return value;
         }
 
-        
+
+        /// <summary>
+        /// Stores the specified value with the associated key.
+        /// </summary>
+        /// <param name="key">The key.</param>
+        /// <param name="value">The value.</param>
+        /// <returns>TValue.</returns>
         public TValue Put(TKey key, TValue value)
         {
             this.Reap();

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/test/Lucene.Net.Core.Tests/Lucene.Net.Core.Tests.csproj
----------------------------------------------------------------------
diff --git a/test/Lucene.Net.Core.Tests/Lucene.Net.Core.Tests.csproj b/test/Lucene.Net.Core.Tests/Lucene.Net.Core.Tests.csproj
index 5ed7234..20870d0 100644
--- a/test/Lucene.Net.Core.Tests/Lucene.Net.Core.Tests.csproj
+++ b/test/Lucene.Net.Core.Tests/Lucene.Net.Core.Tests.csproj
@@ -67,6 +67,7 @@
     <Compile Include="Util\TestCharsRef.cs" />
     <Compile Include="Util\TestConstants.cs" />
     <Compile Include="Util\TestInPlaceMergeSorter.cs" />
+    <Compile Include="Util\TestPurgableThreadLocal.cs" />
     <Compile Include="Util\TestRamEstimatorUsage.cs" />
     <Compile Include="Util\TestSetOnce.cs" />
     <Compile Include="Util\TestVersion.cs" />

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/test/Lucene.Net.Core.Tests/Util/TestPurgableThreadLocal.cs
----------------------------------------------------------------------
diff --git a/test/Lucene.Net.Core.Tests/Util/TestPurgableThreadLocal.cs b/test/Lucene.Net.Core.Tests/Util/TestPurgableThreadLocal.cs
new file mode 100644
index 0000000..f0a1ad4
--- /dev/null
+++ b/test/Lucene.Net.Core.Tests/Util/TestPurgableThreadLocal.cs
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Lucene.Net.Util
+{
+    public class TestPurgeableThreadLocal : LuceneTestCase
+    {
+        public const string TEST_VALUE = "initvaluetest";
+
+        [Test]
+        public virtual void TestInitialValue()
+        {
+            var tl = new PurgeableThreadLocal<string>(() => TEST_VALUE);
+            string str = tl.Value;
+            Equal(TEST_VALUE, str);
+        }
+
+        [Test]
+        public virtual void TestNullValue()
+        {
+            // Tests that null can be set as a valid value (LUCENE-1805). this
+            // previously failed in get().
+            var ctl = new PurgeableThreadLocal<object> {Value = null};
+            Null(ctl.Value);
+        }
+
+        [Test]
+        public virtual void TestDefaultValueWithoutSetting()
+        {
+            // LUCENE-1805: make sure default get returns null,
+            // twice in a row
+            var ctl = new PurgeableThreadLocal<object>();
+            Null(ctl.Value);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/52e11309/test/Lucene.Net.Core.Tests/Util/TestWeakIdentityMap.cs
----------------------------------------------------------------------
diff --git a/test/Lucene.Net.Core.Tests/Util/TestWeakIdentityMap.cs b/test/Lucene.Net.Core.Tests/Util/TestWeakIdentityMap.cs
index 8c1098f..ee18cac 100644
--- a/test/Lucene.Net.Core.Tests/Util/TestWeakIdentityMap.cs
+++ b/test/Lucene.Net.Core.Tests/Util/TestWeakIdentityMap.cs
@@ -83,7 +83,7 @@ namespace Lucene.Net.Util
 
             Equal(2, map.Count);
 
-            // this will not work in .NET, key1, key2, key3 are the same reference
+            // this will not work in .NET: key1, key2, key3 are the same reference
             // Equal("bar1", map.Get(key1));
             // Equal(null, map.Get(key3));
             Equal("bar2", map.Get(key2));


Mime
View raw message