From c7692765ed8223fa4d591a7449b0ca5444025ee3 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Thu, 3 Aug 2023 15:26:23 -0700 Subject: [PATCH 1/8] [MERGE DAMAGED] Notify jiterpreter when a method is freed and ensure it isn't in the jit queues Fix infosByMethod not being populated Checkpoint: Migrate jiterpreter jit queues to C thread-local memory Use tlqueue for interp_entry as well --- src/mono/mono/mini/interp/interp.c | 5 + src/mono/mono/mini/interp/jiterpreter.c | 141 ++++++++++++++++++ src/mono/mono/mini/interp/jiterpreter.h | 11 ++ src/mono/wasm/runtime/cwraps.ts | 8 + src/mono/wasm/runtime/es6/dotnet.es6.lib.js | 2 + src/mono/wasm/runtime/exports-linker.ts | 6 +- src/mono/wasm/runtime/jiterpreter-enums.ts | 5 + .../wasm/runtime/jiterpreter-interp-entry.ts | 32 ++-- src/mono/wasm/runtime/jiterpreter-jit-call.ts | 49 ++++-- src/mono/wasm/runtime/jiterpreter-support.ts | 5 +- .../runtime/jiterpreter-trace-generator.ts | 18 +-- src/mono/wasm/runtime/jiterpreter.ts | 47 ++++-- 12 files changed, 282 insertions(+), 47 deletions(-) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 1b832de431db0c..6e11ef4f35708b 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -3459,6 +3459,11 @@ interp_free_method (MonoMethod *method) jit_mm_lock (jit_mm); imethod = (InterpMethod*)mono_internal_hash_table_lookup (&jit_mm->interp_code_hash, method); + +#if HOST_BROWSER + mono_jiterp_free_method_data (method, imethod); +#endif + mono_internal_hash_table_remove (&jit_mm->interp_code_hash, method); if (imethod && jit_mm->interp_method_pointer_hash) { if (imethod->jit_entry) diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index da0b79b2533916..cbefde9e3d5231 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -27,6 +27,7 @@ void jiterp_preserve_module (void); #include #include #include +#include #include #include @@ -931,6 +932,48 @@ mono_interp_tier_prepare_jiterpreter_fast ( } } +void +mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod) +{ + MonoJitInfo *jinfo = imethod->jinfo; + const guint8 *start; + + // Erase the method and interpmethod from all of the thread local JIT queues + mono_jiterp_tlqueue_purge_all (method); + mono_jiterp_tlqueue_purge_all (imethod); + + // FIXME: Enumerate all active threads and ensure we perform the free_method_data_js + // call on every thread. + // TODO: Migrate the interp_entry and jit_call JIT queues into the native heap, + // so that this method can synchronously remove entries from them. + + if (!jinfo) { + // HACK: Perform a single free operation to clear out any stuff from the jit queues + mono_jiterp_free_method_data_js (method, imethod, 0); + return; + } + + start = (const guint8*) jinfo->code_start; + const guint16 *p = (const guint16 *)start, + *end = (const guint16 *)(start + jinfo->code_size); + while (p < end) { + switch (*p) { + // FIXME: Is it possible for there to actually be any jiterp data + // for a given trace if we see a prepare point? I don't think so + case MINT_TIER_PREPARE_JITERPRETER: + case MINT_TIER_NOP_JITERPRETER: + case MINT_TIER_ENTER_JITERPRETER: + case MINT_TIER_MONITOR_JITERPRETER: { + JiterpreterOpcode *opcode = (JiterpreterOpcode *)p; + guint32 trace_index = opcode->trace_index; + mono_jiterp_free_method_data_js (method, imethod, trace_index); + break; + } + } + p = mono_interp_dis_mintop_len (p); + } +} + // Used to parse runtime options that control the jiterpreter. This is *also* used at runtime // by the jiterpreter typescript to reconfigure the jiterpreter, for example if WASM EH is not // actually available even though it was enabled (to turn it off). @@ -1491,6 +1534,104 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu #endif } +/* + * Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers) + * The queues are all tracked in a global list so that it is possible to perform a global + * 'purge item with this value from all queues' operation, which means queue operations + * are protected by a lock + */ + +#define NUM_QUEUES 2 +pthread_key_t queue_keys[NUM_QUEUES] = { 0 }; +pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT; +mono_mutex_t queue_mutex; +GPtrArray *shared_queues = NULL; + +static void +free_queue (void *ptr) { + mono_os_mutex_lock (&queue_mutex); + g_ptr_array_remove_fast (shared_queues, ptr); + g_ptr_array_free ((GPtrArray *)ptr, TRUE); + mono_os_mutex_unlock (&queue_mutex); +} + +static void +initialize_queue_keys () { + mono_os_mutex_lock (&queue_mutex); + shared_queues = g_ptr_array_new (); + mono_os_mutex_unlock (&queue_mutex); + + for (int i = 0; i < NUM_QUEUES; i++) + g_assert (pthread_key_create (&queue_keys[i], free_queue) == 0); +} + +static pthread_key_t +get_queue_key (int queue) { + g_assert ((queue >= 0) && (queue < NUM_QUEUES)); + pthread_once (&queue_keys_initialized, initialize_queue_keys); + return queue_keys[queue]; +} + +static GPtrArray * +get_queue (int queue) { + pthread_key_t key = get_queue_key (queue); + GPtrArray *result = NULL; + if ((result = (GPtrArray *)pthread_getspecific (key)) == NULL) { + pthread_setspecific (key, result = g_ptr_array_new ()); + mono_os_mutex_lock (&queue_mutex); + g_ptr_array_add (shared_queues, result); + mono_os_mutex_unlock (&queue_mutex); + } + return result; +} + +// Purges this item from all queues +void +mono_jiterp_tlqueue_purge_all (gpointer item) { + mono_os_mutex_lock (&queue_mutex); + for (int i = 0; i < shared_queues->len; i++) { + GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i); + gboolean ok = g_ptr_array_remove_fast (queue, item); + if (ok) { + // g_printf ("Purged %x from queue %x\n", (unsigned int)item, (unsigned int)queue); + } + } + mono_os_mutex_unlock (&queue_mutex); +} + +// Removes the next item from the queue, if any, and returns it (NULL if empty) +EMSCRIPTEN_KEEPALIVE gpointer +mono_jiterp_tlqueue_next (int queue) { + GPtrArray *items = get_queue (queue); + mono_os_mutex_lock (&queue_mutex); + if (items->len < 1) + return NULL; + gpointer result = g_ptr_array_index (items, 0); + g_ptr_array_remove_index_fast (items, 0); + mono_os_mutex_unlock (&queue_mutex); + return result; +} + +// Adds a new item to the end of the queue and returns the new size of the queue +EMSCRIPTEN_KEEPALIVE int +mono_jiterp_tlqueue_add (int queue, gpointer item) { + int result; + GPtrArray *items = get_queue (queue); + mono_os_mutex_lock (&queue_mutex); + g_ptr_array_add (items, item); + result = items->len; + mono_os_mutex_unlock (&queue_mutex); + return result; +} + +EMSCRIPTEN_KEEPALIVE void +mono_jiterp_tlqueue_clear (int queue) { + GPtrArray *items = get_queue (queue); + mono_os_mutex_lock (&queue_mutex); + g_ptr_array_set_size (items, 0); + mono_os_mutex_unlock (&queue_mutex); +} + // HACK: fix C4206 EMSCRIPTEN_KEEPALIVE #endif // HOST_BROWSER diff --git a/src/mono/mono/mini/interp/jiterpreter.h b/src/mono/mono/mini/interp/jiterpreter.h index 41001c53027dfe..643f2c05478289 100644 --- a/src/mono/mono/mini/interp/jiterpreter.h +++ b/src/mono/mono/mini/interp/jiterpreter.h @@ -172,6 +172,11 @@ mono_jiterp_do_jit_call_indirect ( #ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__ +extern void +mono_jiterp_free_method_data_js ( + MonoMethod *method, InterpMethod *imethod, int trace_index +); + typedef struct { InterpMethod *rmethod; ThreadContext *context; @@ -231,9 +236,15 @@ mono_jiterp_placeholder_trace (void *frame, void *pLocals, JiterpreterCallInfo * void mono_jiterp_placeholder_jit_call (void *ret_sp, void *sp, void *ftndesc, gboolean *thrown); +void +mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod); + void * mono_jiterp_get_interp_entry_func (int table); +void +mono_jiterp_tlqueue_purge_all (gpointer item); + #endif // __MONO_MINI_INTERPRETER_INTERNALS_H__ extern WasmDoJitCall jiterpreter_do_jit_call; diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index f905f452cc5060..f8c283eae52cf1 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -144,6 +144,9 @@ const fn_signatures: SigLine[] = [ [true, "mono_jiterp_get_interp_entry_func", "number", ["number"]], [true, "mono_jiterp_get_counter", "number", ["number"]], [true, "mono_jiterp_modify_counter", "number", ["number", "number"]], + [true, "mono_jiterp_tlqueue_next", "number", ["number"]], + [true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]], + [true, "mono_jiterp_tlqueue_clear", "void", ["number"]], ...diagnostics_cwraps, ...legacy_interop_cwraps @@ -283,6 +286,11 @@ export interface t_Cwraps { mono_jiterp_get_interp_entry_func(type: number): number; mono_jiterp_get_counter(counter: number): number; mono_jiterp_modify_counter(counter: number, delta: number): number; + // returns value or, if queue is empty, VoidPtrNull + mono_jiterp_tlqueue_next(queue: number): VoidPtr; + // returns new size of queue after add + mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number; + mono_jiterp_tlqueue_clear(queue: number): void; } const wrapped_c_functions: t_Cwraps = {}; diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index b5fcb08eddf404..4355e993127c3a 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -4,6 +4,8 @@ "use strict"; +// FIXME: MERGE DAMAGE + // -- this javascript file is evaluated by emcc during compilation! -- // because we can't pass custom define symbols to acorn optimizer, we use environment variables to pass other build options diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts index dd4ed9ec2233be..e4210f4a2ada2d 100644 --- a/src/mono/wasm/runtime/exports-linker.ts +++ b/src/mono/wasm/runtime/exports-linker.ts @@ -4,6 +4,8 @@ import { mono_wasm_imports, mono_wasm_legacy_interop_imports, mono_wasm_threads_imports } from "./exports-binding"; import gitHash from "consts:gitHash"; +// FIXME: MERGE DAMAGE + export function export_linker_indexes_as_code(): string { const indexByName: any = { mono_wasm_imports: {}, @@ -30,5 +32,5 @@ export function export_linker_indexes_as_code(): string { `; } -// this is running during runtime compile time inside rollup process. -(globalThis as any).export_linker_indexes_as_code = export_linker_indexes_as_code; \ No newline at end of file +// this is running during runtime compile time inside rollup process. +(globalThis as any).export_linker_indexes_as_code = export_linker_indexes_as_code; diff --git a/src/mono/wasm/runtime/jiterpreter-enums.ts b/src/mono/wasm/runtime/jiterpreter-enums.ts index ee219f635cfdbd..1c98615cc57d27 100644 --- a/src/mono/wasm/runtime/jiterpreter-enums.ts +++ b/src/mono/wasm/runtime/jiterpreter-enums.ts @@ -157,3 +157,8 @@ export const BailoutReasonNames = [ "UnexpectedRetIp", "LeaveCheck", ]; + +export const enum JitQueue { + JitCall = 0, + InterpEntry = 1 +} diff --git a/src/mono/wasm/runtime/jiterpreter-interp-entry.ts b/src/mono/wasm/runtime/jiterpreter-interp-entry.ts index 29ea23aa65eb8f..cedf54cdde9306 100644 --- a/src/mono/wasm/runtime/jiterpreter-interp-entry.ts +++ b/src/mono/wasm/runtime/jiterpreter-interp-entry.ts @@ -14,13 +14,13 @@ import { _now, getRawCwrap, importDef, getWasmFunctionTable, recordFailure, getOptions, JiterpreterOptions, getMemberOffset, - getCounter, modifyCounter, + getCounter, modifyCounter } from "./jiterpreter-support"; import { WasmValtype } from "./jiterpreter-opcodes"; import { mono_log_error, mono_log_info } from "./logging"; import { utf8ToString } from "./strings"; import { - JiterpreterTable, JiterpCounter, JiterpMember + JiterpreterTable, JiterpCounter, JiterpMember, JitQueue } from "./jiterpreter-enums"; // Controls miscellaneous diagnostic output. @@ -59,7 +59,6 @@ let trampBuilder: WasmBuilder; let trampImports: Array<[string, string, Function]> | undefined; let fnTable: WebAssembly.Table; let jitQueueTimeout = 0; -const jitQueue: TrampolineInfo[] = []; const infoTable: { [ptr: number]: TrampolineInfo } = {}; /* @@ -135,6 +134,13 @@ class TrampolineInfo { let mostRecentOptions: JiterpreterOptions | undefined = undefined; +// If a method is freed we need to remove its info (just in case another one gets +// allocated at that exact memory offset later) and more importantly, ensure it is +// not waiting in the jit queue +export function mono_jiterp_free_method_data_interp_entry(imethod: number) { + delete infoTable[imethod]; +} + // FIXME: move this counter into C and make it thread safe export function mono_interp_record_interp_entry(imethod: number) { // clear the unbox bit @@ -154,8 +160,8 @@ export function mono_interp_record_interp_entry(imethod: number) { else if (info.hitCount !== mostRecentOptions!.interpEntryHitCount) return; - jitQueue.push(info); - if (jitQueue.length >= maxJitQueueLength) + const jitQueueLength = cwraps.mono_jiterp_tlqueue_add(JitQueue.InterpEntry, imethod); + if (jitQueueLength >= maxJitQueueLength) flush_wasm_entry_trampoline_jit_queue(); else ensure_jit_is_scheduled(); @@ -222,7 +228,18 @@ function ensure_jit_is_scheduled() { } function flush_wasm_entry_trampoline_jit_queue() { - if (jitQueue.length <= 0) + const jitQueue : TrampolineInfo[] = []; + let methodPtr = 0; + while ((methodPtr = cwraps.mono_jiterp_tlqueue_next(JitQueue.InterpEntry)) != 0) { + const info = infoTable[methodPtr]; + if (!info) { + mono_log_info(`Failed to find corresponding info for method ptr ${methodPtr} from jit queue!`); + continue; + } + jitQueue.push(info); + } + + if (!jitQueue.length) return; // If the function signature contains types that need stackval_from_data, that'll use @@ -268,7 +285,6 @@ function flush_wasm_entry_trampoline_jit_queue() { builder.clear(constantSlots); if (builder.options.wasmBytesLimit <= getCounter(JiterpCounter.BytesGenerated)) { - jitQueue.length = 0; return; } @@ -428,8 +444,6 @@ function flush_wasm_entry_trampoline_jit_queue() { } else if (rejected && !threw) { mono_log_error("failed to generate trampoline for unknown reason"); } - - jitQueue.length = 0; } } diff --git a/src/mono/wasm/runtime/jiterpreter-jit-call.ts b/src/mono/wasm/runtime/jiterpreter-jit-call.ts index 2f24301b590a7d..1af011764e7d8e 100644 --- a/src/mono/wasm/runtime/jiterpreter-jit-call.ts +++ b/src/mono/wasm/runtime/jiterpreter-jit-call.ts @@ -16,7 +16,7 @@ import { getCounter, modifyCounter, jiterpreter_allocate_tables } from "./jiterpreter-support"; -import { JiterpreterTable, JiterpCounter } from "./jiterpreter-enums"; +import { JiterpreterTable, JiterpCounter, JitQueue } from "./jiterpreter-enums"; import { compileDoJitCall } from "./jiterpreter-feature-detect"; @@ -69,7 +69,7 @@ let wasmEhSupported: boolean | undefined = undefined; let nextDisambiguateIndex = 0; const fnCache: Array = []; const targetCache: { [target: number]: TrampolineInfo } = {}; -const jitQueue: TrampolineInfo[] = []; +const infosByMethod: { [method: number]: TrampolineInfo[] } = {}; class TrampolineInfo { method: MonoMethod; @@ -195,6 +195,21 @@ export function mono_interp_invoke_wasm_jit_call_trampoline( } } +// If a method is freed we need to remove its info (just in case another one gets +// allocated at that exact memory offset later) and more importantly, ensure it is +// not waiting in the jit queue +export function mono_jiterp_free_method_data_jit_call(method: MonoMethod) { + // FIXME + const infoArray = infosByMethod[method]; + if (!infoArray) + return; + + for (let i = 0; i < infoArray.length; i++) + delete targetCache[infoArray[i].addr]; + + delete infosByMethod[method]; +} + export function mono_interp_jit_wasm_jit_call_trampoline( method: MonoMethod, rmethod: VoidPtr, cinfo: VoidPtr, arg_offsets: VoidPtr, catch_exceptions: number @@ -227,12 +242,17 @@ export function mono_interp_jit_wasm_jit_call_trampoline( arg_offsets, catch_exceptions !== 0 ); targetCache[cacheKey] = info; - jitQueue.push(info); + const jitQueueLength = cwraps.mono_jiterp_tlqueue_add(JitQueue.JitCall, method); + + let ibm = infosByMethod[method]; + if (!ibm) + ibm = infosByMethod[method] = []; + ibm.push(info); // we don't want the queue to get too long, both because jitting too many trampolines // at once can hit the 4kb limit and because it makes it more likely that we will // fail to jit them early enough - if (jitQueue.length >= maxJitQueueLength) + if (jitQueueLength >= maxJitQueueLength) mono_interp_flush_jitcall_queue(); } @@ -328,7 +348,21 @@ export function mono_jiterp_do_jit_call_indirect( } export function mono_interp_flush_jitcall_queue(): void { - if (jitQueue.length === 0) + const jitQueue : TrampolineInfo[] = []; + let methodPtr = 0; + while ((methodPtr = cwraps.mono_jiterp_tlqueue_next(JitQueue.JitCall)) != 0) { + const infos = infosByMethod[methodPtr]; + if (!infos) { + mono_log_info(`Failed to find corresponding info list for method ptr ${methodPtr} from jit queue!`); + continue; + } + + for (let i = 0; i < infos.length; i++) + if (infos[i].result === 0) + jitQueue.push(infos[i]); + } + + if (!jitQueue.length) return; let builder = trampBuilder; @@ -348,7 +382,7 @@ export function mono_interp_flush_jitcall_queue(): void { builder.clear(0); if (builder.options.wasmBytesLimit <= getCounter(JiterpCounter.BytesGenerated)) { - jitQueue.length = 0; + cwraps.mono_jiterp_tlqueue_clear(JitQueue.JitCall); return; } @@ -377,7 +411,6 @@ export function mono_interp_flush_jitcall_queue(): void { for (let i = 0; i < jitQueue.length; i++) { const info = jitQueue[i]; - const sig: any = {}; if (info.enableDirect) { @@ -552,8 +585,6 @@ export function mono_interp_flush_jitcall_queue(): void { } else if (rejected && !threw) { mono_log_error("failed to generate trampoline for unknown reason"); } - - jitQueue.length = 0; } } diff --git a/src/mono/wasm/runtime/jiterpreter-support.ts b/src/mono/wasm/runtime/jiterpreter-support.ts index 6115f6558858b3..fb62d3f8bdc2bf 100644 --- a/src/mono/wasm/runtime/jiterpreter-support.ts +++ b/src/mono/wasm/runtime/jiterpreter-support.ts @@ -95,6 +95,7 @@ export class WasmBuilder { argumentCount!: number; activeBlocks!: number; base!: MintOpcodePtr; + traceIndex!: number; frame: NativePointer = 0; traceBuf: Array = []; branchTargets = new Set(); @@ -1462,7 +1463,7 @@ export function append_safepoint(builder: WasmBuilder, ip: MintOpcodePtr) { export function append_bailout(builder: WasmBuilder, ip: MintOpcodePtr, reason: BailoutReason) { builder.ip_const(ip); if (builder.options.countBailouts) { - builder.i32_const(builder.base); + builder.i32_const(builder.traceIndex); builder.i32_const(reason); builder.callImport("bailout"); } @@ -1487,7 +1488,7 @@ export function append_exit(builder: WasmBuilder, ip: MintOpcodePtr, opcodeCount builder.ip_const(ip); if (builder.options.countBailouts) { - builder.i32_const(builder.base); + builder.i32_const(builder.traceIndex); builder.i32_const(reason); builder.callImport("bailout"); } diff --git a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts index e2c9acf2948b23..2c7bb17ef9e82c 100644 --- a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts @@ -142,8 +142,6 @@ export function generateWasmBody( let result = 0, prologueOpcodeCounter = 0, conditionalOpcodeCounter = 0; - const traceIp = ip; - eraseInferredState(); // Skip over the enter opcode @@ -161,7 +159,7 @@ export function generateWasmBody( builder.cfg.ip = ip; if (ip >= endOfBody) { - record_abort(traceIp, ip, traceName, "end-of-body"); + record_abort(builder.traceIndex, ip, traceName, "end-of-body"); if (instrumentedTraceId) mono_log_info(`instrumented trace ${traceName} exited at end of body @${(ip).toString(16)}`); break; @@ -174,7 +172,7 @@ export function generateWasmBody( spaceLeft = maxBytesGenerated - builder.bytesGeneratedSoFar - builder.cfg.overheadBytes; if (builder.size >= spaceLeft) { // mono_log_info(`trace too big, estimated size is ${builder.size + builder.bytesGeneratedSoFar}`); - record_abort(traceIp, ip, traceName, "trace-too-big"); + record_abort(builder.traceIndex, ip, traceName, "trace-too-big"); if (instrumentedTraceId) mono_log_info(`instrumented trace ${traceName} exited because of size limit at @${(ip).toString(16)} (spaceLeft=${spaceLeft}b)`); break; @@ -795,7 +793,7 @@ export function generateWasmBody( bailoutOnFailure = (opcode === MintOpcode.MINT_CASTCLASS_INTERFACE), destOffset = getArgU16(ip, 1); if (!klass) { - record_abort(traceIp, ip, traceName, "null-klass"); + record_abort(builder.traceIndex, ip, traceName, "null-klass"); ip = abort; continue; } @@ -880,7 +878,7 @@ export function generateWasmBody( (opcode === MintOpcode.MINT_CASTCLASS_COMMON), destOffset = getArgU16(ip, 1); if (!klass) { - record_abort(traceIp, ip, traceName, "null-klass"); + record_abort(builder.traceIndex, ip, traceName, "null-klass"); ip = abort; continue; } @@ -1014,7 +1012,7 @@ export function generateWasmBody( elementClass = getU32_unaligned(klass + elementClassOffset); if (!klass || !elementClass) { - record_abort(traceIp, ip, traceName, "null-klass"); + record_abort(builder.traceIndex, ip, traceName, "null-klass"); ip = abort; continue; } @@ -1603,7 +1601,7 @@ export function generateWasmBody( } else { if (instrumentedTraceId) mono_log_info(`instrumented trace ${traceName} aborted for opcode ${opname} @${(_ip).toString(16)}`); - record_abort(traceIp, _ip, traceName, opcode); + record_abort(builder.traceIndex, _ip, traceName, opcode); } } @@ -1813,7 +1811,7 @@ function append_ldloc_cknull(builder: WasmBuilder, localOffset: number, ip: Mint if (nullCheckValidation) { builder.local("cknull_ptr"); append_ldloc(builder, localOffset, WasmOpcode.i32_load); - builder.i32_const(builder.base); + builder.i32_const(builder.traceIndex); builder.i32_const(ip); builder.callImport("notnull"); } @@ -2103,7 +2101,7 @@ function emit_fieldop( // cknull_ptr was not used here so all we can do is verify that the target object is not null append_ldloc(builder, objectOffset, WasmOpcode.i32_load); append_ldloc(builder, objectOffset, WasmOpcode.i32_load); - builder.i32_const(builder.base); + builder.i32_const(builder.traceIndex); builder.i32_const(ip); builder.callImport("notnull"); } diff --git a/src/mono/wasm/runtime/jiterpreter.ts b/src/mono/wasm/runtime/jiterpreter.ts index 3ec26451eb3e26..f47149dda1eb15 100644 --- a/src/mono/wasm/runtime/jiterpreter.ts +++ b/src/mono/wasm/runtime/jiterpreter.ts @@ -22,6 +22,8 @@ import { import { generateWasmBody } from "./jiterpreter-trace-generator"; +import { mono_jiterp_free_method_data_interp_entry } from "./jiterpreter-interp-entry"; +import { mono_jiterp_free_method_data_jit_call } from "./jiterpreter-jit-call"; import { mono_log_error, mono_log_info, mono_log_warn } from "./logging"; import { utf8ToString } from "./strings"; @@ -232,15 +234,15 @@ const mathOps1d = "powf", ]; -function recordBailout(ip: number, base: MintOpcodePtr, reason: BailoutReason) { +function recordBailout(ip: number, traceIndex: number, reason: BailoutReason) { cwraps.mono_jiterp_trace_bailout(reason); // Counting these is not meaningful and messes up the end of run statistics if (reason === BailoutReason.Return) return ip; - const info = traceInfo[base]; + const info = traceInfo[traceIndex]; if (!info) { - mono_log_error(`trace info not found for ${base}`); + mono_log_error(`trace info not found for ${traceIndex}`); return; } let table = info.bailoutCounts; @@ -702,11 +704,11 @@ function initialize_builder(builder: WasmBuilder) { } function assert_not_null( - value: number, expectedValue: number, traceIp: MintOpcodePtr, ip: MintOpcodePtr + value: number, expectedValue: number, traceIndex: number, ip: MintOpcodePtr ) { if (value && (value === expectedValue)) return; - const info = traceInfo[traceIp]; + const info = traceInfo[traceIndex]; throw new Error(`expected non-null value ${expectedValue} but found ${value} in trace ${info.name} @ 0x${(ip).toString(16)}`); } @@ -714,8 +716,8 @@ function assert_not_null( function generate_wasm( frame: NativePointer, methodName: string, ip: MintOpcodePtr, startOfBody: MintOpcodePtr, sizeOfBody: MintOpcodePtr, - methodFullName: string | undefined, backwardBranchTable: Uint16Array | null, - presetFunctionPointer: number + traceIndex: number, methodFullName: string | undefined, + backwardBranchTable: Uint16Array | null, presetFunctionPointer: number ): number { // Pre-allocate a decent number of constant slots - this adds fixed size bloat // to the trace but will make the actual pointer constants in the trace smaller @@ -750,7 +752,7 @@ function generate_wasm( let compileStarted = 0; let rejected = true, threw = false; - const ti = traceInfo[ip]; + const ti = traceInfo[traceIndex]; const instrument = ti.isVerbose || (methodFullName && ( instrumentedMethodNames.findIndex( (filter) => methodFullName.indexOf(filter) >= 0 @@ -809,6 +811,7 @@ function generate_wasm( } builder.base = ip; + builder.traceIndex = traceIndex; builder.frame = frame; switch (getU16(ip)) { case MintOpcode.MINT_TIER_PREPARE_JITERPRETER: @@ -971,7 +974,7 @@ export function trace_operands(a: number, b: number) { mostRecentTrace.operand2 = b >>> 0; } -export function record_abort(traceIp: MintOpcodePtr, ip: MintOpcodePtr, traceName: string, reason: string | MintOpcode) { +export function record_abort(traceIndex: number, ip: MintOpcodePtr, traceName: string, reason: string | MintOpcode) { if (typeof (reason) === "number") { cwraps.mono_jiterp_adjust_abort_count(reason, 1); reason = getOpcodeName(reason); @@ -986,9 +989,9 @@ export function record_abort(traceIp: MintOpcodePtr, ip: MintOpcodePtr, traceNam } if ((traceAbortLocations && (reason !== "end-of-body")) || (trace >= 2)) - mono_log_info(`abort ${traceIp} ${traceName}@${ip} ${reason}`); + mono_log_info(`abort #${traceIndex} ${traceName}@${ip} ${reason}`); - traceInfo[traceIp].abortReason = reason; + traceInfo[traceIndex].abortReason = reason; } const JITERPRETER_TRAINING = 0; @@ -1009,10 +1012,10 @@ export function mono_interp_tier_prepare_jiterpreter( else if (mostRecentOptions.wasmBytesLimit <= getCounter(JiterpCounter.BytesGenerated)) return JITERPRETER_NOT_JITTED; - let info = traceInfo[ip]; + let info = traceInfo[index]; if (!info) - traceInfo[ip] = info = new TraceInfo(ip, index, isVerbose); + traceInfo[index] = info = new TraceInfo(ip, index, isVerbose); modifyCounter(JiterpCounter.TraceCandidates, 1); let methodFullName: string | undefined; @@ -1055,8 +1058,8 @@ export function mono_interp_tier_prepare_jiterpreter( const fnPtr = generate_wasm( frame, methodName, ip, startOfBody, - sizeOfBody, methodFullName, backwardBranchTable, - presetFunctionPointer + sizeOfBody, index, methodFullName, + backwardBranchTable, presetFunctionPointer ); if (fnPtr) { @@ -1070,6 +1073,20 @@ export function mono_interp_tier_prepare_jiterpreter( } } +// NOTE: This will potentially be called once for every trace entry point +// in a given method, not just once per method +export function mono_jiterp_free_method_data_js( + method: MonoMethod, imethod: number, traceIndex: number +) { + // TODO: Uninstall the trace function pointer from the function pointer table, + // so that the compiled trace module can be freed by the browser eventually + // Release the trace info object, if present + delete traceInfo[traceIndex]; + // Remove any AOT data and queue entries associated with the method + mono_jiterp_free_method_data_interp_entry(imethod); + mono_jiterp_free_method_data_jit_call(method); +} + export function jiterpreter_dump_stats(b?: boolean, concise?: boolean) { if (!runtimeHelpers.runtimeReady) { return; From 7c4cb626ec95457a97e71936746fbd0376d2ede7 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 14 Aug 2023 19:02:22 -0700 Subject: [PATCH 2/8] Repair merge damage --- src/mono/wasm/runtime/es6/dotnet.es6.lib.js | 2 -- src/mono/wasm/runtime/exports-binding.ts | 5 +++-- src/mono/wasm/runtime/exports-linker.ts | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index 4355e993127c3a..b5fcb08eddf404 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -4,8 +4,6 @@ "use strict"; -// FIXME: MERGE DAMAGE - // -- this javascript file is evaluated by emcc during compilation! -- // because we can't pass custom define symbols to acorn optimizer, we use environment variables to pass other build options diff --git a/src/mono/wasm/runtime/exports-binding.ts b/src/mono/wasm/runtime/exports-binding.ts index 834c9b4588b8de..e3143c04f973be 100644 --- a/src/mono/wasm/runtime/exports-binding.ts +++ b/src/mono/wasm/runtime/exports-binding.ts @@ -8,7 +8,7 @@ import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_s import { mono_wasm_release_cs_owned_object } from "./gc-handles"; import { mono_wasm_bind_cs_function } from "./invoke-cs"; import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js"; -import { mono_interp_tier_prepare_jiterpreter } from "./jiterpreter"; +import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter"; import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry"; import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call"; import { mono_wasm_marshal_promise } from "./marshal-to-js"; @@ -89,6 +89,7 @@ export const mono_wasm_imports = [ mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect, + mono_jiterp_free_method_data_js, mono_wasm_profiler_enter, mono_wasm_profiler_leave, @@ -163,4 +164,4 @@ export function replace_linker_placeholders(imports: WebAssembly.Imports) { } } -} \ No newline at end of file +} diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts index e4210f4a2ada2d..ef2d562111a706 100644 --- a/src/mono/wasm/runtime/exports-linker.ts +++ b/src/mono/wasm/runtime/exports-linker.ts @@ -4,8 +4,6 @@ import { mono_wasm_imports, mono_wasm_legacy_interop_imports, mono_wasm_threads_imports } from "./exports-binding"; import gitHash from "consts:gitHash"; -// FIXME: MERGE DAMAGE - export function export_linker_indexes_as_code(): string { const indexByName: any = { mono_wasm_imports: {}, From 39459602b7d7978adf71172ae6d59497acb07cc6 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 14 Aug 2023 19:41:38 -0700 Subject: [PATCH 3/8] Cleanup free_method_data --- src/mono/mono/mini/interp/jiterpreter.c | 52 +++++++++++++------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index cbefde9e3d5231..79b4a7b4119456 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -937,6 +937,7 @@ mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod) { MonoJitInfo *jinfo = imethod->jinfo; const guint8 *start; + gboolean need_extra_free = TRUE; // Erase the method and interpmethod from all of the thread local JIT queues mono_jiterp_tlqueue_purge_all (method); @@ -944,33 +945,36 @@ mono_jiterp_free_method_data (MonoMethod *method, InterpMethod *imethod) // FIXME: Enumerate all active threads and ensure we perform the free_method_data_js // call on every thread. - // TODO: Migrate the interp_entry and jit_call JIT queues into the native heap, - // so that this method can synchronously remove entries from them. - if (!jinfo) { - // HACK: Perform a single free operation to clear out any stuff from the jit queues - mono_jiterp_free_method_data_js (method, imethod, 0); - return; - } - - start = (const guint8*) jinfo->code_start; - const guint16 *p = (const guint16 *)start, - *end = (const guint16 *)(start + jinfo->code_size); - while (p < end) { - switch (*p) { - // FIXME: Is it possible for there to actually be any jiterp data - // for a given trace if we see a prepare point? I don't think so - case MINT_TIER_PREPARE_JITERPRETER: - case MINT_TIER_NOP_JITERPRETER: - case MINT_TIER_ENTER_JITERPRETER: - case MINT_TIER_MONITOR_JITERPRETER: { - JiterpreterOpcode *opcode = (JiterpreterOpcode *)p; - guint32 trace_index = opcode->trace_index; - mono_jiterp_free_method_data_js (method, imethod, trace_index); - break; + // Scan through the interp opcodes for the method and ensure that any jiterp traces + // owned by it are cleaned up. This will automatically clean up any AOT related data for + // the method in the process + if (jinfo) { + start = (const guint8*) jinfo->code_start; + const guint16 *p = (const guint16 *)start, + *end = (const guint16 *)(start + jinfo->code_size); + + while (p < end) { + switch (*p) { + case MINT_TIER_PREPARE_JITERPRETER: + case MINT_TIER_NOP_JITERPRETER: + case MINT_TIER_ENTER_JITERPRETER: + case MINT_TIER_MONITOR_JITERPRETER: { + JiterpreterOpcode *opcode = (JiterpreterOpcode *)p; + guint32 trace_index = opcode->trace_index; + need_extra_free = FALSE; + mono_jiterp_free_method_data_js (method, imethod, trace_index); + break; + } } + p = mono_interp_dis_mintop_len (p); } - p = mono_interp_dis_mintop_len (p); + } + + if (need_extra_free) { + // HACK: Perform a single free operation to clear out any stuff from the jit queues + // This will happen if we didn't encounter any jiterpreter traces in the method + mono_jiterp_free_method_data_js (method, imethod, 0); } } From 703bb09c39107cfa6bc920dec7ecc096f7fd3770 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 14 Aug 2023 19:44:39 -0700 Subject: [PATCH 4/8] Fix lock held open --- src/mono/mono/mini/interp/jiterpreter.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index 79b4a7b4119456..d8b67976d87332 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -1608,10 +1608,11 @@ EMSCRIPTEN_KEEPALIVE gpointer mono_jiterp_tlqueue_next (int queue) { GPtrArray *items = get_queue (queue); mono_os_mutex_lock (&queue_mutex); - if (items->len < 1) - return NULL; - gpointer result = g_ptr_array_index (items, 0); - g_ptr_array_remove_index_fast (items, 0); + gpointer result = NULL; + if (items->len) { + result = g_ptr_array_index (items, 0); + g_ptr_array_remove_index_fast (items, 0); + } mono_os_mutex_unlock (&queue_mutex); return result; } From c32df64d9f6b3fbd65e559b0f614f5aa602df688 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 14 Aug 2023 19:50:30 -0700 Subject: [PATCH 5/8] Repair merge damage --- src/mono/wasm/runtime/exports-linker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/runtime/exports-linker.ts b/src/mono/wasm/runtime/exports-linker.ts index ef2d562111a706..dd4ed9ec2233be 100644 --- a/src/mono/wasm/runtime/exports-linker.ts +++ b/src/mono/wasm/runtime/exports-linker.ts @@ -30,5 +30,5 @@ export function export_linker_indexes_as_code(): string { `; } -// this is running during runtime compile time inside rollup process. -(globalThis as any).export_linker_indexes_as_code = export_linker_indexes_as_code; +// this is running during runtime compile time inside rollup process. +(globalThis as any).export_linker_indexes_as_code = export_linker_indexes_as_code; \ No newline at end of file From d5579e641b89d534124988cfa2c272be2fbde0db Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 14 Aug 2023 19:55:25 -0700 Subject: [PATCH 6/8] Fix race in trace_info_alloc --- src/mono/mono/mini/interp/jiterpreter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index d8b67976d87332..537fe15c41e622 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -744,7 +744,11 @@ trace_info_get (gint32 index) { static gint32 trace_info_alloc () { +#ifdef DISABLE_THREADS gint32 index = trace_count++, +#else + gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1), +#endif limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE); // Make sure we're not out of space in the trace info table. if (index == limit) From 798aab0df8a92661ccfa4746d7ffe63a73b51adc Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 14 Aug 2023 20:07:42 -0700 Subject: [PATCH 7/8] Annotate races for later cleanup --- src/mono/mono/mini/interp/jiterpreter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index 537fe15c41e622..570bfc9420cbf7 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -479,6 +479,7 @@ mono_jiterp_type_get_raw_value_size (MonoType *type) { EMSCRIPTEN_KEEPALIVE void mono_jiterp_trace_bailout (int reason) { + // FIXME: Harmless race condition if threads are in use if (reason < 256) jiterp_trace_bailout_counts[reason]++; } @@ -671,6 +672,7 @@ should_generate_trace_here (InterpBasicBlock *bb) { for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { int value = jiterp_get_opcode_value(ins, &inside_branch_block); if (value < 0) { + // FIXME: Harmless race condition if threads are in use jiterpreter_abort_counts[ins->opcode]++; return current_trace_value >= mono_opt_jiterpreter_minimum_trace_value; } else if (value >= VALUE_SIMD) { @@ -1390,6 +1392,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals) if (mono_opt_jiterpreter_trace_monitoring_log > 1) g_print ("trace #%d @%d '%s' accepted; average_penalty %f <= %f\n", opcode->trace_index, ip, frame->imethod->method->name, average_penalty, threshold); } else { + // FIXME: Harmless race condition if threads are in use traces_rejected++; if (mono_opt_jiterpreter_trace_monitoring_log > 0) { char * full_name = mono_method_get_full_name (frame->imethod->method); From 2464dd3e4579bfb230a829a7d24da564e972a637 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 15 Aug 2023 18:59:30 -0700 Subject: [PATCH 8/8] Address PR feedback --- src/mono/mono/mini/interp/jiterpreter.c | 54 ++++++++++++++++++++----- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index 570bfc9420cbf7..0dd5ea27f9af7a 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -27,7 +27,10 @@ void jiterp_preserve_module (void); #include #include #include + +#ifndef DISABLE_THREADS #include +#endif #include #include @@ -49,6 +52,7 @@ void jiterp_preserve_module (void); #include #include #include +#include #include "jiterpreter.h" @@ -1549,18 +1553,27 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu * Unordered thread-local pointer queue (used for do_jit_call and interp_entry wrappers) * The queues are all tracked in a global list so that it is possible to perform a global * 'purge item with this value from all queues' operation, which means queue operations - * are protected by a lock + * are protected by a mutex */ #define NUM_QUEUES 2 -pthread_key_t queue_keys[NUM_QUEUES] = { 0 }; +static MonoNativeTlsKey queue_keys[NUM_QUEUES] = { 0 }; +#ifdef DISABLE_THREADS +gboolean queue_keys_initialized = FALSE; +#else pthread_once_t queue_keys_initialized = PTHREAD_ONCE_INIT; -mono_mutex_t queue_mutex; -GPtrArray *shared_queues = NULL; +#endif +// NOTE: We're using OS mutexes here not coop mutexes, because we need to be able to run +// during a GC and at any point (before runtime startup or after shutdown) +// This means if we aren't careful we could deadlock, so we have to be cautious about +// what operations we perform while holding this mutex. +static mono_mutex_t queue_mutex; +static GPtrArray *shared_queues = NULL; static void free_queue (void *ptr) { mono_os_mutex_lock (&queue_mutex); + // WARNING: Ensure we do not call into the runtime or JS while holding this mutex! g_ptr_array_remove_fast (shared_queues, ptr); g_ptr_array_free ((GPtrArray *)ptr, TRUE); mono_os_mutex_unlock (&queue_mutex); @@ -1569,27 +1582,41 @@ free_queue (void *ptr) { static void initialize_queue_keys () { mono_os_mutex_lock (&queue_mutex); + // WARNING: Ensure we do not call into the runtime or JS while holding this mutex! shared_queues = g_ptr_array_new (); - mono_os_mutex_unlock (&queue_mutex); for (int i = 0; i < NUM_QUEUES; i++) - g_assert (pthread_key_create (&queue_keys[i], free_queue) == 0); + g_assert (mono_native_tls_alloc (&queue_keys[i], free_queue)); + +#ifdef DISABLE_THREADS + queue_keys_initialized = TRUE; +#endif + + mono_os_mutex_unlock (&queue_mutex); } -static pthread_key_t +static MonoNativeTlsKey get_queue_key (int queue) { g_assert ((queue >= 0) && (queue < NUM_QUEUES)); + +#ifdef DISABLE_THREADS + if (!queue_keys_initialized) + initialize_queue_keys (); +#else pthread_once (&queue_keys_initialized, initialize_queue_keys); +#endif + return queue_keys[queue]; } static GPtrArray * get_queue (int queue) { - pthread_key_t key = get_queue_key (queue); + MonoNativeTlsKey key = get_queue_key (queue); GPtrArray *result = NULL; - if ((result = (GPtrArray *)pthread_getspecific (key)) == NULL) { - pthread_setspecific (key, result = g_ptr_array_new ()); + if ((result = (GPtrArray *)mono_native_tls_get_value (key)) == NULL) { + g_assert (mono_native_tls_set_value (key, result = g_ptr_array_new ())); mono_os_mutex_lock (&queue_mutex); + // WARNING: Ensure we do not call into the runtime or JS while holding this mutex! g_ptr_array_add (shared_queues, result); mono_os_mutex_unlock (&queue_mutex); } @@ -1600,6 +1627,7 @@ get_queue (int queue) { void mono_jiterp_tlqueue_purge_all (gpointer item) { mono_os_mutex_lock (&queue_mutex); + // WARNING: Ensure we do not call into the runtime or JS while holding this mutex! for (int i = 0; i < shared_queues->len; i++) { GPtrArray *queue = (GPtrArray *)g_ptr_array_index (shared_queues, i); gboolean ok = g_ptr_array_remove_fast (queue, item); @@ -1613,8 +1641,12 @@ mono_jiterp_tlqueue_purge_all (gpointer item) { // Removes the next item from the queue, if any, and returns it (NULL if empty) EMSCRIPTEN_KEEPALIVE gpointer mono_jiterp_tlqueue_next (int queue) { + // This lock-per-call is unpleasant, but the queue is usually only going to contain + // a handful of items and will only be enumerated a few hundred times total during + // execution, so performance is not especially important compared to safety/simplicity GPtrArray *items = get_queue (queue); mono_os_mutex_lock (&queue_mutex); + // WARNING: Ensure we do not call into the runtime or JS while holding this mutex! gpointer result = NULL; if (items->len) { result = g_ptr_array_index (items, 0); @@ -1630,6 +1662,7 @@ mono_jiterp_tlqueue_add (int queue, gpointer item) { int result; GPtrArray *items = get_queue (queue); mono_os_mutex_lock (&queue_mutex); + // WARNING: Ensure we do not call into the runtime or JS while holding this mutex! g_ptr_array_add (items, item); result = items->len; mono_os_mutex_unlock (&queue_mutex); @@ -1640,6 +1673,7 @@ EMSCRIPTEN_KEEPALIVE void mono_jiterp_tlqueue_clear (int queue) { GPtrArray *items = get_queue (queue); mono_os_mutex_lock (&queue_mutex); + // WARNING: Ensure we do not call into the runtime or JS while holding this mutex! g_ptr_array_set_size (items, 0); mono_os_mutex_unlock (&queue_mutex); }