Fixed documentation of Waitable and Request classes; wrote down some ideas to improve the request framework
git-svn-id: file:///srv/devel/repo-conversion/nusu@75 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
73ef5de576
commit
e0cf91a0a4
68
Documents/Request Framework.txt
Normal file
68
Documents/Request Framework.txt
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
The request framework should not require that .NET multithreading is used for
|
||||||
|
asynchronous requests.
|
||||||
|
|
||||||
|
Otherwise, it would prvent overlapped operations, 3rd party APIs (eg. used
|
||||||
|
via P/Invoke) from being able to use the request framework and possibly even
|
||||||
|
spawn duplicate implementations.
|
||||||
|
|
||||||
|
|
||||||
|
Design using interfaces:
|
||||||
|
|
||||||
|
interface IWaitable {
|
||||||
|
|
||||||
|
/// <summary>Fired when the background process has finished</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the process is already finished when a client registers to this event,
|
||||||
|
/// the registered callback will be invoked synchronously right when the
|
||||||
|
/// registration takes place.
|
||||||
|
/// </remarks>
|
||||||
|
event EventHandler Finished;
|
||||||
|
|
||||||
|
/// <summary>Waits until the background process finishes</summary>
|
||||||
|
void Wait();
|
||||||
|
|
||||||
|
/// <summary>Waits until the background process finishes or a timeout occurs</summary>
|
||||||
|
/// <param name="timeoutMilliseconds">
|
||||||
|
/// Number of milliseconds after which to stop waiting and return immediately
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if the background process completed, false if the timeout was reached
|
||||||
|
/// </returns>
|
||||||
|
bool Wait(int timeoutMilliseconds);
|
||||||
|
|
||||||
|
/// <summary>Whether the background process has finished</summary>
|
||||||
|
bool Finished { get; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IThreadedWaitable : IWaitable {
|
||||||
|
|
||||||
|
WaitHandle WaitHandle { get; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRequest : IWaitable {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the background process to complete and re-throws the exception to
|
||||||
|
/// the caller when an error has occured
|
||||||
|
/// </summary>
|
||||||
|
void Join();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRequest<ResultType> : IRequest {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the background process to complete and re-throws the exception to
|
||||||
|
/// the caller when an error has occured
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The result of the background processing</returns>
|
||||||
|
new ResultType Join();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IThreadedRequest : IRequest, IThreadedWaitable { }
|
||||||
|
|
||||||
|
interface IThreadedRequest<ResultType> :
|
||||||
|
IRequest<ResultType>, IThreadedRequest, IThreadedWaitable { }
|
|
@ -217,6 +217,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Documents\Nuclex.Support.txt" />
|
<Content Include="Documents\Nuclex.Support.txt" />
|
||||||
|
<Content Include="Documents\Request Framework.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\v3.0\Microsoft.Xna.GameStudio.Common.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\v3.0\Microsoft.Xna.GameStudio.Common.targets" />
|
||||||
|
|
|
@ -70,7 +70,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// A failed request that reports the provided exception as cause for its failure
|
/// A failed request that reports the provided exception as cause for its failure
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static Request CreateFailedDummyRequest(Exception exception) {
|
public static Request CreateFailedDummy(Exception exception) {
|
||||||
return new EndedDummyRequest(exception);
|
return new EndedDummyRequest(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// A failed request that reports the provided exception as cause for its failure
|
/// A failed request that reports the provided exception as cause for its failure
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public static new Request CreateFailedDummyRequest(Exception exception) {
|
public static new Request CreateFailedDummy(Exception exception) {
|
||||||
return new EndedDummyRequest(exception);
|
return new EndedDummyRequest(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,7 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Nuclex.Support.Tracking {
|
namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>Base class for background processes the user can wait on</summary>
|
||||||
/// Base class for actions on which that give an indication of their progress
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// By encapsulating long-running operations which will ideally be running in
|
/// By encapsulating long-running operations which will ideally be running in
|
||||||
|
@ -40,27 +38,27 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// task has completed. This class deliberately does not provide an Execute()
|
/// task has completed. This class deliberately does not provide an Execute()
|
||||||
/// method or anything similar to clearly seperate the initiation of an operation
|
/// method or anything similar to clearly seperate the initiation of an operation
|
||||||
/// from just monitoring it. By omitting an Execute() method, it also becomes
|
/// from just monitoring it. By omitting an Execute() method, it also becomes
|
||||||
/// possible to construct a progression just-in-time when it is explicitely being
|
/// possible to construct a Waitable just-in-time when it is explicitely being
|
||||||
/// asked for.
|
/// asked for.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public abstract class Waitable {
|
public abstract class Waitable {
|
||||||
|
|
||||||
#region class EndedDummyProgression
|
#region class EndedDummyWaitable
|
||||||
|
|
||||||
/// <summary>Dummy progression which always is in the 'ended' state</summary>
|
/// <summary>Dummy waitable which always is in the 'ended' state</summary>
|
||||||
private class EndedDummyWaitable : Waitable {
|
private class EndedDummyWaitable : Waitable {
|
||||||
|
|
||||||
/// <summary>Initializes a new ended dummy progression</summary>
|
/// <summary>Initializes a new ended dummy waitable</summary>
|
||||||
public EndedDummyWaitable() {
|
public EndedDummyWaitable() {
|
||||||
OnAsyncEnded();
|
OnAsyncEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion // class EndedDummyProgression
|
#endregion // class EndedDummyWaitable
|
||||||
|
|
||||||
/// <summary>A dummy progression that's always in the 'ended' state</summary>
|
/// <summary>A dummy waitable that's always in the 'ended' state</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Useful if an operation is already complete when it's being asked for or
|
/// Useful if an operation is already complete when it's being asked for or
|
||||||
/// when a progression that's lazily created is accessed after the original
|
/// when a progression that's lazily created is accessed after the original
|
||||||
|
@ -68,15 +66,15 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static readonly Waitable EndedDummy = new EndedDummyWaitable();
|
public static readonly Waitable EndedDummy = new EndedDummyWaitable();
|
||||||
|
|
||||||
/// <summary>Will be triggered when the progression has ended</summary>
|
/// <summary>Will be triggered when the Waitable has ended</summary>
|
||||||
public event EventHandler AsyncEnded;
|
public event EventHandler AsyncEnded;
|
||||||
|
|
||||||
/// <summary>Whether the progression has ended already</summary>
|
/// <summary>Whether the Waitable has ended already</summary>
|
||||||
public bool Ended {
|
public bool Ended {
|
||||||
get { return this.ended; }
|
get { return this.ended; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>WaitHandle that can be used to wait for the progression to end</summary>
|
/// <summary>WaitHandle that can be used to wait for the Waitable to end</summary>
|
||||||
public WaitHandle WaitHandle {
|
public WaitHandle WaitHandle {
|
||||||
get {
|
get {
|
||||||
|
|
||||||
|
@ -87,11 +85,11 @@ namespace Nuclex.Support.Tracking {
|
||||||
// We can *not* optimize this lock away since we absolutely must not create
|
// We can *not* optimize this lock away since we absolutely must not create
|
||||||
// two doneEvents -- someone might call .WaitOne() on the first one when only
|
// two doneEvents -- someone might call .WaitOne() on the first one when only
|
||||||
// the second one is referenced by this.doneEvent and thus gets set in the end.
|
// the second one is referenced by this.doneEvent and thus gets set in the end.
|
||||||
if (this.doneEvent == null) {
|
if(this.doneEvent == null) {
|
||||||
|
|
||||||
lock (this) {
|
lock(this) {
|
||||||
|
|
||||||
if (this.doneEvent == null)
|
if(this.doneEvent == null)
|
||||||
this.doneEvent = new ManualResetEvent(this.ended);
|
this.doneEvent = new ManualResetEvent(this.ended);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -121,13 +119,13 @@ namespace Nuclex.Support.Tracking {
|
||||||
// Make sure the progression is not ended more than once. By guaranteeing that
|
// Make sure the progression is not ended more than once. By guaranteeing that
|
||||||
// a progression can only be ended once, we allow users of this class to
|
// a progression can only be ended once, we allow users of this class to
|
||||||
// skip some safeguards against notifications arriving twice.
|
// skip some safeguards against notifications arriving twice.
|
||||||
lock (this) {
|
lock(this) {
|
||||||
|
|
||||||
// No double lock here, this is an exception that indicates an implementation
|
// No double lock here, this is an exception that indicates an implementation
|
||||||
// error that will not be triggered under normal circumstances. We don't want
|
// error that will not be triggered under normal circumstances. We don't want
|
||||||
// to waste any effort optimizing the speed at which an implementation fault
|
// to waste any effort optimizing the speed at which an implementation fault
|
||||||
// will be noticed.
|
// will be noticed.
|
||||||
if (this.ended)
|
if(this.ended)
|
||||||
throw new InvalidOperationException("The progression has already been ended");
|
throw new InvalidOperationException("The progression has already been ended");
|
||||||
|
|
||||||
this.ended = true;
|
this.ended = true;
|
||||||
|
@ -137,12 +135,12 @@ namespace Nuclex.Support.Tracking {
|
||||||
// Doesn't need a lock. If another thread wins the race and creates the event
|
// Doesn't need a lock. If another thread wins the race and creates the event
|
||||||
// after we just saw it being null, it would be created in an already set
|
// after we just saw it being null, it would be created in an already set
|
||||||
// state due to the ended flag (see above) being set to true beforehand!
|
// state due to the ended flag (see above) being set to true beforehand!
|
||||||
if (this.doneEvent != null)
|
if(this.doneEvent != null)
|
||||||
this.doneEvent.Set();
|
this.doneEvent.Set();
|
||||||
|
|
||||||
// Finally, fire the AsyncEnded event
|
// Finally, fire the AsyncEnded event
|
||||||
EventHandler copy = AsyncEnded;
|
EventHandler copy = AsyncEnded;
|
||||||
if (copy != null)
|
if(copy != null)
|
||||||
copy(this, EventArgs.Empty);
|
copy(this, EventArgs.Empty);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user