/// <summary> /// Injects a channel into the current scope, by looking in the parent scope. /// This is particularly useful in isolated scopes, to selectively forward channels /// </summary> /// <param name="names">The names of the channel to create.</param> /// <param name="parent">The scope to look in, <code>null</code> means the current parent</param> public void InjectChannelsFromParent(IEnumerable <string> names, ChannelScope parent = null) { foreach (var n in names) { InjectChannelFromParent(n, parent); } }
/// <summary> /// Handler method to create a profiling channel /// </summary> /// <returns>The profiling channel.</returns> /// <param name="scope">The scope calling the method.</param> /// <param name="attr">The channel attribute.</param> /// <param name="type">The type to create the channel for</param> private static IUntypedChannel Creator(ChannelScope scope, ChannelNameAttribute attr, Type type) { return ((IUntypedChannel)typeof(ProfilerChannelScope) .GetMethod(nameof(CreateProfilingChannel), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.NonPublic) .MakeGenericMethod(type) .Invoke(scope, new object[] { attr })); }
/// <summary> /// Initializes a new instance of the <see cref="CoCoL.ChannelScope"/> class that derives from a parent scope. /// </summary> /// <param name="parent">The parent scope.</param> /// <param name="isolated"><c>True</c> if this is an isolated scope, <c>false</c> otherwise</param> private ChannelScope(ChannelScope parent, bool isolated) { ParentScope = parent; Isolated = isolated; Current = this; lock (__lock) __scopes[m_instancekey] = this; }
/// <summary> /// Wires up all named channels using the supplied scope /// </summary> /// <param name="items">The processes to wire up.</param> /// <param name="scope">The current scope.</param> public static ChannelScope AutoWireChannels <T>(IEnumerable <T> items, ChannelScope scope = null) { scope = scope ?? ChannelScope.Current; foreach (var p in items) { AutoWireChannelsDirect(p, scope); } return(scope); }
/// <summary> /// Injects a channel into the current scope, by looking in the parent scope. /// This is particularly useful in isolated scopes, to selectively forward channels /// </summary> /// <param name="name">The name of the channel to create.</param> /// <param name="parent">The scope to look in, <code>null</code> means the current parent</param> public void InjectChannelFromParent(string name, ChannelScope parent = null) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } parent = parent ?? this.ParentScope; lock (__lock) { var c = parent.RecursiveLookup(name); m_lookup[name] = c ?? throw new ArgumentException($"No channel with the name \"{name}\" was found in the parent scope", nameof(name)); } }
/// <summary> /// Gets or creates a named channel. /// </summary> /// <returns>The named channel.</returns> /// <param name="name">The name of the channel to find.</param> /// <param name="buffersize">The number of buffers in the channel.</param> /// <param name="scope">The scope to create a named channel in, defaults to null which means the current scope</param> /// <param name="maxPendingReaders">The maximum number of pending readers. A negative value indicates infinite</param> /// <param name="maxPendingWriters">The maximum number of pending writers. A negative value indicates infinite</param> /// <param name="pendingReadersOverflowStrategy">The strategy for dealing with overflow for read requests</param> /// <param name="pendingWritersOverflowStrategy">The strategy for dealing with overflow for write requests</param> /// <param name="broadcast"><c>True</c> will create the channel as a broadcast channel, the default <c>false</c> will create a normal channel</param> /// <param name="initialBroadcastBarrier">The number of readers required on the channel before sending the first broadcast, can only be used with broadcast channels</param> /// <param name="broadcastMinimum">The minimum number of readers required on the channel, before a broadcast can be performed, can only be used with broadcast channels</param> /// <typeparam name="T">The channel type.</typeparam> public static IChannel <T> GetChannel <T>(string name, int buffersize = 0, ChannelScope scope = null, int maxPendingReaders = -1, int maxPendingWriters = -1, QueueOverflowStrategy pendingReadersOverflowStrategy = QueueOverflowStrategy.Reject, QueueOverflowStrategy pendingWritersOverflowStrategy = QueueOverflowStrategy.Reject, bool broadcast = false, int initialBroadcastBarrier = -1, int broadcastMinimum = -1) { if (!broadcast && (initialBroadcastBarrier >= 0 || broadcastMinimum >= 0)) { throw new ArgumentException(string.Format("Cannot set \"{0}\" or \"{1}\" unless the channel is a broadcast channel", "initialBroadcastBarrier", "broadcastMinimum")); } var attr = broadcast ? new BroadcastChannelNameAttribute(name, buffersize, ChannelNameScope.Local, maxPendingReaders, maxPendingWriters, pendingReadersOverflowStrategy, pendingWritersOverflowStrategy, initialBroadcastBarrier, broadcastMinimum) : new ChannelNameAttribute(name, buffersize, ChannelNameScope.Local, maxPendingReaders, maxPendingWriters, pendingReadersOverflowStrategy, pendingWritersOverflowStrategy); return(GetChannel <T>(attr, scope)); }
/// <summary> /// Wires up all named channels using the supplied scope. /// Checks if the supplied item is an IEnumerable and iterates it /// if possible /// </summary> /// <param name="item">The item to wire up.</param> /// <param name="scope">The current scope.</param> public static T AutoWireChannels <T>(T item, ChannelScope scope = null) { // Due to the type matching, we can end up here when // the user supplies an array or similar // The encapsulation ensures that we only expand the single // outer most instance if (typeof(System.Collections.IEnumerable).IsAssignableFrom(typeof(T))) { var en = item as System.Collections.IEnumerable; foreach (var x in en) { AutoWireChannelsDirect(x, scope); } } return(AutoWireChannelsDirect(item)); }
/// <summary> /// Injects a channel into the current scope, by looking in the parent scope. /// This is particularly useful in isolated scopes, to selectively forward channels /// </summary> /// <param name="name">The name of the channel to create.</param> /// <param name="parent">The scope to look in, <code>null</code> means the current parent</param> public void InjectChannelFromParent(string name, ChannelScope parent = null) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException("name"); } parent = parent ?? this.ParentScope; lock (__lock) { var c = parent.RecursiveLookup(name); if (c == null) { throw new Exception(string.Format("No channel with the name {0} was found in the parent scope")); } m_lookup[name] = c; } }
/// <summary> /// Wires up all named channels using the supplied scope /// </summary> /// <param name="item">The item to wire up.</param> /// <param name="scope">The current scope.</param> public static IProcess AutoWireChannels(this IProcess item, ChannelScope scope = null) { return(AutoWireChannelsDirect <IProcess>(item, scope)); }
/// <summary> /// Wires up all named channels using the supplied scope /// </summary> /// <param name="items">The processes to wire up.</param> /// <param name="scope">The current scope.</param> public static ChannelScope AutoWireChannels(this IEnumerable <IProcess> items, ChannelScope scope = null) { return(AutoWireChannels <IProcess>(items, scope)); }
/// <summary> /// Wires up all named channels using the supplied scope for the given element. /// Does not check if the given item is an IEnumerable /// </summary> /// <param name="item">The item to wire up.</param> /// <param name="scope">The current scope.</param> public static T AutoWireChannelsDirect <T>(T item, ChannelScope scope = null) { // TODO: Throw an exception if we have no channels to wire and item != null ? scope = scope ?? ChannelScope.Current; foreach (var c in GetAllFieldAndPropertyValuesOfType <IRetireAbleChannel>(item)) { try { var marker = c.Value as ChannelNameMarker; // Make sure we do not continue with a marker class instance if (marker != null) { if (c.Key is FieldInfo fi) { fi.SetValue(item, null); } else if (c.Key is PropertyInfo pi) { pi.SetValue(item, null); } else { throw new InvalidOperationException("Marker was neither a field nor a property"); } } var autocreate = true; // Skip if already assigned if (c.Value != null && !(c.Value is ChannelNameMarker)) { autocreate = false; } var attr = c.Key.GetCustomAttribute(typeof(ChannelNameAttribute), true) as ChannelNameAttribute; // Override if we get a marker if (attr == null && marker != null) { attr = marker.Attribute; } // Skip if the channel does not have a name if (attr == null || string.IsNullOrWhiteSpace(attr.Name)) { autocreate = false; } var channelType = c.Key is FieldInfo ? ((FieldInfo)c.Key).FieldType : ((PropertyInfo)c.Key).PropertyType; if (autocreate) { // Figure out what type of channel we expect var readInterface = new [] { channelType }.Union(channelType.GetInterfaces()).Where(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == (typeof(IReadChannel <>))).FirstOrDefault(); var writeInterface = new [] { channelType }.Union(channelType.GetInterfaces()).Where(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == (typeof(IWriteChannel <>))).FirstOrDefault(); if (readInterface == null && writeInterface == null) { throw new ArgumentException(string.Format("Item {0} had a channelname attribute but is not of the channel type", c.Key.Name)); } var isOnlyReadOrWrite = (readInterface == null) != (writeInterface == null); // Extract the channel data type Type dataType = (readInterface ?? writeInterface).GenericTypeArguments[0]; // Honor scope requirements var curscope = scope; if (attr.TargetScope == ChannelNameScope.Parent) { curscope = scope.ParentScope ?? curscope; } else if (attr.TargetScope == ChannelNameScope.Global) { curscope = ChannelScope.Root; } // Instantiate or fetch the channel var chan = curscope.GetOrCreate(attr, dataType); if (isOnlyReadOrWrite) { if (readInterface != null && channelType.IsAssignableFrom(typeof(IReadChannelEnd <>).MakeGenericType(dataType))) { chan = (IRetireAbleChannel)typeof(ChannelExtensions).GetMethod("AsReadOnly").MakeGenericMethod(dataType).Invoke(null, new object[] { chan }); } else if (writeInterface != null && channelType.IsAssignableFrom(typeof(IWriteChannelEnd <>).MakeGenericType(dataType))) { chan = (IRetireAbleChannel)typeof(ChannelExtensions).GetMethod("AsWriteOnly").MakeGenericMethod(dataType).Invoke(null, new object[] { chan }); } } // Assign the channel to the field or property if (c.Key is FieldInfo fi) { fi.SetValue(item, chan); } else if (c.Key is PropertyInfo pi) { pi.SetValue(item, chan); } else { throw new InvalidOperationException("Item is neither field nor property"); } JoinChannel(chan, channelType); } else { JoinChannel(c.Value, channelType); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Failed to set channel: {1}, message: {0}", ex, c.Key.Name); } } //TODO: Support Enumerables too? foreach (var c in GetAllFieldAndPropertyValuesOfType <Array>(item)) { for (var i = 0; i < c.Value.Length; i++) { try { JoinChannel(c.Value.GetValue(i), (c.Key is FieldInfo ? ((FieldInfo)c.Key).FieldType : ((PropertyInfo)c.Key).PropertyType).GetElementType()); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Failed to join channel: {1}, message: {0}", ex, c.Key.Name); } } } return(item); }
/// <summary> /// Static initializer to control the creation order /// </summary> static ChannelScope() { __lock = new object(); Root = new ChannelScope(null, true); }
/// <summary> /// Creates a channel, possibly unnamed. /// If a channel name is provided, the channel is created in the supplied scope. /// If a channel with the given name is already found in the supplied scope, the named channel is returned. /// </summary> /// <returns>The channel.</returns> /// <param name="name">The name of the channel, or null.</param> /// <param name="buffersize">The number of buffers in the channel.</param> /// <param name="scope">The scope to create a named channel in, defaults to null which means the current scope</param> /// <param name="maxPendingReaders">The maximum number of pending readers. A negative value indicates infinite</param> /// <param name="maxPendingWriters">The maximum number of pending writers. A negative value indicates infinite</param> /// <param name="pendingReadersOverflowStrategy">The strategy for dealing with overflow for read requests</param> /// <param name="pendingWritersOverflowStrategy">The strategy for dealing with overflow for write requests</param> /// <param name="broadcast"><c>True</c> will create the channel as a broadcast channel, the default <c>false</c> will create a normal channel</param> /// <param name="initialBroadcastBarrier">The number of readers required on the channel before sending the first broadcast, can only be used with broadcast channels</param> /// <param name="broadcastMinimum">The minimum number of readers required on the channel, before a broadcast can be performed, can only be used with broadcast channels</param> /// <typeparam name="T">The channel type.</typeparam> public static IChannel <T> Create <T>(string name = null, int buffersize = 0, ChannelScope scope = null, int maxPendingReaders = -1, int maxPendingWriters = -1, QueueOverflowStrategy pendingReadersOverflowStrategy = QueueOverflowStrategy.Reject, QueueOverflowStrategy pendingWritersOverflowStrategy = QueueOverflowStrategy.Reject, bool broadcast = false, int initialBroadcastBarrier = -1, int broadcastMinimum = -1) { return(ChannelManager.CreateChannel <T>(name, buffersize, scope, maxPendingReaders, maxPendingWriters, pendingReadersOverflowStrategy, pendingWritersOverflowStrategy, broadcast, initialBroadcastBarrier, broadcastMinimum)); }
/// <summary> /// Gets or creates a named channel. /// </summary> /// <returns>The named channel.</returns> /// <param name="attr">The attribute describing the channel.</param> /// <param name="scope">The scope to create a named channel in, defaults to null which means the current scope</param> /// <typeparam name="T">The channel type.</typeparam> public static IChannel <T> Get <T>(ChannelNameAttribute attr, ChannelScope scope = null) { return(ChannelManager.GetChannel <T>(attr, scope)); }
/// <summary> /// Gets or creates a named channel. /// </summary> /// <returns>The named channel.</returns> /// <param name="attr">The attribute describing the channel.</param> /// <param name="scope">The scope to create a named channel in, defaults to null which means the current scope</param> /// <typeparam name="T">The channel type.</typeparam> public static IChannel <T> GetChannel <T>(ChannelNameAttribute attr, ChannelScope scope = null) { return((scope ?? ChannelScope.Current).GetOrCreate <T>(attr)); }
/// <summary> /// Creates a channel, possibly unnamed. /// If a channel name is provided, the channel is created in the supplied scope. /// If a channel with the given name is already found in the supplied scope, the named channel is returned. /// </summary> /// <returns>The named channel.</returns> /// <param name="attr">The attribute describing the channel.</param> /// <param name="scope">The scope to create a named channel in, defaults to null which means the current scope</param> /// <typeparam name="T">The channel type.</typeparam> public static IChannel <T> CreateChannel <T>(ChannelNameAttribute attr, ChannelScope scope = null) { return(GetChannel <T>(attr, scope)); }