public IxLock(IIxInstanceLock instanceLock) { Critical.Assert(instanceLock != null, "Lock target should not be null."); _instanceLock = instanceLock; _target = (T)_instanceLock.Target.Object; }
/// <summary> /// Processes UoW disposed event. /// </summary> /// <param name="uow">Disposed UoW.</param> /// <remarks>SyncRoot <c>lock</c> required here.</remarks> internal void OnUowDisposed(TUnitOfWork uow) { if (!_openedUoWs.TryRemove(uow, out var _)) { Critical.Assert(false, "Wrong Unit of Work or double dispose."); } }
/// <inheritdoc/> public void RemoveLock(IIxInstanceLock instanceLock) { if (instanceLock == null) { throw new ArgumentNullException(nameof(instanceLock)); } EnsureTreeLocked(); if (ReferenceEquals(instanceLock, _initTempLock)) { Critical.Assert( _objectCreationTask != null, "You need to setup instance object factory before releasing creator lock."); Critical.Assert( _objectCreationTask.Value.IsCompleted, "Creator lock should be removed only after object instantiation completes."); _initTempLock = null; } // --------- We are under tree lock -------------- if (!_locks.Remove(instanceLock)) { Critical.Assert(false, "Lock was not registered in the target or already removed."); } UpdateDisposeSuspendState(); UpdateChildrenDisposeCompleteSuspendState(); }
/// <inheritdoc/> public override async ValueTask <IIxInstanceLock> GetInstance( IIxInstance parentInstance, IxIdentifier identifier, IxHost.IxResolveContext context, [CanBeNull] IxResolveFrame frame) { Critical.Assert(false, "Not supported."); return(null); }
/// <summary> /// Initializes a new instance of the <see cref="IxInstancePinLock"/> class. /// </summary> /// <param name="target">IndirectX instance.</param> public IxInstancePinLock(IIxInstance target) { Critical.Assert(target != null, "Pin lock target should not be null."); Target = target; lock (target.ProviderNode.Host.InstanceTreeSyncRoot) { Target.AddLock(this); } }
private void UpdateChildrenDisposeCompleteSuspendState() { try { _childrenDisposeCompleted.SuspendTrigger(_locks.Any()); } catch (InvalidOperationException) { Critical.Assert(false, "Cannot set child lock, full dispose was completed."); } }
/// <summary> /// Registers persistence <c>plugin</c>. /// </summary> /// <param name="plugin">Persistence <c>plugin</c>.</param> internal void RegisterPlugin(PersistencePluginBase <TPersistence, TUnitOfWork> plugin) { if (plugin == null) { throw new ArgumentNullException(nameof(plugin)); } Critical.Assert(!_isInitialized, "You cannot register plugin after persistence become initialized."); _plugins.Add(plugin); }
private async ValueTask <IIxInstanceLock> RegistrationScopeBinder( IIxInstance originInstance, IxResolvePath resolvePath, IxResolveContext context, [CanBeNull] IxResolveFrame frame, IxResolveBoundDelegate resolveBound) { IIxInstance curInstance = originInstance; Critical.Assert(resolvePath.Path.Any(), "Registration scope binder does not support empty path."); lock (InstanceTreeSyncRoot) { while (curInstance != null) { if (curInstance.ProviderNode == resolvePath.Root) { break; } curInstance = curInstance.ParentInstance; } } if (curInstance == null) { throw new InvalidOperationException("Resolve algorithms problems"); } Func <IIxInstance, int, ValueTask <IIxInstanceLock> > resolvePathElements = null; resolvePathElements = async(parentInstance, index) => { if (index < resolvePath.Path.Count - 1) { using (IIxInstanceLock instanceLock = await Resolve( parentInstance, resolvePath.Path[index].Identifier, context, frame)) { return(await resolvePathElements(instanceLock.Target, index + 1)); } } return(await resolveBound(parentInstance, resolvePath.Path.Last(), context)); }; IIxInstanceLock resultInstanceLock = await resolvePathElements(curInstance, 0); return(resultInstanceLock); }
public object GetData(IxProviderNode providerNode) { Critical.CheckedAssert(providerNode != null, "providerNode != null"); if (!Monitor.IsEntered(_originInstance.ProviderNode.Host.InstanceTreeSyncRoot)) { Critical.Assert(false, "Data manipulations should be performed under lock."); } _rootContext.ProvidersData.TryGetValue(providerNode, out object result); return(result); }
/// <inheritdoc/> public void AddOwnedLock(IIxInstanceLock instanceLock) { if (instanceLock == null) { throw new ArgumentNullException(nameof(instanceLock)); } EnsureTreeLocked(); if (!_ownedLocks.Add(instanceLock)) { Critical.Assert(false, "Owned Lock already registered in the owner."); } }
/// <inheritdoc/> public void RemoveOwnedLock(IIxInstanceLock instanceLock) { if (instanceLock == null) { throw new ArgumentNullException(nameof(instanceLock)); } EnsureTreeLocked(); // --------- We are under tree lock -------------- if (!_ownedLocks.Remove(instanceLock)) { Critical.Assert(false, "Owned lock was not registered in the owner or already removed."); } }
private void UpdateDisposeSuspendState() { try { bool nonChildLocksExists = !Locks.All(x => x is IxInstanceChildLock); SetDisposeSuspended(nonChildLocksExists); if (!nonChildLocksExists && ProviderNode.AutoDisposeEnabled) { DisposeAsync(); } } catch (InvalidOperationException) { Critical.Assert(false, "Cannot set lock, self dispose was started."); } }
/// <summary> /// Initializes object creation task. /// </summary> /// <param name="objectCreateTask">The object creation task.</param> protected void SetObjectCreationTask(ValueTask <object> objectCreateTask) { // If everything fine this method will be called from one thread and only once. // We can skip thread safety check for this method. if (objectCreateTask == null) { throw new ArgumentNullException(nameof(objectCreateTask)); } EnsureTreeLocked(); Critical.Assert( _objectCreationTask == null, "Instance already initialized, you cannot init Object property twice."); _objectCreationTask = ObjectFactoryProxy(objectCreateTask); }
/// <inheritdoc/> public void AddLock(IIxInstanceLock instanceLock) { if (instanceLock == null) { throw new ArgumentNullException(nameof(instanceLock)); } EnsureTreeLocked(); bool inserted = _locks.Add(instanceLock); Critical.Assert(inserted, "Lock already registered in the target instance."); UpdateDisposeSuspendState(); UpdateChildrenDisposeCompleteSuspendState(); }
public object GetData(IxProviderNode providerNode) { if (providerNode == null) { throw new ArgumentNullException(nameof(providerNode)); } if (!Monitor.IsEntered(ProviderNode.Host.InstanceTreeSyncRoot)) { Critical.Assert(false, "Data manipulations should be performed under lock."); } object result; ChildrenData.TryGetValue(providerNode, out result); return(result); }
/// <inheritdoc/> public void SetData(IxProviderNode providerNode, [CanBeNull] object data) { Critical.CheckedAssert(providerNode != null, "providerNode != null"); if (!Monitor.IsEntered(_originInstance.ProviderNode.Host.InstanceTreeSyncRoot)) { Critical.Assert(false, "Data manipulations should be performed under tree lock."); } if (data == null) { _rootContext.ProvidersData.Remove(providerNode); } else { _rootContext.ProvidersData[providerNode] = data; } }
/// <inheritdoc/> protected override async Task DisposeAsyncCore() { // Under tree lock here, until first await. EnsureTreeLocked(); try { if (_locks.Any(x => !(x is IxInstanceChildLock))) { Critical.Assert( false, "Dependencies schema problem, after instance object disposed no any lock are allowed."); } if (!ObjectCreationTask.IsFaulted) { // Only fully-initialized instance should be self-disposed. await SelfDispose(); } // This call is not required. ////UpdateChildrenDisposeCompleteSuspendState(); _childrenDisposeCompleted.Set(); // Sync or async execution here. } finally { // Sync or async execution here. // ------------------------------------ lock (ProviderNode.Host.InstanceTreeSyncRoot) { // Freeing all owned locks. while (OwnedLocks.Any()) { // Here other objects can dispose synchronously. OwnedLocks.First().Dispose(); } } await _childrenDisposeCompleted; } }
/// <inheritdoc/> public void Dispose() { try { try { _instanceLock?.Dispose(); } catch (Exception ex) when(ex.IsProcessable()) { Critical.Assert(false, "Dispose should not raise any error."); } } catch { // Dispose should decouple errors propagation. // Usually dispose errors relates only to disposing component, but not to it's caller. } }
/// <inheritdoc/> public void Dispose() { try { Critical.Assert(!_disposed, "Lock already disposed."); lock (Target.ProviderNode.Host.InstanceTreeSyncRoot) { Critical.Assert(!_disposed, "Lock already disposed."); _disposed = true; Target.RemoveLock(this); } } catch { // Dispose should decouple errors propagation. // Usually dispose errors relates only to disposing component, but not to it's caller. } }
/// <inheritdoc/> public void SetData(IxProviderNode providerNode, [CanBeNull] object data) { if (providerNode == null) { throw new ArgumentNullException(nameof(providerNode)); } if (!Monitor.IsEntered(ProviderNode.Host.InstanceTreeSyncRoot)) { Critical.Assert(false, "Data manipulations should be performed under tree lock."); } if (data == null) { ChildrenData.Remove(providerNode); } else { ChildrenData[providerNode] = data; } }
/// <summary> /// Initializes a new instance of the <see cref="IxArgumentProvider"/> class. /// </summary> /// <param name="host">IndirectX host.</param> public IxArgumentProvider(IxHost host) : base( host, null, new IxStdProviderConfig { Identifier = new IxIdentifier(typeof(object)), }, null, identifier => true, identifier => true, identifier => true, async(a, b, c, d, e) => { Critical.Assert(false, "Not supported."); return(null); }, false, obj => TaskEx.CompletedTask) { }
/// <inheritdoc/> public IUnitOfWorkImpl OpenUnitOfWork() { VerifyInitialized(); TUnitOfWork uow = CreateUnitOfWork(); _openedUoWs.TryAdd(uow, default); try { // Handler should never rise an exception. OnUnitOfWorkOpened(uow); } catch (Exception ex) { Critical.Assert( false, $"UnitOfWorkOpened handlers should never rise any exception. Error = {ex.Message}"); } return(uow); }
/// <summary> /// Initializes a new instance of the <see cref="IxReferenceLock"/> class. /// </summary> /// <param name="target">The instance to lock.</param> /// <param name="owner">The owner of this lock.</param> public IxReferenceLock(IIxInstance target, IIxInstance owner) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (owner == null) { throw new ArgumentNullException(nameof(owner)); } Critical.Assert(!ReferenceEquals(target, owner), "Cannot create reference from self to self."); Target = target; Owner = owner; lock (Target.ProviderNode.Host.InstanceTreeSyncRoot) { Target.AddLock(this); Owner.AddOwnedLock(this); } }
/// <summary> /// Creates or updates entity. /// </summary> /// <remarks> /// Update or Create operations are performed in the critical section, so left any processing out-of those functions. /// </remarks> /// <param name="entityKey">The entity key.</param> /// <param name="updateAction">The update action. It will be called when entity with the specified key exists.</param> /// <param name="createFunc">The create function. It will be called when entity with the specified key not exists.</param> public void CreateOrUpdateEntity(TKey entityKey, Action <TEntity> updateAction, Func <TEntity> createFunc) { VxArgs.NotNull(entityKey, nameof(entityKey)); VxArgs.NotNull(updateAction, nameof(updateAction)); VxArgs.NotNull(createFunc, nameof(createFunc)); lock (_entities) { if (_entities.TryGetValue(entityKey, out var entity)) { updateAction(entity); } else { TEntity newEntity = createFunc(); // Following max capacity rule. if (MaxCapacity != null && _gcFifoQueue != null) { if (_gcFifoQueue.Count >= MaxCapacity) { if (_gcFifoQueue.TryDequeue(out var itemToRemove)) { _entities.Remove(itemToRemove.Key); } } _gcFifoQueue.AddFirst(entityKey, default); } _entities.Add(entityKey, newEntity); Critical.Assert( newEntity.Key.Equals(entityKey), "Created entity should have the same key as was queried to the CreateOrUpdateEntity method."); } } }
private ResolveDelegate SelfToChildrenResolver(ResolveDelegate next) { return(async(originInstance, identifier, context, frame) => { if (identifier.Name == null) { IxResolvePath resolvePath; if (originInstance.ProviderNode.VisibleNodes.TryGetValue(identifier, out resolvePath)) { if (!resolvePath.Path.Any()) { lock (InstanceTreeSyncRoot) { IIxInstance curInstance = originInstance; while (curInstance != null) { if (curInstance.ProviderNode == resolvePath.Root) { break; } curInstance = curInstance.ParentInstance; } Critical.Assert( curInstance != null, "Instance of an appropriate provider should be found."); return new IxInstancePinLock(curInstance); } } } } return await next(originInstance, identifier, context, frame); }); }
private InstanceFactoryBuilderDelegate ClassInstanceFactoryBuilder( InstanceFactoryBuilderDelegate next) { return(factoryConfig => { TypeInfo configTypeInfo = factoryConfig.GetType().GetTypeInfo(); if (configTypeInfo.IsGenericType && (configTypeInfo.GetGenericTypeDefinition() == typeof(IxClassInstanceBuilderConfig <>))) { TypeInfo instanceClass = configTypeInfo.GenericTypeArguments[0].GetTypeInfo(); ConstructorInfo[] constructors = instanceClass.DeclaredConstructors.Where(x => !x.IsStatic).ToArray(); if (constructors.Length == 0) { // Currently impossible case. throw new IxConfigurationException( $"Cannot use IxClassInstanceFactory because no any constructors found in the class {instanceClass.FullName}."); } if (constructors.Length > 1) { throw new IxConfigurationException( $"Cannot use IxClassInstanceFactory because more than one constructors defined in the class {instanceClass.FullName}."); } ConstructorInfo constructorInfo = constructors.Single(); HashSet <IxIdentifier> dependencies = constructorInfo.GetParameters().Select(x => new IxIdentifier(x.ParameterType)).ToHashSet(); if (dependencies.Count != constructorInfo.GetParameters().Length) { throw new IxConfigurationException( "Multiple parameters with the same type not supported by IxClassRawFactory."); } // TODO: Add tests for this feature List <RequireAttribute> requireAttributes = constructorInfo.GetCustomAttributes <RequireAttribute>().ToList(); foreach (RequireAttribute requireAttribute in requireAttributes) { if (!dependencies.Add(new IxIdentifier(requireAttribute.Type))) { throw new IxConfigurationException( "Multiple parameters with the same type not supported by IxClassRawFactory."); } } // instance argument here is half-instantiated instance. return new IxInstanceFactory( (instance, parentInstance, resolveContext, frame) => ResolveList( instance, dependencies, resolveContext, frame, async resolvedDependencies => { try { object[] arguments = constructorInfo.GetParameters() .Select( x => resolvedDependencies[new IxIdentifier(x.ParameterType)] .Object) .ToArray(); object instanceObj = constructorInfo.Invoke(arguments); Critical.Assert( instanceObj != null, "Constructor call through reflection should not return null."); lock (instance.ProviderNode.Host.InstanceTreeSyncRoot) { foreach (KeyValuePair <IxIdentifier, IIxInstance> kvp in resolvedDependencies) { // ReSharper disable once ObjectCreationAsStatement new IxReferenceLock(kvp.Value, instance); } } // Just to avoid multiple functions. return instanceObj; } catch (TargetInvocationException ex) { throw ex.InnerException; } }), configTypeInfo.GenericTypeArguments[0]); } return next(factoryConfig); }); }
private InstanceFactoryBuilderDelegate DelegateInstanceBuilder( InstanceFactoryBuilderDelegate next) { return(instanceBuilderConfig => { TypeInfo configTypeInfo = instanceBuilderConfig.GetType().GetTypeInfo(); var delegateInstanceBuilderConfig = instanceBuilderConfig as IxDelegateInstanceBuilderConfig; if (delegateInstanceBuilderConfig == null) { return next(instanceBuilderConfig); } TypeInfo delegateType = delegateInstanceBuilderConfig.Func.GetType().GetTypeInfo(); MethodInfo methodInfo = delegateType.GetDeclaredMethod("Invoke"); HashSet <IxIdentifier> dependencies = methodInfo.GetParameters().Select(x => new IxIdentifier(x.ParameterType)).ToHashSet(); if (dependencies.Count != methodInfo.GetParameters().Length) { throw new IxConfigurationException( "Multiple parameters with the same type not supported by IxClassRawFactory."); } // TODO: Add tests for this feature List <RequireAttribute> requireAttributes = methodInfo.GetCustomAttributes <RequireAttribute>().ToList(); foreach (RequireAttribute requireAttribute in requireAttributes) { if (!dependencies.Add(new IxIdentifier(requireAttribute.Type))) { throw new IxConfigurationException( "Multiple parameters with the same type not supported by IxClassRawFactory."); } } return new IxInstanceFactory( (instance, parentInstance, resolveContext, frame) => ResolveList( instance, dependencies, resolveContext, frame, async resolvedDependencies => { object[] arguments = methodInfo.GetParameters() .Select( x => resolvedDependencies[new IxIdentifier(x.ParameterType)].Object) .ToArray(); object instanceObjTask; Task instanceObjTaskCasted; try { // TODO: Optimize me. instanceObjTask = methodInfo.Invoke( delegateInstanceBuilderConfig.Func, arguments); instanceObjTaskCasted = (Task)instanceObjTask.GetType().GetTypeInfo() .GetDeclaredMethod("AsTask") .Invoke(instanceObjTask, new object[] { }); await instanceObjTaskCasted; } catch (TargetInvocationException ex) { throw ex.InnerException; } object instanceObj = instanceObjTaskCasted.GetResult(); // TODO: TryCast, // TODO: Allow null result. Critical.Assert( instanceObj != null, "Constructor call through reflection should not return null."); lock (instance.ProviderNode.Host.InstanceTreeSyncRoot) { foreach (KeyValuePair <IxIdentifier, IIxInstance> kvp in resolvedDependencies) { // ReSharper disable once ObjectCreationAsStatement new IxReferenceLock(kvp.Value, instance); } } return instanceObj; }), methodInfo.ReturnType.GenericTypeArguments[0]); }); }
void IIxInstance.RemoveOwnedLock(IIxInstanceLock instanceLock) { Critical.Assert(false, "This is completely virtual object and cannot own locks."); }
void IIxInstance.SetData(IxProviderNode providerNode, [CanBeNull] object data) { Critical.Assert(false, "This object not intended to have children and children data."); }
protected void EnsureTreeLocked() { Critical.Assert( Monitor.IsEntered(ProviderNode.Host.InstanceTreeSyncRoot), "Lock related operation should be performed under global tree lock."); }