locked
Worker thread exception not being handled by continuation

    Question

  • How do I figure out what's causing it? I read about unobserved_task_exception Class and turned on Visual Studios option to break on exceptions but it's not helping.

    It doesn't happen if I comment out the code in this method:

    void ClassicDialog::AsyncLoadGame()
    {
    	task<boost::shared_ptr<std::istream>> inPtrTask = AsyncFileStream::Reader(DocumentPath(GameView::GetSaveFilename()).c_str());
    	inPtrTask.then([&] (task<boost::shared_ptr<std::istream>> inPtrTask) {
    		boost::shared_ptr<std::istream> inPtr;
    		try {
    			inPtr = inPtrTask.get();
    		}
    		catch (Platform::Exception^ exc )
    		{
    			LOG("Classic mode save game not loaded");
    			LOG(exc->Message);
    			return;
    		}
    		catch (...)
    		{
    			LOG("Classic mode save game not loaded");
    			LOG("C++ exception");
    			return;
    		}
    		this->game.reset(new Game(*inPtr));
    		resumeButton.Enable();
    	});
    }


    I expect an exception, because there is no saved game file yet, and my exception handler does run as expected. But some time after, a worker thread throws the exception. Every task that I start in AsyncFileStream::Reader is chained to it's output, so any exceptions that occur should be routed to my continuation, correct? Here's the code for the Reader method:

    Concurrency::task<boost::shared_ptr<std::istream>> AsyncFileStream::Reader(std::string pathName)
    {
    	using namespace Windows::Storage;
    	using namespace Concurrency;
    
    	task<StorageFile^> getFileTask(StorageFile::GetFileFromPathAsync(stops(pathName)));
    
    	auto readBufferTask = getFileTask.then([] (StorageFile^ f) {
    		return FileIO::ReadBufferAsync(f);
    	} );
    
    	task<Platform::Array<unsigned char>^> byteArrayTask = readBufferTask.then([] (Streams::IBuffer^ b) {
    		auto a = ref new Platform::Array<unsigned char>(b->Length);
    		Streams::DataReader::FromBuffer(b)->ReadBytes(a);
    		return a;
    	} );
    
    	return byteArrayTask.then([] (task<Platform::Array<unsigned char>^> resultTask) {
    		Platform::Array<byte>^ a = resultTask.get();
    		boost::shared_ptr<std::stringstream> result(new std::stringstream);
    		result->write((char*)&a[0], a->Length);
    		return boost::static_pointer_cast<std::istream>(result);
    	} );
    }


    Monday, April 23, 2012 9:14 PM

Answers

  • Brent,

    I confirmed that this is a known bug in the Consumer Preview version of Visual Studio 11. The bug has been fixed since and your code will work fine in the next consumer facing release.

    What I'm seeing is an unhandled exception in __abi_winrt_ptr_dtor (at the top of the stack), with ~unobserved_task_exception a frame below. Can you confirm that this is what you are seeing as well?

    Throwing a WinRT exception from within the body of a task (and not catching it within the task itself) exposes this bug. This is what happens when you call get() inside your final continuation in Reader(..). It also explains why removing that call to get() does not reproduce the problem.

    --Geni

    Wednesday, April 25, 2012 2:00 AM

All replies

  • Hello,

    How about the try-catch block in

    Platform::Array<byte>^ a = resultTask.get();

    The runtime throws unobserved_task_exception to indicate that a task threw an exception that was not handled by the task or by its continuations, and task::get or task::wait was never called on that task or any of the value based continuations in its tree. When this exception is thrown, it likely indicates a problem with your code. If your application crashes due to this exception, you can configure Visual Studio to break when C++ exceptions are thrown. After you diagnose the location of the unhandled exception, use a task-based continuation to handle it.

    Based on that I think maybe there is not throw error when you call  resultTask.get();

    Best regards,
    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, April 24, 2012 7:11 AM
  • OK, so I've gotten it working by getting rid of the resultTask.get()

    	return byteArrayTask.then([] (ByteArrayWrapper byteArrayWrapper) {
    		Platform::Array<byte>^ byteArray = byteArrayWrapper.data;
    		boost::shared_ptr<std::stringstream> result(new std::stringstream);
    		result->write((char*)&byteArray[0], byteArray->Length);
    		return (boost::shared_ptr<std::istream>)result;
    	} );

    I'm a bit confused, though. I still don't understand why an unobserved_task_exception is thrown when the continuation chain and exception handlers are otherwise the exact same. Why does task::get break the chain? It reached the task::get, which throws an exception, which should be caught by the next continuation. I'm successfully printing the error message in the last continuation - which suggests the chain worked. Why is there still something in the background that says I never handled it, when I clearly did?





    Tuesday, April 24, 2012 7:17 PM
  • Brent,

    I've glanced at your code a couple times and it looks correct. The exception from the resultTask.get() in the final continuation in AsyncFileStream::Reader *should* get propagated to the next continuation, and all exceptions should be marked observed. The fact that you're seeing otherwise makes we wonder if you are encountering a bug in the tasks runtime. I will try to see if I can repro something like this on the VS11 Consumer Preview build.

    --Geni

    Wednesday, April 25, 2012 1:20 AM
  • Brent,

    I confirmed that this is a known bug in the Consumer Preview version of Visual Studio 11. The bug has been fixed since and your code will work fine in the next consumer facing release.

    What I'm seeing is an unhandled exception in __abi_winrt_ptr_dtor (at the top of the stack), with ~unobserved_task_exception a frame below. Can you confirm that this is what you are seeing as well?

    Throwing a WinRT exception from within the body of a task (and not catching it within the task itself) exposes this bug. This is what happens when you call get() inside your final continuation in Reader(..). It also explains why removing that call to get() does not reproduce the problem.

    --Geni

    Wednesday, April 25, 2012 2:00 AM
  • Yes, I do see that.

    Software Engineer, Brainium Studios LLC

    Thursday, April 26, 2012 1:19 AM
  • Thanks. For issues with async programming using concurrency::task, you may also post here. Let us know if you run into any additional issues or have feedback.

    --Geni

    Thursday, April 26, 2012 5:55 PM