IGNITE-1292: Moved handle registry to Ignite. Contributed by Pavel Tupitsyn.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/7c351bf3 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/7c351bf3 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/7c351bf3 Branch: refs/heads/ignite-1093 Commit: 7c351bf3e0ef5fd28b88d9a279a32f4796a60c91 Parents: 08be80f Author: vozerov-gridgain <[email protected]> Authored: Wed Aug 26 12:10:32 2015 +0300 Committer: vozerov-gridgain <[email protected]> Committed: Wed Aug 26 12:10:32 2015 +0300 ---------------------------------------------------------------------- .../Apache.Ignite.Core.csproj | 3 + .../Apache.Ignite.Core/Impl/Handle/Handle.cs | 69 ++++ .../Impl/Handle/HandleRegistry.cs | 340 +++++++++++++++++++ .../Apache.Ignite.Core/Impl/Handle/IHandle.cs | 35 ++ 4 files changed, 447 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/7c351bf3/modules/platform/src/main/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj index c6c8324..658e5fb 100644 --- a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -48,6 +48,9 @@ </ItemGroup> <ItemGroup> <Compile Include="Ignition.cs" /> + <Compile Include="Impl\Handle\Handle.cs" /> + <Compile Include="Impl\Handle\HandleRegistry.cs" /> + <Compile Include="Impl\Handle\IHandle.cs" /> <Compile Include="Impl\Memory\IPlatformMemory.cs" /> <Compile Include="Impl\Memory\PlatformBigEndianMemoryStream.cs" /> <Compile Include="Impl\Memory\PlatformMemory.cs" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/7c351bf3/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/Handle.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/Handle.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/Handle.cs new file mode 100644 index 0000000..0168963 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/Handle.cs @@ -0,0 +1,69 @@ +/* + * 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 Apache.Ignite.Core.Impl.Handle +{ + using System; + using System.Threading; + + /// <summary> + /// Wrapper over some resource ensuring it's release. + /// </summary> + public class Handle<T> : IHandle + { + /** Target.*/ + private readonly T _target; + + /** Release action. */ + private readonly Action<T> _releaseAction; + + /** Release flag. */ + private int _released; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="target">Target.</param> + /// <param name="releaseAction">Release action.</param> + public Handle(T target, Action<T> releaseAction) + { + _target = target; + _releaseAction = releaseAction; + } + + /// <summary> + /// Target. + /// </summary> + public T Target + { + get { return _target; } + } + + /** <inheritdoc /> */ + public void Release() + { + if (Interlocked.CompareExchange(ref _released, 1, 0) == 0) + _releaseAction(_target); + } + + /** <inheritdoc /> */ + public bool Released + { + get { return Thread.VolatileRead(ref _released) == 1; } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7c351bf3/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/HandleRegistry.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/HandleRegistry.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/HandleRegistry.cs new file mode 100644 index 0000000..2a67c41 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/HandleRegistry.cs @@ -0,0 +1,340 @@ +/* + * 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 Apache.Ignite.Core.Impl.Handle +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + + /// <summary> + /// Resource registry. + /// </summary> + public class HandleRegistry + { + /** Default critical resources capacity. */ + internal const int DfltFastCap = 1024; + + /** Array for fast-path. */ + private readonly object[] _fast; + + /** Dictionery for slow-path. */ + private readonly ConcurrentDictionary<long, object> _slow; + + /** Capacity of fast array. */ + private readonly int _fastCap; + + /** Counter for fast-path. */ + private int _fastCtr; + + /** Counter for slow-path. */ + private long _slowCtr; + + /** Close flag. */ + private int _closed; + + /// <summary> + /// Constructor. + /// </summary> + public HandleRegistry() : this(DfltFastCap) + { + // No-op. + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="fastCap">Amount of critical resources this registry can allocate in "fast" mode.</param> + public HandleRegistry(int fastCap) + { + _fastCap = fastCap; + _fast = new object[fastCap]; + + _slow = new ConcurrentDictionary<long, object>(); + _slowCtr = fastCap; + } + + /// <summary> + /// Allocate a handle for resource. + /// </summary> + /// <param name="target">Target.</param> + /// <returns>Pointer.</returns> + public long Allocate(object target) + { + return Allocate0(target, false, false); + } + + /// <summary> + /// Allocate a handle in safe mode. + /// </summary> + /// <param name="target">Target.</param> + /// <returns>Pointer.</returns> + public long AllocateSafe(object target) + { + return Allocate0(target, false, true); + } + + /// <summary> + /// Allocate a handle for critical resource. + /// </summary> + /// <param name="target">Target.</param> + /// <returns>Pointer.</returns> + public long AllocateCritical(object target) + { + return Allocate0(target, true, false); + } + + /// <summary> + /// Allocate a handle for critical resource in safe mode. + /// </summary> + /// <param name="target">Target.</param> + /// <returns>Pointer.</returns> + public long AllocateCriticalSafe(object target) + { + return Allocate0(target, true, true); + } + + /// <summary> + /// Internal allocation routine. + /// </summary> + /// <param name="target">Target.</param> + /// <param name="critical">Critical flag.</param> + /// <param name="safe">Safe flag.</param> + /// <returns>Pointer.</returns> + private long Allocate0(object target, bool critical, bool safe) + { + if (Closed) + throw ClosedException(); + + // Try allocating on critical path. + if (critical) + { + if (_fastCtr < _fastCap) // Non-volatile read could yield in old value, but increment resolves this. + { + int fastIdx = Interlocked.Increment(ref _fastCtr); + + if (fastIdx < _fastCap) + { + Thread.VolatileWrite(ref _fast[fastIdx], target); + + if (safe && Closed) + { + Thread.VolatileWrite(ref _fast[fastIdx], null); + + Release0(target, true); + + throw ClosedException(); + } + + return fastIdx; + } + } + } + + // Critical allocation failed, fallback to slow mode. + long slowIdx = Interlocked.Increment(ref _slowCtr); + + _slow[slowIdx] = target; + + if (safe && Closed) + { + _slow[slowIdx] = null; + + Release0(target, true); + + throw ClosedException(); + } + + return slowIdx; + } + + + /// <summary> + /// Release handle. + /// </summary> + /// <param name="id">Identifier.</param> + /// <param name="quiet">Whether release must be quiet or not.</param> + public void Release(long id, bool quiet = false) + { + if (id < _fastCap) + { + object target = Thread.VolatileRead(ref _fast[id]); + + if (target != null) + { + Thread.VolatileWrite(ref _fast[id], null); + + Release0(target, quiet); + } + } + else + { + object target; + + if (_slow.TryRemove(id, out target)) + Release0(target, quiet); + } + } + + /// <summary> + /// Internal release routine. + /// </summary> + /// <param name="target">Target.</param> + /// <param name="quiet">Whether release must be quiet or not.</param> + private static void Release0(object target, bool quiet) + { + IHandle target0 = target as IHandle; + + if (target0 != null) + { + if (quiet) + { + try + { + target0.Release(); + } + catch (Exception) + { + // No-op. + } + } + else + target0.Release(); + } + } + + /// <summary> + /// Gets handle target. + /// </summary> + /// <param name="id">Identifier.</param> + /// <returns>Target.</returns> + public T Get<T>(long id) + { + return Get<T>(id, false); + } + + /// <summary> + /// Gets handle target. + /// </summary> + /// <param name="id">Identifier.</param> + /// <param name="throwOnAbsent">Whether to throw an exception if resource is not found.</param> + /// <returns>Target.</returns> + public T Get<T>(long id, bool throwOnAbsent) + { + object target; + + if (id < _fastCap) + { + target = Thread.VolatileRead(ref _fast[id]); + + if (target != null) + return (T)target; + } + else + { + if (_slow.TryGetValue(id, out target)) + return (T) target; + } + + if (throwOnAbsent) + throw new InvalidOperationException("Resource handle has been released (is grid stopping?)."); + + return default(T); + } + + /// <summary> + /// Close the registry. All resources allocated at the moment of close are + /// guaranteed to be released. + /// </summary> + public void Close() + { + if (Interlocked.CompareExchange(ref _closed, 1, 0) == 0) + { + // Cleanup on fast-path. + for (int i = 0; i < _fastCap; i++) + { + object target = Thread.VolatileRead(ref _fast[i]); + + if (target != null) + { + Thread.VolatileWrite(ref _fast[i], null); + + Release0(target, true); + } + } + + // Cleanup on slow-path. + foreach (var item in _slow) + { + object target = item.Value; + + if (target != null) + Release0(target, true); + } + + _slow.Clear(); + } + } + + /// <summary> + /// Closed flag. + /// </summary> + public bool Closed + { + get { return Thread.VolatileRead(ref _closed) == 1; } + } + + /// <summary> + /// Gets the current handle count. + /// </summary> + public int Count + { + get + { + Thread.MemoryBarrier(); + + return _fast.Count(x => x != null) + _slow.Count; + } + } + + /// <summary> + /// Gets a snapshot of currently referenced objects list. + /// </summary> + public List<KeyValuePair<long, object>> GetItems() + { + Thread.MemoryBarrier(); + + return + _fast.Select((x, i) => new KeyValuePair<long, object>(i, x)) + .Where(x => x.Value != null) + .Concat(_slow) + .ToList(); + } + + /// <summary> + /// Create new exception for closed state. + /// </summary> + /// <returns>Exception.</returns> + private static Exception ClosedException() + { + return new InvalidOperationException("Cannot allocate a resource handle because grid is stopping."); + } + } +} + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/7c351bf3/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/IHandle.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/IHandle.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/IHandle.cs new file mode 100644 index 0000000..d147f8b --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Handle/IHandle.cs @@ -0,0 +1,35 @@ +/* + * 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 Apache.Ignite.Core.Impl.Handle +{ + /// <summary> + /// Wrapper over some resource ensuring it's release. + /// </summary> + public interface IHandle + { + /// <summary> + /// Release the resource. + /// </summary> + void Release(); + + /// <summary> + /// Resource released flag. + /// </summary> + bool Released { get; } + } +} \ No newline at end of file
