GBA SIO: Fix freezing issues

This commit is contained in:
Jeffrey Pfau 2016-09-02 00:33:14 -07:00
parent b0a9a595ef
commit 08b78cb468
4 changed files with 112 additions and 82 deletions

View File

@ -11,27 +11,6 @@
#define LOCKSTEP_INCREMENT 3000
static bool _waitMaster(struct GBASIOLockstepNode* node, uint32_t mask) {
uint32_t oldMask = 0;
if (ATOMIC_CMPXCHG(node->p->masterWaiting, oldMask, mask | 0x10)) {
node->p->wait(node->p, 0);
}
return true;
}
static bool _signalMaster(struct GBASIOLockstepNode* node, uint32_t mask) {
uint32_t waiting = ATOMIC_AND(node->p->masterWaiting, ~mask);
if (waiting == 0x10) {
if (!ATOMIC_CMPXCHG(node->p->masterWaiting, waiting, 0)) {
return false;
}
node->p->signal(node->p, 0);
return true;
}
return false;
}
static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver);
static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver);
static bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver);
@ -53,7 +32,6 @@ void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
lockstep->attached = 0;
lockstep->attachedMulti = 0;
lockstep->transferActive = 0;
lockstep->masterWaiting = 0;
#ifndef NDEBUG
lockstep->transferId = 0;
#endif
@ -152,14 +130,9 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
break;
}
if (node->id) {
_signalMaster(node, 1 << node->id);
}
int i;
for (i = 0; i < node->p->attached; ++i) {
if (i == node->id) {
continue;
}
node->p->signal(node->p, i);
node->p->signal(node->p, 1 << node->id);
} else {
node->p->addCycles(node->p, 0, node->eventDiff);
}
return true;
}
@ -284,26 +257,23 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
ATOMIC_STORE(node->p->transferActive, TRANSFER_IDLE);
break;
}
if (needsToWait) {
int mask = 0;
for (i = 1; i < node->p->attached; ++i) {
if (node->p->players[i]->mode == node->mode) {
mask |= 1 << i;
}
int mask = 0;
for (i = 1; i < node->p->attached; ++i) {
if (node->p->players[i]->mode == node->mode) {
mask |= 1 << i;
}
if (mask) {
if (!_waitMaster(node, mask)) {
#ifndef NDEBUG
}
if (mask) {
if (needsToWait) {
if (!node->p->wait(node->p, mask)) {
abort();
#endif
}
} else {
node->p->signal(node->p, mask);
}
}
// Tell the other GBAs they can continue up to where we were
for (i = 1; i < node->p->attached; ++i) {
ATOMIC_ADD(node->p->players[i]->nextEvent, node->eventDiff);
node->p->signal(node->p, i);
}
node->p->addCycles(node->p, 0, node->eventDiff);
#ifndef NDEBUG
node->phase = node->p->transferActive;
#endif
@ -319,7 +289,7 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
switch (node->p->transferActive) {
case TRANSFER_IDLE:
if (!node->d.p->multiplayerControl.ready) {
return ATOMIC_ADD(node->nextEvent, LOCKSTEP_INCREMENT);
node->p->addCycles(node->p, node->id, LOCKSTEP_INCREMENT);
}
break;
case TRANSFER_STARTING:
@ -357,19 +327,11 @@ static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) {
signal = true;
break;
}
int32_t cycles;
ATOMIC_LOAD(cycles, node->nextEvent);
if (cycles <= 0) {
node->p->wait(node->p, node->id);
}
#ifndef NDEBUG
node->phase = node->p->transferActive;
#endif
if (signal) {
_signalMaster(node, 1 << node->id);
if (cycles > 0) {
return cycles;
}
node->p->signal(node->p, 1 << node->id);
}
return 0;
}
@ -380,17 +342,20 @@ static int32_t GBASIOLockstepNodeProcessEvents(struct GBASIODriver* driver, int3
return INT_MAX;
}
node->eventDiff += cycles;
cycles = ATOMIC_ADD(node->nextEvent, -cycles);
if (cycles <= 0) {
node->nextEvent -= cycles;
if (node->nextEvent <= 0) {
if (!node->id) {
cycles = _masterUpdate(node);
} else {
cycles = _slaveUpdate(node);
node->nextEvent += node->p->useCycles(node->p, node->id, node->eventDiff);
}
node->eventDiff = 0;
if (cycles <= 0) {
return 0;
}
} else {
cycles = node->nextEvent;
}
if (cycles < 0) {
return 0;
}
return cycles;
}

View File

@ -27,10 +27,10 @@ struct GBASIOLockstep {
enum GBASIOLockstepPhase transferActive;
int32_t transferCycles;
uint32_t masterWaiting;
void (*signal)(struct GBASIOLockstep*, int id);
void (*wait)(struct GBASIOLockstep*, int id);
bool (*signal)(struct GBASIOLockstep*, unsigned mask);
bool (*wait)(struct GBASIOLockstep*, unsigned mask);
void (*addCycles)(struct GBASIOLockstep*, int id, int32_t cycles);
int32_t (*useCycles)(struct GBASIOLockstep*, int id, int32_t cycles);
void* context;
#ifndef NDEBUG
int transferId;

View File

@ -19,21 +19,70 @@ using namespace QGBA;
MultiplayerController::MultiplayerController() {
GBASIOLockstepInit(&m_lockstep);
m_lockstep.context = this;
m_lockstep.signal = [](GBASIOLockstep* lockstep, int id) {
m_lockstep.signal = [](GBASIOLockstep* lockstep, unsigned mask) {
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
GameController* game = controller->m_players[id];
Player* player = &controller->m_players[0];
bool woke = false;
controller->m_lock.lock();
++controller->m_awake[id];
mCoreThreadStopWaiting(game->thread());
player->waitMask &= ~mask;
if (!player->waitMask && player->awake < 1) {
mCoreThreadStopWaiting(player->controller->thread());
player->awake = 1;
woke = true;
}
controller->m_lock.unlock();
return woke;
};
m_lockstep.wait = [](GBASIOLockstep* lockstep, unsigned mask) {
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
controller->m_lock.lock();
Player* player = &controller->m_players[0];
bool slept = false;
player->waitMask |= mask;
if (player->awake > 0) {
mCoreThreadWaitFromThread(player->controller->thread());
player->awake = 0;
slept = true;
}
controller->m_lock.unlock();
return slept;
};
m_lockstep.addCycles = [](GBASIOLockstep* lockstep, int id, int32_t cycles) {
if (cycles < 0) {
abort();
}
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
controller->m_lock.lock();
if (!id) {
for (int i = 1; i < controller->m_players.count(); ++i) {
Player* player = &controller->m_players[i];
if (player->node->mode != controller->m_players[0].node->mode) {
continue;
}
player->cyclesPosted += cycles;
if (player->awake < 1) {
player->node->nextEvent += player->cyclesPosted;
mCoreThreadStopWaiting(player->controller->thread());
player->awake = 1;
}
}
} else {
controller->m_players[id].cyclesPosted += cycles;
}
controller->m_lock.unlock();
};
m_lockstep.wait = [](GBASIOLockstep* lockstep, int id) {
m_lockstep.useCycles = [](GBASIOLockstep* lockstep, int id, int32_t cycles) {
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
controller->m_lock.lock();
controller->m_awake[id] = 0;
GameController* game = controller->m_players[id];
mCoreThreadWaitFromThread(game->thread());
Player* player = &controller->m_players[id];
player->cyclesPosted -= cycles;
if (player->cyclesPosted <= 0) {
mCoreThreadWaitFromThread(player->controller->thread());
player->awake = 0;
}
cycles = player->cyclesPosted;
controller->m_lock.unlock();
return cycles;
};
}
@ -58,8 +107,13 @@ bool MultiplayerController::attachGame(GameController* controller) {
GBASIOLockstepNode* node = new GBASIOLockstepNode;
GBASIOLockstepNodeCreate(node);
GBASIOLockstepAttachNode(&m_lockstep, node);
m_players.append(controller);
m_awake.append(1);
m_players.append({
controller,
node,
1,
0,
0
});
GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
GBASIOSetDriver(&gba->sio, &node->d, SIO_NORMAL_32);
@ -73,9 +127,6 @@ bool MultiplayerController::attachGame(GameController* controller) {
}
void MultiplayerController::detachGame(GameController* controller) {
if (!m_players.contains(controller)) {
return;
}
mCoreThread* thread = controller->thread();
if (!thread) {
return;
@ -92,14 +143,22 @@ void MultiplayerController::detachGame(GameController* controller) {
}
}
#endif
int i = m_players.indexOf(controller);
m_players.removeAt(i);
m_players.removeAt(i);
for (int i = 0; i < m_players.count(); ++i) {
if (m_players[i].controller == controller) {
m_players.removeAt(i);
break;
}
}
emit gameDetached();
}
int MultiplayerController::playerId(GameController* controller) {
return m_players.indexOf(controller);
for (int i = 0; i < m_players.count(); ++i) {
if (m_players[i].controller == controller) {
return i;
}
}
return -1;
}
int MultiplayerController::attached() {

View File

@ -36,9 +36,15 @@ signals:
void gameDetached();
private:
struct Player {
GameController* controller;
GBASIOLockstepNode* node;
int awake;
int32_t cyclesPosted;
unsigned waitMask;
};
GBASIOLockstep m_lockstep;
QList<GameController*> m_players;
QList<int> m_awake;
QList<Player> m_players;
QMutex m_lock;
};