Fixed race condition in MPU401 shutdown

svn-id: r8432
This commit is contained in:
Jamieson Christian 2003-06-11 05:54:46 +00:00
parent 167ad2575a
commit d2c952b315
8 changed files with 43 additions and 21 deletions

View file

@ -90,6 +90,8 @@ int MidiDriver_CORE::open() {
} }
void MidiDriver_CORE::close() { void MidiDriver_CORE::close() {
MidiDriver_MPU401::close();
// Stop the output // Stop the output
AudioOutputUnitStop(au_output); AudioOutputUnitStop(au_output);

View file

@ -68,6 +68,7 @@ int MidiDriver_ETUDE::open()
void MidiDriver_ETUDE::close() void MidiDriver_ETUDE::close()
{ {
MidiDriver_MPU401::close();
exit_morphos_music(); exit_morphos_music();
_isOpen = false; _isOpen = false;
} }

View file

@ -26,7 +26,6 @@
class MidiDriver_NULL : public MidiDriver_MPU401 { class MidiDriver_NULL : public MidiDriver_MPU401 {
public: public:
int open(); int open();
void close() { }
void send(uint32 b) { } void send(uint32 b) { }
}; };

View file

@ -115,6 +115,8 @@ bail:
void MidiDriver_QT::close() void MidiDriver_QT::close()
{ {
MidiDriver_MPU401::close();
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
if (qtNoteChannel[i] != NULL) if (qtNoteChannel[i] != NULL)
NADisposeNoteChannel(qtNoteAllocator, qtNoteChannel[i]); NADisposeNoteChannel(qtNoteAllocator, qtNoteChannel[i]);

View file

@ -87,6 +87,7 @@ int MidiDriver_SEQ::open() {
} }
void MidiDriver_SEQ::close() { void MidiDriver_SEQ::close() {
MidiDriver_MPU401::close();
::close(device); ::close(device);
_isOpen = false; _isOpen = false;
} }

View file

@ -68,6 +68,7 @@ void MidiDriver_WIN::close() {
if (!_isOpen) if (!_isOpen)
return; return;
_isOpen = false; _isOpen = false;
MidiDriver_MPU401::close();
midiOutUnprepareHeader (_mo, &_streamHeader, sizeof (_streamHeader)); midiOutUnprepareHeader (_mo, &_streamHeader, sizeof (_streamHeader));
check_error(midiOutClose(_mo)); check_error(midiOutClose(_mo));
CloseHandle (_streamEvent); CloseHandle (_streamEvent);

View file

@ -76,18 +76,29 @@ const char *MidiDriver::getErrorName(int error_code) {
return midi_errors[error_code]; return midi_errors[error_code];
} }
MidiDriver_MPU401::MidiDriver_MPU401() : MidiDriver() { MidiDriver_MPU401::MidiDriver_MPU401() :
MidiDriver(),
_started_thread (false),
_timer_proc (0),
_timer_param (0),
_mutex (0)
{
uint i; uint i;
_started_thread = false; // palmos
_timer_proc = NULL; // palmos
_timer_param = NULL; // palmos
for (i = 0; i < ARRAYSIZE(_midi_channels); ++i) { for (i = 0; i < ARRAYSIZE(_midi_channels); ++i) {
_midi_channels [i].init (this, i); _midi_channels [i].init (this, i);
} }
} }
void MidiDriver_MPU401::close() {
if (_mutex) {
_started_thread = false;
g_system->lock_mutex (_mutex); // Wait for the timer thread to shutdown.
g_system->unlock_mutex (_mutex);
g_system->delete_mutex (_mutex);
}
}
MidiChannel *MidiDriver_MPU401::allocateChannel() { MidiChannel *MidiDriver_MPU401::allocateChannel() {
MidiChannel_MPU401 *chan; MidiChannel_MPU401 *chan;
uint i; uint i;
@ -106,40 +117,45 @@ MidiChannel *MidiDriver_MPU401::allocateChannel() {
void MidiDriver_MPU401::setTimerCallback (void *timer_param, void (*timer_proc) (void *)) { void MidiDriver_MPU401::setTimerCallback (void *timer_param, void (*timer_proc) (void *)) {
if (!_timer_proc || !timer_proc) { if (!_timer_proc || !timer_proc) {
_timer_proc = (TimerCallback *) timer_proc; _timer_proc = timer_proc;
_timer_param = timer_param; _timer_param = timer_param;
if (!_started_thread && timer_proc) { if (!_started_thread && timer_proc) {
// TODO: This is the only place in ScummVM where create_thread is // TODO: This is the only place in ScummVM where create_thread is
// being used. And it's used for a timer like thread. So if we // being used. And it's used for a timer like thread. So if we
// could convert it to use the timer API instead, we could get // could convert it to use the timer API instead, we could get
// rid of create_thread completely. // rid of create_thread completely.
_mutex = g_system->create_mutex();
_started_thread = true;
g_system->create_thread(midi_driver_thread, this); g_system->create_thread(midi_driver_thread, this);
} }
_started_thread = true;
} }
} }
#if !defined(__MORPHOS__) && !defined(__PALM_OS__) #if !defined(__MORPHOS__) && !defined(__PALM_OS__)
typedef void (*TimerCallback) (void*);
int MidiDriver_MPU401::midi_driver_thread(void *param) { int MidiDriver_MPU401::midi_driver_thread(void *param) {
volatile MidiDriver_MPU401 *mid = (MidiDriver_MPU401 *)param; MidiDriver_MPU401 *mid = (MidiDriver_MPU401 *)param;
int old_time, cur_time; int old_time, cur_time;
old_time = g_system->get_msecs(); // Grab the MidiDriver's mutex. When the MidiDriver
// shuts down, it will wait on that mutex until we've
// detected the shutdown and quit looping.
g_system->lock_mutex (mid->_mutex);
for (;;) { old_time = g_system->get_msecs();
while (mid->_started_thread) {
g_system->delay_msecs(10); g_system->delay_msecs(10);
cur_time = g_system->get_msecs(); cur_time = g_system->get_msecs();
while (old_time < cur_time) { while (old_time < cur_time) {
old_time += 10; old_time += 10;
// Don't use mid->_se_on_timer()
// We must come in through IMuseMonitor to protect
// against conflicts with script access to IMuse.
if (mid->_timer_proc) if (mid->_timer_proc)
(*(mid->_timer_proc)) (mid->_timer_param); (*(TimerCallback)(mid->_timer_proc)) (mid->_timer_param);
} }
} }
g_system->unlock_mutex (mid->_mutex);
return 0; return 0;
} }
#endif #endif

View file

@ -30,8 +30,6 @@
// //
//////////////////////////////////////// ////////////////////////////////////////
typedef void TimerCallback (void *);
class MidiDriver_MPU401; class MidiDriver_MPU401;
class MidiChannel_MPU401 : public MidiChannel { class MidiChannel_MPU401 : public MidiChannel {
@ -78,8 +76,9 @@ public:
class MidiDriver_MPU401 : public MidiDriver { class MidiDriver_MPU401 : public MidiDriver {
private: private:
MidiChannel_MPU401 _midi_channels [16]; MidiChannel_MPU401 _midi_channels [16];
bool _started_thread; volatile bool _started_thread;
TimerCallback *_timer_proc; void *_mutex; // Concurrent shutdown barrier
volatile void *_timer_proc;
void *_timer_param; void *_timer_param;
static int midi_driver_thread (void *param); static int midi_driver_thread (void *param);
@ -87,8 +86,9 @@ private:
public: public:
MidiDriver_MPU401(); MidiDriver_MPU401();
virtual void close();
void setTimerCallback(void *timer_param, void (*timer_proc) (void *)); void setTimerCallback(void *timer_param, void (*timer_proc) (void *));
uint32 getBaseTempo(void) { return 10000; } // 0x4A0000; } // Now referenced in microseconds between callbacks uint32 getBaseTempo(void) { return 10000; }
MidiChannel *allocateChannel(); MidiChannel *allocateChannel();
MidiChannel *getPercussionChannel() { return &_midi_channels [9]; } MidiChannel *getPercussionChannel() { return &_midi_channels [9]; }