This changes almost all locks held by guest threads to use a single global critical region. This emulates the behavior on the PPC of disabling interrupts (by calls like KeRaiseIrqlToDpcLevel or masking interrupts), and prevents deadlocks from occuring when threads are suspended or otherwise blocked. This has performance implications and a pass is needed to ensure the locking is as granular as possible. It could also break everything because it's fundamentally unsound. We'll see.
92 lines
2.7 KiB
C++
92 lines
2.7 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/cpu/entry_table.h"
|
|
|
|
#include "xenia/base/threading.h"
|
|
#include "xenia/profiling.h"
|
|
|
|
namespace xe {
|
|
namespace cpu {
|
|
|
|
EntryTable::EntryTable() = default;
|
|
|
|
EntryTable::~EntryTable() {
|
|
auto global_lock = global_critical_region_.Acquire();
|
|
for (auto it : map_) {
|
|
Entry* entry = it.second;
|
|
delete entry;
|
|
}
|
|
}
|
|
|
|
Entry* EntryTable::Get(uint32_t address) {
|
|
auto global_lock = global_critical_region_.Acquire();
|
|
const auto& it = map_.find(address);
|
|
Entry* entry = it != map_.end() ? it->second : nullptr;
|
|
if (entry) {
|
|
// TODO(benvanik): wait if needed?
|
|
if (entry->status != Entry::STATUS_READY) {
|
|
entry = nullptr;
|
|
}
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
Entry::Status EntryTable::GetOrCreate(uint32_t address, Entry** out_entry) {
|
|
// TODO(benvanik): replace with a map with wait-free for find.
|
|
// https://github.com/facebook/folly/blob/master/folly/AtomicHashMap.h
|
|
|
|
auto global_lock = global_critical_region_.Acquire();
|
|
const auto& it = map_.find(address);
|
|
Entry* entry = it != map_.end() ? it->second : nullptr;
|
|
Entry::Status status;
|
|
if (entry) {
|
|
// If we aren't ready yet spin and wait.
|
|
if (entry->status == Entry::STATUS_COMPILING) {
|
|
// Still compiling, so spin.
|
|
do {
|
|
global_lock.unlock();
|
|
// TODO(benvanik): sleep for less time?
|
|
xe::threading::Sleep(std::chrono::microseconds(10));
|
|
global_lock.lock();
|
|
} while (entry->status == Entry::STATUS_COMPILING);
|
|
}
|
|
status = entry->status;
|
|
} else {
|
|
// Create and return for initialization.
|
|
entry = new Entry();
|
|
entry->address = address;
|
|
entry->end_address = 0;
|
|
entry->status = Entry::STATUS_COMPILING;
|
|
entry->function = 0;
|
|
map_[address] = entry;
|
|
status = Entry::STATUS_NEW;
|
|
}
|
|
global_lock.unlock();
|
|
*out_entry = entry;
|
|
return status;
|
|
}
|
|
|
|
std::vector<Function*> EntryTable::FindWithAddress(uint32_t address) {
|
|
auto global_lock = global_critical_region_.Acquire();
|
|
std::vector<Function*> fns;
|
|
for (auto& it : map_) {
|
|
Entry* entry = it.second;
|
|
if (address >= entry->address && address <= entry->end_address) {
|
|
if (entry->status == Entry::STATUS_READY) {
|
|
fns.push_back(entry->function);
|
|
}
|
|
}
|
|
}
|
|
return fns;
|
|
}
|
|
|
|
} // namespace cpu
|
|
} // namespace xe
|