Getting a grip on the PPL and WinRT C++-Javascript interaction


  • Hello all,

    we're currently in the process of adding concurrency support to our SQLite3 WinJS Wrapper. During debugging, exceptions we were throwing in our Javascript unit tests to test transaction rollback were showing up in the native debugger as Js::JavascriptExceptionObject. But we were pretty surprised when we looked at the callstack: apparently the component DLL is reentrant into jscript9.dll with a very deep callstack.

    My hope is that someone on the MSDN forums can explain to me how this works.

    Every now and then - very, very hard to reproduce - an exception RPC_E_WRONG_THREAD is thrown and I wonder if this may somehow indicate that the component DLL re-entered into the main application from another thread.

    I'd be grateful for any documentation and explanations about how the asynchronism mechanism works under the hood and across the different languages and maybe what pattern the PPL uses to create and end threads.

    Thursday, June 28, 2012 1:37 PM

All replies

  • Could you clarify what reentrancy you were seeing (e.g.: a callstack) and in what context?

    The RPC_E_WRONG_THREAD error usually occurs when you pass an object which is not agile between apartments/threads and access it.  If you are scheduling continuations in C++ (via .then) off tasks which were constructed via an IAsync* interface, the PPL is careful to make sure that the continuation occurs in the same apartment where the original .then was called (e.g.: if you did a .then on the UI thread, the continuation will run back on the UI thread).  Do you know what object was being accessed in what context / on what thread when this error occurs for you?

    From the C++ perspective, there is a bunch of documentation regarding PPL and asynchronous tasks here on MSDN.  As well, there's a bunch of general documentation on the PPL and surrounding constructs here on MSDN.  If those don't answer your questions, let us know more specifically what you trying to find.

    Thursday, June 28, 2012 5:18 PM
  • Hey Bill, sorry for taking so long to get back to this thread.

    The reentrant problem seems to have been solved as a side-effect of some refactoring. But every now and then, some RPC_E_WRONG_THREAD exception still occurs. It is very hard to reproduce and very elusive because it comes wrapped in an unobserved_task_exception that I can't get behind.

    There's basically two times where we produce tasks: once via create_async to immediately pass it back to the main Javascript application as an IAsyncAction. And then from the CoreDispatcher::RunAsync which is immediately waited upon by using get() and destroyed (out of scope) immediately afterwards. So from what I understand at the moment, there shouldn't be any unobserved exceptions because exceptions within the create_async lambda correctly get passed to Javascript and any exception in the RunAsync task should be passed on to where get() is called, which is the same level where other exceptions work without problems.

    Reading the documentation on the unobserved_task_exception it should only happen if a task is destroyed without having a chance to pass on its exception to a parent and this really shouldn't be the case here.

    Also RPC_E_WRONG_THREAD is quite confusing because afaik it should only relate to COM classes, yet the only classes we use - beside our own ref class which should be agile by default - are Platform String, Boolean, some Platform integer and double types which also should all be agile. Also, there's Windows::Foundation::DateTime but as a value class it shouldn't be susceptible to this kind of problem at all.

    It would be great if you could shed any light on this mystery :)

    Friday, July 20, 2012 4:47 PM
  • Another followup:

    I found that when trying to access an IVectorView<Platform::Object^>^ that is passed in from JavaScript, I can easily reproduce a WrongThreadException. Putting aside the fact that it would probably be a good idea to only pass in agile classes to facilitate and promote asynchronous code in C++ components, this leaves me with the observation that when a WrongThreadException is thrown in my code, I get a perfectly good callstack showing me the line where the exception occurred even if it's inside a create_async lambda.

    With these infrequent WrongThreadExceptions we are observing, the callstack only contains template-generated code from the PPL. Here's a copy, in case that helps somehow:

     	KernelBase.dll!RaiseException()	Unknown
     	msvcr110d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 152	C++
     	vccorlib110d.dll!__abi_WinRTraiseWrongThreadException() Line 482	C++
     	SQLite3.dll!__abi_WinRTraiseException(long __hrArg) Line 953	C++
    >	SQLite3.dll!__abi_ThrowIfFailed(long __hrArg) Line 79	C++
     	SQLite3.dll!Windows::Foundation::AsyncActionCompletedHandler::__abi_IDelegate::Invoke(Windows::Foundation::IAsyncAction ^ __param0, Windows::Foundation::AsyncStatus __param1)	C++
     	SQLite3.dll!Windows::Foundation::AsyncActionCompletedHandler::NonVInvoke(Windows::Foundation::IAsyncAction ^ __param0, Windows::Foundation::AsyncStatus __param1)	C++
     	SQLite3.dll!<lambda_09cbd158943487671d3a8e5b3d4b79e2>::operator()() Line 5251	C++
     	SQLite3.dll!std::_Callable_obj<<lambda_09cbd158943487671d3a8e5b3d4b79e2>,0>::_ApplyX<void>() Line 431	C++
     	SQLite3.dll!std::_Func_impl<std::_Callable_obj<<lambda_09cbd158943487671d3a8e5b3d4b79e2>,0>,std::allocator<std::_Func_class<void,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >,void,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::_Do_call() Line 239	C++
     	SQLite3.dll!std::_Func_class<void,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()() Line 514	C++
     	SQLite3.dll!Concurrency::details::_ContextCallback::_CallInContext(std::function<void __cdecl(void)> _Func) Line 590	C++
     	SQLite3.dll!Concurrency::details::_AsyncInfoBase<Concurrency::details::_AsyncAttributes<<lambda_40b2cc0fe3af800a1447cf4d727ba30c>,void,void,Concurrency::details::_TaskTypeTraits<void,0>,0,0>,1>::_FireCompletion() Line 5252	C++
     	SQLite3.dll!<lambda_fb398587cbaf80b98897fed00463e403>::operator()(Concurrency::task<void> _Antecedent) Line 5550	C++
     	SQLite3.dll!std::_Callable_obj<<lambda_fb398587cbaf80b98897fed00463e403>,0>::_ApplyX<void,Concurrency::task<void> >(Concurrency::task<void> && _V0) Line 431	C++
     	SQLite3.dll!std::_Func_impl<std::_Callable_obj<<lambda_fb398587cbaf80b98897fed00463e403>,0>,std::allocator<std::_Func_class<void,Concurrency::task<void>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >,void,Concurrency::task<void>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::_Do_call(Concurrency::task<void> && _V0) Line 239	C++
     	SQLite3.dll!std::_Func_class<void,Concurrency::task<void>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()(Concurrency::task<void> _Vx0) Line 515	C++
     	SQLite3.dll!<lambda_7907aee8f010b6a3fece194c1c9e04c5>::operator()(Concurrency::task<void> t) Line 2204	C++
     	SQLite3.dll!std::_Callable_obj<<lambda_7907aee8f010b6a3fece194c1c9e04c5>,0>::_ApplyX<unsigned char,Concurrency::task<void> >(Concurrency::task<void> && _V0) Line 431	C++
     	SQLite3.dll!std::_Func_impl<std::_Callable_obj<<lambda_7907aee8f010b6a3fece194c1c9e04c5>,0>,std::allocator<std::_Func_class<unsigned char,Concurrency::task<void>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >,unsigned char,Concurrency::task<void>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::_Do_call(Concurrency::task<void> && _V0) Line 239	C++
     	SQLite3.dll!std::_Func_class<unsigned char,Concurrency::task<void>,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()(Concurrency::task<void> _Vx0) Line 515	C++
     	SQLite3.dll!Concurrency::task<unsigned char>::_ContinuationTaskHandle<void,void,<lambda_fb398587cbaf80b98897fed00463e403>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>::_Continue(std::integral_constant<bool,1> __formal, Concurrency::details::_TypeSelectorNoAsync __formal) Line 3393	C++
     	SQLite3.dll!Concurrency::task<unsigned char>::_ContinuationTaskHandle<void,void,<lambda_fb398587cbaf80b98897fed00463e403>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>::_Perform() Line 3301	C++
     	SQLite3.dll!Concurrency::details::_PPLTaskHandle<unsigned char,Concurrency::task<unsigned char>::_ContinuationTaskHandle<void,void,<lambda_fb398587cbaf80b98897fed00463e403>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>,Concurrency::details::_ContinuationTaskHandleBase>::operator()() Line 1253	C++
     	SQLite3.dll!Concurrency::details::_UnrealizedChore::_InvokeBridge<Concurrency::details::_PPLTaskHandle<unsigned char,Concurrency::task<unsigned char>::_ContinuationTaskHandle<void,void,<lambda_fb398587cbaf80b98897fed00463e403>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>,Concurrency::details::_ContinuationTaskHandleBase> >(Concurrency::details::_PPLTaskHandle<unsigned char,Concurrency::task<unsigned char>::_ContinuationTaskHandle<void,void,<lambda_fb398587cbaf80b98897fed00463e403>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>,Concurrency::details::_ContinuationTaskHandleBase> * _PChore) Line 4464	C++
     	msvcr110d.dll!Concurrency::details::_UnrealizedChore::_UnstructuredChoreWrapper(Concurrency::details::_UnrealizedChore * pChore) Line 293	C++
     	msvcr110d.dll!Concurrency::details::_UnrealizedChore::_Invoke() Line 4420	C++
     	msvcr110d.dll!Concurrency::details::WorkItem::Invoke() Line 172	C++
     	msvcr110d.dll!Concurrency::details::InternalContextBase::ExecuteChoreInline(Concurrency::details::WorkItem * pWork) Line 1605	C++
     	msvcr110d.dll!Concurrency::details::InternalContextBase::Dispatch(Concurrency::DispatchState * pDispatchState) Line 1719	C++
     	msvcr110d.dll!Concurrency::details::FreeThreadProxy::Dispatch() Line 197	C++
     	msvcr110d.dll!Concurrency::details::ThreadProxy::ThreadProxyMain(void * lpParameter) Line 171	C++
     	kernel32.dll!BaseThreadInitThunk()	Unknown
     	ntdll.dll!RtlUserThreadStart()	Unknown

    Monday, July 23, 2012 1:48 PM