Disable iothread pool wait-around under TSan

The iothread pool has a feature where, if the thread is emptied, some
threads will choose to wait around in case new work appears, up to a
certain amount of time (500 msec). This prevents thrashing where new
threads are rapidly created and destroyed as the user types. This is
implemented via `std::condition_variable::wait_for`. However this function
is not properly instrumented under Thread Sanitizer (see
https://github.com/google/sanitizers/issues/1259) so TSan reports false
positives. Just disable this feature under TSan.
This commit is contained in:
ridiculousfish 2021-02-06 22:05:43 -08:00
parent a942df3886
commit ced56d492f
2 changed files with 22 additions and 3 deletions

View File

@ -31,8 +31,26 @@
// which is too low, even tho the system can handle more than 64 threads.
#define IO_MAX_THREADS 1024
// iothread has a thread pool. Sometimes there's no work to do, but extant threads wait around for a
// while (on a condition variable) in case new work comes soon. However condition variables are not
// properly instrumented with Thread Sanitizer, so it fails to recognize when our mutex is locked.
// See https://github.com/google/sanitizers/issues/1259
// When using TSan, disable the wait-around feature.
#if defined(__has_feature)
#if __has_feature(thread_sanitizer)
#define IOTHREAD_TSAN_WORKAROUND 1
#endif
#endif
#ifdef __SANITIZE_THREAD__
#define IOTHREAD_TSAN_WORKAROUND 1
#endif
// The amount of time an IO thread many hang around to service requests, in milliseconds.
#ifdef IOTHREAD_TSAN_WORKAROUND
#define IO_WAIT_FOR_WORK_DURATION_MS 0
#else
#define IO_WAIT_FOR_WORK_DURATION_MS 500
#endif
using void_function_t = std::function<void()>;
@ -173,7 +191,8 @@ maybe_t<work_request_t> thread_pool_t::dequeue_work_or_commit_to_exit() {
auto data = this->req_data.acquire();
// If the queue is empty, check to see if we should wait.
// We should wait if our exiting would drop us below the soft min.
if (data->request_queue.empty() && data->total_threads == this->soft_min_threads) {
if (data->request_queue.empty() && data->total_threads == this->soft_min_threads &&
IO_WAIT_FOR_WORK_DURATION_MS > 0) {
data->waiting_threads += 1;
this->queue_cond.wait_for(data.get_lock(),
std::chrono::milliseconds(IO_WAIT_FOR_WORK_DURATION_MS));

View File

@ -17,11 +17,11 @@
// block) and use select() to poll it.
#if defined(__has_feature)
#if __has_feature(thread_sanitizer)
#define TOPIC_MONITOR_TSAN_WORKAROUND
#define TOPIC_MONITOR_TSAN_WORKAROUND 1
#endif
#endif
#ifdef __SANITIZE_THREAD__
#define TOPIC_MONITOR_TSAN_WORKAROUND
#define TOPIC_MONITOR_TSAN_WORKAROUND 1
#endif
wcstring generation_list_t::describe() const {