mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-08 17:12:25 -10:00
MapleDevices[][] in maple_if.cpp holds shared_ptr<maple_device>s with
static storage, so the VMUs are destroyed when the process exits.
~maple_sega_vmu() clears the LCD on any attached maple link, which goes
through MapleLink::GetMapleLink() and locks MapleLink::Mutex, another
object with static storage, in another translation unit, that may
already be gone. With libc++ locking a destroyed mutex throws
("mutex lock failed: Invalid argument") and the process aborts; the
unit test binary exits with SIGABRT on macOS after every test passed.
On glibc the same lock happens to succeed, which is why Linux never
showed it. The Links table is read in the same state.
Give both to function-local statics that are allocated once and never
destroyed, so they outlive everything that can still reach them.
Co-authored-by: Claude Fable 5.1 <[email protected]>
[ci skip]
188 lines
4.3 KiB
C++
188 lines
4.3 KiB
C++
/*
|
|
This file is part of Flycast.
|
|
|
|
Flycast is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Flycast is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "maplelink.h"
|
|
#include "cfg/option.h"
|
|
#include "hw/maple/maple_if.h"
|
|
|
|
std::mutex& MapleLink::mutex()
|
|
{
|
|
static std::mutex *m = new std::mutex();
|
|
return *m;
|
|
}
|
|
|
|
MapleLink::LinkTable& MapleLink::links()
|
|
{
|
|
static LinkTable *l = new LinkTable();
|
|
return *l;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
struct GameState
|
|
{
|
|
GameState()
|
|
{
|
|
EventManager::listen(Event::Start, [](Event, void*) {
|
|
started = true;
|
|
});
|
|
EventManager::listen(Event::Terminate, [](Event, void*) {
|
|
started = false;
|
|
});
|
|
}
|
|
static bool started;
|
|
static GameState instance;
|
|
};
|
|
bool GameState::started;
|
|
GameState GameState::instance;
|
|
}
|
|
|
|
std::size_t MapleLink::activeLinkCount(int bus) const
|
|
{
|
|
std::size_t count = 0;
|
|
if (bus >= 0 && bus < (int)links().size())
|
|
{
|
|
std::lock_guard<std::mutex> _(mutex());
|
|
for (const std::list<MapleLink::Ptr>& list : links()[bus]) {
|
|
if (!list.empty() && list.front().get() == this)
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
bool MapleLink::isGameStarted() const {
|
|
return GameState::started;
|
|
}
|
|
|
|
void MapleLink::registerLink(int bus, int port)
|
|
{
|
|
if (bus >= 0 && bus < (int)links().size()
|
|
&& port >= 0 && port < (int)links()[0].size())
|
|
{
|
|
if (this->bus != bus) {
|
|
this->bus = bus;
|
|
this->ports = 0;
|
|
}
|
|
this->ports |= 1 << port;
|
|
std::lock_guard<std::mutex> _(mutex());
|
|
links()[bus][port].push_front(shared_from_this());
|
|
}
|
|
}
|
|
void MapleLink::unregisterLink(int bus, int port)
|
|
{
|
|
if (bus >= 0 && bus < (int)links().size()
|
|
&& port >= 0 && port < (int)links()[0].size())
|
|
{
|
|
if (this->bus != bus) {
|
|
this->bus = -1;
|
|
this->ports = 0;
|
|
}
|
|
else {
|
|
this->ports &= ~(1 << port);
|
|
}
|
|
std::lock_guard<std::mutex> _(mutex());
|
|
links()[bus][port].remove_if([this](const Ptr& item) { return item.get() == this; });
|
|
}
|
|
}
|
|
|
|
bool MapleLink::StorageEnabled()
|
|
{
|
|
std::lock_guard<std::mutex> _(mutex());
|
|
for (const auto& ports : links())
|
|
{
|
|
for (const std::list<MapleLink::Ptr>& list : ports)
|
|
if (!list.empty() && list.front()->storageEnabled())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BaseMapleLink::BaseMapleLink(bool storageSupported)
|
|
: storageSupported(storageSupported)
|
|
{
|
|
EventManager::listen(Event::LoadState, eventHandler, this);
|
|
EventManager::listen(Event::Start, eventHandler, this);
|
|
EventManager::listen(Event::Terminate, eventHandler, this);
|
|
if (isGameStarted())
|
|
vmuStorage = storageSupported && config::UsePhysicalVmuMemory;
|
|
}
|
|
|
|
BaseMapleLink::~BaseMapleLink()
|
|
{
|
|
EventManager::unlisten(Event::LoadState, eventHandler, this);
|
|
EventManager::unlisten(Event::Start, eventHandler, this);
|
|
EventManager::unlisten(Event::Terminate, eventHandler, this);
|
|
}
|
|
|
|
void BaseMapleLink::gameStarted() {
|
|
vmuStorage = storageSupported && config::UsePhysicalVmuMemory && isConnected();
|
|
}
|
|
|
|
void BaseMapleLink::eventHandler(Event event, void *p)
|
|
{
|
|
BaseMapleLink *self = (BaseMapleLink *)p;
|
|
switch (event)
|
|
{
|
|
case Event::Start:
|
|
self->gameStarted();
|
|
break;
|
|
case Event::Terminate:
|
|
self->gameTermination();
|
|
break;
|
|
case Event::LoadState:
|
|
if (self->vmuStorage) {
|
|
WARN_LOG(INPUT, "State loaded but VMU has storage enabled");
|
|
self->disableStorage();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void BaseMapleLink::disableStorage()
|
|
{
|
|
if (!vmuStorage)
|
|
return;
|
|
vmuStorage = false;
|
|
if (isGameStarted())
|
|
{
|
|
emu.run([bus=this->bus, ports=this->ports]() {
|
|
if (bus != -1 && (ports & 1))
|
|
maple_ReconnectDevice(bus, 0);
|
|
if (bus != -1 && (ports & 2))
|
|
maple_ReconnectDevice(bus, 1);
|
|
});
|
|
}
|
|
}
|
|
|
|
bool BaseMapleLink::storageEnabled()
|
|
{
|
|
if (!isConnected())
|
|
return false;
|
|
if (!isGameStarted())
|
|
return storageSupported && config::UsePhysicalVmuMemory;
|
|
else
|
|
return vmuStorage;
|
|
}
|
|
|
|
bool BaseMapleLink::handleGetLastError(const MapleMsg&)
|
|
{
|
|
// Just acknowledge by default
|
|
return true;
|
|
}
|