http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/JobEvent.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/JobEvent.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/JobEvent.cs new file mode 100644 index 0000000..81d537f --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/JobEvent.cs @@ -0,0 +1,100 @@ +/* + * 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.Events +{ + using System; + using Apache.Ignite.Core.Cluster; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Ignite job event. + /// </summary> + public sealed class JobEvent : EventBase + { + /** */ + private readonly string _taskName; + + /** */ + private readonly string _taskClassName; + + /** */ + private readonly IgniteGuid _taskSessionId; + + /** */ + private readonly IgniteGuid _jobId; + + /** */ + private readonly IClusterNode _taskNode; + + /** */ + private readonly Guid _taskSubjectId; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="r">The reader to read data from.</param> + internal JobEvent(IPortableRawReader r) : base(r) + { + _taskName = r.ReadString(); + _taskClassName = r.ReadString(); + _taskSessionId = IgniteGuid.ReadPortable(r); + _jobId = IgniteGuid.ReadPortable(r); + _taskNode = ReadNode(r); + _taskSubjectId = r.ReadGuid() ?? Guid.Empty; + } + + /// <summary> + /// Gets name of the task that triggered the event. + /// </summary> + public string TaskName { get { return _taskName; } } + + /// <summary> + /// Gets name of task class that triggered this event. + /// </summary> + public string TaskClassName { get { return _taskClassName; } } + + /// <summary> + /// Gets task session ID of the task that triggered this event. + /// </summary> + public IgniteGuid TaskSessionId { get { return _taskSessionId; } } + + /// <summary> + /// Gets job ID. + /// </summary> + public IgniteGuid JobId { get { return _jobId; } } + + /// <summary> + /// Get node where parent task of the job has originated. + /// </summary> + public IClusterNode TaskNode { get { return _taskNode; } } + + /// <summary> + /// Gets task subject ID. + /// </summary> + public Guid TaskSubjectId { get { return _taskSubjectId; } } + + /** <inheritDoc /> */ + public override string ToShortString() + { + return string.Format("{0}: TaskName={1}, TaskClassName={2}, TaskSessionId={3}, JobId={4}, TaskNode={5}, " + + "TaskSubjectId={6}", Name, TaskName, TaskClassName, TaskSessionId, JobId, TaskNode, + TaskSubjectId); + } + } +}
http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/SwapSpaceEvent.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/SwapSpaceEvent.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/SwapSpaceEvent.cs new file mode 100644 index 0000000..676c2e0 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/SwapSpaceEvent.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 Apache.Ignite.Core.Events +{ + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Grid swap space event. + /// </summary> + public sealed class SwapSpaceEvent : EventBase + { + /** */ + private readonly string _space; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="r">The reader to read data from.</param> + internal SwapSpaceEvent(IPortableRawReader r) : base(r) + { + _space = r.ReadString(); + } + + /// <summary> + /// Gets swap space name. + /// </summary> + public string Space { get { return _space; } } + + /** <inheritDoc /> */ + public override string ToShortString() + { + return string.Format("{0}: Space={1}", Name, Space); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/TaskEvent.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/TaskEvent.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/TaskEvent.cs new file mode 100644 index 0000000..7149fb3 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Events/TaskEvent.cs @@ -0,0 +1,91 @@ +/* + * 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.Events +{ + using System; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Ignite task event. + /// </summary> + public sealed class TaskEvent : EventBase + { + /** */ + private readonly string _taskName; + + /** */ + private readonly string _taskClassName; + + /** */ + private readonly IgniteGuid _taskSessionId; + + /** */ + private readonly bool _internal; + + /** */ + private readonly Guid _subjectId; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="r">The reader to read data from.</param> + internal TaskEvent(IPortableRawReader r) : base(r) + { + _taskName = r.ReadString(); + _taskClassName = r.ReadString(); + _taskSessionId = IgniteGuid.ReadPortable(r); + _internal = r.ReadBoolean(); + _subjectId = r.ReadGuid() ?? Guid.Empty; + } + + /// <summary> + /// Gets name of the task that triggered the event. + /// </summary> + public string TaskName { get { return _taskName; } } + + /// <summary> + /// Gets name of task class that triggered this event. + /// </summary> + public string TaskClassName { get { return _taskClassName; } } + + /// <summary> + /// Gets session ID of the task that triggered the event. + /// </summary> + public IgniteGuid TaskSessionId { get { return _taskSessionId; } } + + /// <summary> + /// Returns true if task is created by Ignite and is used for system needs. + /// </summary> + public bool Internal { get { return _internal; } } + + /// <summary> + /// Gets security subject ID initiated this task event, if available. This property is not available for + /// <see cref="EventType.EvtTaskSessionAttrSet" /> task event. + /// Subject ID will be set either to node ID or client ID initiated task execution. + /// </summary> + public Guid SubjectId { get { return _subjectId; } } + + /** <inheritDoc /> */ + public override string ToShortString() + { + return string.Format("{0}: TaskName={1}, TaskClassName={2}, TaskSessionId={3}, Internal={4}, " + + "SubjectId={5}", Name, TaskName, TaskClassName, TaskSessionId, Internal, SubjectId); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/IIgnite.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/IIgnite.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/IIgnite.cs new file mode 100644 index 0000000..b691254 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/IIgnite.cs @@ -0,0 +1,168 @@ +/* + * 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 +{ + using System; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Cluster; + using Apache.Ignite.Core.Compute; + using Apache.Ignite.Core.Datastream; + using Apache.Ignite.Core.Events; + using Apache.Ignite.Core.Messaging; + using Apache.Ignite.Core.Portable; + using Apache.Ignite.Core.Services; + using Apache.Ignite.Core.Transactions; + + /// <summary> + /// Main entry point for all Ignite APIs. + /// You can obtain an instance of <c>IGrid</c> through <see cref="Ignition.GetIgnite()"/>, + /// or for named grids you can use <see cref="Ignition.GetIgnite(string)"/>. Note that you + /// can have multiple instances of <c>IGrid</c> running in the same process by giving + /// each instance a different name. + /// <para/> + /// All members are thread-safe and may be used concurrently from multiple threads. + /// </summary> + public interface IIgnite : IDisposable + { + /// <summary> + /// Gets the name of the grid this Ignite instance (and correspondingly its local node) belongs to. + /// Note that single process can have multiple Ignite instances all belonging to different grids. Grid + /// name allows to indicate to what grid this particular Ignite instance (i.e. Ignite runtime and its + /// local node) belongs to. + /// <p/> + /// If default Ignite instance is used, then <c>null</c> is returned. Refer to <see cref="Ignition"/> documentation + /// for information on how to start named grids. + /// </summary> + /// <returns>Name of the grid, or <c>null</c> for default grid.</returns> + string Name { get; } + + /// <summary> + /// Gets an instance of <see cref="ICluster" /> interface. + /// </summary> + ICluster Cluster { get; } + + /// <summary> + /// Gets compute functionality over this grid projection. All operations + /// on the returned ICompute instance will only include nodes from + /// this projection. + /// </summary> + /// <returns>Compute instance over this grid projection.</returns> + ICompute Compute(); + + /// <summary> + /// Gets compute functionality over specified grid projection. All operations + /// on the returned ICompute instance will only include nodes from + /// that projection. + /// </summary> + /// <returns>Compute instance over specified grid projection.</returns> + ICompute Compute(IClusterGroup clusterGroup); + + /// <summary> + /// Gets the cache instance for the given name to work with keys and values of specified types. + /// <para/> + /// You can get instances of ICache of the same name, but with different key/value types. + /// These will use the same named cache, but only allow working with entries of specified types. + /// Attempt to retrieve an entry of incompatible type will result in <see cref="InvalidCastException"/>. + /// Use <see cref="Cache{Object, Object}"/> in order to work with entries of arbitrary types. + /// </summary> + /// <param name="name">Cache name.</param> + /// <returns>Cache instance for given name.</returns> + /// <typeparam name="TK">Cache key type.</typeparam> + /// <typeparam name="TV">Cache value type.</typeparam> + ICache<TK, TV> Cache<TK, TV>(string name); + + /// <summary> + /// Gets existing cache with the given name or creates new one using template configuration. + /// </summary> + /// <typeparam name="TK">Cache key type.</typeparam> + /// <typeparam name="TV">Cache value type.</typeparam> + /// <param name="name">Cache name.</param> + /// <returns>Existing or newly created cache.</returns> + ICache<TK, TV> GetOrCreateCache<TK, TV>(string name); + + /// <summary> + /// Dynamically starts new cache using template configuration. + /// </summary> + /// <typeparam name="TK">Cache key type.</typeparam> + /// <typeparam name="TV">Cache value type.</typeparam> + /// <param name="name">Cache name.</param> + /// <returns>Existing or newly created cache.</returns> + ICache<TK, TV> CreateCache<TK, TV>(string name); + + /// <summary> + /// Gets a new instance of data streamer associated with given cache name. Data streamer + /// is responsible for loading external data into Ignite. For more information + /// refer to <see cref="IDataStreamer{K,V}"/> documentation. + /// </summary> + /// <param name="cacheName">Cache name (<c>null</c> for default cache).</param> + /// <returns>Data streamer.</returns> + IDataStreamer<TK, TV> DataStreamer<TK, TV>(string cacheName); + + /// <summary> + /// Gets an instance of <see cref="IPortables"/> interface. + /// </summary> + /// <returns>Instance of <see cref="IPortables"/> interface</returns> + IPortables Portables(); + + /// <summary> + /// Gets affinity service to provide information about data partitioning and distribution. + /// </summary> + /// <param name="name">Cache name.</param> + /// <returns>Cache data affinity service.</returns> + ICacheAffinity Affinity(string name); + + /// <summary> + /// Gets Ignite transactions facade. + /// </summary> + ITransactions Transactions { get; } + + /// <summary> + /// Gets messaging facade over all cluster nodes. + /// </summary> + /// <returns>Messaging instance over all cluster nodes.</returns> + IMessaging Message(); + + /// <summary> + /// Gets messaging facade over nodes within the cluster group. All operations on the returned + /// <see cref="IMessaging"/>> instance will only include nodes from the specified cluster group. + /// </summary> + /// <param name="clusterGroup">Cluster group.</param> + /// <returns>Messaging instance over given cluster group.</returns> + IMessaging Message(IClusterGroup clusterGroup); + + /// <summary> + /// Gets events facade over all cluster nodes. + /// </summary> + /// <returns>Events facade over all cluster nodes.</returns> + IEvents Events(); + + /// <summary> + /// Gets events facade over nodes within the cluster group. All operations on the returned + /// <see cref="IEvents"/>> instance will only include nodes from the specified cluster group. + /// </summary> + /// <param name="clusterGroup">Cluster group.</param> + /// <returns>Events instance over given cluster group.</returns> + IEvents Events(IClusterGroup clusterGroup); + + /// <summary> + /// Gets services facade over all cluster nodes. + /// </summary> + /// <returns>Services facade over all cluster nodes.</returns> + IServices Services(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs new file mode 100644 index 0000000..5a03e93 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/IgniteConfiguration.cs @@ -0,0 +1,140 @@ +/* + * 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 +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using Apache.Ignite.Core.Lifecycle; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Grid configuration. + /// </summary> + public class IgniteConfiguration + { + /// <summary> + /// Default initial JVM memory in megabytes. + /// </summary> + public const int DefaultJvmInitMem = 512; + + /// <summary> + /// Default maximum JVM memory in megabytes. + /// </summary> + public const int DefaultJvmMaxMem = 1024; + + /// <summary> + /// Default constructor. + /// </summary> + public IgniteConfiguration() + { + JvmInitialMemoryMb = DefaultJvmInitMem; + JvmMaxMemoryMb = DefaultJvmMaxMem; + } + + /// <summary> + /// Copying constructor. + /// </summary> + /// <param name="cfg">Configuration.</param> + internal IgniteConfiguration(IgniteConfiguration cfg) + { + SpringConfigUrl = cfg.SpringConfigUrl; + JvmDllPath = cfg.JvmDllPath; + IgniteHome = cfg.IgniteHome; + JvmClasspath = cfg.JvmClasspath; + SuppressWarnings = cfg.SuppressWarnings; + + JvmOptions = cfg.JvmOptions != null ? new List<string>(cfg.JvmOptions) : null; + Assemblies = cfg.Assemblies != null ? new List<string>(cfg.Assemblies) : null; + + PortableConfiguration = cfg.PortableConfiguration != null + ? new PortableConfiguration(cfg.PortableConfiguration) + : null; + + LifecycleBeans = cfg.LifecycleBeans != null ? new List<ILifecycleBean>(cfg.LifecycleBeans) : null; + + JvmInitialMemoryMb = cfg.JvmInitialMemoryMb; + JvmMaxMemoryMb = cfg.JvmMaxMemoryMb; + } + + /// <summary> + /// Gets or sets the portable configuration. + /// </summary> + /// <value> + /// The portable configuration. + /// </value> + public PortableConfiguration PortableConfiguration { get; set; } + + /// <summary> + /// URL to Spring configuration file. + /// </summary> + [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] + public string SpringConfigUrl { get; set; } + + /// <summary> + /// Path jvm.dll file. If not set, it's location will be determined + /// using JAVA_HOME environment variable. + /// If path is neither set nor determined automatically, an exception + /// will be thrown. + /// </summary> + public string JvmDllPath { get; set; } + + /// <summary> + /// Path to Ignite home. If not set environment variable IGNITE_HOME will be used. + /// </summary> + public string IgniteHome { get; set; } + + /// <summary> + /// Classpath used by JVM on Ignite start. + /// </summary> + public string JvmClasspath { get; set; } + + /// <summary> + /// Collection of options passed to JVM on Ignite start. + /// </summary> + public ICollection<string> JvmOptions { get; set; } + + /// <summary> + /// List of additional .Net assemblies to load on Ignite start. Each item can be either + /// fully qualified assembly name, path to assembly to DLL or path to a directory when + /// assemblies reside. + /// </summary> + public IList<string> Assemblies { get; set; } + + /// <summary> + /// Whether to suppress warnings. + /// </summary> + public bool SuppressWarnings { get; set; } + + /// <summary> + /// Lifecycle beans. + /// </summary> + public ICollection<ILifecycleBean> LifecycleBeans { get; set; } + + /// <summary> + /// Initial amount of memory in megabytes given to JVM. Maps to -Xms Java option. + /// Defaults to <see cref="DefaultJvmInitMem"/>. + /// </summary> + public int JvmInitialMemoryMb { get; set; } + + /// <summary> + /// Maximum amount of memory in megabytes given to JVM. Maps to -Xmx Java option. + /// Defaults to <see cref="DefaultJvmMaxMem"/>. + /// </summary> + public int JvmMaxMemoryMb { get; set; } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Ignition.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Ignition.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Ignition.cs index a61a1a8..ef79008 100644 --- a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Ignition.cs +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Ignition.cs @@ -1,4 +1,4 @@ -/* +/* * 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. @@ -15,9 +15,629 @@ * limitations under the License. */ -namespace Apache.Ignite.Core +namespace Apache.Ignite.Core { - public class Ignition + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Runtime; + using System.Runtime.InteropServices; + using System.Threading; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Impl; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Impl.Handle; + using Apache.Ignite.Core.Impl.Interop; + using Apache.Ignite.Core.Impl.Memory; + using Apache.Ignite.Core.Impl.Portable; + using Apache.Ignite.Core.Impl.Portable.IO; + using Apache.Ignite.Core.Impl.Unmanaged; + using Apache.Ignite.Core.Lifecycle; + using UU = Apache.Ignite.Core.Impl.Unmanaged.UnmanagedUtils; + using PU = Apache.Ignite.Core.Impl.Portable.PortableUtils; + + /// <summary> + /// This class defines a factory for the main Ignite API. + /// <p/> + /// Use <see cref="Ignition.Start()"/> method to start Ignite with default configuration. + /// <para/> + /// All members are thread-safe and may be used concurrently from multiple threads. + /// <example> + /// You can also use <see cref="IgniteConfiguration"/> to override some default configuration. + /// Below is an example on how to start Ignite with custom configuration for portable types and + /// provide path to Spring XML configuration file: + /// <code> + /// IgniteConfiguration cfg = new IgniteConfiguration(); + /// + /// // Create portable type configuration. + /// PortableConfiguration portableCfg = new PortableConfiguration(); + /// + /// cfg.SpringConfigUrl = "examples\\config\\example-cache.xml"; + /// + /// portableCfg.TypeConfigurations = new List<PortableTypeConfiguration> + /// { + /// new PortableTypeConfiguration(typeof(Address)), + /// new PortableTypeConfiguration(typeof(Organization)) + /// }; + /// + /// cfg.PortableConfiguration = portableCfg; + /// + /// // Start Ignite node with Ignite configuration. + /// var ignite = Ignition.Start(cfg); + /// </code> + /// </example> + /// </summary> + public static class Ignition { + /** */ + private const string DefaultCfg = "config/default-config.xml"; + + /** */ + private static readonly object SyncRoot = new object(); + + /** GC warning flag. */ + private static int _gcWarn; + + /** */ + private static readonly IDictionary<NodeKey, Ignite> Nodes = new Dictionary<NodeKey, Ignite>(); + + /** Current DLL name. */ + private static readonly string IgniteDllName = Path.GetFileName(Assembly.GetExecutingAssembly().Location); + + /** Startup info. */ + [ThreadStatic] + private static Startup _startup; + + /** Client mode flag. */ + [ThreadStatic] + private static bool _clientMode; + + /// <summary> + /// Static initializer. + /// </summary> + static Ignition() + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + } + + /// <summary> + /// Gets or sets a value indicating whether Ignite should be started in client mode. + /// Client nodes cannot hold data in caches. + /// </summary> + public static bool ClientMode + { + get { return _clientMode; } + set { _clientMode = value; } + } + + /// <summary> + /// Starts Ignite with default configuration. By default this method will + /// use Ignite configuration defined in <code>IGNITE/config/default-config.xml</code> + /// configuration file. If such file is not found, then all system defaults will be used. + /// </summary> + /// <returns>Started Ignite.</returns> + public static IIgnite Start() + { + return Start(new IgniteConfiguration()); + } + + /// <summary> + /// Starts all grids specified within given Spring XML configuration file. If Ignite with given name + /// is already started, then exception is thrown. In this case all instances that may + /// have been started so far will be stopped too. + /// </summary> + /// <param name="springCfgPath">Spring XML configuration file path or URL. Note, that the path can be + /// absolute or relative to IGNITE_HOME.</param> + /// <returns>Started Ignite. If Spring configuration contains multiple Ignite instances, then the 1st + /// found instance is returned.</returns> + public static IIgnite Start(string springCfgPath) + { + return Start(new IgniteConfiguration {SpringConfigUrl = springCfgPath}); + } + + /// <summary> + /// Starts Ignite with given configuration. + /// </summary> + /// <returns>Started Ignite.</returns> + public unsafe static IIgnite Start(IgniteConfiguration cfg) + { + IgniteArgumentCheck.NotNull(cfg, "cfg"); + + // Copy configuration to avoid changes to user-provided instance. + IgniteConfigurationEx cfgEx = cfg as IgniteConfigurationEx; + + cfg = cfgEx == null ? new IgniteConfiguration(cfg) : new IgniteConfigurationEx(cfgEx); + + // Set default Spring config if needed. + if (cfg.SpringConfigUrl == null) + cfg.SpringConfigUrl = DefaultCfg; + + lock (SyncRoot) + { + // 1. Check GC settings. + CheckServerGc(cfg); + + // 2. Create context. + IgniteUtils.LoadDlls(cfg.JvmDllPath); + + var cbs = new UnmanagedCallbacks(); + + void* ctx = IgniteManager.GetContext(cfg, cbs); + + sbyte* cfgPath0 = IgniteUtils.StringToUtf8Unmanaged(cfg.SpringConfigUrl ?? DefaultCfg); + + string gridName = cfgEx != null ? cfgEx.GridName : null; + sbyte* gridName0 = IgniteUtils.StringToUtf8Unmanaged(gridName); + + // 3. Create startup object which will guide us through the rest of the process. + _startup = new Startup(cfg) { Context = ctx }; + + IUnmanagedTarget interopProc = null; + + try + { + // 4. Initiate Ignite start. + interopProc = UU.IgnitionStart(cbs.Context, cfg.SpringConfigUrl ?? DefaultCfg, + cfgEx != null ? cfgEx.GridName : null, ClientMode); + + // 5. At this point start routine is finished. We expect STARTUP object to have all necessary data. + Ignite node = new Ignite(cfg, _startup.Name, interopProc, _startup.Marshaller, + _startup.LifecycleBeans, cbs); + + // 6. On-start callback (notify lifecycle components). + node.OnStart(); + + Nodes[new NodeKey(_startup.Name)] = node; + + return node; + } + catch (Exception) + { + // 1. Perform keys cleanup. + string name = _startup.Name; + + if (name != null) + { + NodeKey key = new NodeKey(name); + + if (Nodes.ContainsKey(key)) + Nodes.Remove(key); + } + + // 2. Stop Ignite node if it was started. + if (interopProc != null) + UU.IgnitionStop(interopProc.Context, gridName, true); + + // 3. Throw error further (use startup error if exists because it is more precise). + if (_startup.Error != null) + throw _startup.Error; + + throw; + } + finally + { + _startup = null; + + Marshal.FreeHGlobal((IntPtr)cfgPath0); + + if ((IntPtr)gridName0 != IntPtr.Zero) + Marshal.FreeHGlobal((IntPtr)gridName0); + + if (interopProc != null) + UU.ProcessorReleaseStart(interopProc); + } + } + } + + /// <summary> + /// Check whether GC is set to server mode. + /// </summary> + /// <param name="cfg">Configuration.</param> + private static void CheckServerGc(IgniteConfiguration cfg) + { + if (!cfg.SuppressWarnings && !GCSettings.IsServerGC && Interlocked.CompareExchange(ref _gcWarn, 1, 0) == 0) + Console.WriteLine("GC server mode is not enabled, this could lead to less " + + "than optimal performance on multi-core machines (to enable see " + + "http://msdn.microsoft.com/en-us/library/ms229357(v=vs.110).aspx)."); + } + + /// <summary> + /// Prepare callback invoked from Java. + /// </summary> + /// <param name="inStream">Intput stream with data.</param> + /// <param name="outStream">Output stream.</param> + /// <param name="handleRegistry">Handle registry.</param> + internal static void OnPrepare(PlatformMemoryStream inStream, PlatformMemoryStream outStream, + HandleRegistry handleRegistry) + { + try + { + PortableReaderImpl reader = PU.Marshaller.StartUnmarshal(inStream); + + PrepareConfiguration(reader.ReadObject<InteropDotNetConfiguration>()); + + PrepareLifecycleBeans(reader, outStream, handleRegistry); + } + catch (Exception e) + { + _startup.Error = e; + + throw; + } + } + + /// <summary> + /// Preapare configuration. + /// </summary> + /// <param name="dotNetCfg">Dot net configuration.</param> + private static void PrepareConfiguration(InteropDotNetConfiguration dotNetCfg) + { + // 1. Load assemblies. + IgniteConfiguration cfg = _startup.Configuration; + + LoadAssemblies(cfg.Assemblies); + + if (dotNetCfg != null) + LoadAssemblies(dotNetCfg.Assemblies); + + // 2. Create marshaller only after assemblies are loaded. + if (cfg.PortableConfiguration == null && dotNetCfg != null && dotNetCfg.PortableCfg != null) + cfg.PortableConfiguration = dotNetCfg.PortableCfg.ToPortableConfiguration(); + + _startup.Marshaller = new PortableMarshaller(cfg.PortableConfiguration); + } + + /// <summary> + /// Prepare lifecycle beans. + /// </summary> + /// <param name="reader">Reader.</param> + /// <param name="outStream">Output stream.</param> + /// <param name="handleRegistry">Handle registry.</param> + private static void PrepareLifecycleBeans(PortableReaderImpl reader, PlatformMemoryStream outStream, + HandleRegistry handleRegistry) + { + IList<LifecycleBeanHolder> beans = new List<LifecycleBeanHolder>(); + + // 1. Read beans defined in Java. + int cnt = reader.ReadInt(); + + for (int i = 0; i < cnt; i++) + beans.Add(new LifecycleBeanHolder(CreateLifecycleBean(reader))); + + // 2. Append beans definied in local configuration. + ICollection<ILifecycleBean> nativeBeans = _startup.Configuration.LifecycleBeans; + + if (nativeBeans != null) + { + foreach (ILifecycleBean nativeBean in nativeBeans) + beans.Add(new LifecycleBeanHolder(nativeBean)); + } + + // 3. Write bean pointers to Java stream. + outStream.WriteInt(beans.Count); + + foreach (LifecycleBeanHolder bean in beans) + outStream.WriteLong(handleRegistry.AllocateCritical(bean)); + + outStream.SynchronizeOutput(); + + // 4. Set beans to STARTUP object. + _startup.LifecycleBeans = beans; + } + + /// <summary> + /// Create lifecycle bean. + /// </summary> + /// <param name="reader">Reader.</param> + /// <returns>Lifecycle bean.</returns> + internal static ILifecycleBean CreateLifecycleBean(PortableReaderImpl reader) + { + // 1. Instantiate. + string assemblyName = reader.ReadString(); + string clsName = reader.ReadString(); + + object bean = IgniteUtils.CreateInstance(assemblyName, clsName); + + // 2. Set properties. + IDictionary<string, object> props = reader.ReadGenericDictionary<string, object>(); + + IgniteUtils.SetProperties(bean, props); + + return bean as ILifecycleBean; + } + + /// <summary> + /// Kernal start callback. + /// </summary> + /// <param name="stream">Stream.</param> + internal static void OnStart(IPortableStream stream) + { + try + { + // 1. Read data and leave critical state ASAP. + PortableReaderImpl reader = PU.Marshaller.StartUnmarshal(stream); + + // ReSharper disable once PossibleInvalidOperationException + var name = reader.ReadString(); + + // 2. Set ID and name so that Start() method can use them later. + _startup.Name = name; + + if (Nodes.ContainsKey(new NodeKey(name))) + throw new IgniteException("Ignite with the same name already started: " + name); + + } + catch (Exception e) + { + // 5. Preserve exception to throw it later in the "Start" method and throw it further + // to abort startup in Java. + _startup.Error = e; + + throw; + } + } + + /// <summary> + /// Load assemblies. + /// </summary> + /// <param name="assemblies">Assemblies.</param> + private static void LoadAssemblies(IEnumerable<string> assemblies) + { + if (assemblies != null) + { + foreach (string s in assemblies) + { + // 1. Try loading as directory. + if (Directory.Exists(s)) + { + string[] files = Directory.GetFiles(s, "*.dll"); + +#pragma warning disable 0168 + + foreach (string dllPath in files) + { + if (!SelfAssembly(dllPath)) + { + try + { + Assembly.LoadFile(dllPath); + } + + catch (BadImageFormatException) + { + // No-op. + } + } + } + +#pragma warning restore 0168 + + continue; + } + + // 2. Try loading using full-name. + try + { + Assembly assembly = Assembly.Load(s); + + if (assembly != null) + continue; + } + catch (Exception e) + { + if (!(e is FileNotFoundException || e is FileLoadException)) + throw new IgniteException("Failed to load assembly: " + s, e); + } + + // 3. Try loading using file path. + try + { + Assembly assembly = Assembly.LoadFrom(s); + + if (assembly != null) + continue; + } + catch (Exception e) + { + if (!(e is FileNotFoundException || e is FileLoadException)) + throw new IgniteException("Failed to load assembly: " + s, e); + } + + // 4. Not found, exception. + throw new IgniteException("Failed to load assembly: " + s); + } + } + } + + /// <summary> + /// Whether assembly points to Ignite binary. + /// </summary> + /// <param name="assembly">Assembly to check..</param> + /// <returns><c>True</c> if this is one of GG assemblies.</returns> + private static bool SelfAssembly(string assembly) + { + return assembly.EndsWith(IgniteDllName, StringComparison.OrdinalIgnoreCase); + } + + /// <summary> + /// Gets a named Ignite instance. If Ignite name is {@code null} or empty string, + /// then default no-name Ignite will be returned. Note that caller of this method + /// should not assume that it will return the same instance every time. + /// <p/> + /// Note that single process can run multiple Ignite instances and every Ignite instance (and its + /// node) can belong to a different grid. Ignite name defines what grid a particular Ignite + /// instance (and correspondingly its node) belongs to. + /// </summary> + /// <param name="name">Ignite name to which requested Ignite instance belongs. If <code>null</code>, + /// then Ignite instance belonging to a default no-name Ignite will be returned. + /// </param> + /// <returns>An instance of named grid.</returns> + public static IIgnite GetIgnite(string name) + { + lock (SyncRoot) + { + Ignite result; + + if (!Nodes.TryGetValue(new NodeKey(name), out result)) + throw new IgniteException("Ignite instance was not properly started or was already stopped: " + name); + + return result; + } + } + + /// <summary> + /// Gets an instance of default no-name grid. Note that + /// caller of this method should not assume that it will return the same + /// instance every time. + /// </summary> + /// <returns>An instance of default no-name grid.</returns> + public static IIgnite GetIgnite() + { + return GetIgnite(null); + } + + /// <summary> + /// Stops named grid. If <code>cancel</code> flag is set to <code>true</code> then + /// all jobs currently executing on local node will be interrupted. If + /// grid name is <code>null</code>, then default no-name Ignite will be stopped. + /// </summary> + /// <param name="name">Grid name. If <code>null</code>, then default no-name Ignite will be stopped.</param> + /// <param name="cancel">If <code>true</code> then all jobs currently executing will be cancelled + /// by calling <code>ComputeJob.cancel</code>method.</param> + /// <returns><code>true</code> if named Ignite instance was indeed found and stopped, <code>false</code> + /// othwerwise (the instance with given <code>name</code> was not found).</returns> + public static bool Stop(string name, bool cancel) + { + lock (SyncRoot) + { + NodeKey key = new NodeKey(name); + + Ignite node; + + if (!Nodes.TryGetValue(key, out node)) + return false; + + node.Stop(cancel); + + Nodes.Remove(key); + + GC.Collect(); + + return true; + } + } + + /// <summary> + /// Stops <b>all</b> started grids. If <code>cancel</code> flag is set to <code>true</code> then + /// all jobs currently executing on local node will be interrupted. + /// </summary> + /// <param name="cancel">If <code>true</code> then all jobs currently executing will be cancelled + /// by calling <code>ComputeJob.cancel</code>method.</param> + public static void StopAll(bool cancel) + { + lock (SyncRoot) + { + while (Nodes.Count > 0) + { + var entry = Nodes.First(); + + entry.Value.Stop(cancel); + + Nodes.Remove(entry.Key); + } + } + + GC.Collect(); + } + + /// <summary> + /// Handles the AssemblyResolve event of the CurrentDomain control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="args">The <see cref="ResolveEventArgs"/> instance containing the event data.</param> + /// <returns>Manually resolved assembly, or null.</returns> + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + return LoadedAssembliesResolver.Instance.GetAssembly(args.Name); + } + + /// <summary> + /// Grid key. + /// </summary> + private class NodeKey + { + /** */ + private readonly string _name; + + /// <summary> + /// Initializes a new instance of the <see cref="NodeKey"/> class. + /// </summary> + /// <param name="name">The name.</param> + internal NodeKey(string name) + { + _name = name; + } + + /** <inheritdoc /> */ + public override bool Equals(object obj) + { + var other = obj as NodeKey; + + return other != null && Equals(_name, other._name); + } + + /** <inheritdoc /> */ + public override int GetHashCode() + { + return _name == null ? 0 : _name.GetHashCode(); + } + } + + /// <summary> + /// Value object to pass data between .Net methods during startup bypassing Java. + /// </summary> + private unsafe class Startup + { + /// <summary> + /// Constructor. + /// </summary> + /// <param name="cfg">Configuration.</param> + internal Startup(IgniteConfiguration cfg) + { + Configuration = cfg; + } + + /// <summary> + /// Configuration. + /// </summary> + internal IgniteConfiguration Configuration { get; private set; } + + /// <summary> + /// Lifecycle beans. + /// </summary> + internal IList<LifecycleBeanHolder> LifecycleBeans { get; set; } + + /// <summary> + /// Node name. + /// </summary> + internal string Name { get; set; } + + /// <summary> + /// Marshaller. + /// </summary> + internal PortableMarshaller Marshaller { get; set; } + + /// <summary> + /// Start error. + /// </summary> + internal Exception Error { get; set; } + + /// <summary> + /// Gets or sets the context. + /// </summary> + internal void* Context { get; set; } + } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheAffinityImpl.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheAffinityImpl.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheAffinityImpl.cs new file mode 100644 index 0000000..6d577ce --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheAffinityImpl.cs @@ -0,0 +1,275 @@ +/* + * 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.Cache +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Cluster; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Impl.Portable; + using Apache.Ignite.Core.Impl.Portable.IO; + using Apache.Ignite.Core.Impl.Unmanaged; + using UU = Apache.Ignite.Core.Impl.Unmanaged.UnmanagedUtils; + + /// <summary> + /// Cache affinity implementation. + /// </summary> + internal class CacheAffinityImpl : PlatformTarget, ICacheAffinity + { + /** */ + private const int OpAffinityKey = 1; + + /** */ + private const int OpAllPartitions = 2; + + /** */ + private const int OpBackupPartitions = 3; + + /** */ + private const int OpIsBackup = 4; + + /** */ + private const int OpIsPrimary = 5; + + /** */ + private const int OpIsPrimaryOrBackup = 6; + + /** */ + private const int OpMapKeyToNode = 7; + + /** */ + private const int OpMapKeyToPrimaryAndBackups = 8; + + /** */ + private const int OpMapKeysToNodes = 9; + + /** */ + private const int OpMapPartitionToNode = 10; + + /** */ + private const int OpMapPartitionToPrimaryAndBackups = 11; + + /** */ + private const int OpMapPartitionsToNodes = 12; + + /** */ + private const int OpPartition = 13; + + /** */ + private const int OpPrimaryPartitions = 14; + + /** */ + private readonly bool _keepPortable; + + /** Grid. */ + private readonly Ignite _ignite; + + /// <summary> + /// Initializes a new instance of the <see cref="CacheAffinityImpl" /> class. + /// </summary> + /// <param name="target">Target.</param> + /// <param name="marsh">Marshaller.</param> + /// <param name="keepPortable">Keep portable flag.</param> + /// <param name="ignite">Grid.</param> + public CacheAffinityImpl(IUnmanagedTarget target, PortableMarshaller marsh, bool keepPortable, + Ignite ignite) : base(target, marsh) + { + _keepPortable = keepPortable; + + Debug.Assert(ignite != null); + + _ignite = ignite; + } + + /** <inheritDoc /> */ + public int Partitions + { + get { return UU.AffinityPartitions(Target); } + } + + /** <inheritDoc /> */ + public int Partition<TK>(TK key) + { + IgniteArgumentCheck.NotNull(key, "key"); + + return (int)DoOutOp(OpPartition, key); + } + + /** <inheritDoc /> */ + public bool IsPrimary<TK>(IClusterNode n, TK key) + { + IgniteArgumentCheck.NotNull(n, "n"); + + IgniteArgumentCheck.NotNull(key, "key"); + + return DoOutOp(OpIsPrimary, n.Id, key) == True; + } + + /** <inheritDoc /> */ + public bool IsBackup<TK>(IClusterNode n, TK key) + { + IgniteArgumentCheck.NotNull(n, "n"); + + IgniteArgumentCheck.NotNull(key, "key"); + + return DoOutOp(OpIsBackup, n.Id, key) == True; + } + + /** <inheritDoc /> */ + public bool IsPrimaryOrBackup<TK>(IClusterNode n, TK key) + { + IgniteArgumentCheck.NotNull(n, "n"); + + IgniteArgumentCheck.NotNull(key, "key"); + + return DoOutOp(OpIsPrimaryOrBackup, n.Id, key) == True; + } + + /** <inheritDoc /> */ + public int[] PrimaryPartitions(IClusterNode n) + { + IgniteArgumentCheck.NotNull(n, "n"); + + return DoOutInOp<Guid, int[]>(OpPrimaryPartitions, n.Id); + } + + /** <inheritDoc /> */ + public int[] BackupPartitions(IClusterNode n) + { + IgniteArgumentCheck.NotNull(n, "n"); + + return DoOutInOp<Guid, int[]>(OpBackupPartitions, n.Id); + } + + /** <inheritDoc /> */ + public int[] AllPartitions(IClusterNode n) + { + IgniteArgumentCheck.NotNull(n, "n"); + + return DoOutInOp<Guid, int[]>(OpAllPartitions, n.Id); + } + + /** <inheritDoc /> */ + public TR AffinityKey<TK, TR>(TK key) + { + IgniteArgumentCheck.NotNull(key, "key"); + + return DoOutInOp<TK, TR>(OpAffinityKey, key); + } + + /** <inheritDoc /> */ + public IDictionary<IClusterNode, IList<TK>> MapKeysToNodes<TK>(IList<TK> keys) + { + IgniteArgumentCheck.NotNull(keys, "keys"); + + return DoOutInOp(OpMapKeysToNodes, w => w.WriteObject(keys), + reader => ReadDictionary(reader, ReadNode, r => r.ReadObject<IList<TK>>())); + } + + /** <inheritDoc /> */ + public IClusterNode MapKeyToNode<TK>(TK key) + { + IgniteArgumentCheck.NotNull(key, "key"); + + return GetNode(DoOutInOp<TK, Guid?>(OpMapKeyToNode, key)); + } + + /** <inheritDoc /> */ + public IList<IClusterNode> MapKeyToPrimaryAndBackups<TK>(TK key) + { + IgniteArgumentCheck.NotNull(key, "key"); + + return DoOutInOp(OpMapKeyToPrimaryAndBackups, w => w.WriteObject(key), r => ReadNodes(r)); + } + + /** <inheritDoc /> */ + public IClusterNode MapPartitionToNode(int part) + { + return GetNode(DoOutInOp<int, Guid?>(OpMapPartitionToNode, part)); + } + + /** <inheritDoc /> */ + public IDictionary<int, IClusterNode> MapPartitionsToNodes(IList<int> parts) + { + IgniteArgumentCheck.NotNull(parts, "parts"); + + return DoOutInOp(OpMapPartitionsToNodes, + w => w.WriteObject(parts), + reader => ReadDictionary(reader, r => r.ReadInt(), ReadNode)); + } + + /** <inheritDoc /> */ + public IList<IClusterNode> MapPartitionToPrimaryAndBackups(int part) + { + return DoOutInOp(OpMapPartitionToPrimaryAndBackups, w => w.WriteObject(part), r => ReadNodes(r)); + } + + /** <inheritDoc /> */ + protected override T Unmarshal<T>(IPortableStream stream) + { + return Marshaller.Unmarshal<T>(stream, _keepPortable); + } + + + /// <summary> + /// Gets the node by id. + /// </summary> + /// <param name="id">The id.</param> + /// <returns>Node.</returns> + private IClusterNode GetNode(Guid? id) + { + return _ignite.GetNode(id); + } + + /// <summary> + /// Reads a node from stream. + /// </summary> + private IClusterNode ReadNode(PortableReaderImpl r) + { + return GetNode(r.ReadGuid()); + } + + /// <summary> + /// Reads nodes from stream. + /// </summary> + private IList<IClusterNode> ReadNodes(IPortableStream reader) + { + return IgniteUtils.ReadNodes(Marshaller.StartUnmarshal(reader, _keepPortable)); + } + + /// <summary> + /// Reads a dictionary from stream. + /// </summary> + private Dictionary<TK, TV> ReadDictionary<TK, TV>(IPortableStream reader, Func<PortableReaderImpl, TK> readKey, + Func<PortableReaderImpl, TV> readVal) + { + var r = Marshaller.StartUnmarshal(reader, _keepPortable); + + var cnt = r.ReadInt(); + + var dict = new Dictionary<TK, TV>(cnt); + + for (var i = 0; i < cnt; i++) + dict[readKey(r)] = readVal(r); + + return dict; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs new file mode 100644 index 0000000..e28b3e2 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntry.cs @@ -0,0 +1,126 @@ +/* + * 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.Cache +{ + using System.Collections.Generic; + using Apache.Ignite.Core.Cache; + + /// <summary> + /// Represents a cache entry. + /// </summary> + internal struct CacheEntry<TK, TV> : ICacheEntry<TK, TV> + { + /** Key. */ + private readonly TK _key; + + /** Value. */ + private readonly TV _val; + + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntry{K,V}"/> struct. + /// </summary> + /// <param name="key">The key.</param> + /// <param name="val">The value.</param> + public CacheEntry(TK key, TV val) + { + _key = key; + _val = val; + } + + /// <summary> + /// Gets the key. + /// </summary> + public TK Key + { + get { return _key; } + } + + /// <summary> + /// Gets the value. + /// </summary> + public TV Value + { + get { return _val; } + } + + /// <summary> + /// Determines whether the specified <see cref="CacheEntry{K,V}"/>, is equal to this instance. + /// </summary> + /// <param name="other">The <see cref="CacheEntry{K,V}"/> to compare with this instance.</param> + /// <returns> + /// <c>true</c> if the specified <see cref="CacheEntry{K,V}"/> is equal to this instance; + /// otherwise, <c>false</c>. + /// </returns> + public bool Equals(CacheEntry<TK, TV> other) + { + return EqualityComparer<TK>.Default.Equals(_key, other._key) && + EqualityComparer<TV>.Default.Equals(_val, other._val); + } + + /** <inheritDoc /> */ + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; + + return obj is CacheEntry<TK, TV> && Equals((CacheEntry<TK, TV>) obj); + } + + /** <inheritDoc /> */ + public override int GetHashCode() + { + unchecked + { + return (EqualityComparer<TK>.Default.GetHashCode(_key) * 397) ^ + EqualityComparer<TV>.Default.GetHashCode(_val); + } + } + + /** <inheritDoc /> */ + public override string ToString() + { + return string.Format("CacheEntry [Key={0}, Value={1}]", _key, _val); + } + + /// <summary> + /// Implements the operator ==. + /// </summary> + /// <param name="a">First item.</param> + /// <param name="b">Second item.</param> + /// <returns> + /// The result of the operator. + /// </returns> + public static bool operator ==(CacheEntry<TK, TV> a, CacheEntry<TK, TV> b) + { + return a.Equals(b); + } + + /// <summary> + /// Implements the operator !=. + /// </summary> + /// <param name="a">First item.</param> + /// <param name="b">Second item.</param> + /// <returns> + /// The result of the operator. + /// </returns> + public static bool operator !=(CacheEntry<TK, TV> a, CacheEntry<TK, TV> b) + { + return !(a == b); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryFilterHolder.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryFilterHolder.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryFilterHolder.cs new file mode 100644 index 0000000..1181645 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryFilterHolder.cs @@ -0,0 +1,147 @@ +/* + * 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.Cache +{ + using System; + using System.Diagnostics; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Impl.Portable; + using Apache.Ignite.Core.Impl.Portable.IO; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Non-generic portable filter wrapper. + /// </summary> + internal class CacheEntryFilterHolder : IPortableWriteAware + { + /** Wrapped ICacheEntryFilter */ + private readonly object _pred; + + /** Invoker function that takes key and value and invokes wrapped ICacheEntryFilter */ + private readonly Func<object, object, bool> _invoker; + + /** Keep portable flag. */ + private readonly bool _keepPortable; + + /** Grid. */ + private readonly PortableMarshaller _marsh; + + /** Handle. */ + private readonly long _handle; + + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntryFilterHolder" /> class. + /// </summary> + /// <param name="pred">The <see cref="ICacheEntryFilter{TK,TV}" /> to wrap.</param> + /// <param name="invoker">The invoker func that takes key and value and invokes wrapped ICacheEntryFilter.</param> + /// <param name="marsh">Marshaller.</param> + /// <param name="keepPortable">Keep portable flag.</param> + public CacheEntryFilterHolder(object pred, Func<object, object, bool> invoker, PortableMarshaller marsh, + bool keepPortable) + { + Debug.Assert(pred != null); + Debug.Assert(invoker != null); + Debug.Assert(marsh != null); + + _pred = pred; + _invoker = invoker; + _marsh = marsh; + _keepPortable = keepPortable; + + _handle = marsh.Ignite.HandleRegistry.Allocate(this); + } + + /// <summary> + /// Gets the handle. + /// </summary> + public long Handle + { + get { return _handle; } + } + + /// <summary> + /// Invokes the cache filter. + /// </summary> + /// <param name="input">The input stream.</param> + /// <returns>Invocation result.</returns> + public int Invoke(IPortableStream input) + { + var rawReader = _marsh.StartUnmarshal(input, _keepPortable).RawReader(); + + return _invoker(rawReader.ReadObject<object>(), rawReader.ReadObject<object>()) ? 1 : 0; + } + + /** <inheritdoc /> */ + public void WritePortable(IPortableWriter writer) + { + var writer0 = (PortableWriterImpl)writer.RawWriter(); + + writer0.DetachNext(); + PortableUtils.WritePortableOrSerializable(writer0, _pred); + + writer0.WriteBoolean(_keepPortable); + } + + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntryFilterHolder"/> class. + /// </summary> + /// <param name="reader">The reader.</param> + public CacheEntryFilterHolder(IPortableReader reader) + { + var reader0 = (PortableReaderImpl)reader.RawReader(); + + _pred = PortableUtils.ReadPortableOrSerializable<object>(reader0); + + _keepPortable = reader0.ReadBoolean(); + + _marsh = reader0.Marshaller; + + _invoker = GetInvoker(_pred); + + _handle = _marsh.Ignite.HandleRegistry.Allocate(this); + } + + /// <summary> + /// Gets the invoker func. + /// </summary> + private static Func<object, object, bool> GetInvoker(object pred) + { + var func = DelegateTypeDescriptor.GetCacheEntryFilter(pred.GetType()); + + return (key, val) => func(pred, key, val); + } + + /// <summary> + /// Creates an instance of this class from a stream. + /// </summary> + /// <param name="memPtr">Memory pointer.</param> + /// <param name="grid">Grid.</param> + /// <returns>Deserialized instance of <see cref="CacheEntryFilterHolder"/></returns> + public static CacheEntryFilterHolder CreateInstance(long memPtr, Ignite grid) + { + var stream = IgniteManager.Memory.Get(memPtr).Stream(); + + Debug.Assert(grid != null); + + var marsh = grid.Marshaller; + + return marsh.Unmarshal<CacheEntryFilterHolder>(stream); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorHolder.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorHolder.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorHolder.cs new file mode 100644 index 0000000..4ec1e1e --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorHolder.cs @@ -0,0 +1,145 @@ +/* + * 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.Cache +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Reflection; + using Apache.Ignite.Core.Cache; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Impl.Portable; + using Apache.Ignite.Core.Impl.Resource; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Portable wrapper for the <see cref="ICacheEntryProcessor{TK,TV,TA,TR}"/> and it's argument. + /// Marshals and executes wrapped processor with a non-generic interface. + /// </summary> + internal class CacheEntryProcessorHolder : IPortableWriteAware + { + // generic processor + private readonly object _proc; + + // argument + private readonly object _arg; + + // func to invoke Process method on ICacheEntryProcessor in form of object. + private readonly Func<IMutableCacheEntryInternal, object, object> _processFunc; + + // entry creator delegate + private readonly Func<object, object, bool, IMutableCacheEntryInternal> _entryCtor; + + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntryProcessorHolder"/> class. + /// </summary> + /// <param name="proc">The processor to wrap.</param> + /// <param name="arg">The argument.</param> + /// <param name="processFunc">Delegate to call generic <see cref="ICacheEntryProcessor{K, V, A, R}.Process"/> on local node.</param> + /// <param name="keyType">Type of the key.</param> + /// <param name="valType">Type of the value.</param> + public CacheEntryProcessorHolder(object proc, object arg, + Func<IMutableCacheEntryInternal, object, object> processFunc, Type keyType, Type valType) + { + Debug.Assert(proc != null); + Debug.Assert(processFunc != null); + + _proc = proc; + _arg = arg; + _processFunc = processFunc; + + _processFunc = GetProcessFunc(_proc); + + _entryCtor = MutableCacheEntry.GetCtor(keyType, valType); + } + + /// <summary> + /// Processes specified cache entry. + /// </summary> + /// <param name="key">The cache entry key.</param> + /// <param name="value">The cache entry value.</param> + /// <param name="exists">Indicates whether cache entry exists.</param> + /// <param name="grid"></param> + /// <returns> + /// Pair of resulting cache entry and result of processing it. + /// </returns> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", + Justification = "User processor can throw any exception")] + public CacheEntryProcessorResultHolder Process(object key, object value, bool exists, Ignite grid) + { + ResourceProcessor.Inject(_proc, grid); + + var entry = _entryCtor(key, value, exists); + + try + { + return new CacheEntryProcessorResultHolder(entry, _processFunc(entry, _arg), null); + } + catch (TargetInvocationException ex) + { + return new CacheEntryProcessorResultHolder(null, null, ex.InnerException); + } + catch (Exception ex) + { + return new CacheEntryProcessorResultHolder(null, null, ex); + } + } + + /** <inheritDoc /> */ + public void WritePortable(IPortableWriter writer) + { + var writer0 = (PortableWriterImpl) writer.RawWriter(); + + writer0.DetachNext(); + PortableUtils.WritePortableOrSerializable(writer0, _proc); + PortableUtils.WritePortableOrSerializable(writer0, _arg); + } + + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntryProcessorHolder"/> class. + /// </summary> + /// <param name="reader">The reader.</param> + public CacheEntryProcessorHolder(IPortableReader reader) + { + var reader0 = (PortableReaderImpl) reader.RawReader(); + + _proc = PortableUtils.ReadPortableOrSerializable<object>(reader0); + _arg = PortableUtils.ReadPortableOrSerializable<object>(reader0); + + _processFunc = GetProcessFunc(_proc); + + var kvTypes = DelegateTypeDescriptor.GetCacheEntryProcessorTypes(_proc.GetType()); + + _entryCtor = MutableCacheEntry.GetCtor(kvTypes.Item1, kvTypes.Item2); + } + + /// <summary> + /// Gets a delegate to call generic <see cref="ICacheEntryProcessor{K, V, A, R}.Process"/>. + /// </summary> + /// <param name="proc">The processor instance.</param> + /// <returns> + /// Delegate to call generic <see cref="ICacheEntryProcessor{K, V, A, R}.Process"/>. + /// </returns> + private static Func<IMutableCacheEntryInternal, object, object> GetProcessFunc(object proc) + { + var func = DelegateTypeDescriptor.GetCacheEntryProcessor(proc.GetType()); + + return (entry, arg) => func(proc, entry, arg); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResult.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResult.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResult.cs new file mode 100644 index 0000000..9a0af4f --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResult.cs @@ -0,0 +1,65 @@ +/* + * 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.Cache +{ + using System; + using Apache.Ignite.Core.Cache; + + /// <summary> + /// Represents a result of <see cref="ICacheEntryProcessor{TK,TV,TA,TR}"/> invocation. + /// </summary> + /// <typeparam name="T">Result type.</typeparam> + internal class CacheEntryProcessorResult<T> : ICacheEntryProcessorResult<T> + { + // Result + private readonly T _res; + + // Error + private readonly Exception _err; + + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntryProcessorResult{T}"/> class. + /// </summary> + /// <param name="result">The result.</param> + public CacheEntryProcessorResult(T result) + { + _res = result; + } + + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntryProcessorResult{T}"/> class. + /// </summary> + /// <param name="error">The error.</param> + public CacheEntryProcessorResult(Exception error) + { + _err = error; + } + + /** <inheritdoc /> */ + public T Result + { + get + { + if (_err != null) + throw _err as CacheEntryProcessorException ?? new CacheEntryProcessorException(_err); + + return _res; + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResultHolder.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResultHolder.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResultHolder.cs new file mode 100644 index 0000000..04cd557 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEntryProcessorResultHolder.cs @@ -0,0 +1,127 @@ +/* + * 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.Cache +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using Apache.Ignite.Core.Impl.Common; + using Apache.Ignite.Core.Impl.Portable; + using Apache.Ignite.Core.Impl.Portable.IO; + + /// <summary> + /// Manages cache entry processing result in non-generic form. + /// </summary> + internal class CacheEntryProcessorResultHolder + { + /// <summary> + /// Initializes a new instance of the <see cref="CacheEntryProcessorResultHolder"/> class. + /// </summary> + /// <param name="entry">Entry.</param> + /// <param name="processResult">Process result.</param> + /// <param name="error">Error.</param> + public CacheEntryProcessorResultHolder(IMutableCacheEntryInternal entry, object processResult, Exception error) + { + Entry = entry; + ProcessResult = processResult; + Error = error; + } + + /// <summary> + /// Gets the entry. + /// </summary> + public IMutableCacheEntryInternal Entry { get; private set; } + + /// <summary> + /// Gets the process result. + /// </summary> + public object ProcessResult { get; private set; } + + /// <summary> + /// Gets the error. + /// </summary> + public Exception Error { get; private set; } + + /// <summary> + /// Writes this instance to the stream. + /// </summary> + /// <param name="stream">Stream.</param> + /// <param name="marsh">Marshaller.</param> + public void Write(IPortableStream stream, PortableMarshaller marsh) + { + var writer = marsh.StartMarshal(stream); + + try + { + Marshal(writer); + } + finally + { + marsh.FinishMarshal(writer); + } + } + + /// <summary> + /// Marshal this instance. + /// </summary> + /// <param name="writer">Writer.</param> + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", + Justification = "Any kind of exception can be thrown during user type marshalling.")] + private void Marshal(PortableWriterImpl writer) + { + var pos = writer.Stream.Position; + + try + { + if (Error == null) + { + writer.WriteByte((byte) Entry.State); + + if (Entry.State == MutableCacheEntryState.ValueSet) + writer.Write(Entry.Value); + + writer.Write(ProcessResult); + } + else + { + writer.WriteByte((byte) MutableCacheEntryState.ErrPortable); + writer.Write(new PortableResultWrapper(Error)); + } + } + catch (Exception marshErr) + { + writer.Stream.Seek(pos, SeekOrigin.Begin); + + writer.WriteByte((byte) MutableCacheEntryState.ErrString); + + if (Error == null) + { + writer.WriteString(string.Format( + "CacheEntryProcessor completed with error, but result serialization failed [errType={0}, " + + "err={1}, serializationErrMsg={2}]", marshErr.GetType().Name, marshErr, marshErr.Message)); + } + else + { + writer.WriteString(string.Format( + "CacheEntryProcessor completed with error, and error serialization failed [errType={0}, " + + "err={1}, serializationErrMsg={2}]", marshErr.GetType().Name, marshErr, marshErr.Message)); + } + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/5cec202c/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEnumerable.cs ---------------------------------------------------------------------- diff --git a/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEnumerable.cs b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEnumerable.cs new file mode 100644 index 0000000..2dd03c9 --- /dev/null +++ b/modules/platform/src/main/dotnet/Apache.Ignite.Core/Impl/Cache/CacheEnumerable.cs @@ -0,0 +1,82 @@ +/* + * 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.Cache +{ + using System.Collections; + using System.Collections.Generic; + using Apache.Ignite.Core.Cache; + + /// <summary> + /// Cache enumerable. + /// </summary> + internal class CacheEnumerable<TK, TV> : IEnumerable<ICacheEntry<TK, TV>> + { + /** Target cache. */ + private readonly CacheImpl<TK, TV> _cache; + + /** Local flag. */ + private readonly bool _loc; + + /** Peek modes. */ + private readonly int _peekModes; + + /// <summary> + /// Constructor for distributed iterator. + /// </summary> + /// <param name="cache">Target cache.</param> + public CacheEnumerable(CacheImpl<TK, TV> cache) : this(cache, false, 0) + { + // No-op. + } + + /// <summary> + /// Constructor for local iterator. + /// </summary> + /// <param name="cache">Target cache.</param> + /// <param name="peekModes">Peek modes.</param> + public CacheEnumerable(CacheImpl<TK, TV> cache, int peekModes) : this(cache, true, peekModes) + { + // No-op. + } + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="cache">Target cache.</param> + /// <param name="loc">Local flag.</param> + /// <param name="peekModes">Peek modes.</param> + private CacheEnumerable(CacheImpl<TK, TV> cache, bool loc, int peekModes) + { + _cache = cache; + _loc = loc; + _peekModes = peekModes; + } + + /** <inheritdoc /> */ + public IEnumerator<ICacheEntry<TK, TV>> GetEnumerator() + { + return new CacheEnumeratorProxy<TK, TV>(_cache, _loc, _peekModes); + } + + /** <inheritdoc /> */ + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +}
