@@ -221,6 +221,22 @@ UMEntryThunkData * GetMostRecentUMEntryThunkData()
221221}
222222#endif
223223
224+ static void PatchUMEntryPrecodeIfNecessary (UMEntryThunkData * pUMEntryThunkData, PCODE callTarget)
225+ {
226+ #ifdef FEATURE_INTERPRETER
227+ // HACK: We currently rely on the thread-local stash in TheUMEntryPrestubWorker for proper operation
228+ // of reverse p/invoke IL stubs in the interpreter, so we can't patch the precode in this scenario.
229+ // Instead, every reverse p/invoke will go through TheUMEntryPrestubWorker.
230+ MethodDesc *pMD = NonVirtualEntry2MethodDesc (callTarget);
231+ if (pMD && pMD->GetInterpreterCode ())
232+ return ;
233+ #endif
234+
235+ // NOTE: This may be run concurrently by multiple threads, but it should be safe since we will just be
236+ // setting the target of the precode to the same thing multiple times in a row.
237+ pUMEntryThunkData->PatchPrecode ();
238+ }
239+
224240PCODE TheUMEntryPrestubWorker (UMEntryThunkData * pUMEntryThunkData)
225241{
226242 STATIC_CONTRACT_THROWS;
@@ -240,23 +256,36 @@ PCODE TheUMEntryPrestubWorker(UMEntryThunkData * pUMEntryThunkData)
240256 if (pUMEntryThunkData->IsCollectedDelegate ())
241257 CallbackOnCollectedDelegate (pUMEntryThunkData);
242258
243- INSTALL_MANAGED_EXCEPTION_DISPATCHER;
244- // this method is called by stubs which are called by managed code,
245- // so we need an unwind and continue handler so that our internal
246- // exceptions don't leak out into managed code.
247- INSTALL_UNWIND_AND_CONTINUE_HANDLER;
248-
259+ PCODE callTarget = (PCODE)0 ;
249260#ifdef FEATURE_INTERPRETER
250261 // HACK: Stash the entry thunk data address so that the interpreter can recover it.
251262 // The InterpreterStub overwrites the register that normally would contain this address.
252263 t_MostRecentUMEntryThunkData = pUMEntryThunkData;
264+
265+ // See whether another thread has already prepared the IL stub for the reverse p/invoke.
266+ callTarget = pUMEntryThunkData->GetCachedCallTarget ();
253267#endif
254- pUMEntryThunkData->RunTimeInit ();
255268
256- UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
257- UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
269+ // We don't have a pre-prepared reverse p/invoke, and might be racing with another thread.
270+ if (!callTarget)
271+ {
272+ INSTALL_MANAGED_EXCEPTION_DISPATCHER;
273+ // this method is called by stubs which are called by managed code,
274+ // so we need an unwind and continue handler so that our internal
275+ // exceptions don't leak out into managed code.
276+ INSTALL_UNWIND_AND_CONTINUE_HANDLER;
277+
278+ pUMEntryThunkData->RunTimeInit ();
279+ callTarget = pUMEntryThunkData->GetCachedCallTarget ();
280+ // We're likely to be the first thread to have gotten here for this UM entry thunk, so make
281+ // sure that the precode is patched to point to the IL stub instead of us, if necessary.
282+ PatchUMEntryPrecodeIfNecessary (pUMEntryThunkData, callTarget);
283+
284+ UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
285+ UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
286+ }
258287
259- return (PCODE)pUMEntryThunkData-> GetCode () ;
288+ return callTarget ;
260289}
261290
262291UMEntryThunkData* UMEntryThunkData::CreateUMEntryThunk ()
0 commit comments