all repos — mgba @ 08b78cb468d97102d896c68a07a25f705faaebc7

mGBA Game Boy Advance Emulator

GBA SIO: Fix freezing issues
Jeffrey Pfau jeffrey@endrift.com
Fri, 02 Sep 2016 00:33:14 -0700
commit

08b78cb468d97102d896c68a07a25f705faaebc7

parent

b0a9a595ef37f8bb1bff7ecbd00673e7b9c4c038

M src/gba/sio/lockstep.csrc/gba/sio/lockstep.c

@@ -11,27 +11,6 @@ #include "gba/video.h"

#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 @@ lockstep->multiRecv[3] = 0xFFFF;

lockstep->attached = 0; lockstep->attachedMulti = 0; lockstep->transferActive = 0; - lockstep->masterWaiting = 0; #ifndef NDEBUG lockstep->transferId = 0; #endif

@@ -152,14 +130,9 @@ default:

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 @@ node->nextEvent += LOCKSTEP_INCREMENT;

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 @@ bool signal = false;

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 @@ _finishTransfer(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 @@ if (node->p->attached < 2) {

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; }
M src/gba/sio/lockstep.hsrc/gba/sio/lockstep.h

@@ -27,10 +27,10 @@ uint32_t normalRecv[MAX_GBAS];

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;
M src/platform/qt/MultiplayerController.cppsrc/platform/qt/MultiplayerController.cpp

@@ -19,21 +19,70 @@

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); + Player* player = &controller->m_players[0]; + bool woke = false; + controller->m_lock.lock(); + 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); - GameController* game = controller->m_players[id]; controller->m_lock.lock(); - ++controller->m_awake[id]; - mCoreThreadStopWaiting(game->thread()); + 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 @@

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 @@ return false;

} void MultiplayerController::detachGame(GameController* controller) { - if (!m_players.contains(controller)) { - return; - } mCoreThread* thread = controller->thread(); if (!thread) { return;

@@ -92,14 +143,22 @@ delete node;

} } #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() {
M src/platform/qt/MultiplayerController.hsrc/platform/qt/MultiplayerController.h

@@ -36,9 +36,15 @@ void gameAttached();

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; };