forked from synhershko/NAppUpdate
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Breaking change: reworked how errors and async operations are handled
The UpdateManager methods will now throw for all errors, instead of returning ambigous return values. You should wrap all calls to them in try/catch blocks and handle those exceptions. The Async versions of the UpdateManager methods have been renamed, and now use the BeginFoo/EndFoo scheme, as per the CLR APM. Calling them will give you an object implementing IAsyncResult with which you can poll or block. You can still pass a callback method to be called when done, only now it will have much more info on the process, and can throw if an exception is present.
- Loading branch information
1 parent
a2c26cb
commit 5e8317a
Showing
7 changed files
with
284 additions
and
220 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/NAppUpdate.Framework/Common/UpdateProcessAsyncResult.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
using System; | ||
using System.Threading; | ||
|
||
namespace NAppUpdate.Framework.Common | ||
{ | ||
public class UpdateProcessAsyncResult : IAsyncResult | ||
{ | ||
private readonly AsyncCallback _asyncCallback; | ||
private readonly Object _asyncState; | ||
|
||
private const Int32 StatePending = 0; | ||
private const Int32 StateCompletedSynchronously = 1; | ||
private const Int32 StateCompletedAsynchronously = 2; | ||
private Int32 _completedState = StatePending; | ||
|
||
private ManualResetEvent _asyncWaitHandle; | ||
private Exception _exception; | ||
|
||
public UpdateProcessAsyncResult(AsyncCallback asyncCallback, Object state) | ||
{ | ||
_asyncCallback = asyncCallback; | ||
_asyncState = state; | ||
} | ||
|
||
public void SetAsCompleted(Exception exception, Boolean completedSynchronously) | ||
{ | ||
// Passing null for exception means no error occurred. | ||
// This is the common case | ||
_exception = exception; | ||
|
||
// The m_CompletedState field MUST be set prior calling the callback | ||
Int32 prevState = Interlocked.Exchange(ref _completedState, | ||
completedSynchronously ? StateCompletedSynchronously : StateCompletedAsynchronously); | ||
if (prevState != StatePending) | ||
throw new InvalidOperationException("You can set a result only once"); | ||
|
||
// If the event exists, set it | ||
if (_asyncWaitHandle != null) _asyncWaitHandle.Set(); | ||
|
||
// If a callback method was set, call it | ||
if (_asyncCallback != null) _asyncCallback(this); | ||
} | ||
|
||
public void EndInvoke() | ||
{ | ||
// This method assumes that only 1 thread calls EndInvoke | ||
// for this object | ||
if (!IsCompleted) | ||
{ | ||
// If the operation isn't done, wait for it | ||
AsyncWaitHandle.WaitOne(); | ||
AsyncWaitHandle.Close(); | ||
_asyncWaitHandle = null; // Allow early GC | ||
} | ||
|
||
// Operation is done: if an exception occured, throw it | ||
if (_exception != null) throw _exception; | ||
} | ||
|
||
public bool IsCompleted | ||
{ | ||
get | ||
{ | ||
return Thread.VolatileRead(ref _completedState) != StatePending; | ||
} | ||
} | ||
|
||
public WaitHandle AsyncWaitHandle | ||
{ | ||
get | ||
{ | ||
if (_asyncWaitHandle == null) | ||
{ | ||
bool done = IsCompleted; | ||
var mre = new ManualResetEvent(done); | ||
if (Interlocked.CompareExchange(ref _asyncWaitHandle, mre, null) != null) | ||
{ | ||
// Another thread created this object's event; dispose | ||
// the event we just created | ||
mre.Close(); | ||
} | ||
else | ||
{ | ||
if (!done && IsCompleted) | ||
{ | ||
// If the operation wasn't done when we created | ||
// the event but now it is done, set the event | ||
_asyncWaitHandle.Set(); | ||
} | ||
} | ||
} | ||
return _asyncWaitHandle; | ||
} | ||
} | ||
|
||
public Object AsyncState { get { return _asyncState; } } | ||
|
||
public Boolean CompletedSynchronously | ||
{ | ||
get | ||
{ | ||
return Thread.VolatileRead(ref _completedState) == StateCompletedSynchronously; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.