0

I'm writing an Windows phone application with C++/CX. The function tries to copy input array to output array asynchronously:

IAsyncAction CopyAsync(const Platform::Array<byte, 1>^ input, Platform::WriteOnlyArray<byte, 1>^ output) 
{
    byte *inputData = input->Data;
    byte *outputData = output->Data;
    int byteCount = input->Length;
    // if I put it here, there is no error
    //memcpy_s(outputData, byteCount, inputData, byteCount);
    return concurrency::create_async([&]() -> void {
        memcpy_s(outputData, byteCount, inputData, byteCount); // access violation exception
        return;
    });
}

This function compiles but cannot run correctly and produces an "Access violation exception". How can I modify values in the output array?

quacker
  • 336
  • 4
  • 15
  • 1
    The value of `output->Data` changed between the time you captured it at the top of the function and used it inside the lambda. – Raymond Chen Aug 24 '13 at 15:53

1 Answers1

1

This is Undefined Behaviour: by the time you use your 3 captured (by reference) variables inputData/outputData/byteCount in the lambda, you already returned from CopyAsync and the stack has been trashed.

It's really the same issue as if you returned a reference to a local variable from a function (which we know is evil), except that here the references are hidden inside the lambda so it's a bit harder to see at first glance.


If you are sure that input and output won't change and will still be reachable between the moment you call CopyAsync and the moment you run the asynchronous action, you can capture your variables by value instead of by reference:

return concurrency::create_async([=]() -> void {
//                                ^ here
    memcpy_s(outputData, byteCount, inputData, byteCount);
    return;
});

Since they're only pointers (and an int), you won't be copying the pointed-to data, only the pointers themselves.


Or you could just capture input and output by value: since they're garbage-collected pointers this will at least make sure the objects are still reachable by the time you run the lambda:

return concurrency::create_async([=]() -> void {
    memcpy_s(output->Data, input->Length, input->Data, input->Length);
    return;
});

I for one prefer this second solution, it provides more guarantees (namely, object reachability) than the first one.

syam
  • 14,701
  • 3
  • 41
  • 65
  • I think your reason was correct but both of your suggestions did not solve my problem, I still got an Access violation exception. I think the problem was that the function only captured the pointer to data, not the data it self even if they were captured by values. How can I capture the whole array into lambda function? – quacker Aug 24 '13 at 17:32
  • 1
    I believe the second one *should* work if you don't manually delete or otherwise modify your `input/output` objects after calling `CopyAsync` (although I'm not quite familiar with MS' `^` weird GC pointer). Are you somehow trying to reuse those objects after `CopyAsync`? As to capturing the array by value, it would probably work but then you'd make one copy synchronously (capture) and then another one asynchronously (lambda), which doesn't make much sense: you might as well do the copy synchronously once and for all. – syam Aug 24 '13 at 20:47