Artur Laksberg, Microsoft - United States

Artur Laksberg, Microsoft - United States

“Asynchronous Programming in C++ using Parallel Pattern Library”

Bd1c4acb24d143c7ca8dff849461ebe3?s=128

Multicore World

July 16, 2012
Tweet

Transcript

  1. Asynchronous Programming in C++ Using PPL Artur Laksberg (arturl@microsoft.com) Visual

    C++ Team Microsoft Corporation
  2. Any Questions? 2 template<typename _OutType> class _Continuation_func_transformer<void, _OutType> { public:

    static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) { return details::_MakeUnitToTFunc<_OutType>(_Func); } }; template<typename _InType> class _Continuation_func_transformer<_InType, void> { public: static auto _Perform(std::function<void(_InType)> _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) { return details::_MakeTToUnitFunc<_InType>(_Func); } }; template<> class _Continuation_func_transformer<void, void> { public: static auto _Perform(std::function<void(void)> _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) { return details::_MakeUnitToUnitFunc(_Func); } }; /// <summary> /// The task handle type used to create a 'continuation task'. /// </summary> template <typename _InternalReturnType, typename _ContinuationReturnType, typename _Function, typename _IsTaskBased, typename _TypeSelection> struct _ContinuationTaskHandle : details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type, _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> { typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; _Function _M_function; _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode _InliningMode) : _M_ancestorTaskImpl(_AncestorImpl), _PPLTaskHandle(_ContinuationImpl), _M_function(_Func) { _M_isTaskBasedContinuation = _IsTaskBased::value; _M_continuationContext = _Context; _M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); _M_inliningMode = _InliningMode; } virtual ~_ContinuationTaskHandle() {} void _Perform() const { _Continue(_IsTaskBased(), _TypeSelection()); } // // Overload 0-0: _InternalReturnType -> _TaskType // // This is a straight task continuation which simply invokes its target with the ancestor's completion argument // void _Continue(std::false_type, details::_TypeSelectorNoAsync) const { _M_pTask->_FinalizeAndRunContinuations(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult())); } // // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only uder /ZW) // or // _InternalReturnType -> task<_TaskType> // // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation // Depending on the output type, the right _AsyncInit gets invoked // void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const { typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( _M_pTask, _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult()) ); }
  3. Welcome To the Jungle …The transitions to multicore processors, GPU

    computing, and HaaS cloud computing are not separate trends, but aspects of a single trend – mainstream computers … are being permanently transformed into heterogeneous supercomputer clusters. Henceforth, a single compute-intensive application will need to harness different kinds of cores, in immense numbers, to get its job done. Herb Sutter
  4. Hello, Multicore World! 4 void foo() { printf("Hello, "); }

    void bar() { printf("Multicore World!"); } int main() { foo(); bar(); return 0; }
  5. Multicore World! Hello, 5 #include "ppl.h" void foo() { printf("Hello,

    "); } void bar() { printf("Multicore World!"); } int main() { concurrency::parallel_invoke( [] { foo(); }, [] { bar(); }); return 0; }
  6. Matrix Multiplication (serial) 6 void MatrixMultiply(int rows1, int cols1, float

    **mat1, int rows2, int cols2, float **mat2, float **result) { for (int i=0;i<rows1;i++) { for(int j=0;j<cols2;j++) { float tempResult = 0; for(int k=0;k<rows2;k++) { tempResult += mat1[i][k]*mat2[k][j]; } result[i][j] = tempResult; } } }
  7. Matrix Multiplication (parallel) 7 void MatrixMultiplyPar(int rows1, int cols1, float

    **mat1, int rows2, int cols2, float **mat2, float **result) { concurrency::parallel_for(0, rows1, [=](int i) { for(int j=0;j<cols2;j++) { float tempResult = 0; for(int k=0;k<rows2;k++) { tempResult += mat1[i][k]*mat2[k][j]; } result[i][j] = tempResult; } }); }
  8. Bubble Sort 8 void BubbleSort(int* input, int n) { for(int

    y = 0; y < n; y++) { for ( int k = 0; k < n - 1 - y; k++ ) { if(input[k]>input[k+1]) { int temp = input[k+1]; input[k+1] = input[k]; input[k] = temp; } } } }
  9. Parallel Bubble Sort* 9 void BubbleSort(int* input, int n) {

    concurrency::parallel_for(0, n, [=](int y) { for ( int k = 0; k < n - 1 - y; k++ ) { if(input[k]>input[k+1]) { int temp = input[k+1]; input[k+1] = input[k]; input[k] = temp; } } }); } *Doesn’t work
  10. Task Parallelism in PPL • Fork-join • Ad-hoc 10

  11. Ad Hoc Parallelism int n=40; concurrency::task<int> t([=]() { return fib(n);

    }); // do other work, come back later ... cout << t.get(); 11
  12. Actionless Task task_completion_event<string> tce; std::thread my_thread([tce]() { string greeting =

    "Hello, Multicore World!"; tce.set(greeting); }); task<string> t(tce); // do other work, come back later cout << t.get(); 12
  13. Continuations task<int> t([]() { return fib(40); }); task<void> t2 =

    t.then ([](int n) { cout << n; }); 13 Must match Returns a task
  14. Putting it together: when_any (almost the actual impl.) template<typename Iterator>

    auto when_any(Iterator tasks_begin, Iterator tasks_end) -> typename std::iterator_traits<Iterator>::value_type { typedef std::iterator_traits<Iterator>::value_type::result_type ValueType; task_completion_event<ValueType> completed; std::for_each( tasks_begin, tasks_end, [=](task<ValueType> t) { t.then([=](ValueType result) { completed.set(result); }); }); return task<ValueType>(completed); } 14
  15. Choice task<string> tasks[] = {t1, t2, t3}; auto taskResult =

    when_any (begin(tasks), end(tasks)) .then([](string result) { cout << "Got one element " << result; }); taskResult.wait(); 15
  16. Join task<string> tasks[] = {t1, t2, t3}; auto taskResult =

    when_all (begin(tasks), end(tasks)) .then([](std::vector<string> results) { printf("Got all %d elements\n",results.size()); for each(auto result in results) { cout << "Got element " << result << endl; } }); taskResult.wait(); 16
  17. Operator Syntax • Choice: auto t = t1 || t2;

    // same as when_any • Join: auto t = t1 && t2; // same as when_all 17
  18. Example: Project Build Project A = {"A"}; // A B

    Project B = {"B"}; // / \ / Project C = {"C"}; // C D Project D = {"D"}; // task<void> a([=]() { Build(A); }); task<void> b([=]() { Build(B); }); // Build C after A auto c = a.then([=]() { Build(C); }); // Build D after both A and B auto d = (a && b).then([=]() { return Build(D); }); 18
  19. Example: Timeout auto loadWebPageTask = create_task([=]() { string text =

    Download("www.microsoft.com"); return pair<string,bool>(text, true); }); auto timeoutTask = create_task([=]() { Sleep(3000); return pair<string,bool>("", false); }); (loadWebPageTask || timeoutTask) .then([=](pair<string,bool> first_result) { if( first_result.second ) { printf("Downloaded string '%s'\n", first_result.first.c_str()); } else { printf("Time out!\n"); } }) .wait(); 19
  20. Callback-based Asynchrony Hollywood Principle: “Don’t call us, we’ll call you!”

    20
  21. Hollywood Principle AJAX: 21 http.open("GET", "customer.html"); http.onreadystatechange = function() {

    if(http.readyState == 4) { var serverResponse = http.responseText; // process serverResponse here: ... } }
  22. Hollywood Principle Node.js: 22 var http = require("http"); http.createServer(function(request, response)

    { response.writeHead(200, {"Content-Type": "text/plain"}); response.write("Hello World"); response.end(); }).listen(8888);
  23. Windows 8 Asynchrony Callbacks + PPL = Better Asynchrony 23

  24. Read From File, Windows 8 Runtime 24 template<typename Callback> void

    ReadString(String^ fileName, Callback func) { StorageFolder^ item = KnownFolders::PicturesLibrary; IAsyncOperation<StorageFile^>^ getFileOp = item->GetFileAsync(fileName); getFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^> ([=](IAsyncOperation<StorageFile^>^ operation, AsyncStatus status) { auto storageFile = operation->GetResults(); auto openOp = storageFile->OpenAsync(FileAccessMode::Read); openOp->Completed = ref new AsyncOperationCompletedHandler<IRandomAccessStream^> ([=](IAsyncOperation<IRandomAccessStream^>^ operation, AsyncStatus status) { auto istream = operation->GetResults(); auto reader = ref new DataReader(istream); auto loadOp = reader->LoadAsync(istream->Size); loadOp->Completed = ref new AsyncOperationCompletedHandler<UINT> ([=](IAsyncOperation<UINT>^ operation, AsyncStatus status) { auto bytesRead = operation->GetResults(); auto str = reader->ReadString(bytesRead); func(str); }); }); }); } Error handling?
  25. Read From File, with error handling 25 template<typename Callback, typename

    ErrorHandler> void ReadStringWithErrorHandling(String^ fileName, Callback func, ErrorHandler errFunc) { StorageFolder^ item = KnownFolders::PicturesLibrary; auto getFileOp = item->GetFileAsync(fileName); getFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([=](IAsyncOperation<StorageFile^>^ operation, AsyncStatus status) { try { auto storageFile = operation->GetResults(); auto openOp = storageFile->OpenAsync(FileAccessMode::Read); openOp->Completed = ref new AsyncOperationCompletedHandler<IRandomAccessStream^>([=](IAsyncOperation<IRandomAccessStream^>^ operation, AsyncStatus status) { try { auto istream = operation->GetResults(); auto reader = ref new DataReader(istream); auto loadOp = reader->LoadAsync(istream->Size); loadOp->Completed = ref new AsyncOperationCompletedHandler<UINT>([=](IAsyncOperation<UINT>^ operation, AsyncStatus status) { try { auto bytesRead = operation->GetResults(); auto str = reader->ReadString(bytesRead); func(str); } catch(Platform::Exception^ ex) { errFunc(ex); } }); } catch(Platform::Exception^ ex) { errFunc(ex); } }); } catch(Platform::Exception^ ex) { errFunc(ex); } }); }
  26. PPL Tasks Benefits • Write Less Code • Composability 26

  27. Asynchronous Unwrapping 27 task<int> t([]() { int n; n =

    return fib(40); }).then ([](int n) { printf("%d\n", n); } Recall: task<int> t([]() { task<int> fibTask = ... return fibTask; }).then ([](int n) { printf("%d\n", n); } Now consider:
  28. Asynchronous Unwrapping 28 task<int> t([]() { int n; n =

    return fib(40); }).then ([](int n) { printf("%d\n", n); } Recall: task<int> t([]() { IAsyncOperation<int>^ fibOp = ... return fibOp; }).then ([](int n) { printf("%d\n", n); } Now consider:
  29. Read from file, PPL 29 task<String^> ReadStringTask(String^ fileName) { StorageFolder^

    item = KnownFolders::PicturesLibrary; auto reader = std::make_shared<IDataReader^>(nullptr); task<StorageFile^> getFileTask(item->GetFileAsync(fileName)); return getFileTask.then([](StorageFile^ storageFile) { return storageFile->OpenAsync(FileAccessMode::Read); }).then([reader](IRandomAccessStream^ istream) { *reader = ref new DataReader(istream); return (*reader)->LoadAsync(istream->Size); }).then([reader](UINT bytesRead) { return (*reader)->ReadString(bytesRead); }); }
  30. Read from file, Error-handling 30 ReadStringTask("foo.txt").then([](task<String^> t) { try {

    String^ str = t.get(); } catch(Exception^ ex) { // handle error here } });
  31. Write less code with PPL 31 task<String^> ReadStringTask(String^ fileName) {

    StorageFolder^ item = KnownFolders::PicturesLibrary; auto reader = std::make_shared<IDataReader^>(nullptr); task<StorageFile^> getFileTask(item->GetFileAsync(fileName)); return getFileTask.then([](StorageFile^ storageFile) { return storageFile->OpenAsync(FileAccessMode::Read); }).then([reader](IRandomAccessStream^ istream) { *reader = ref new DataReader(istream); return (*reader)->LoadAsync(istream->Size); }).then([reader](UINT bytesRead) { return (*reader)->ReadString(bytesRead); }); } template<typename Callback> void ReadString(String^ fileName, Callback func) { StorageFolder^ item = KnownFolders::PicturesLibrary; IAsyncOperation<StorageFile^>^ getFileOp = item->GetFileAsync(fileName); getFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^> ([=](IAsyncOperation<StorageFile^>^ operation, AsyncStatus status) { auto storageFile = operation->GetResults(); auto openOp = storageFile->OpenAsync(FileAccessMode::Read); openOp->Completed = ref new AsyncOperationCompletedHandler<IRandomAccessStream^> ([=](IAsyncOperation<IRandomAccessStream^>^ operation, AsyncStatus status) { auto istream = operation->GetResults(); auto reader = ref new DataReader(istream); auto loadOp = reader->LoadAsync(istream->Size); loadOp->Completed = ref new AsyncOperationCompletedHandler<UINT> ([=](IAsyncOperation<UINT>^ operation, AsyncStatus status) { auto bytesRead = operation->GetResults(); auto str = reader->ReadString(bytesRead); func(str); }); }); }); }
  32. PPL Tasks Benefits • Write Less Code • Composability 32

  33. Concatenate Two Files (naïve) 33 template<typename Callback> void ConcatFiles1(String^ file1,

    String^ file2, Callback func) { ReadString(file1, [func](String^ str1) { ReadString(file2, [func](String^ str2) { func(str1+str2); }); }); }
  34. ref struct ResultHolder { String^ str; }; template<typename Callback> void

    ConcatFiles(String^ file1, String^ file2, Callback func) { auto results = ref new ResultHolder(); ReadString(file1, [=](String^ str) { if(results->str != nullptr) { func(str + results->str); } else{ results->str = str; } }); ReadString(file2, [=](String^ str) { if(results->str != nullptr) { func(results->str + str); } else{ results->str = str; } }); } Concatenate Two Files (racy) 34 Race condition! Race condition!
  35. task<String^> ConcatFiles(String^ file1, String^ file2) { auto strings_task = ReadStringTask(file1)

    && ReadStringTask(file2); return strings_task.then([](std::vector<String^> strings) { return strings[0] + strings[1]; }); } Concatenate Two Files (terse, efficient and safe) 35
  36. Peeking Into The Future Why can’t we be like a

    certain other language? 36 (Axum)
  37. Can we do better? 37 task<String^> ReadStringTask(String^ fileName) { StorageFolder^

    item = KnownFolders::PicturesLibrary; auto reader = std::make_shared<IDataReader^>(nullptr); task<StorageFile^> getFileTask(item->GetFileAsync(fileName)); return getFileTask.then([](StorageFile^ storageFile) { return storageFile->OpenAsync(FileAccessMode::Read); }).then([reader](IRandomAccessStream^ istream) { *reader = ref new DataReader(istream); return (*reader)->LoadAsync(istream->Size); }).then([reader](UINT bytesRead) { return (*reader)->ReadString(bytesRead); }); }
  38. Kind of like this… 38 task<String^> ReadStringAsync(String^ fileName) . .

    . { StorageFolder^ item = KnownFolders::PicturesLibrary; auto storageFile = . . . item->GetFileAsync(fileName); auto istream = . . . storageFile->OpenAsync(FileAccessMode::Read); auto reader = ref new DataReader(istream); auto bytesRead = . . . reader->LoadAsync(istream->Size); return reader->ReadString(bytesRead); }
  39. Yes we can! 39 task<String^> ReadStringAsync(String^ fileName) resumable { StorageFolder^

    item = KnownFolders::PicturesLibrary; auto storageFile = await item->GetFileAsync(fileName); auto istream = await storageFile->OpenAsync(FileAccessMode::Read); auto reader = ref new DataReader(istream); auto bytesRead = await reader->LoadAsync(istream->Size); return reader->ReadString(bytesRead); }
  40. Compare 40 task<String^> ReadStringTask(String^ fileName) { StorageFolder^ item = KnownFolders::PicturesLibrary;

    auto reader = std::make_shared<IDataReader^>(nullptr); task<StorageFile^> getFileTask(item->GetFileAsync(fileName)); return getFileTask.then([](StorageFile^ storageFile) { return storageFile->OpenAsync(FileAccessMode::Read); }).then([reader](IRandomAccessStream^ istream) { *reader = ref new DataReader(istream); return (*reader)->LoadAsync(istream->Size); }).then([reader](UINT bytesRead) { return (*reader)->ReadString(bytesRead); }); } task<String^> ReadStringAsync(String^ fileName) resumable { StorageFolder^ item = KnownFolders::PicturesLibrary; auto storageFile= await item->GetFileAsync(fileName); auto istream = await storageFile->OpenAsync(FileAccessMode::Read); auto reader = ref new DataReader(istream); auto bytesRead = await reader->LoadAsync(istream->Size); return reader->ReadString(bytesRead); } PPL Language support for asynchrony
  41. Compare 41 task<String^> ReadStringTask(String^ fileName) { StorageFolder^ item = KnownFolders::PicturesLibrary;

    auto reader = std::make_shared<IDataReader^>(nullptr); task<StorageFile^> getFileTask(item->GetFileAsync(fileName)); return getFileTask.then([](StorageFile^ storageFile) { return storageFile->OpenAsync(FileAccessMode::Read); }).then([reader](IRandomAccessStream^ istream) { *reader = ref new DataReader(istream); return (*reader)->LoadAsync(istream->Size); }).then([reader](UINT bytesRead) { return (*reader)->ReadString(bytesRead); }); } task<String^> ReadStringAsync(String^ fileName) resumable { StorageFolder^ item = KnownFolders::PicturesLibrary; auto storageFile= await item->GetFileAsync(fileName); auto istream = await storageFile->OpenAsync(FileAccessMode::Read); auto reader = ref new DataReader(istream); auto bytesRead = await reader->LoadAsync(istream->Size); return reader->ReadString(bytesRead); } template<typename Callback> void ReadString(String^ fileName, Callback func) { StorageFolder^ item = KnownFolders::PicturesLibrary; IAsyncOperation<StorageFile^>^ getFileOp = item->GetFileAsync(fileName); getFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^> ([=](IAsyncOperation<StorageFile^>^ operation, AsyncStatus status) { auto storageFile = operation->GetResults(); auto openOp = storageFile->OpenAsync(FileAccessMode::Read); openOp->Completed = ref new AsyncOperationCompletedHandler<IRandomAccessStream^> ([=](IAsyncOperation<IRandomAccessStream^>^ operation, AsyncStatus status) { auto istream = operation->GetResults(); auto reader = ref new DataReader(istream); auto loadOp = reader->LoadAsync(istream->Size); loadOp->Completed = ref new AsyncOperationCompletedHandler<UINT> ([=](IAsyncOperation<UINT>^ operation, AsyncStatus status) { auto bytesRead = operation->GetResults(); auto str = reader->ReadString(bytesRead); func(str); }); }); }); } PPL Language support for asynchrony “Plain” WinRT
  42. “Concurrent Programs Wait Faster” Tony Hoare

  43. Thanks! More on PPL: • MSDN Magazine Feb 2012 •

    Native Concurrency Blog • PPL Sample Pack: – Search for “Windows Asynchrony with PPL” • C++ Resumable Functions Proposal, ISO proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3328.pdf • arturl@microsoft.com 43
  44. © 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows

    Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries. The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.
  45. Backup

  46. IAsyncOperation<float>^ operation = create_async([]() { return 42.0f; }); IAsyncAction^ action

    = create_async([]() { // Do something, return nothing }); IAsyncOperationWithProgress<float,int>^ operation_with_progress = create_async([](progress_reporter<int> reporter) { for(int percent=0; percent<100; percent++) { reporter.report(percent); } return 42.0f; }); IAsyncActionWithProgress<int>^ action_with_progress = create_async([](progress_reporter<int> reporter) { for(int percent=0; percent<100; percent++) { reporter.report(percent); } }); Authoring Async Operations using PPL 46
  47. Read from file, PPL with error handling 47 Error handling

    task<String^> ReadStringTaskWithErrorHandling(String^ fileName) { StorageFolder^ item = KnownFolders::PicturesLibrary; auto holder = ref new Holder(); task<StorageFile^> getFileTask(item->GetFileAsync(fileName)); return getFileTask.then([](StorageFile^ storageFile) { return storageFile->OpenAsync(FileAccessMode::Read); }).then([holder](IRandomAccessStream^ istream) { holder->Reader = ref new DataReader(istream); return holder->Reader->LoadAsync(istream->Size); }).then([holder](task<UINT> bytesReadTask) { try { UINT bytesRead = bytesReadTask.get(); return holder->Reader->ReadString(bytesRead); } catch (Exception^ ex) { String^ result = ""; // return empty string return result; } }); }
  48. Advanced Composition • Sequential composition: t.then(A).then(B); • Parallel composition: t.then(A);

    t.then(B); • Join and Choice: auto t = a && b; auto t = a || b; • Iterations (see next page): async_do async_for 48
  49. template <typename Function> task<bool> async_do (Function func) { task<bool> first

    = func(); return first.then([=](bool guard) { if (guard) return async_do(func); else return first; }); } Loops with asynchronous unwrapping 49 FileReader reader("foo.txt"); auto loop = async_do([&reader]() { return reader.GetNext().then([](String^s) { if( s == nullptr ) return false; return true; }); });
  50. Compress a File, WinRT: part 1 50 void CompressStream(IInputStream^ istream,

    IOutputStream^ ostream, HANDLE sigEvent); [Platform::MTAThread] void main() { String^ fileName = "foo.txt"; HANDLE sigEvent = CreateEvent(NULL, FALSE, FALSE, NULL ); int openTwoFiles = 0; IInputStream^ istream; IOutputStream^ ostream; StorageFolder^ item = Windows::Storage::KnownFolders::DocumentsLibrary; auto openStorageFileOp = item->GetFileAsync(fileName); openStorageFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([&] (IAsyncOperation<StorageFile^>^ storageFileOp) { auto inputStorageFile = storageFileOp->GetResults(); auto openOp = inputStorageFile->OpenForReadAsync(); openOp->Completed = ref new AsyncOperationCompletedHandler<IInputStream^>([&] (IAsyncOperation<IInputStream^>^ istreamOp) { istream = istreamOp->GetResults(); if( ++openTwoFiles == 2 ) { CompressStream(istream, ostream, sigEvent); } }); openOp->Start(); }); openStorageFileOp->Start(); auto createStorageFileOp = item->CreateFileAsync("compressed.zip"); createStorageFileOp->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>([&] (IAsyncOperation<StorageFile^>^ storageFileOp) { auto outputStorageFile = storageFileOp->GetResults(); auto openOp = outputStorageFile->OpenAsync(FileAccessMode::ReadWrite); openOp->Completed = ref new AsyncOperationCompletedHandler<IRandomAccessStream^>([&] (IAsyncOperation<IRandomAccessStream^>^ rastreamOp) { auto rastream = rastreamOp->GetResults(); ostream = rastream->GetOutputStreamAt(0); if( ++openTwoFiles == 2 ) { CompressStream(istream, ostream, sigEvent); } }); openOp->Start(); }); createStorageFileOp->Start(); DWORD dummy; CoWaitForMultipleHandles(COWAIT_INPUTAVAILABLE, INFINITE, 1, &sigEvent, &dummy); CloseHandle(sigEvent); } See next page “Join”
  51. Compress a File, WinRT: part 2 51 void CompressStream(IInputStream^ istream,

    IOutputStream^ ostream, HANDLE sigEvent) { auto reader = ref new DataReader(istream); auto buffer = reader->DetachBuffer(); Compressor^ compressor = ref new Compressor(ostream, CompressAlgorithm::Lzms); int *pbothFlushed = new int; // can't leave it on the stack! *pbothFlushed = 0; auto writeOp = compressor->WriteAsync(buffer); writeOp->Completed = ref new AsyncOperationWithProgressCompletedHandler<UINT,UINT>([pbothFlushed,compressor,ostream,sigEvent](IAsyncOperationWithProgress<UINT,UINT>^ writeOp) { auto ostreamFlushOp = ostream->FlushAsync(); ostreamFlushOp->Completed = ref new AsyncOperationCompletedHandler<bool>([pbothFlushed,compressor,sigEvent] (IAsyncOperation<bool>^ flushOp) { if( ++(*pbothFlushed) == 2 ) { SetEvent(sigEvent); delete pbothFlushed; } }); ostreamFlushOp->Start(); auto compressorFinishOp = compressor->FinishAsync(); compressorFinishOp->Completed = ref new AsyncOperationCompletedHandler<bool>([pbothFlushed,sigEvent] (IAsyncOperation<bool>^ flushOp) { if( ++(*pbothFlushed) == 2 ) { SetEvent(sigEvent); delete pbothFlushed; } }); compressorFinishOp->Start(); }); writeOp->Start(); } Error handling, anyone?...
  52. Compress a File, PPL Tasks 52 [Platform::MTAThread] void main() {

    String^ fileName = "somefile.txt"; StorageFolder^ item = Windows::Storage::KnownFolders::DocumentsLibrary; auto getFileTask = create_task(item->GetFileAsync(fileName)); auto createFileTask = create_task(item->CreateFileAsync("compressed.txt")) task<IOutputStream^> getStreamTask = createFileTask.then([](StorageFile^ storageFile) { return storageFile->OpenAsync(FileAccessMode::ReadWrite); }).then([&](IRandomAccessStream^ rastream) { return rastream->GetOutputStreamAt(0); }); IBuffer^ buffer; getFileTask.then([&](StorageFile^ storageFile) { return storageFile->OpenForReadAsync(); }).then([&](IInputStream^ istream) { DataReader^ reader = ref new DataReader(istream); buffer = reader->DetachBuffer(); return getStreamTask; }).then([&](IOutputStream^ ostream) { Compressor^ compressor = ref new Compressor(ostream, CompressAlgorithm::Lzms); return compressor->WriteAsync(buffer); }).then([&](IAsyncOperationWithProgress<UINT,UINT>^ op) { return create_task(flushStreamTaskostream->FlushAsync()) && create_task(compressor->FinishAsync()); }).wait(); } “Join”