/// <summary> /// Creates new instance and adds to pool /// </summary> protected virtual async Task <PoolServiceDescriptor <TService> > CreateNew(IContainerScope scope, bool locked) { PoolServiceDescriptor <TService> descriptor = new PoolServiceDescriptor <TService>(); descriptor.Locked = locked; descriptor.Scope = scope; descriptor.LockExpiration = DateTime.UtcNow.Add(Options.MaximumLockDuration); if (Type == ImplementationType.Scoped && scope != null) { //we couldn't find any created instance. create new. object instance = await Container.CreateInstance(typeof(TImplementation), scope); scope.PutItem(typeof(TService), instance); descriptor.Instance = (TService)instance; } else { object instance = await Container.CreateInstance(typeof(TImplementation), scope); descriptor.Instance = (TService)instance; } if (_func != null) { _func(descriptor.Instance); } lock (_descriptors) _descriptors.Add(descriptor); return(descriptor); }
/// <summary> /// Waits until an item is available. /// If any available item cannot be found, creates new if exceed possible. Otherwise returns null /// </summary> private async Task WaitForAvailable(IContainerScope scope, TaskCompletionSource <PoolServiceDescriptor <TService> > state) { //try to get when available if (Options.WaitAvailableDuration > TimeSpan.Zero) { DateTime waitMax = DateTime.UtcNow.Add(Options.WaitAvailableDuration); while (DateTime.UtcNow < waitMax) { await Task.Delay(5); PoolServiceDescriptor <TService> pdesc = GetFromCreatedItem(scope); if (pdesc != null) { state.SetResult(pdesc); return; } } } //tried to get but timed out, if we can exceed limit, create new one and return PoolServiceDescriptor <TService> result = Options.ExceedLimitWhenWaitTimeout ? (await CreateNew(scope, true)) : null; state.SetResult(result); }
/// <summary> /// Gets service descriptor for re-use from already created services list /// </summary> private PoolServiceDescriptor <TService> GetFromCreatedItem(IContainerScope scope) { lock (Descriptors) { if (Type == ImplementationType.Scoped) { PoolServiceDescriptor <TService> scoped = Descriptors.FirstOrDefault(x => x.Scope == scope); if (scoped != null) { if (Options.IdleTimeout > TimeSpan.Zero) { scoped.IdleTimeout = DateTime.UtcNow + Options.IdleTimeout; } else { scoped.IdleTimeout = null; } } return(scoped); } PoolServiceDescriptor <TService> transient = Descriptors.FirstOrDefault(x => !x.Locked || x.LockExpiration < DateTime.UtcNow); if (transient == null) { return(null); } transient.Scope = scope; transient.Locked = true; transient.LockExpiration = DateTime.UtcNow.Add(Options.MaximumLockDuration); if (Options.IdleTimeout > TimeSpan.Zero) { transient.IdleTimeout = DateTime.UtcNow + Options.IdleTimeout; } else { transient.IdleTimeout = null; } return(transient); } }
/// <summary> /// Gets service descriptor for re-use from already created services list /// </summary> private PoolServiceDescriptor <TService> GetFromCreatedItem(IContainerScope scope) { lock (_descriptors) { if (Type == ImplementationType.Scoped) { PoolServiceDescriptor <TService> scoped = _descriptors.FirstOrDefault(x => x.Scope == scope); return(scoped); } PoolServiceDescriptor <TService> transient = _descriptors.FirstOrDefault(x => !x.Locked || x.LockExpiration < DateTime.UtcNow); if (transient == null) { return(null); } transient.Scope = scope; transient.Locked = true; transient.LockExpiration = DateTime.UtcNow.Add(Options.MaximumLockDuration); return(transient); } }
/// <summary> /// Creates new instance and adds to pool /// </summary> protected virtual PoolServiceDescriptor <TService> CreateNew(IContainerScope scope, bool locked) { PoolServiceDescriptor <TService> descriptor = new PoolServiceDescriptor <TService>(); descriptor.Locked = locked; descriptor.Scope = scope; descriptor.LockExpiration = DateTime.UtcNow.Add(Options.MaximumLockDuration); if (Options.IdleTimeout > TimeSpan.Zero) { descriptor.IdleTimeout = DateTime.UtcNow + Options.IdleTimeout; } else { descriptor.IdleTimeout = null; } object instance = Descriptor.CreateInstance((TwinoServiceProvider)Container.GetProvider(), scope); if (Type == ImplementationType.Scoped && scope != null) { //we couldn't find any created instance. create new. scope.PutItem(typeof(TService), instance); descriptor.Instance = (TService)instance; } else { descriptor.Instance = (TService)instance; } if (_func != null) { _func(descriptor.Instance); } lock (Descriptors) Descriptors.Add(descriptor); return(descriptor); }
/// <summary> /// Get an item from pool and locks it to prevent multiple usage at same time. /// The item should be released with Release method. /// </summary> public async Task <PoolServiceDescriptor> GetAndLock(IContainerScope scope = null) { PoolServiceDescriptor <TService> descriptor = GetFromCreatedItem(scope); if (descriptor != null) { return(descriptor); } //if there is no available instance and we have space in pool, create new int count; lock (_descriptors) count = _descriptors.Count; if (count < Options.PoolMaxSize) { return(await CreateNew(scope, true)); } //if there is no available instance and there is no space to create new TaskCompletionSource <PoolServiceDescriptor <TService> > completionSource = new TaskCompletionSource <PoolServiceDescriptor <TService> >(TaskCreationOptions.RunContinuationsAsynchronously); ThreadPool.UnsafeQueueUserWorkItem(async state => { try { await WaitForAvailable(scope, state); } catch (Exception e) { completionSource.SetException(e); } }, completionSource, false); return(await completionSource.Task); }
/// <summary> /// Releases pool item for re-using /// </summary> public void Release(PoolServiceDescriptor descriptor) { descriptor.Locked = false; }