download with progress

Jan 27, 2014 at 6:23 AM
Edited Jan 27, 2014 at 8:48 AM
I modified a bit of the download() function in the 'live_connect.h' header file of the WindowsLiveAuth sample application to show the download progress.
pplx::task<size_t> downloadWithProgress(const utility::string_t& file_id, Windows::Storage::StorageFile^ file)
            {
                if (file == nullptr){
                    throw std::invalid_argument("file reference cannot be null");
                }

                return concurrency::streams::file_stream<uint8_t>::open_ostream(file).then(
                    [file_id, this](concurrency::streams::ostream stream)
                {
                    web::http::uri_builder bldr;
                    bldr.append(file_id);
                    bldr.append_path(U("content"));
                    //OutputDebugString(L"URI=");
                    //OutputDebugString(bldr.to_string().c_str());

                    web::http::http_request req(web::http::methods::GET);
                    req.set_request_uri(bldr.to_string());

                    return _make_request(req)
                        .then([stream](web::http::http_response response) -> pplx::task<size_t>
                    {
                        if (response.status_code() >= 400)
                        {
                            return response.extract_string().then(
                                [](utility::string_t message) -> pplx::task<size_t>
                            {
                                return pplx::task_from_exception<size_t>(std::exception(utility::conversions::to_utf8string(message).c_str()));
                            });
                        }



                        size_t size=0;
                        response.body().read(stream.streambuf(), 1);
                        while (!response.body().is_eof()){
                            response.body().read( stream.streambuf(),1);
                            size += 1;
                            OutputDebugString(size.ToString()->Data());
                        }
                        return pplx::task<size_t>([size](){return size;});



                        .then([stream](pplx::task<size_t> response)
                    {
                        OutputDebugString(L"Closing Stream");
                        stream.flush();
                        stream.close();
                        return response;
                    });
                });
            }
On debugging i get the following exception
First-chance exception at 0x77892EEC (KernelBase.dll) in MicrosoftAccount.exe: 0x40080201: WinRT originate error (parameters: 0x80004001, 0x00000011, 0x043BCCD4).
First-chance exception at 0x77892EEC in MicrosoftAccount.exe: Microsoft C++ exception: web::http::http_exception at memory location 0x0625ED8C.
Closing StreamFirst-chance exception at 0x77892EEC (KernelBase.dll) in MicrosoftAccount.exe: 0x40080201: WinRT originate error (parameters: 0x8000000C, 0x00000065, 0x0679E35C).
First-chance exception at 0x77892EEC in MicrosoftAccount.exe: Microsoft C++ exception: Platform::ChangedStateException ^ at memory location 0x0679E7FC. HRESULT:0x8000000C A concurrent or interleaved operation changed the state of the object, invalidating this operation.
WinRT information: A concurrent or interleaved operation changed the state of the object, invalidating this operation.
What am i doing wrong here?
Coordinator
Feb 6, 2014 at 1:45 AM
response.body() returns an istream. All read operations on an input stream are asynchronous, they return a task. => the read loop is creating tasks for each read operation but not waiting for any of this to finish. The reads may not be atomic, hence having multiple simultaneous reads may cause the stream to get to inconsistent state.
Also, while checking for eof, the previous read tasks may not have completed, hence it will assume you have not yet reached the end of file and spawn another task for another read.
I would suggest either waiting for a read task to complete before initiating the next one or to attach a continuations and perform the subsequent read in the continuation.
For example:
                            response.body().read(stream.streambuf(), 1).wait();
                            while (!response.body().is_eof())
                            {
                                response.body().read( stream.streambuf(),1).wait();
                                size += 1;
                                OutputDebugString(size.ToString()->Data());
                            }
Thanks
Kavya.