1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Generalise AsyncTaskQueue, DeferringAsyncTaskQueue and AsyncUpdater into a single template.

This commit is contained in:
Thomas Harte 2022-07-14 16:39:26 -04:00
parent 126838e7c7
commit bf03bda314
48 changed files with 218 additions and 358 deletions

View File

@ -47,7 +47,7 @@ template <typename MachineType> class MultiInterface {
std::recursive_mutex &machines_mutex_;
private:
std::vector<Concurrency::AsyncTaskQueue> queues_;
std::vector<Concurrency::TaskQueue<true>> queues_;
};
class MultiTimedMachine: public MultiInterface<MachineTypes::TimedMachine>, public MachineTypes::TimedMachine {

View File

@ -315,7 +315,7 @@ template <class T, class LocalTimeScale = HalfCycles, class TargetTimeScale = Lo
LocalTimeScale time_since_update_;
TargetTimeScale threshold_;
bool is_flushed_ = true;
Concurrency::AsyncTaskQueue task_queue_;
Concurrency::TaskQueue<true> task_queue_;
};
#endif /* JustInTime_h */

View File

@ -12,18 +12,18 @@
using namespace MOS::MOS6560;
AudioGenerator::AudioGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
AudioGenerator::AudioGenerator(Concurrency::TaskQueue<false> &audio_queue) :
audio_queue_(audio_queue) {}
void AudioGenerator::set_volume(uint8_t volume) {
audio_queue_.defer([this, volume]() {
audio_queue_.enqueue([this, volume]() {
volume_ = int16_t(volume) * range_multiplier_;
});
}
void AudioGenerator::set_control(int channel, uint8_t value) {
audio_queue_.defer([this, channel, value]() {
audio_queue_.enqueue([this, channel, value]() {
control_registers_[channel] = value;
});
}

View File

@ -21,7 +21,7 @@ namespace MOS6560 {
// audio state
class AudioGenerator: public ::Outputs::Speaker::SampleSource {
public:
AudioGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue);
AudioGenerator(Concurrency::TaskQueue<false> &audio_queue);
void set_volume(uint8_t volume);
void set_control(int channel, uint8_t value);
@ -33,7 +33,7 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource {
static constexpr bool get_is_stereo() { return false; }
private:
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::TaskQueue<false> &audio_queue_;
unsigned int counters_[4] = {2, 1, 0, 0}; // create a slight phase offset for the three channels
unsigned int shift_registers_[4] = {0, 0, 0, 0};
@ -433,7 +433,7 @@ template <class BusHandler> class MOS6560 {
BusHandler &bus_handler_;
Outputs::CRT::CRT crt_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
AudioGenerator audio_generator_;
Outputs::Speaker::PullLowpass<AudioGenerator> speaker_;

View File

@ -16,7 +16,7 @@
using namespace GI::AY38910;
template <bool is_stereo>
AY38910<is_stereo>::AY38910(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {
AY38910<is_stereo>::AY38910(Personality personality, Concurrency::TaskQueue<false> &task_queue) : task_queue_(task_queue) {
// Don't use the low bit of the envelope position if this is an AY.
envelope_position_mask_ |= personality == Personality::AY38910;
@ -252,7 +252,7 @@ template <bool is_stereo> void AY38910<is_stereo>::set_register_value(uint8_t va
// If this is a register that affects audio output, enqueue a mutation onto the
// audio generation thread.
if(selected_register_ < 14) {
task_queue_.defer([this, selected_register = selected_register_, value] () {
task_queue_.enqueue([this, selected_register = selected_register_, value] () {
// Perform any register-specific mutation to output generation.
uint8_t masked_value = value;
switch(selected_register) {

View File

@ -71,7 +71,7 @@ enum class Personality {
template <bool is_stereo> class AY38910: public ::Outputs::Speaker::SampleSource {
public:
/// Creates a new AY38910.
AY38910(Personality, Concurrency::DeferringAsyncTaskQueue &);
AY38910(Personality, Concurrency::TaskQueue<false> &);
/// Sets the value the AY would read from its data lines if it were not outputting.
void set_data_input(uint8_t r);
@ -114,7 +114,7 @@ template <bool is_stereo> class AY38910: public ::Outputs::Speaker::SampleSource
static constexpr bool get_is_stereo() { return is_stereo; }
private:
Concurrency::DeferringAsyncTaskQueue &task_queue_;
Concurrency::TaskQueue<false> &task_queue_;
int selected_register_ = 0;
uint8_t registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

View File

@ -10,7 +10,7 @@
using namespace Audio;
Audio::Toggle::Toggle(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
Audio::Toggle::Toggle(Concurrency::TaskQueue<false> &audio_queue) :
audio_queue_(audio_queue) {}
void Toggle::get_samples(std::size_t number_of_samples, std::int16_t *target) {
@ -28,7 +28,7 @@ void Toggle::skip_samples(std::size_t) {}
void Toggle::set_output(bool enabled) {
if(is_enabled_ == enabled) return;
is_enabled_ = enabled;
audio_queue_.defer([this, enabled] {
audio_queue_.enqueue([this, enabled] {
level_ = enabled ? volume_ : 0;
});
}

View File

@ -19,7 +19,7 @@ namespace Audio {
*/
class Toggle: public Outputs::Speaker::SampleSource {
public:
Toggle(Concurrency::DeferringAsyncTaskQueue &audio_queue);
Toggle(Concurrency::TaskQueue<false> &audio_queue);
void get_samples(std::size_t number_of_samples, std::int16_t *target);
void set_sample_volume_range(std::int16_t range);
@ -31,7 +31,7 @@ class Toggle: public Outputs::Speaker::SampleSource {
private:
// Accessed on the calling thread.
bool is_enabled_ = false;
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::TaskQueue<false> &audio_queue_;
// Accessed on the audio thread.
int16_t level_ = 0, volume_ = 0;

View File

@ -12,7 +12,7 @@
using namespace Konami;
SCC::SCC(Concurrency::DeferringAsyncTaskQueue &task_queue) :
SCC::SCC(Concurrency::TaskQueue<false> &task_queue) :
task_queue_(task_queue) {}
bool SCC::is_zero_level() const {
@ -55,7 +55,7 @@ void SCC::write(uint16_t address, uint8_t value) {
address &= 0xff;
if(address < 0x80) ram_[address] = value;
task_queue_.defer([this, address, value] {
task_queue_.enqueue([this, address, value] {
// Check for a write into waveform memory.
if(address < 0x80) {
waves_[address >> 5].samples[address & 0x1f] = value;

View File

@ -24,7 +24,7 @@ namespace Konami {
class SCC: public ::Outputs::Speaker::SampleSource {
public:
/// Creates a new SCC.
SCC(Concurrency::DeferringAsyncTaskQueue &task_queue);
SCC(Concurrency::TaskQueue<false> &task_queue);
/// As per ::SampleSource; provides a broadphase test for silence.
bool is_zero_level() const;
@ -41,7 +41,7 @@ class SCC: public ::Outputs::Speaker::SampleSource {
uint8_t read(uint16_t address);
private:
Concurrency::DeferringAsyncTaskQueue &task_queue_;
Concurrency::TaskQueue<false> &task_queue_;
// State from here on down is accessed ony from the audio thread.
int master_divider_ = 0;

View File

@ -26,9 +26,9 @@ template <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource
}
protected:
OPLBase(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {}
OPLBase(Concurrency::TaskQueue<false> &task_queue) : task_queue_(task_queue) {}
Concurrency::DeferringAsyncTaskQueue &task_queue_;
Concurrency::TaskQueue<false> &task_queue_;
private:
uint8_t selected_register_ = 0;

View File

@ -12,7 +12,7 @@
using namespace Yamaha::OPL;
OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider, bool is_vrc7):
OPLL::OPLL(Concurrency::TaskQueue<false> &task_queue, int audio_divider, bool is_vrc7):
OPLBase(task_queue), audio_divider_(audio_divider), is_vrc7_(is_vrc7) {
// Due to the way that sound mixing works on the OPLL, the audio divider may not
// be larger than 4.
@ -74,7 +74,7 @@ OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider,
void OPLL::write_register(uint8_t address, uint8_t value) {
// The OPLL doesn't have timers or other non-audio functions, so all writes
// go to the audio queue.
task_queue_.defer([this, address, value] {
task_queue_.enqueue([this, address, value] {
// The first 8 locations are used to define the custom instrument, and have
// exactly the same format as the patch set arrays at the head of this file.
if(address < 8) {

View File

@ -24,7 +24,7 @@ namespace OPL {
class OPLL: public OPLBase<OPLL> {
public:
/// Creates a new OPLL or VRC7.
OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider = 1, bool is_vrc7 = false);
OPLL(Concurrency::TaskQueue<false> &task_queue, int audio_divider = 1, bool is_vrc7 = false);
/// As per ::SampleSource; provides audio output.
void get_samples(std::size_t number_of_samples, std::int16_t *target);

View File

@ -13,7 +13,7 @@
using namespace TI;
SN76489::SN76489(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue, int additional_divider) : task_queue_(task_queue) {
SN76489::SN76489(Personality personality, Concurrency::TaskQueue<false> &task_queue, int additional_divider) : task_queue_(task_queue) {
set_sample_volume_range(0);
switch(personality) {
@ -49,7 +49,7 @@ void SN76489::set_sample_volume_range(std::int16_t range) {
}
void SN76489::write(uint8_t value) {
task_queue_.defer([value, this] () {
task_queue_.enqueue([value, this] () {
if(value & 0x80) {
active_register_ = value;
}

View File

@ -23,7 +23,7 @@ class SN76489: public Outputs::Speaker::SampleSource {
};
/// Creates a new SN76489.
SN76489(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue, int additional_divider = 1);
SN76489(Personality personality, Concurrency::TaskQueue<false> &task_queue, int additional_divider = 1);
/// Writes a new value to the SN76489.
void write(uint8_t value);
@ -41,7 +41,7 @@ class SN76489: public Outputs::Speaker::SampleSource {
void evaluate_output_volume();
int volumes_[16];
Concurrency::DeferringAsyncTaskQueue &task_queue_;
Concurrency::TaskQueue<false> &task_queue_;
struct ToneChannel {
// Programmatically-set state; updated by the processor.

View File

@ -1,110 +0,0 @@
//
// AsyncTaskQueue.cpp
// Clock Signal
//
// Created by Thomas Harte on 07/10/2016.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "AsyncTaskQueue.hpp"
using namespace Concurrency;
AsyncTaskQueue::AsyncTaskQueue()
#ifndef USE_GCD
:
should_destruct_(false),
thread_([this] () {
while(!should_destruct_) {
std::function<void(void)> next_function;
// Take lock, check for a new task.
std::unique_lock lock(queue_mutex_);
if(!pending_tasks_.empty()) {
next_function = pending_tasks_.front();
pending_tasks_.pop_front();
}
if(next_function) {
// If there is a task, release lock and perform it.
lock.unlock();
next_function();
} else {
// If there isn't a task, atomically block on the processing condition and release the lock
// until there's something pending (and then release it again via scope).
processing_condition_.wait(lock);
}
}
})
#else
: serial_dispatch_queue_(dispatch_queue_create("com.thomasharte.clocksignal.asyntaskqueue", DISPATCH_QUEUE_SERIAL))
#endif
{}
AsyncTaskQueue::~AsyncTaskQueue() {
#ifdef USE_GCD
flush();
dispatch_release(serial_dispatch_queue_);
#else
// Set should destruct, and then give the thread a bit of a nudge
// via an empty enqueue.
should_destruct_ = true;
enqueue([](){});
// Wait for the thread safely to terminate.
thread_.join();
#endif
}
void AsyncTaskQueue::enqueue(std::function<void(void)> function) {
#ifdef USE_GCD
dispatch_async(serial_dispatch_queue_, ^{function();});
#else
std::lock_guard lock(queue_mutex_);
pending_tasks_.push_back(function);
processing_condition_.notify_all();
#endif
}
void AsyncTaskQueue::flush() {
#ifdef USE_GCD
dispatch_sync(serial_dispatch_queue_, ^{});
#else
auto flush_mutex = std::make_shared<std::mutex>();
auto flush_condition = std::make_shared<std::condition_variable>();
std::unique_lock lock(*flush_mutex);
enqueue([=] () {
std::unique_lock inner_lock(*flush_mutex);
flush_condition->notify_all();
});
flush_condition->wait(lock);
#endif
}
DeferringAsyncTaskQueue::~DeferringAsyncTaskQueue() {
perform();
flush();
}
void DeferringAsyncTaskQueue::defer(std::function<void(void)> function) {
if(!deferred_tasks_) {
deferred_tasks_ = std::make_unique<TaskList>();
deferred_tasks_->reserve(16);
}
deferred_tasks_->push_back(function);
}
void DeferringAsyncTaskQueue::perform() {
if(!deferred_tasks_) return;
enqueue([deferred_tasks_raw = deferred_tasks_.release()] {
std::unique_ptr<TaskList> deferred_tasks(deferred_tasks_raw);
for(const auto &function : *deferred_tasks) {
function();
}
});
}
void DeferringAsyncTaskQueue::flush() {
perform();
AsyncTaskQueue::flush();
}

View File

@ -12,11 +12,11 @@
#include <atomic>
#include <condition_variable>
#include <functional>
#include <list>
#include <memory>
#include <thread>
#include <vector>
#include "../ClockReceiver/TimeTypes.hpp"
#if defined(__APPLE__) && !defined(IGNORE_APPLE)
#include <dispatch/dispatch.h>
#define USE_GCD
@ -24,81 +24,159 @@
namespace Concurrency {
using TaskList = std::vector<std::function<void(void)>>;
/// An implementation detail; provides the time-centric part of a TaskQueue with a real Performer.
template <typename Performer> struct TaskQueueStorage {
template <typename... Args> TaskQueueStorage(Args&&... args) :
performer(std::forward<Args>(args)...),
last_fired_(Time::nanos_now()) {}
/*!
An async task queue allows a caller to enqueue void(void) functions. Those functions are guaranteed
to be performed serially and asynchronously from the caller. A caller may also request to flush,
causing it to block until all previously-enqueued functions are complete.
*/
class AsyncTaskQueue {
public:
AsyncTaskQueue();
virtual ~AsyncTaskQueue();
Performer performer;
/*!
Adds @c function to the queue.
@discussion Functions will be performed serially and asynchronously. This method is safe to
call from multiple threads.
@parameter function The function to enqueue.
*/
void enqueue(std::function<void(void)> function);
/*!
Blocks the caller until all previously-enqueud functions have completed.
*/
void flush();
protected:
void update() {
auto time_now = Time::nanos_now();
performer.perform(time_now - last_fired_);
last_fired_ = time_now;
}
private:
#ifdef USE_GCD
dispatch_queue_t serial_dispatch_queue_;
#else
std::atomic_bool should_destruct_;
std::condition_variable processing_condition_;
std::mutex queue_mutex_;
std::list<std::function<void(void)>> pending_tasks_;
Time::Nanos last_fired_;
};
std::thread thread_;
#endif
/// An implementation detail; provides a no-op implementation of time advances for TaskQueues without a Performer.
template <> struct TaskQueueStorage<int> {
TaskQueueStorage() {}
protected:
void update() {}
};
/*!
A deferring async task queue is one that accepts a list of functions to be performed but defers
any action until told to perform. It performs them by enquing a single asynchronous task that will
perform the deferred tasks in order.
A task queue allows a caller to enqueue void(void) functions. Those functions are guaranteed
to be performed serially and asynchronously from the caller.
It therefore offers similar semantics to an asynchronous task queue, but allows for management of
synchronisation costs, since neither defer nor perform make any effort to be thread safe.
If @c perform_automatically is true, functions will be performed as soon as is possible,
at the cost of thread synchronisation.
If @c perform_automatically is false, functions will be queued up and not dispatched
until a call to perform().
If a @c Performer type is supplied then a public member, @c performer will be constructed
with the arguments supplied to TaskQueue's constructor, and that class will receive calls of the
form @c .perform(nanos) to update it to every batch of new actions.
*/
class DeferringAsyncTaskQueue: public AsyncTaskQueue {
template <bool perform_automatically, typename Performer = int> class TaskQueue: public TaskQueueStorage<Performer> {
public:
~DeferringAsyncTaskQueue();
template <typename... Args> TaskQueue(Args&&... args) :
TaskQueueStorage<Performer>(std::forward<Args>(args)...),
thread_{
[this] {
ActionVector actions;
/*!
Adds a function to the deferral list.
while(!should_quit) {
// Wait for new actions to be signalled, and grab them.
std::unique_lock lock(condition_mutex_);
while(actions_.empty()) {
condition_.wait(lock);
}
std::swap(actions, actions_);
lock.unlock();
This is not thread safe; it should be serialised with other calls to itself and to perform.
*/
void defer(std::function<void(void)> function);
// Update to now (which is possibly a no-op).
TaskQueueStorage<Performer>::update();
/*!
Enqueues a function that will perform all currently deferred functions, in the
order that they were deferred.
// Perform the actions and destroy them.
for(const auto &action: actions) {
action();
}
actions.clear();
}
}
} {}
This is not thread safe; it should be serialised with other calls to itself and to defer.
*/
void perform();
/// Enqueus @c post_action to be performed asynchronously at some point
/// in the future. If @c perform_automatically is @c true then the action
/// will be performed as soon as possible. Otherwise it will sit unsheculed until
/// a call to @c perform().
///
/// Actions may be elided.
///
/// If this TaskQueue has a @c Performer then the action will be performed
/// on the same thread as the performer, after the performer has been updated
/// to 'now'.
void enqueue(const std::function<void(void)> &post_action) {
std::lock_guard guard(condition_mutex_);
actions_.push_back(post_action);
/*!
Blocks the caller until all previously-enqueud functions have completed.
*/
void flush();
if constexpr (perform_automatically) {
condition_.notify_all();
}
}
/// Causes any enqueued actions that are not yet scheduled to be scheduled.
void perform() {
if(actions_.empty()) {
return;
}
condition_.notify_all();
}
/// Permanently stops this task queue, blocking until that has happened.
/// All pending actions will be performed first.
///
/// The queue cannot be restarted; this is a destructive action.
void stop() {
if(thread_.joinable()) {
should_quit = true;
enqueue([] {});
if constexpr (!perform_automatically) {
perform();
}
thread_.join();
}
}
/// Schedules any remaining unscheduled work, then blocks synchronously
/// until all scheduled work has been performed.
void flush() {
std::mutex flush_mutex;
std::condition_variable flush_condition;
bool has_run = false;
std::unique_lock lock(flush_mutex);
enqueue([&flush_mutex, &flush_condition, &has_run] () {
std::unique_lock inner_lock(flush_mutex);
has_run = true;
flush_condition.notify_all();
});
if constexpr (!perform_automatically) {
perform();
}
flush_condition.wait(lock, [&has_run] { return has_run; });
}
~TaskQueue() {
stop();
}
private:
std::unique_ptr<TaskList> deferred_tasks_;
// The list of actions waiting be performed. These will be elided,
// increasing their latency, if the emulation thread falls behind.
using ActionVector = std::vector<std::function<void(void)>>;
ActionVector actions_;
// Necessary synchronisation parts.
std::atomic<bool> should_quit = false;
std::mutex condition_mutex_;
std::condition_variable condition_;
// Ensure the thread isn't constructed until after the mutex
// and condition variable.
std::thread thread_;
};
}
#endif /* Concurrency_hpp */
#endif /* AsyncTaskQueue_hpp */

View File

@ -1,98 +0,0 @@
//
// AsyncUpdater.h
// Clock Signal
//
// Created by Thomas Harte on 06/07/2022.
// Copyright © 2022 Thomas Harte. All rights reserved.
//
#ifndef AsyncUpdater_hpp
#define AsyncUpdater_hpp
#include <atomic>
#include <condition_variable>
#include <mutex>
#include "../ClockReceiver/TimeTypes.hpp"
namespace Concurrency {
template <typename Performer> class AsyncUpdater {
public:
template <typename... Args> AsyncUpdater(Args&&... args) :
performer(std::forward<Args>(args)...),
performer_thread_{
[this] {
Time::Nanos last_fired = Time::nanos_now();
ActionVector actions;
while(!should_quit) {
// Wait for new actions to be signalled, and grab them.
std::unique_lock lock(condition_mutex_);
while(actions_.empty()) {
condition_.wait(lock);
}
std::swap(actions, actions_);
lock.unlock();
// Update to now.
auto time_now = Time::nanos_now();
performer.perform(time_now - last_fired);
last_fired = time_now;
// Perform the actions.
for(const auto& action: actions) {
action();
}
actions.clear();
}
}
} {}
/// Run the performer up to 'now' and then perform @c post_action.
///
/// @c post_action will be performed asynchronously, on the same
/// thread as the performer.
///
/// Actions may be elided,
void update(const std::function<void(void)> &post_action) {
std::lock_guard guard(condition_mutex_);
actions_.push_back(post_action);
condition_.notify_all();
}
void stop() {
if(performer_thread_.joinable()) {
should_quit = true;
update([] {});
performer_thread_.join();
}
}
~AsyncUpdater() {
stop();
}
// The object that will actually receive time advances.
Performer performer;
private:
// The list of actions waiting be performed. These will be elided,
// increasing their latency, if the emulation thread falls behind.
using ActionVector = std::vector<std::function<void(void)>>;
ActionVector actions_;
// Necessary synchronisation parts.
std::atomic<bool> should_quit = false;
std::mutex condition_mutex_;
std::condition_variable condition_;
// Ensure the thread isn't constructed until after the mutex
// and condition variable.
std::thread performer_thread_;
};
}
#endif /* AsyncUpdater_hpp */

View File

@ -149,7 +149,7 @@ class Audio: public DMADevice<4> {
// Transient output state, and its destination.
Outputs::Speaker::PushLowpass<true> speaker_;
Concurrency::AsyncTaskQueue queue_;
Concurrency::TaskQueue<true> queue_;
using AudioBuffer = std::array<int16_t, 4096>;
static constexpr int BufferCount = 3;

View File

@ -156,7 +156,7 @@ class AYDeferrer {
}
private:
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
GI::AY38910::AY38910<true> ay_;
Outputs::Speaker::PullLowpass<GI::AY38910::AY38910<true>> speaker_;
HalfCycles cycles_since_update_;

View File

@ -95,7 +95,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
uint8_t ram_[65536], aux_ram_[65536];
std::vector<uint8_t> rom_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
Audio::Toggle audio_toggle_;
Outputs::Speaker::PullLowpass<Audio::Toggle> speaker_;
Cycles cycles_since_audio_update_;

View File

@ -1150,7 +1150,7 @@ class ConcreteMachine:
Apple::Disk::DiskIIDrive drives525_[2];
// The audio parts.
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
Apple::IIgs::Sound::GLU sound_glu_;
Audio::Toggle audio_toggle_;
using AudioSource = Outputs::Speaker::CompoundSource<Apple::IIgs::Sound::GLU, Audio::Toggle>;

View File

@ -16,7 +16,7 @@
using namespace Apple::IIgs::Sound;
GLU::GLU(Concurrency::DeferringAsyncTaskQueue &audio_queue) : audio_queue_(audio_queue) {
GLU::GLU(Concurrency::TaskQueue<false> &audio_queue) : audio_queue_(audio_queue) {
// Reset all pending stores.
MemoryWrite disabled_write;
disabled_write.enabled = false;
@ -42,7 +42,7 @@ void GLU::set_data(uint8_t data) {
// Register access.
const auto address = address_; // To make sure I don't inadvertently 'capture' address_.
local_.set_register(address, data);
audio_queue_.defer([this, address, data] () {
audio_queue_.enqueue([this, address, data] () {
remote_.set_register(address, data);
});
}
@ -191,7 +191,7 @@ void GLU::set_sample_volume_range(std::int16_t range) {
void GLU::set_control(uint8_t control) {
local_.control = control;
audio_queue_.defer([this, control] () {
audio_queue_.enqueue([this, control] () {
remote_.control = control;
});
}

View File

@ -21,7 +21,7 @@ namespace Sound {
class GLU: public Outputs::Speaker::SampleSource {
public:
GLU(Concurrency::DeferringAsyncTaskQueue &audio_queue);
GLU(Concurrency::TaskQueue<false> &audio_queue);
void set_control(uint8_t);
uint8_t get_control();
@ -42,7 +42,7 @@ class GLU: public Outputs::Speaker::SampleSource {
void skip_samples(const std::size_t number_of_samples);
private:
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::TaskQueue<false> &audio_queue_;
uint16_t address_ = 0;

View File

@ -18,7 +18,7 @@ const std::size_t sample_length = 352 / 2;
}
Audio::Audio(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {}
Audio::Audio(Concurrency::TaskQueue<false> &task_queue) : task_queue_(task_queue) {}
// MARK: - Inputs
@ -35,7 +35,7 @@ void Audio::set_volume(int volume) {
posted_volume_ = volume;
// Post the volume change as a deferred event.
task_queue_.defer([this, volume] () {
task_queue_.enqueue([this, volume] () {
volume_ = volume;
set_volume_multiplier();
});
@ -47,7 +47,7 @@ void Audio::set_enabled(bool on) {
posted_enable_mask_ = int(on);
// Post the enabled mask change as a deferred event.
task_queue_.defer([this, on] () {
task_queue_.enqueue([this, on] () {
enabled_mask_ = int(on);
set_volume_multiplier();
});

View File

@ -27,7 +27,7 @@ namespace Macintosh {
*/
class Audio: public ::Outputs::Speaker::SampleSource {
public:
Audio(Concurrency::DeferringAsyncTaskQueue &task_queue);
Audio(Concurrency::TaskQueue<false> &task_queue);
/*!
Macintosh audio is (partly) sourced by the same scanning
@ -58,7 +58,7 @@ class Audio: public ::Outputs::Speaker::SampleSource {
constexpr static bool get_is_stereo() { return false; }
private:
Concurrency::DeferringAsyncTaskQueue &task_queue_;
Concurrency::TaskQueue<false> &task_queue_;
// A queue of fetched samples; read from by one thread,
// written to by another.

View File

@ -16,7 +16,7 @@ namespace Apple {
namespace Macintosh {
struct DeferredAudio {
Concurrency::DeferringAsyncTaskQueue queue;
Concurrency::TaskQueue<false> queue;
Audio audio;
Outputs::Speaker::PullLowpass<Audio> speaker;
HalfCycles time_since_update;

View File

@ -39,7 +39,7 @@ class Bus {
PIA mos6532_;
TIA tia_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
TIASound tia_sound_;
Outputs::Speaker::PullLowpass<TIASound> speaker_;

View File

@ -10,7 +10,7 @@
using namespace Atari2600;
Atari2600::TIASound::TIASound(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
Atari2600::TIASound::TIASound(Concurrency::TaskQueue<false> &audio_queue) :
audio_queue_(audio_queue),
poly4_counter_{0x00f, 0x00f},
poly5_counter_{0x01f, 0x01f},
@ -18,20 +18,20 @@ Atari2600::TIASound::TIASound(Concurrency::DeferringAsyncTaskQueue &audio_queue)
{}
void Atari2600::TIASound::set_volume(int channel, uint8_t volume) {
audio_queue_.defer([target = &volume_[channel], volume]() {
audio_queue_.enqueue([target = &volume_[channel], volume]() {
*target = volume & 0xf;
});
}
void Atari2600::TIASound::set_divider(int channel, uint8_t divider) {
audio_queue_.defer([this, channel, divider]() {
audio_queue_.enqueue([this, channel, divider]() {
divider_[channel] = divider & 0x1f;
divider_counter_[channel] = 0;
});
}
void Atari2600::TIASound::set_control(int channel, uint8_t control) {
audio_queue_.defer([target = &control_[channel], control]() {
audio_queue_.enqueue([target = &control_[channel], control]() {
*target = control & 0xf;
});
}

View File

@ -20,7 +20,7 @@ constexpr int CPUTicksPerAudioTick = 2;
class TIASound: public Outputs::Speaker::SampleSource {
public:
TIASound(Concurrency::DeferringAsyncTaskQueue &audio_queue);
TIASound(Concurrency::TaskQueue<false> &audio_queue);
void set_volume(int channel, uint8_t volume);
void set_divider(int channel, uint8_t divider);
@ -32,7 +32,7 @@ class TIASound: public Outputs::Speaker::SampleSource {
static constexpr bool get_is_stereo() { return false; }
private:
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::TaskQueue<false> &audio_queue_;
uint8_t volume_[2];
uint8_t divider_[2];

View File

@ -485,7 +485,7 @@ class ConcreteMachine:
JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> keyboard_acia_;
JustInTimeActor<Motorola::ACIA::ACIA, HalfCycles, 16> midi_acia_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
GI::AY38910::AY38910<false> ay_;
Outputs::Speaker::PullLowpass<GI::AY38910::AY38910<false>> speaker_;
HalfCycles cycles_since_audio_update_;

View File

@ -381,7 +381,7 @@ class ConcreteMachine:
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
JustInTimeActor<TI::TMS::TMS9918> vdp_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
TI::SN76489 sn76489_;
GI::AY38910::AY38910<false> ay_;
Outputs::Speaker::CompoundSource<TI::SN76489, GI::AY38910::AY38910<false>> mixer_;

View File

@ -770,7 +770,7 @@ template <bool has_scsi_bus> class ConcreteMachine:
// Outputs
JustInTimeActor<VideoOutput, Cycles> video_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
SoundGenerator sound_generator_;
Outputs::Speaker::PullLowpass<SoundGenerator> speaker_;

View File

@ -12,7 +12,7 @@
using namespace Electron;
SoundGenerator::SoundGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
SoundGenerator::SoundGenerator(Concurrency::TaskQueue<false> &audio_queue) :
audio_queue_(audio_queue) {}
void SoundGenerator::set_sample_volume_range(std::int16_t range) {
@ -36,13 +36,13 @@ void SoundGenerator::skip_samples(std::size_t number_of_samples) {
}
void SoundGenerator::set_divider(uint8_t divider) {
audio_queue_.defer([this, divider]() {
audio_queue_.enqueue([this, divider]() {
divider_ = divider * 32 / clock_rate_divider;
});
}
void SoundGenerator::set_is_enabled(bool is_enabled) {
audio_queue_.defer([this, is_enabled]() {
audio_queue_.enqueue([this, is_enabled]() {
is_enabled_ = is_enabled;
counter_ = 0;
});

View File

@ -16,7 +16,7 @@ namespace Electron {
class SoundGenerator: public ::Outputs::Speaker::SampleSource {
public:
SoundGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue);
SoundGenerator(Concurrency::TaskQueue<false> &audio_queue);
void set_divider(uint8_t divider);
@ -31,7 +31,7 @@ class SoundGenerator: public ::Outputs::Speaker::SampleSource {
static constexpr bool get_is_stereo() { return false; }
private:
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::TaskQueue<false> &audio_queue_;
unsigned int counter_ = 0;
unsigned int divider_ = 0;
bool is_enabled_ = false;

View File

@ -12,12 +12,12 @@ using namespace Enterprise::Dave;
// MARK: - Audio generator
Audio::Audio(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
Audio::Audio(Concurrency::TaskQueue<false> &audio_queue) :
audio_queue_(audio_queue) {}
void Audio::write(uint16_t address, uint8_t value) {
address &= 0x1f;
audio_queue_.defer([address, value, this] {
audio_queue_.enqueue([address, value, this] {
switch(address) {
case 0: case 2: case 4:
channels_[address >> 1].reload = (channels_[address >> 1].reload & 0xff00) | value;
@ -63,7 +63,7 @@ void Audio::write(uint16_t address, uint8_t value) {
}
void Audio::set_sample_volume_range(int16_t range) {
audio_queue_.defer([range, this] {
audio_queue_.enqueue([range, this] {
volume_ = range / (63*4);
});
}

View File

@ -30,7 +30,7 @@ enum class Interrupt: uint8_t {
*/
class Audio: public Outputs::Speaker::SampleSource {
public:
Audio(Concurrency::DeferringAsyncTaskQueue &audio_queue);
Audio(Concurrency::TaskQueue<false> &audio_queue);
/// Modifies an register in the audio range; only the low 4 bits are
/// used for register decoding so it's assumed that the caller has
@ -43,7 +43,7 @@ class Audio: public Outputs::Speaker::SampleSource {
void get_samples(std::size_t number_of_samples, int16_t *target);
private:
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::TaskQueue<false> &audio_queue_;
// Global divider (i.e. 8MHz/12Mhz switch).
uint8_t global_divider_;

View File

@ -705,7 +705,7 @@ template <bool has_disk_controller, bool is_6mhz> class ConcreteMachine:
bool previous_nick_interrupt_line_ = false;
// Cf. timing guesses above.
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
Dave::Audio dave_audio_;
Outputs::Speaker::PullLowpass<Dave::Audio> speaker_;
HalfCycles time_since_audio_update_;

View File

@ -747,7 +747,7 @@ class ConcreteMachine:
JustInTimeActor<TI::TMS::TMS9918> vdp_;
Intel::i8255::i8255<i8255PortHandler> i8255_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
GI::AY38910::AY38910<false> ay_;
Audio::Toggle audio_toggle_;
Konami::SCC scc_;

View File

@ -459,7 +459,7 @@ class ConcreteMachine:
// This is as per the audio control register;
// see https://www.smspower.org/Development/AudioControlPort
update_audio();
audio_queue_.defer([this, mode] {
audio_queue_.enqueue([this, mode] {
switch(mode & 3) {
case 0: // SN76489 only; the default.
mixer_.set_relative_volumes({1.0f, 0.0f});
@ -487,7 +487,7 @@ class ConcreteMachine:
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
JustInTimeActor<TI::TMS::TMS9918> vdp_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
TI::SN76489 sn76489_;
Yamaha::OPL::OPLL opll_;
Outputs::Speaker::CompoundSource<decltype(sn76489_), decltype(opll_)> mixer_;

View File

@ -178,7 +178,7 @@ class TapePlayer: public Storage::Tape::BinaryTapePlayer {
*/
class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
public:
VIAPortHandler(Concurrency::DeferringAsyncTaskQueue &audio_queue, AY &ay8910, Speaker &speaker, TapePlayer &tape_player, Keyboard &keyboard) :
VIAPortHandler(Concurrency::TaskQueue<false> &audio_queue, AY &ay8910, Speaker &speaker, TapePlayer &tape_player, Keyboard &keyboard) :
audio_queue_(audio_queue), ay8910_(ay8910), speaker_(speaker), tape_player_(tape_player), keyboard_(keyboard)
{
// Attach a couple of joysticks.
@ -254,7 +254,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
uint8_t porta_output_ = 0xff;
HalfCycles cycles_since_ay_update_;
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::TaskQueue<false> &audio_queue_;
AY &ay8910_;
Speaker &speaker_;
TapePlayer &tape_player_;
@ -711,7 +711,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS
// Outputs
JustInTimeActor<VideoOutput, Cycles> video_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
GI::AY38910::AY38910<false> ay8910_;
Speaker speaker_;

View File

@ -468,7 +468,7 @@ template<bool is_zx81> class ConcreteMachine:
}
// MARK: - Audio
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
using AY = GI::AY38910::AY38910<false>;
AY ay_;
Outputs::Speaker::PullLowpass<AY> speaker_;

View File

@ -849,7 +849,7 @@ template<Model model> class ConcreteMachine:
}
// MARK: - Audio.
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::TaskQueue<false> audio_queue_;
GI::AY38910::AY38910<false> ay_;
Audio::Toggle audio_toggle_;
Outputs::Speaker::CompoundSource<GI::AY38910::AY38910<false>, Audio::Toggle> mixer_;

View File

@ -33,7 +33,6 @@
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; };
4B055A7E1FAE84AA0060FFFF /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B055A7C1FAE84A50060FFFF /* main.cpp */; };
4B055A8D1FAE85920060FFFF /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; };
4B055A8F1FAE85A90060FFFF /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; };
4B055A901FAE85A90060FFFF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; };
4B055A911FAE85B50060FFFF /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; };
@ -221,7 +220,6 @@
4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B322E031F5A2E3C004EB04C /* Z80Base.cpp */; };
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */; };
4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */; };
4B3940E71DA83C8300427841 /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; };
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; };
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */; };
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */; };
@ -348,7 +346,6 @@
4B7752C128217F490073E2C5 /* FAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B477709268FBE4D005C2340 /* FAT.cpp */; };
4B7752C228217F5C0073E2C5 /* Spectrum.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5D5C9525F56FC7001B4623 /* Spectrum.cpp */; };
4B7752C328217F720073E2C5 /* Z80.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD39526360DDF00B3C866 /* Z80.cpp */; };
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; };
4B778EF023A5D68C0000D260 /* 68000Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */; };
4B778EF123A5D6B50000D260 /* 9918.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04F91FC9FA3100F43484 /* 9918.cpp */; };
4B778EF323A5DB230000D260 /* PCMSegment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518731F75E91800926311 /* PCMSegment.cpp */; };
@ -1298,7 +1295,6 @@
4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDump.hpp; sourceTree = "<group>"; };
4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AmstradCPC.cpp; sourceTree = "<group>"; };
4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AmstradCPC.hpp; sourceTree = "<group>"; };
4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AsyncTaskQueue.cpp; sourceTree = "<group>"; };
4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AsyncTaskQueue.hpp; sourceTree = "<group>"; };
4B3AF7D02413470E00873C0B /* Enum.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Enum.hpp; sourceTree = "<group>"; };
4B3AF7D12413472200873C0B /* Struct.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Struct.hpp; sourceTree = "<group>"; };
@ -2138,7 +2134,6 @@
4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMMachine.hpp; sourceTree = "<group>"; };
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MachineCycleTests.swift; sourceTree = "<group>"; };
4BE0151C286A8C8E00EA42E9 /* MemorySwitches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MemorySwitches.hpp; sourceTree = "<group>"; };
4BE0151E28766ECF00EA42E9 /* AsyncUpdater.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AsyncUpdater.hpp; sourceTree = "<group>"; };
4BE0A3EC237BB170002AB46F /* ST.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ST.cpp; sourceTree = "<group>"; };
4BE0A3ED237BB170002AB46F /* ST.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ST.hpp; sourceTree = "<group>"; };
4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_no_Rockwell_test.bin; path = "Klaus Dormann/65C02_no_Rockwell_test.bin"; sourceTree = "<group>"; };
@ -2753,9 +2748,7 @@
4B3940E81DA83C8700427841 /* Concurrency */ = {
isa = PBXGroup;
children = (
4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */,
4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */,
4BE0151E28766ECF00EA42E9 /* AsyncUpdater.hpp */,
);
name = Concurrency;
path = ../../Concurrency;
@ -5637,7 +5630,6 @@
4B0ACC2923775819008902D0 /* DMAController.cpp in Sources */,
4B055A951FAE85BB0060FFFF /* BitReverse.cpp in Sources */,
4B055ACE1FAE9B030060FFFF /* Plus3.cpp in Sources */,
4B055A8D1FAE85920060FFFF /* AsyncTaskQueue.cpp in Sources */,
4BAD13441FF709C700FD114A /* MSX.cpp in Sources */,
4B055AC41FAE9AE80060FFFF /* Keyboard.cpp in Sources */,
4B8DF506254E3C9D00F3433C /* ADB.cpp in Sources */,
@ -5887,7 +5879,6 @@
4B17B58B20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */,
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
4B051CA826781D6500CA44E8 /* StaticAnalyser.cpp in Sources */,
4B3940E71DA83C8300427841 /* AsyncTaskQueue.cpp in Sources */,
4B0E04FA1FC9FA3100F43484 /* 9918.cpp in Sources */,
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
4B4518841F75E91A00926311 /* UnformattedTrack.cpp in Sources */,
@ -6118,7 +6109,6 @@
4B7752A728217E060073E2C5 /* Blitter.cpp in Sources */,
4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */,
4B8DD3682633B2D400B3C866 /* SpectrumVideoContentionTests.mm in Sources */,
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */,
4B7752A928217E200073E2C5 /* 65816Storage.cpp in Sources */,
4B7752AC28217E6E0073E2C5 /* StaticAnalyser.cpp in Sources */,
4B778F1223A5EC720000D260 /* CRT.cpp in Sources */,

View File

@ -24,7 +24,7 @@
#include "../../../../ClockReceiver/TimeTypes.hpp"
#include "../../../../ClockReceiver/ScanSynchroniser.hpp"
#include "../../../../Concurrency/AsyncUpdater.hpp"
#include "../../../../Concurrency/AsyncTaskQueue.hpp"
#import "CSStaticAnalyser+TargetVector.h"
#import "NSBundle+DataResource.h"
@ -122,7 +122,7 @@ struct ActivityObserver: public Activity::Observer {
CSJoystickManager *_joystickManager;
NSMutableArray<CSMachineLED *> *_leds;
Concurrency::AsyncUpdater<MachineUpdater> updater;
Concurrency::TaskQueue<true, MachineUpdater> updater;
Time::ScanSynchroniser _scanSynchroniser;
NSTimer *_joystickTimer;
@ -455,7 +455,7 @@ struct ActivityObserver: public Activity::Observer {
}
- (void)applyInputEvent:(dispatch_block_t)event {
updater.update([event] {
updater.enqueue([event] {
event();
});
}
@ -669,13 +669,13 @@ struct ActivityObserver: public Activity::Observer {
#pragma mark - Timer
- (void)audioQueueIsRunningDry:(nonnull CSAudioQueue *)audioQueue {
updater.update([self] {
updater.enqueue([self] {
updater.performer.machine->flush_output(MachineTypes::TimedMachine::Output::Audio);
});
}
- (void)scanTargetViewDisplayLinkDidFire:(CSScanTargetView *)view now:(const CVTimeStamp *)now outputTime:(const CVTimeStamp *)outputTime {
updater.update([self] {
updater.enqueue([self] {
// Grab a pointer to the timed machine from somewhere where it has already
// been dynamically cast, to avoid that cost here.
MachineTypes::TimedMachine *const timed_machine = updater.performer.machine;

View File

@ -367,12 +367,12 @@ template <typename SampleSource> class PullLowpass: public LowpassBase<PullLowpa
The speaker will advance by obtaining data from the sample source supplied
at construction, filtering it and passing it on to the speaker's delegate if there is one.
*/
void run_for(Concurrency::DeferringAsyncTaskQueue &queue, const Cycles cycles) {
void run_for(Concurrency::TaskQueue<false> &queue, const Cycles cycles) {
if(cycles == Cycles(0)) {
return;
}
queue.defer([this, cycles] {
queue.enqueue([this, cycles] {
run_for(cycles);
});
}

View File

@ -80,7 +80,7 @@ class DiskImageHolderBase: public Disk {
protected:
std::set<Track::Address> unwritten_tracks_;
std::map<Track::Address, std::shared_ptr<Track>> cached_tracks_;
std::unique_ptr<Concurrency::AsyncTaskQueue> update_queue_;
std::unique_ptr<Concurrency::TaskQueue<true>> update_queue_;
};
/*!

View File

@ -20,7 +20,7 @@ template <typename T> bool DiskImageHolder<T>::get_is_read_only() {
template <typename T> void DiskImageHolder<T>::flush_tracks() {
if(!unwritten_tracks_.empty()) {
if(!update_queue_) update_queue_ = std::make_unique<Concurrency::AsyncTaskQueue>();
if(!update_queue_) update_queue_ = std::make_unique<Concurrency::TaskQueue<true>>();
using TrackMap = std::map<Track::Address, std::shared_ptr<Track>>;
std::shared_ptr<TrackMap> track_copies(new TrackMap);