Applied planned changes to the Waitable class; Waitable class now has a Wait() method that can be used instead of the WaitHandle; publicized Waitable's members to deriving classes so they don't have to duplicate them when doing a custom implementation; virtualized all Methods to allow a custom Waitable implementation to completely provide its own methods of Waiting, determining the Ended state and registering to the AsyncEnded event
git-svn-id: file:///srv/devel/repo-conversion/nusu@77 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
26365177dd
commit
df860b8e57
|
@ -33,6 +33,18 @@ Design using interfaces:
|
||||||
/// <summary>Whether the background process has finished</summary>
|
/// <summary>Whether the background process has finished</summary>
|
||||||
bool Finished { get; }
|
bool Finished { get; }
|
||||||
|
|
||||||
|
// ?
|
||||||
|
/// <summary>Wait handle that can be used to wait for multiple waitables</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Only use the WaitHandle to wait if you're running in a different thread than
|
||||||
|
/// the request, or you may deadlock. Libraries based on single-threaded
|
||||||
|
/// multitasking may employ concepts such as window message processing to achieve
|
||||||
|
/// parallelism which could be stalled by improper waiting using the WaitHandle
|
||||||
|
/// whereas the Wait() method typically has a suitable implementation compatible
|
||||||
|
/// with the request's requirements.
|
||||||
|
/// </remarks>
|
||||||
|
WaitHandle WaitHandle { get; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IThreadedWaitable : IWaitable {
|
interface IThreadedWaitable : IWaitable {
|
||||||
|
|
|
@ -84,8 +84,11 @@ namespace Nuclex.Support.Tracking {
|
||||||
public virtual void Join() {
|
public virtual void Join() {
|
||||||
|
|
||||||
// If the progression itself hasn't ended yet, block the caller until it has.
|
// If the progression itself hasn't ended yet, block the caller until it has.
|
||||||
|
// We could just use WaitHandle.WaitOne() here, but since the WaitHandle is created
|
||||||
|
// on-the-fly only when it is requested, we can avoid the WaitHandle creation in
|
||||||
|
// case the request is already finished!
|
||||||
if(!Ended)
|
if(!Ended)
|
||||||
WaitHandle.WaitOne();
|
Wait();
|
||||||
|
|
||||||
// Allow the implementor to throw an exception in case an error has occured
|
// Allow the implementor to throw an exception in case an error has occured
|
||||||
ReraiseExceptions();
|
ReraiseExceptions();
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// the registered callback will be invoked synchronously right when the
|
/// the registered callback will be invoked synchronously right when the
|
||||||
/// registration takes place.
|
/// registration takes place.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public event EventHandler AsyncEnded {
|
public virtual event EventHandler AsyncEnded {
|
||||||
add {
|
add {
|
||||||
|
|
||||||
// If the background process has not yet ended, add the delegate to the
|
// If the background process has not yet ended, add the delegate to the
|
||||||
|
@ -84,12 +84,12 @@ namespace Nuclex.Support.Tracking {
|
||||||
if(!this.ended) {
|
if(!this.ended) {
|
||||||
|
|
||||||
// The subscriber list is also created lazily ;-)
|
// The subscriber list is also created lazily ;-)
|
||||||
if(ReferenceEquals(this.subscribers, null)) {
|
if(ReferenceEquals(this.endedEventSubscribers, null)) {
|
||||||
this.subscribers = new List<EventHandler>();
|
this.endedEventSubscribers = new List<EventHandler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe the event handler to the list
|
// Subscribe the event handler to the list
|
||||||
this.subscribers.Add(value);
|
this.endedEventSubscribers.Add(value);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -103,17 +103,17 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
remove {
|
remove {
|
||||||
|
|
||||||
// Unsubscribing a non-subscribed delegate from an event is allowed and should
|
|
||||||
// not throw an exception. Due to the stupid design of the .NET collection
|
|
||||||
// classes (has anyone at Microsoft ever written a single proper collection
|
|
||||||
// in his life?) we have to search the collection twice.
|
|
||||||
lock(this) {
|
lock(this) {
|
||||||
|
|
||||||
// Only try to remove the event handler if the subscriber list was created,
|
// Only try to remove the event handler if the subscriber list was created,
|
||||||
// otherwise, we can be sure that no actual subscribers exist.
|
// otherwise, we can be sure that no actual subscribers exist.
|
||||||
if(!ReferenceEquals(this.subscribers, null)) {
|
if(!ReferenceEquals(this.endedEventSubscribers, null)) {
|
||||||
if(this.subscribers.Contains(value)) {
|
int eventHandlerIndex = this.endedEventSubscribers.IndexOf(value);
|
||||||
this.subscribers.Remove(value);
|
|
||||||
|
// Unsubscribing a non-subscribed delegate from an event is allowed and should
|
||||||
|
// not throw an exception.
|
||||||
|
if(eventHandlerIndex != -1) {
|
||||||
|
this.endedEventSubscribers.RemoveAt(eventHandlerIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,13 +122,40 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Waits until the background process finishes</summary>
|
||||||
|
public virtual void Wait() {
|
||||||
|
WaitHandle.WaitOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Waits until the background process finishes or a timeout occurs</summary>
|
||||||
|
/// <param name="timeout">
|
||||||
|
/// Time span after which to stop waiting and return immediately
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if the background process completed, false if the timeout was reached
|
||||||
|
/// </returns>
|
||||||
|
public virtual bool Wait(TimeSpan timeout) {
|
||||||
|
return WaitHandle.WaitOne(timeout, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
public virtual bool Wait(int timeoutMilliseconds) {
|
||||||
|
return WaitHandle.WaitOne(timeoutMilliseconds, false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Whether the Waitable has ended already</summary>
|
/// <summary>Whether the Waitable has ended already</summary>
|
||||||
public bool Ended {
|
public virtual bool Ended {
|
||||||
get { return this.ended; }
|
get { return this.ended; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>WaitHandle that can be used to wait for the Waitable to end</summary>
|
/// <summary>WaitHandle that can be used to wait for the Waitable to end</summary>
|
||||||
public WaitHandle WaitHandle {
|
public virtual WaitHandle WaitHandle {
|
||||||
get {
|
get {
|
||||||
|
|
||||||
// The WaitHandle will only be created when someone asks for it!
|
// The WaitHandle will only be created when someone asks for it!
|
||||||
|
@ -189,11 +216,15 @@ namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire the ended events to all event subscribers. We can freely use the list
|
if(!ReferenceEquals(this.endedEventSubscribers, null)) {
|
||||||
// without synchronization at this point on since once this.ended is set to true,
|
|
||||||
// the subscribers list will not be accessed any longer
|
// Fire the ended events to all event subscribers. We can freely use the list
|
||||||
for(int index = 0; index < this.subscribers.Count; ++index) {
|
// without synchronization at this point on since once this.ended is set to true,
|
||||||
this.subscribers[index](this, EventArgs.Empty);
|
// the subscribers list will not be accessed any longer
|
||||||
|
for(int index = 0; index < this.endedEventSubscribers.Count; ++index) {
|
||||||
|
this.endedEventSubscribers[index](this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -202,15 +233,15 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Does not need to be volatile since it's only accessed inside
|
/// Does not need to be volatile since it's only accessed inside
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private volatile List<EventHandler> subscribers;
|
protected volatile List<EventHandler> endedEventSubscribers;
|
||||||
/// <summary>Whether the operation has completed yet</summary>
|
/// <summary>Whether the operation has completed yet</summary>
|
||||||
private volatile bool ended;
|
protected volatile bool ended;
|
||||||
/// <summary>Event that will be set when the progression is completed</summary>
|
/// <summary>Event that will be set when the progression is completed</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This event is will only be created when it is specifically asked for using
|
/// This event is will only be created when it is specifically asked for using
|
||||||
/// the WaitHandle property.
|
/// the WaitHandle property.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private volatile ManualResetEvent doneEvent;
|
protected volatile ManualResetEvent doneEvent;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user