Skip to content
This repository has been archived by the owner on Feb 14, 2022. It is now read-only.

Commit

Permalink
Implemented generic async wrapper
Browse files Browse the repository at this point in the history
- completed async logic core
- closes #41
- closes #42
  • Loading branch information
JacopoWolf committed May 13, 2020
1 parent ca6c90c commit cfacef5
Show file tree
Hide file tree
Showing 17 changed files with 279 additions and 149 deletions.
72 changes: 72 additions & 0 deletions StackInjector/Core/AsyncStackWrapperCore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using StackInjector.Wrappers;

namespace StackInjector.Core
{
internal abstract partial class AsyncStackWrapperCore<T> : AsyncStackWrapperCore, IAsyncStackWrapperCore<T>
{

// used to cancel everything
protected internal readonly CancellationTokenSource cancelPendingTasksSource = new CancellationTokenSource();

// exposes the token
public CancellationToken PendingTasksCancellationToken
=> this.cancelPendingTasksSource.Token;

// used to lock access to tasks
protected internal readonly object listAccessLock = new object();

// asyncronously waited for new events if TaskList is empty
protected internal readonly SemaphoreSlim emptyListAwaiter = new SemaphoreSlim(0);

// pending tasks
protected internal LinkedList<Task<T>> tasks = new LinkedList<Task<T>>();


internal AsyncStackWrapperCore ( WrapperCore core, Type toRegister ) : base(core, toRegister)
{
// register an event that in case the list is empty, release the empty event listener.
this.cancelPendingTasksSource.Token.Register(this.ReleaseListAwaiter);
}



#region IDisposable Support

private bool disposedValue = false;

public override void Dispose ()
{
if( !this.disposedValue )
{

// managed resources
this.cancelPendingTasksSource.Cancel();
this.ReleaseListAwaiter(); // in case it's waiting on the empty list

this.cancelPendingTasksSource.Dispose();
this.emptyListAwaiter.Dispose();


// big objects
this.tasks.Clear();
this.tasks = null;

// clean instantiated objects
this.Core.RemoveInstancesDiff();


this.disposedValue = true;
}
}

#endregion

}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackInjector.Wrappers
namespace StackInjector.Core
{

internal partial class AsyncStackWrapper
internal abstract partial class AsyncStackWrapperCore<T>
{

///<inheritdoc/>
public void Submit ( object submitted )
// call the semaphore
protected internal void ReleaseListAwaiter ()
{
var task = this.GetAsyncEntryPoint().Digest(submitted,this.cancelPendingTasksSource.Token);

this.emptyListAwaiter.Release();
}

public void Submit ( Task<T> work )
{
lock( this.listAccessLock )
this.tasks.AddLast(task);

this.tasks.AddLast( work );

// if the list was empty just an item ago, signal it's not anymore.
// this limit avoids useless cross thread calls that would slow everything down.
if( this.tasks.Count == 1 )
this.ReleaseListAwaiter();
}

public bool AnyTaskLeft ()
{
lock( this.listAccessLock )
return this.tasks.Any();
}


/// <inheritdoc/>
public async IAsyncEnumerable<T> Elaborated<T> ()
public async IAsyncEnumerable<T> Elaborated ()
{
while( !this.cancelPendingTasksSource.IsCancellationRequested )
while( ! this.cancelPendingTasksSource.IsCancellationRequested )
{
// avoid deadlocks
if( this.tasks.Any() )
Expand All @@ -39,7 +43,7 @@ public async IAsyncEnumerable<T> Elaborated<T> ()
lock( this.listAccessLock )
this.tasks.Remove(completed);

yield return (T)completed.Result;
yield return completed.Result;
continue;
}
else
Expand All @@ -51,21 +55,5 @@ public async IAsyncEnumerable<T> Elaborated<T> ()
}
}

/// <inheritdoc/>
public bool AnyTaskLeft ()
{
lock( this.listAccessLock )
return this.tasks.Any();
}


/// <summary>
/// gets the entry point for this stack
/// </summary>
/// <returns></returns>
internal IAsyncStackEntryPoint GetAsyncEntryPoint ()
=>
this.Core.GetEntryPoint<IAsyncStackEntryPoint>();

}
}
}
5 changes: 5 additions & 0 deletions StackInjector/Core/Cloning/ClonedCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using StackInjector.Wrappers;
using StackInjector.Wrappers.Generic;

namespace StackInjector.Core.Cloning
{
Expand All @@ -25,6 +26,10 @@ public IAsyncStackWrapper ToAsyncWrapper<T> () where T : IAsyncStackEntryPoint
return wrapper;
}

//todo implement
public IAsyncStackWrapper<TEntry, TIn, TOut> ToGenericAsync<TEntry, TIn, TOut> ( AsyncStackDigest<TEntry, TIn, TOut> digest )
=> throw new NotImplementedException();


public IStackWrapper ToWrapper<T> () where T : IStackEntryPoint
{
Expand Down
12 changes: 12 additions & 0 deletions StackInjector/Core/Cloning/IClonedCore.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using StackInjector.Wrappers;
using StackInjector.Wrappers.Generic;

namespace StackInjector.Core.Cloning
{
Expand All @@ -23,6 +24,17 @@ public interface IClonedCore
/// <returns>the new wrapper</returns>
IAsyncStackWrapper ToAsyncWrapper<T> () where T : IAsyncStackEntryPoint;


/// <summary>
/// convert this to an <see cref="IAsyncStackWrapper{TEntry, TIn, TOut}"/>
/// </summary>
/// <typeparam name="TEntry">entry instantiation poin of the new wrapper</typeparam>
/// <typeparam name="TIn">type of input elements</typeparam>
/// <typeparam name="TOut">type of output elements</typeparam>
/// <param name="digest">action to perform on elements</param>
/// <returns>the new wrapper</returns>
IAsyncStackWrapper<TEntry, TIn, TOut> ToGenericAsync<TEntry, TIn, TOut> ( AsyncStackDigest<TEntry, TIn,TOut> digest );

}

}
22 changes: 19 additions & 3 deletions StackInjector/Core/IAsyncStackWrapperCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,37 @@
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace StackInjector.Core
{
/// <summary>
/// base interface for all asyncronous stackwrappers
/// base interface for all asyncronous stackwrappers.
/// </summary>
public interface IAsyncStackWrapperCore : IStackWrapperCore
/// <typeparam name="T">the type tasks will return</typeparam>
public interface IAsyncStackWrapperCore<T> : IStackWrapperCore
{

/// <summary>
/// Used to signal cancellation of every pending task
/// </summary>
public CancellationToken PendingTasksCancellationToken { get; }
CancellationToken PendingTasksCancellationToken { get; }


/// <summary>
/// submit new work to this wrapper
/// </summary>
/// <param name="work"></param>
void Submit ( Task<T> work );

/// <summary>
/// The loop you ca use to <c>await foreach</c> tasks in elaboration, converted to the specified type.
/// When the pending tasks list is empty, unless <see cref="IDisposable.Dispose"/> is explocitly called
/// this will wait indefinitively.
/// </summary>
/// <returns>An asyncronous enumerable of completed tasks</returns>
IAsyncEnumerable<T> Elaborated ();

/// <summary>
/// check if there are tasks left to elaborate
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions StackInjector/Core/StackWrapperCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace StackInjector.Core
{
internal abstract class StackWrapperCore : IStackWrapperCore
internal abstract class AsyncStackWrapperCore : IStackWrapperCore
{
public ref readonly StackWrapperSettings Settings
=> ref this.Core.settings;
Expand All @@ -13,7 +13,7 @@ public ref readonly StackWrapperSettings Settings
private protected readonly WrapperCore Core;


public StackWrapperCore ( WrapperCore core, Type toRegister )
public AsyncStackWrapperCore ( WrapperCore core, Type toRegister )
{
this.Core = core;

Expand Down
16 changes: 7 additions & 9 deletions StackInjector/Core/WrapperCore.logic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ internal partial class WrapperCore

internal void ServeAll ()
{

////// setting for referencing this object from instances inside
////if( this.settings.registerSelf )
//// this.instances.AddInstance(this.GetType(), this);

var toInject = new Queue<object>();

// saves time in later elaboration
this.entryPoint = this.ClassOrFromInterface(this.entryPoint);

// instantiates and enqueues the EntryPoint
toInject.Enqueue
(
Expand All @@ -29,6 +27,9 @@ internal void ServeAll ()
foreach( var service in usedServices )
toInject.Enqueue(service);
}



}


Expand All @@ -42,10 +43,7 @@ internal T GetEntryPoint<T> ()
return
(T)this
.instances
.OfType
(
this.ClassOrFromInterface(this.entryPoint)
)
.OfType( this.entryPoint )
.First();
}
}
Expand Down
41 changes: 38 additions & 3 deletions StackInjector/Injector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
using StackInjector.Exceptions;
using StackInjector.Settings;
using StackInjector.Wrappers;
using StackInjector.Wrappers.Generic;

namespace StackInjector
{
//todo edit documentation to allow clone cloning
/// <summary>
/// <para>Static factory class exposing methods to create new StackWrappers</para>
/// <para>Note that using any of the exposed methods will analyze the whole target assembly,
Expand Down Expand Up @@ -64,8 +64,7 @@ public static IAsyncStackWrapper AsyncFrom<T> ( StackWrapperSettings settings =
// create a new async stack wrapper
var core = new WrapperCore( settings )
{
entryPoint = typeof(T),
instances = new SingleInstanceHolder()
entryPoint = typeof(T)
};

var wrapper = new AsyncStackWrapper(core);
Expand All @@ -76,6 +75,42 @@ public static IAsyncStackWrapper AsyncFrom<T> ( StackWrapperSettings settings =
return wrapper;
}

/// <summary>
/// Create a new generic asyncronous StackWrapper from the <typeparamref name="TEntry"/>
/// entry class with the specified delegate to apply to apply as digest
/// </summary>
/// <typeparam name="TEntry">class from which start injection</typeparam>
/// <typeparam name="TIn">type of elements in input</typeparam>
/// <typeparam name="TOut">type of elements in output</typeparam>
/// <param name="digest">delegate used to call the relative method to perform on submitted items</param>
/// <param name="settings">the settings to use with this object. If null, use default.</param>
/// <returns></returns>
public static IAsyncStackWrapper<TEntry,TIn,TOut> AsyncFrom<TEntry,TIn,TOut>
(
AsyncStackDigest<TEntry, TIn, TOut> digest,
StackWrapperSettings settings = null
)
{
if( settings == null )
settings = StackWrapperSettings.Default;

// create a new generic async wrapper
var core = new WrapperCore( settings )
{
entryPoint = typeof(TEntry)
};

var wrapper = new AsyncStackWrapper<TEntry, TIn,TOut>(core)
{
StackDigest = digest
};

core.ReadAssemblies();
core.ServeAll();

return wrapper;
}


}
}
2 changes: 1 addition & 1 deletion StackInjector/Settings/StackWrapperSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static StackWrapperSettings Default
.VersioningMethod(ServedVersionTargetingMethod.None, @override: false);


//todo maybe add a DefaultInner for nested wrappers
//? maybe add a DefaultInner for nested wrappers

#endregion

Expand Down
Loading

0 comments on commit cfacef5

Please sign in to comment.