1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-29 12:50:28 +00:00

Merge pull request #1067 from TomHarte/MachineLeak

macOS: Avoid likely leak of machines.
This commit is contained in:
Thomas Harte 2022-07-15 15:36:21 -04:00 committed by GitHub
commit b6f40fdcc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 18 deletions

View File

@ -73,7 +73,7 @@ template <bool perform_automatically, typename Performer = int> class TaskQueue:
[this] { [this] {
ActionVector actions; ActionVector actions;
while(!should_quit) { while(!should_quit_) {
// Wait for new actions to be signalled, and grab them. // Wait for new actions to be signalled, and grab them.
std::unique_lock lock(condition_mutex_); std::unique_lock lock(condition_mutex_);
while(actions_.empty()) { while(actions_.empty()) {
@ -127,7 +127,7 @@ template <bool perform_automatically, typename Performer = int> class TaskQueue:
/// The queue cannot be restarted; this is a destructive action. /// The queue cannot be restarted; this is a destructive action.
void stop() { void stop() {
if(thread_.joinable()) { if(thread_.joinable()) {
should_quit = true; should_quit_ = true;
enqueue([] {}); enqueue([] {});
if constexpr (!perform_automatically) { if constexpr (!perform_automatically) {
perform(); perform();
@ -168,7 +168,7 @@ template <bool perform_automatically, typename Performer = int> class TaskQueue:
ActionVector actions_; ActionVector actions_;
// Necessary synchronisation parts. // Necessary synchronisation parts.
std::atomic<bool> should_quit = false; std::atomic<bool> should_quit_ = false;
std::mutex condition_mutex_; std::mutex condition_mutex_;
std::condition_variable condition_; std::condition_variable condition_;

View File

@ -47,7 +47,11 @@
if(self) { if(self) {
_deallocLock = [[NSLock alloc] init]; _deallocLock = [[NSLock alloc] init];
_deallocLock.name = @"Dealloc lock";
_queueLock = [[NSLock alloc] init]; _queueLock = [[NSLock alloc] init];
_queueLock.name = @"Audio queue access lock";
atomic_store_explicit(&_enqueuedBuffers, 0, memory_order_relaxed); atomic_store_explicit(&_enqueuedBuffers, 0, memory_order_relaxed);
_samplingRate = samplingRate; _samplingRate = samplingRate;
@ -115,11 +119,12 @@
- (void)dealloc { - (void)dealloc {
[_deallocLock lock]; [_deallocLock lock];
// Ensure no buffers remain enqueued by stopping the queue.
if(_audioQueue) { if(_audioQueue) {
OSSGuard(AudioQueueDispose(_audioQueue, true)); OSSGuard(AudioQueueStop(_audioQueue, true));
_audioQueue = NULL;
} }
// Free all buffers.
for(size_t c = 0; c < NumBuffers; c++) { for(size_t c = 0; c < NumBuffers; c++) {
if(_buffers[c]) { if(_buffers[c]) {
OSSGuard(AudioQueueFreeBuffer(_audioQueue, _buffers[c])); OSSGuard(AudioQueueFreeBuffer(_audioQueue, _buffers[c]));
@ -127,6 +132,12 @@
} }
} }
// Dispose of the queue.
if(_audioQueue) {
OSSGuard(AudioQueueDispose(_audioQueue, true));
_audioQueue = NULL;
}
// nil out the dealloc lock before entering the critical section such // nil out the dealloc lock before entering the critical section such
// that it becomes impossible for anyone else to acquire. // that it becomes impossible for anyone else to acquire.
NSLock *deallocLock = _deallocLock; NSLock *deallocLock = _deallocLock;

View File

@ -669,41 +669,57 @@ struct ActivityObserver: public Activity::Observer {
#pragma mark - Timer #pragma mark - Timer
- (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue { - (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue {
updater.enqueue([self] { __weak CSMachine *weakSelf = self;
updater.performer.machine->flush_output(MachineTypes::TimedMachine::Output::Audio);
updater.enqueue([weakSelf] {
CSMachine *const strongSelf = weakSelf;
if(strongSelf) {
strongSelf->updater.performer.machine->flush_output(MachineTypes::TimedMachine::Output::Audio);
}
}); });
} }
- (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime { - (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime {
updater.enqueue([self] { __weak CSMachine *weakSelf = self;
updater.enqueue([weakSelf] {
CSMachine *const strongSelf = weakSelf;
if(!strongSelf) {
return;
}
// Grab a pointer to the timed machine from somewhere where it has already // Grab a pointer to the timed machine from somewhere where it has already
// been dynamically cast, to avoid that cost here. // been dynamically cast, to avoid that cost here.
MachineTypes::TimedMachine *const timed_machine = updater.performer.machine; MachineTypes::TimedMachine *const timed_machine = strongSelf->updater.performer.machine;
// Definitely update video; update audio too if that pipeline is looking a little dry. // Definitely update video; update audio too if that pipeline is looking a little dry.
auto outputs = MachineTypes::TimedMachine::Output::Video; auto outputs = MachineTypes::TimedMachine::Output::Video;
if(_audioQueue.isRunningDry) { if(strongSelf->_audioQueue.isRunningDry) {
outputs |= MachineTypes::TimedMachine::Output::Audio; outputs |= MachineTypes::TimedMachine::Output::Audio;
} }
timed_machine->flush_output(outputs); timed_machine->flush_output(outputs);
// Attempt sync-matching if this machine is a fit. // Attempt sync-matching if this machine is a fit.
// const auto scanStatus = strongSelf->_machine->scan_producer()->get_scan_status();
// TODO: either cache scan_producer(), or possibly start caching these things const bool canSynchronise = strongSelf->_scanSynchroniser.can_synchronise(
// inside the DynamicMachine? scanStatus,
const auto scanStatus = self->_machine->scan_producer()->get_scan_status(); strongSelf.view.refreshPeriod
const bool canSynchronise = self->_scanSynchroniser.can_synchronise(scanStatus, self.view.refreshPeriod); );
if(canSynchronise) { if(canSynchronise) {
const double multiplier = self->_scanSynchroniser.next_speed_multiplier(self->_machine->scan_producer()->get_scan_status()); const double multiplier = strongSelf->_scanSynchroniser.next_speed_multiplier(
strongSelf->_machine->scan_producer()->get_scan_status()
);
timed_machine->set_speed_multiplier(multiplier); timed_machine->set_speed_multiplier(multiplier);
} else { } else {
timed_machine->set_speed_multiplier(1.0); timed_machine->set_speed_multiplier(1.0);
} }
// Ask Metal to rasterise all that just happened and present it. // Ask Metal to rasterise all that just happened and present it.
[self.view updateBacking]; [strongSelf.view updateBacking];
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.view draw]; // This is safe even if weakSelf has been nulled out in the interim.
[weakSelf.view draw];
}); });
}); });
} }