1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00

Merge pull request #230 from TomHarte/CyclicShutdown

Eliminates potential cyclic entry into CSMachine during its `-dealloc`.
This commit is contained in:
Thomas Harte 2017-08-31 21:23:15 -04:00 committed by GitHub
commit a5f9869769

View File

@ -19,26 +19,38 @@
- (void)machineDidChangeClockIsUnlimited; - (void)machineDidChangeClockIsUnlimited;
@end @end
struct SpeakerDelegate: public Outputs::Speaker::Delegate { struct LockProtectedDelegate {
__weak CSMachine *machine; // Contractual promise is: machine — the pointer **and** the object ** — may be accessed only
// in sections protected by the machineAccessLock;
NSLock *machineAccessLock;
__unsafe_unretained CSMachine *machine;
};
struct SpeakerDelegate: public Outputs::Speaker::Delegate, public LockProtectedDelegate {
void speaker_did_complete_samples(Outputs::Speaker *speaker, const std::vector<int16_t> &buffer) { void speaker_did_complete_samples(Outputs::Speaker *speaker, const std::vector<int16_t> &buffer) {
[machineAccessLock lock];
[machine speaker:speaker didCompleteSamples:buffer.data() length:(int)buffer.size()]; [machine speaker:speaker didCompleteSamples:buffer.data() length:(int)buffer.size()];
[machineAccessLock unlock];
} }
}; };
struct MachineDelegate: CRTMachine::Machine::Delegate { struct MachineDelegate: CRTMachine::Machine::Delegate, public LockProtectedDelegate {
__weak CSMachine *machine;
void machine_did_change_clock_rate(CRTMachine::Machine *sender) { void machine_did_change_clock_rate(CRTMachine::Machine *sender) {
[machineAccessLock lock];
[machine machineDidChangeClockRate]; [machine machineDidChangeClockRate];
[machineAccessLock unlock];
} }
void machine_did_change_clock_is_unlimited(CRTMachine::Machine *sender) { void machine_did_change_clock_is_unlimited(CRTMachine::Machine *sender) {
[machineAccessLock lock];
[machine machineDidChangeClockIsUnlimited]; [machine machineDidChangeClockIsUnlimited];
[machineAccessLock unlock];
} }
}; };
@implementation CSMachine { @implementation CSMachine {
SpeakerDelegate _speakerDelegate; SpeakerDelegate _speakerDelegate;
MachineDelegate _machineDelegate; MachineDelegate _machineDelegate;
NSLock *_delegateMachineAccessLock;
CRTMachine::Machine *_machine; CRTMachine::Machine *_machine;
} }
@ -46,8 +58,12 @@ struct MachineDelegate: CRTMachine::Machine::Delegate {
self = [super init]; self = [super init];
if(self) { if(self) {
_machine = (CRTMachine::Machine *)machine; _machine = (CRTMachine::Machine *)machine;
_delegateMachineAccessLock = [[NSLock alloc] init];
_machineDelegate.machine = self; _machineDelegate.machine = self;
_speakerDelegate.machine = self; _speakerDelegate.machine = self;
_machineDelegate.machineAccessLock = _delegateMachineAccessLock;
_speakerDelegate.machineAccessLock = _delegateMachineAccessLock;
_machine->set_delegate(&_machineDelegate); _machine->set_delegate(&_machineDelegate);
} }
@ -67,6 +83,17 @@ struct MachineDelegate: CRTMachine::Machine::Delegate {
} }
- (void)dealloc { - (void)dealloc {
// The two delegate's references to this machine are nilled out here because close_output may result
// in a data flush, which might cause an audio callback, which could cause the audio queue to decide
// that it's out of data, resulting in an attempt further to run the machine while it is dealloc'ing.
//
// They are nilled inside an explicit lock because that allows the delegates to protect their entire
// call into the machine, not just the pointer access.
[_delegateMachineAccessLock lock];
_machineDelegate.machine = nil;
_speakerDelegate.machine = nil;
[_delegateMachineAccessLock unlock];
[_view performWithGLContext:^{ [_view performWithGLContext:^{
@synchronized(self) { @synchronized(self) {
_machine->close_output(); _machine->close_output();
@ -77,8 +104,7 @@ struct MachineDelegate: CRTMachine::Machine::Delegate {
- (float)idealSamplingRateFromRange:(NSRange)range { - (float)idealSamplingRateFromRange:(NSRange)range {
@synchronized(self) { @synchronized(self) {
std::shared_ptr<Outputs::Speaker> speaker = _machine->get_speaker(); std::shared_ptr<Outputs::Speaker> speaker = _machine->get_speaker();
if(speaker) if(speaker) {
{
return speaker->get_ideal_clock_rate_in_range((float)range.location, (float)(range.location + range.length)); return speaker->get_ideal_clock_rate_in_range((float)range.location, (float)(range.location + range.length));
} }
return 0; return 0;
@ -94,8 +120,7 @@ struct MachineDelegate: CRTMachine::Machine::Delegate {
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(float)sampleRate bufferSize:(NSUInteger)bufferSize { - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(float)sampleRate bufferSize:(NSUInteger)bufferSize {
@synchronized(self) { @synchronized(self) {
std::shared_ptr<Outputs::Speaker> speaker = _machine->get_speaker(); std::shared_ptr<Outputs::Speaker> speaker = _machine->get_speaker();
if(speaker) if(speaker) {
{
speaker->set_output_rate(sampleRate, (int)bufferSize); speaker->set_output_rate(sampleRate, (int)bufferSize);
speaker->set_delegate(delegate); speaker->set_delegate(delegate);
return YES; return YES;