alphaleonis/AlphaVSS

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