Lucene.Net.Core.Util.SetOnce: Changed back to original implementation that uses AtomicBoolean rather than Interlocked. Also added a class generic constraint to revert back to using the volatile keyword and fixed the test to use a reference type for testing.
Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/50bbf51b Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/50bbf51b Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/50bbf51b Branch: refs/heads/api-work Commit: 50bbf51be1674906a4457cf155d86284a79feb73 Parents: d9f979c Author: Shad Storhaug <[email protected]> Authored: Thu Mar 23 23:18:44 2017 +0700 Committer: Shad Storhaug <[email protected]> Committed: Thu Mar 23 23:35:01 2017 +0700 ---------------------------------------------------------------------- src/Lucene.Net.Core/Util/SetOnce.cs | 33 +++++++++---------- src/Lucene.Net.Tests/Util/TestSetOnce.cs | 47 +++++++++++++++++---------- 2 files changed, 45 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucenenet/blob/50bbf51b/src/Lucene.Net.Core/Util/SetOnce.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Core/Util/SetOnce.cs b/src/Lucene.Net.Core/Util/SetOnce.cs index 2710842..2ac06fe 100644 --- a/src/Lucene.Net.Core/Util/SetOnce.cs +++ b/src/Lucene.Net.Core/Util/SetOnce.cs @@ -1,5 +1,5 @@ +using Lucene.Net.Support; using System; -using System.Threading; namespace Lucene.Net.Util { @@ -23,16 +23,16 @@ namespace Lucene.Net.Util /// <summary> /// A convenient class which offers a semi-immutable object wrapper /// implementation which allows one to set the value of an object exactly once, - /// and retrieve it many times. If <seealso cref="#set(Object)"/> is called more than once, - /// <seealso cref="AlreadySetException"/> is thrown and the operation + /// and retrieve it many times. If <see cref="Set(T)"/> is called more than once, + /// <see cref="AlreadySetException"/> is thrown and the operation /// will fail. - /// + /// <para/> /// @lucene.experimental /// </summary> - public sealed class SetOnce<T> + public sealed class SetOnce<T> where T : class // LUCENENET specific - added class constraint so we don't accept value types (which cannot be volatile) { /// <summary> - /// Thrown when <seealso cref="SetOnce#set(Object)"/> is called more than once. </summary> + /// Thrown when <see cref="SetOnce.Set(T)"/> is called more than once. </summary> // LUCENENET: All exeption classes should be marked serializable #if FEATURE_SERIALIZABLE [Serializable] @@ -45,37 +45,36 @@ namespace Lucene.Net.Util } } - private T obj = default(T); - private int set; + private volatile T obj = default(T); + private readonly AtomicBoolean set; /// <summary> /// A default constructor which does not set the internal object, and allows - /// setting it by calling <seealso cref="#set(Object)"/>. + /// setting it by calling <see cref="Set(T)"/>. /// </summary> public SetOnce() { - set = 0; + set = new AtomicBoolean(false); } /// <summary> /// Creates a new instance with the internal object set to the given object. - /// Note that any calls to <seealso cref="#set(Object)"/> afterwards will result in - /// <seealso cref="AlreadySetException"/> + /// Note that any calls to <see cref="Set(T)"/> afterwards will result in + /// <see cref="AlreadySetException"/> /// </summary> /// <exception cref="AlreadySetException"> if called more than once </exception> - /// <seealso cref= #set(Object) </seealso> + /// <seealso cref="Set(T)"/> public SetOnce(T obj) { this.obj = obj; - set = 1; + set = new AtomicBoolean(true); } /// <summary> /// Sets the given object. If the object has already been set, an exception is thrown. </summary> public void Set(T obj) { - //if (set.compareAndSet(false, true)) - if (Interlocked.CompareExchange(ref set, 1, 0) == 0) + if (set.CompareAndSet(false, true)) { this.obj = obj; } @@ -86,7 +85,7 @@ namespace Lucene.Net.Util } /// <summary> - /// Returns the object set by <seealso cref="#set(Object)"/>. </summary> + /// Returns the object set by <see cref="Set(T)"/>. </summary> public T Get() { return obj; http://git-wip-us.apache.org/repos/asf/lucenenet/blob/50bbf51b/src/Lucene.Net.Tests/Util/TestSetOnce.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Tests/Util/TestSetOnce.cs b/src/Lucene.Net.Tests/Util/TestSetOnce.cs index fd53d74..e54c080 100644 --- a/src/Lucene.Net.Tests/Util/TestSetOnce.cs +++ b/src/Lucene.Net.Tests/Util/TestSetOnce.cs @@ -1,7 +1,8 @@ -using System.Threading; using Lucene.Net.Support; using NUnit.Framework; using System; +using System.Globalization; +using System.Threading; namespace Lucene.Net.Util { @@ -25,10 +26,20 @@ namespace Lucene.Net.Util [TestFixture] public class TestSetOnce : LuceneTestCase { + private class Integer // LUCENENET specific class for testing (since int? is not a reference type) + { + public Integer(int value) + { + this.value = value; + } + + internal int value; + } + private sealed class SetOnceThread : ThreadClass { - internal SetOnce<int?> Set; - internal bool Success = false; + internal SetOnce<Integer> set; + internal bool success = false; internal readonly Random RAND; public SetOnceThread(Random random) @@ -41,8 +52,8 @@ namespace Lucene.Net.Util try { Sleep(RAND.Next(10)); // sleep for a short time - Set.Set(new int?(Convert.ToInt32(Name.Substring(2)))); - Success = true; + set.Set(new Integer(Convert.ToInt32(Name.Substring(2), CultureInfo.InvariantCulture))); + success = true; } #if !NETSTANDARD catch (ThreadInterruptedException) @@ -54,7 +65,7 @@ namespace Lucene.Net.Util { // TODO: change exception type // expected. - Success = false; + success = false; } } } @@ -62,38 +73,38 @@ namespace Lucene.Net.Util [Test] public virtual void TestEmptyCtor() { - SetOnce<int?> set = new SetOnce<int?>(); + SetOnce<Integer> set = new SetOnce<Integer>(); Assert.IsNull(set.Get()); } [Test] public virtual void TestSettingCtor() { - SetOnce<int?> set = new SetOnce<int?>(new int?(5)); - Assert.AreEqual(5, (int)set.Get()); - Assert.Throws<SetOnce<int?>.AlreadySetException>(() => set.Set(new int?(7))); + SetOnce<Integer> set = new SetOnce<Integer>(new Integer(5)); + Assert.AreEqual(5, set.Get().value); + Assert.Throws<SetOnce<Integer>.AlreadySetException>(() => set.Set(new Integer(7))); } [Test] public virtual void TestSetOnce_mem() { - SetOnce<int?> set = new SetOnce<int?>(); - set.Set(new int?(5)); - Assert.AreEqual(5, (int)set.Get()); - Assert.Throws<SetOnce<int?>.AlreadySetException>(() => set.Set(new int?(7))); + SetOnce<Integer> set = new SetOnce<Integer>(); + set.Set(new Integer(5)); + Assert.AreEqual(5, set.Get().value); + Assert.Throws<SetOnce<Integer>.AlreadySetException>(() => set.Set(new Integer(7))); } [Test] public virtual void TestSetMultiThreaded() { - SetOnce<int?> set = new SetOnce<int?>(); + SetOnce<Integer> set = new SetOnce<Integer>(); SetOnceThread[] threads = new SetOnceThread[10]; Random random = Random(); for (int i = 0; i < threads.Length; i++) { threads[i] = new SetOnceThread(random); threads[i].Name = "t-" + (i + 1); - threads[i].Set = set; + threads[i].set = set; } foreach (ThreadClass t in threads) @@ -108,10 +119,10 @@ namespace Lucene.Net.Util foreach (SetOnceThread t in threads) { - if (t.Success) + if (t.success) { int expectedVal = Convert.ToInt32(t.Name.Substring(2)); - Assert.AreEqual(expectedVal, t.Set.Get(), "thread " + t.Name); + Assert.AreEqual(expectedVal, t.set.Get().value, "thread " + t.Name); } } }
