Implement async support using TPL.
zDougie opened this issue · 10 comments
I looked for a way to email you some stuff, but ...
You don't [yet] support .net 4.5 async functions. I have added it with very little effort, thought maybe it would help.
Added to your IVssAsyncResult interface:
/// <summary>Current status of the work</summary>
uint QueryStatus();
/// <summary>Wait a period of time. Result is true only if completed successfully.</summary>
bool Wait(TimeSpan TimeOut);
Then added an extension:
/// Provides extensions (note - only works in NET35 and higher
public static partial class Extensions
{
public static TimeSpan
TimeOut_Default = new TimeSpan(0, 5, 0);
/// Provides an awaitable process while running async operations
/// Since this is an extension, this is the object being extended
/// Maximum time to wait (default uses the static field "TimeOut_Default")
/// If operation succeded before the timeout occured, true. Otherwise false.
async static public System.Threading.Tasks.Task WaitAsync(this IVssAsyncResult Async, TimeSpan TimeOut)
{
if (Async==null || Async.IsCompleted)
return true;
System.Diagnostics.StackTrace // this part isn't necessary ... helps me follow the true stack
preException = new System.Diagnostics.StackTrace(1, true);
if (TimeOut.Milliseconds <= 0)
TimeOut = TimeOut_Default;
try
{
System.Threading.Tasks.Task
tsk = new System.Threading.Tasks.Task(() => { return Async.Wait(TimeOut); });
if (tsk.Status == System.Threading.Tasks.TaskStatus.Created)
tsk.Start();
await tsk;
if (tsk.Exception != null)
throw tsk.Exception;
return tsk.Result;
}
catch (Exception ex)
{ // this part isn't necessary ... helps me follow the true stack
Exception
iEx = new Exception("Fail waiting on Asyncronous operation");
zDougie.ExceptionExtensions.SetStack(ex, preException.ToString());
zDougie.ExceptionExtensions.InnerException(ex, iEx);
zDougie.ExceptionExtensions.Rethrow(ex);
return false; // will never happen - here just to keep compiler happy
}
}
}
Then I added a few lines to your C++:
namespace Alphaleonis {
namespace Win32 {
namespace Vss
{
VssAsyncResult::VssAsyncResult(::IVssAsync *vssAsync, AsyncCallback^ userCallback, Object^ asyncState)
: m_isComplete(0), m_asyncCallback(userCallback), m_asyncState(asyncState), m_asyncWaitHandle(nullptr), m_vssAsync(vssAsync), m_exception(nullptr)
{
if (!ThreadPool::QueueUserWorkItem(gcnew WaitCallback(this, &VssAsyncResult::WaitForAsyncCompletion)))
{
throw gcnew Exception(L"ThreadPool::QueueUserWorkItem failed.");
}
}
void VssAsyncResult::WaitForAsyncCompletion(Object^ state)
{
HRESULT hrResult;
#if ZDOUGIE
if (IsCompleted || m_vssAsync == 0)
return;
#endif
hrResult = m_vssAsync->Wait();
int prevState = Interlocked::Exchange(m_isComplete, -1);
if (prevState != 0)
throw gcnew InvalidOperationException("WaitForAsyncCompletion can only be called once.");
if (SUCCEEDED(hrResult))
{
#if ZDOUGIE
bool
t1 = m_vssAsync != 0,
t2 = m_vssAsync == nullptr,
t3 = m_vssAsync == NULL;
if (m_vssAsync != 0)
{
HRESULT hr = m_vssAsync->QueryStatus(&hrResult, NULL);
if (FAILED(hr))
hrResult = hr;
}
#else
HRESULT hr = m_vssAsync->QueryStatus(&hrResult, NULL);
if (FAILED(hr))
hrResult = hr;
#endif
}
if (FAILED(hrResult))
m_exception = GetExceptionForHr(hrResult);
else if (hrResult == VSS_S_ASYNC_CANCELLED)
m_exception = gcnew OperationCanceledException();
if (m_asyncWaitHandle != nullptr)
m_asyncWaitHandle->Set();
if (m_asyncCallback != nullptr)
m_asyncCallback(this);
}
void VssAsyncResult::EndInvoke()
{
// This method assumes that only 1 thread calls EndInvoke
// for this object
if (!IsCompleted)
{
AsyncWaitHandle->WaitOne();
AsyncWaitHandle->Close();
m_asyncWaitHandle = nullptr;
}
if (m_exception != nullptr)
throw m_exception;
}
Object^ VssAsyncResult::AsyncState::get()
{
return m_asyncState;
}
bool VssAsyncResult::CompletedSynchronously::get()
{
return false;
}
WaitHandle^ VssAsyncResult::AsyncWaitHandle::get()
{
if (m_asyncWaitHandle == nullptr)
{
bool done = IsCompleted;
ManualResetEvent^ ev = gcnew ManualResetEvent(done);
if (Interlocked::CompareExchange<ManualResetEvent^>(m_asyncWaitHandle, ev, nullptr) != nullptr)
{
ev->Close();
}
else
{
if (!done && IsCompleted)
{
m_asyncWaitHandle->Set();
}
}
}
return m_asyncWaitHandle;
}
bool VssAsyncResult::IsCompleted::get()
{
return Thread::VolatileRead(m_isComplete) != 0;
}
VssAsyncResult^ VssAsyncResult::Create(::IVssAsync *vssAsync, AsyncCallback^ userCallback, Object^ asyncState)
{
try
{
return gcnew VssAsyncResult(vssAsync, userCallback, asyncState);
}
catch (...)
{
vssAsync->Release();
throw;
}
}
VssAsyncResult::~VssAsyncResult()
{
this->!VssAsyncResult();
}
VssAsyncResult::!VssAsyncResult()
{
if (m_vssAsync != 0)
{
m_vssAsync->Release();
m_vssAsync = 0;
}
}
void VssAsyncResult::Cancel()
{
#if ZDOUGIE
if (m_vssAsync != 0)
{
CheckCom(m_vssAsync->Cancel());
}
#endif // ZDOUGIE
}
#if ZDOUGIE && NET45
UINT VssAsyncResult::QueryStatus()
{
if (m_vssAsync != 0)
{
HRESULT
hr = 0;
Int32
resv = 0;
CheckCom(m_vssAsync->QueryStatus(&hr, &resv));
return hr;
}
return (zDougie::NativeMethods::NtStatus)S_OK;
}
bool VssAsyncResult::Wait(TimeSpan TimeOut)
{
if (m_vssAsync != 0)
{
UInt32
ms = (UInt32)TimeOut.TotalMilliseconds;
if (ms <= 0)
ms = 1000 * 5 * 60;
UINT
result = m_vssAsync->Wait(ms);
if ((UINT)result == VSS_E_UNEXPECTED || (UINT)result == E_ACCESSDENIED)
zDougie::NativeMethods::ThrowOnFailure(result, "Waiting for Async Operation");
return (UINT)result == S_OK;
}
return true;
}
#endif // ZDOUGIE && NET45
}
}}
Thanks,
Thanks for the effort. Any chance you could send this as a pull-request instead?
I’m not sure what that means. I have never worked in a team before. I’ll look for the term and see if I can figure it out. J
From: Peter Palotas [mailto:notifications@github.com]
Sent: Monday, February 09, 2015 00:19
To: alphaleonis/AlphaVSS
Cc: zDougie
Subject: Re: [AlphaVSS] Net 4.5 and Async (#1)
Thanks for the effort. Any chance you could send this as a pull-request instead?
—
Reply to this email directly or view it on GitHub #1 (comment) .Image removed by sender.
Basically it means you create a fork of the project, do your changes there and commit them and a button will appear allowing you to create a pull-request. If you don't figure it out, I'll have a look at the source you pasted above and try to see if I can incorporate it.
I haven’t figured it out yet. I “cloned” and then edited the files. I’ve hit “sync” on Github for windows … but Github website still says there are no changes.
Is it just me or is github hard to learn?
Thanks,
From: Doug Boteler [mailto:doug@dboteler.com]
Sent: Monday, February 09, 2015 08:27
To: 'alphaleonis/AlphaVSS'; 'alphaleonis/AlphaVSS'
Subject: RE: [AlphaVSS] Net 4.5 and Async (#1)
I’m not sure what that means. I have never worked in a team before. I’ll look for the term and see if I can figure it out. J
From: Peter Palotas [mailto:notifications@github.com]
Sent: Monday, February 09, 2015 00:19
To: alphaleonis/AlphaVSS
Cc: zDougie
Subject: Re: [AlphaVSS] Net 4.5 and Async (#1)
Thanks for the effort. Any chance you could send this as a pull-request instead?
—
Reply to this email directly or view it on GitHub #1 (comment) .Image removed by sender.
Where is “commit”? My github for windows has like absolutely no buttons or options that I can detect …
From: Peter Palotas [mailto:notifications@github.com]
Sent: Monday, February 09, 2015 10:51
To: alphaleonis/AlphaVSS
Cc: zDougie
Subject: Re: [AlphaVSS] Net 4.5 and Async (#1)
Basically it means you create a fork of the project, do your changes there and commit them and a button will appear allowing you to create a pull-request. If you don't figure it out, I'll have a look at the source you pasted above and try to see if I can incorporate it.
—
Reply to this email directly or view it on GitHub #1 (comment) .Image removed by sender.
I deleted everything and started over. Now I get the option to commit but it says I don't have permissions to commit. I must be stupid ...
If it helps at all, I'm working on https://github.com/zDougie/AlphaVSS
Ok. I did things in the wrong order. I think it worked this time.
A question regarding this PR that unfortunately I completely forgot about. What's wrong with simply using something like Task.Factory.FromAsync(bc.BeginBackupComplete, bc.EndBackupComplete, null);
?
Fixed in 0519b2c