1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Resolves data race on write_pointers_ close to machine setup.

This commit is contained in:
Thomas Harte 2020-02-18 20:41:51 -05:00
parent a5e1765ce4
commit 95756f9716
2 changed files with 24 additions and 6 deletions

View File

@ -131,6 +131,8 @@ void ScanTarget::set_modals(Modals modals) {
Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() { Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() {
if(allocation_has_failed_) return nullptr; if(allocation_has_failed_) return nullptr;
std::lock_guard<std::mutex> lock_guard(write_pointers_mutex_);
const auto result = &scan_buffer_[write_pointers_.scan_buffer]; const auto result = &scan_buffer_[write_pointers_.scan_buffer];
const auto read_pointers = read_pointers_.load(); const auto read_pointers = read_pointers_.load();
@ -154,6 +156,7 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() {
void ScanTarget::end_scan() { void ScanTarget::end_scan() {
if(vended_scan_) { if(vended_scan_) {
std::lock_guard<std::mutex> lock_guard(write_pointers_mutex_);
vended_scan_->data_y = TextureAddressGetY(vended_write_area_pointer_); vended_scan_->data_y = TextureAddressGetY(vended_write_area_pointer_);
vended_scan_->line = write_pointers_.line; vended_scan_->line = write_pointers_.line;
vended_scan_->scan.end_points[0].data_offset += TextureAddressGetX(vended_write_area_pointer_); vended_scan_->scan.end_points[0].data_offset += TextureAddressGetX(vended_write_area_pointer_);
@ -182,6 +185,8 @@ uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignmen
return nullptr; return nullptr;
} }
std::lock_guard<std::mutex> lock_guard(write_pointers_mutex_);
// Determine where the proposed write area would start and end. // Determine where the proposed write area would start and end.
uint16_t output_y = TextureAddressGetY(write_pointers_.write_area); uint16_t output_y = TextureAddressGetY(write_pointers_.write_area);
@ -222,6 +227,8 @@ uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignmen
void ScanTarget::end_data(size_t actual_length) { void ScanTarget::end_data(size_t actual_length) {
if(allocation_has_failed_ || !data_is_allocated_) return; if(allocation_has_failed_ || !data_is_allocated_) return;
std::lock_guard<std::mutex> lock_guard(write_pointers_mutex_);
// Bookend the start of the new data, to safeguard for precision errors in sampling. // Bookend the start of the new data, to safeguard for precision errors in sampling.
memcpy( memcpy(
&write_area_texture_[size_t(write_pointers_.write_area - 1) * data_type_size_], &write_area_texture_[size_t(write_pointers_.write_area - 1) * data_type_size_],
@ -268,6 +275,7 @@ void ScanTarget::announce(Event event, bool is_visible, const Outputs::Display::
if(output_is_visible_ == is_visible) return; if(output_is_visible_ == is_visible) return;
if(is_visible) { if(is_visible) {
const auto read_pointers = read_pointers_.load(); const auto read_pointers = read_pointers_.load();
std::lock_guard<std::mutex> lock_guard(write_pointers_mutex_);
// Commit the most recent line only if any scans fell on it. // Commit the most recent line only if any scans fell on it.
// Otherwise there's no point outputting it, it'll contribute nothing. // Otherwise there's no point outputting it, it'll contribute nothing.
@ -336,14 +344,20 @@ void ScanTarget::announce(Event event, bool is_visible, const Outputs::Display::
void ScanTarget::setup_pipeline() { void ScanTarget::setup_pipeline() {
const auto data_type_size = Outputs::Display::size_for_data_type(modals_.input_data_type); const auto data_type_size = Outputs::Display::size_for_data_type(modals_.input_data_type);
if(data_type_size != data_type_size_) {
// TODO: flush output.
data_type_size_ = data_type_size; // Ensure the lock guard here has a restricted scope; this is the only time that a thread
write_area_texture_.resize(WriteAreaWidth*WriteAreaHeight*data_type_size_); // other than the main owner of write_pointers_ may adjust it.
{
std::lock_guard<std::mutex> lock_guard(write_pointers_mutex_);
if(data_type_size != data_type_size_) {
// TODO: flush output.
write_pointers_.scan_buffer = 0; data_type_size_ = data_type_size;
write_pointers_.write_area = 0; write_area_texture_.resize(WriteAreaWidth*WriteAreaHeight*data_type_size_);
write_pointers_.scan_buffer = 0;
write_pointers_.write_area = 0;
}
} }
// Prepare to bind line shaders. // Prepare to bind line shaders.

View File

@ -116,6 +116,10 @@ class ScanTarget: public Outputs::Display::ScanTarget {
/// A pointer to the next thing that should be provided to the caller for data. /// A pointer to the next thing that should be provided to the caller for data.
PointerSet write_pointers_; PointerSet write_pointers_;
/// A mutex for gettng access to write_pointers_; access to write_pointers_
/// or data_type_size_ is almost never contended, so this is cheap for the main use case.
std::mutex write_pointers_mutex_;
/// A pointer to the final thing currently cleared for submission. /// A pointer to the final thing currently cleared for submission.
std::atomic<PointerSet> submit_pointers_; std::atomic<PointerSet> submit_pointers_;