diff --git a/src/xenia/cpu/entry_table.cc b/src/xenia/cpu/entry_table.cc index 4b9181be7..28426164a 100644 --- a/src/xenia/cpu/entry_table.cc +++ b/src/xenia/cpu/entry_table.cc @@ -83,6 +83,19 @@ Entry::Status EntryTable::GetOrCreate(uint32_t address, Entry** out_entry) { return status; } +void EntryTable::MarkReady(Entry* entry, Function* function, + uint32_t end_address) { + auto global_lock = global_critical_region_.Acquire(); + entry->function = function; + entry->end_address = end_address; + entry->status = Entry::STATUS_READY; +} + +void EntryTable::MarkFailed(Entry* entry) { + auto global_lock = global_critical_region_.Acquire(); + entry->status = Entry::STATUS_FAILED; +} + void EntryTable::Delete(uint32_t address) { auto global_lock = global_critical_region_.Acquire(); // doesnt this leak memory by not deleting the entry? diff --git a/src/xenia/cpu/entry_table.h b/src/xenia/cpu/entry_table.h index 19830868c..bbb789ee2 100644 --- a/src/xenia/cpu/entry_table.h +++ b/src/xenia/cpu/entry_table.h @@ -41,6 +41,16 @@ class EntryTable { Entry* Get(uint32_t address); Entry::Status GetOrCreate(uint32_t address, Entry** out_entry); + // Publishes the result of compiling `entry` (obtained via GetOrCreate + // returning STATUS_NEW) under the same lock GetOrCreate's spin-wait uses to + // read entry->status. Callers must go through these instead of writing + // entry->status/function/end_address directly -- unsynchronized writes here + // raced against the lock-protected reads in GetOrCreate's spin-wait, so a + // waiting thread on a weak memory model (e.g. Apple Silicon) could observe + // STATUS_READY before entry->function was actually visible, returning a + // stale/torn function pointer. + void MarkReady(Entry* entry, Function* function, uint32_t end_address); + void MarkFailed(Entry* entry); void Delete(uint32_t address); std::vector FindWithAddress(uint32_t address); diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index e7965c3fe..23c406970 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -263,12 +263,12 @@ Function* Processor::ResolveFunction(uint32_t address) { auto function = LookupFunction(address); if (!function) { - entry->status = Entry::STATUS_FAILED; + entry_table_.MarkFailed(entry); return nullptr; } if (!DemandFunction(function)) { - entry->status = Entry::STATUS_FAILED; + entry_table_.MarkFailed(entry); return nullptr; } // only add it to the list of resolved functions if resolving succeeded @@ -282,9 +282,8 @@ Function* Processor::ResolveFunction(uint32_t address) { } } - entry->function = function; - entry->end_address = function->end_address(); - status = entry->status = Entry::STATUS_READY; + entry_table_.MarkReady(entry, function, function->end_address()); + status = Entry::STATUS_READY; } if (status == Entry::STATUS_READY) { // Ready to use.