[llvm] r256087 - ThreadPool unittest: reimplement concurrency test, deterministically this time.

Mehdi Amini via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 18 23:06:39 PST 2015



Sent from my iPhone

> On Dec 18, 2015, at 8:30 PM, Vedant Kumar <vsk at apple.com> wrote:
> 
> Thanks :).
> 
>> +
>> +  /// Make sure this thread not progress faster than the main thread.
>> +  void waitForMainThread() {
>> +    while (!MainThreadReady) {
>> +      std::unique_lock<std::mutex> LockGuard(WaitMainThreadMutex);
>> +      WaitMainThread.wait(LockGuard, [&] { return MainThreadReady; });
> 
> Nit: wait(CV, Predicate) is guaranteed to not wake up spuriously. The surrounding while loop here is redundant.
> 
> 
>> +    }
>> +  }
>> +  std::condition_variable WaitMainThread;
>> +  std::mutex WaitMainThreadMutex;
>> +  bool MainThreadReady;
>> +
>> };
>> 
>> #define CHECK_UNSUPPORTED() \
>> @@ -68,12 +80,17 @@ TEST_F(ThreadPoolTest, AsyncBarrier) {
>> 
>>  std::atomic_int checked_in{0};
>> 
>> +  MainThreadReady = false;
>>  ThreadPool Pool;
>>  for (size_t i = 0; i < 5; ++i) {
>> -    Pool.async([&checked_in, i] {
>> +    Pool.async([this, &checked_in, i] {
>> +      waitForMainThread();
>>      ++checked_in;
>>    });
>>  }
>> +  ASSERT_EQ(0, checked_in);
>> +  MainThreadReady = true;
> 
> Paranoid nit: Can this assignment be reordered before the assert? If so, the main thread could context-switch to an async worker that's awake.

It's even worse: you're supposed to get the mutex before notifying a condition variable. I always forget that, Teresa already caught this on the original patch.



> Later, the assert will fail. Wdyt of changing this to --
> 
>  void setMainThreadReadyState(bool Ready) {
>    std::unique_lock<std::mutex> LockGuard(WaitMainThreadMutex);
>    MainThreadReady = Ready;
>  }
> 
>  setMainThreadReadyState(true);
> 
> That should still test the async behavior.

Would need the notify_all while holding the mutex. Otherwise LGTM.


Mehdi

> 
> vedant
> 
>> +  WaitMainThread.notify_all();
>>  Pool.wait();
>>  ASSERT_EQ(5, checked_in);
>> }
>> @@ -97,10 +114,15 @@ TEST_F(ThreadPoolTest, Async) {
>>  CHECK_UNSUPPORTED();
>>  ThreadPool Pool;
>>  std::atomic_int i{0};
>> -  Pool.async([&i] {
>> +  MainThreadReady = false;
>> +  Pool.async([this, &i] {
>> +    waitForMainThread();
>>    ++i;
>>  });
>>  Pool.async([&i] { ++i; });
>> +  ASSERT_NE(2, i.load());
>> +  MainThreadReady = true;
>> +  WaitMainThread.notify_all();
>>  Pool.wait();
>>  ASSERT_EQ(2, i.load());
>> }
>> @@ -109,11 +131,16 @@ TEST_F(ThreadPoolTest, GetFuture) {
>>  CHECK_UNSUPPORTED();
>>  ThreadPool Pool;
>>  std::atomic_int i{0};
>> -  Pool.async([&i] {
>> +  MainThreadReady = false;
>> +  Pool.async([this, &i] {
>> +    waitForMainThread();
>>    ++i;
>>  });
>>  // Force the future using get()
>>  Pool.async([&i] { ++i; }).get();
>> +  ASSERT_NE(2, i.load());
>> +  MainThreadReady = true;
>> +  WaitMainThread.notify_all();
>>  Pool.wait();
>>  ASSERT_EQ(2, i.load());
>> }
>> @@ -122,14 +149,18 @@ TEST_F(ThreadPoolTest, PoolDestruction)
>>  CHECK_UNSUPPORTED();
>>  // Test that we are waiting on destruction
>>  std::atomic_int checked_in{0};
>> -
>>  {
>> +    MainThreadReady = false;
>>    ThreadPool Pool;
>>    for (size_t i = 0; i < 5; ++i) {
>> -      Pool.async([&checked_in, i] {
>> +      Pool.async([this, &checked_in, i] {
>> +        waitForMainThread();
>>        ++checked_in;
>>      });
>>    }
>> +    ASSERT_EQ(0, checked_in);
>> +    MainThreadReady = true;
>> +    WaitMainThread.notify_all();
>>  }
>>  ASSERT_EQ(5, checked_in);
>> }
>> 
>> 
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
> 


More information about the llvm-commits mailing list