diff --git a/src/io.cpp b/src/io.cpp index 742f6bf41..f8f91d648 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -95,12 +95,11 @@ void io_buffer_t::begin_filling(autoclose_fd_t fd) { // indicates that the command substitution is done); in this case we will read until we get // EAGAIN and then give up. - // Construct a promise that can go into our background thread. + // Construct a promise. We will fulfill it in our fill thread, and wait for it in + // complete_background_fillthread(). Note that TSan complains if the promise's dtor races with + // the future's call to wait(), so we store the promise, not just its future (#7681). auto promise = std::make_shared>(); - - // Get the future associated with our promise. - // Note this should only ever be called once. - fillthread_waiter_ = promise->get_future(); + this->fill_waiter_ = promise; // Run our function to read until the receiver is closed. // It's OK to capture 'this' by value because 'this' waits for the promise in its dtor. @@ -146,8 +145,8 @@ separated_buffer_t io_buffer_t::complete_background_fillthread_and_take_buffer() // Wait for the fillthread to fulfill its promise, and then clear the future so we know we no // longer have one. - fillthread_waiter_.wait(); - fillthread_waiter_ = std::future{}; + fill_waiter_->get_future().wait(); + fill_waiter_.reset(); // Return our buffer, transferring ownership. auto locked_buff = buffer_.acquire(); diff --git a/src/io.h b/src/io.h index bb8d38e2d..e29eec16d 100644 --- a/src/io.h +++ b/src/io.h @@ -314,7 +314,7 @@ public: separated_buffer_t complete_background_fillthread_and_take_buffer(); /// Helper to return whether the fillthread is running. - bool fillthread_running() const { return fillthread_waiter_.valid(); } + bool fillthread_running() const { return fill_waiter_.get() != nullptr; } /// Buffer storing what we have read. owning_lock buffer_; @@ -322,9 +322,9 @@ public: /// Atomic flag indicating our fillthread should shut down. relaxed_atomic_bool_t shutdown_fillthread_{false}; - /// The future allowing synchronization with the background fillthread, if the fillthread is - /// running. The fillthread fulfills the corresponding promise when it exits. - std::future fillthread_waiter_{}; + /// A promise, allowing synchronization with the background fill operation. + /// The operation has a reference to this as well, and fulfills this promise when it exits. + std::shared_ptr> fill_waiter_{}; /// The item id of our background fillthread fd monitor item. uint64_t item_id_{0};