Hash :
ca2378b7
Author :
Date :
2023-03-08T18:44:58
Assert that waitForStep steps go in increasing order. This should make race conditions like the one in https://crrev.com/c/4321809 much more obvious from the logs and catch some of the issues when writing or updates tests. The test that has to be updated here is an example why we probably don't want to just replace `== step` with `>= step` Bug: angleproject:8071 Change-Id: I2c740931046bb5a9115474b7d67f76e9a21c30a4 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4322049 Commit-Queue: Roman Lavrov <romanl@google.com> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// MultiThreadSteps.h:
// Synchronization help for tests that use multiple threads.
#include "gl_raii.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
class EGLWindow;
namespace angle
{
namespace
{
// The following class is used by tests that need multiple threads that coordinate their actions
// via an enum of "steps". This enum is the template type E. The enum must have at least the
// following values:
//
// - Finish This value indicates that one thread has finished its last step and is cleaning up.
// The other thread waits for this before it does its last step and cleans up.
// - Abort This value indicates that one thread encountered a GL error and has exited. This
// will cause the other thread (that is waiting for a different step) to also abort.
//
// This class is RAII. It is declared at the top of a thread, and will be deconstructed at the end
// of the thread's outer block. If the thread encounters a GL error, the deconstructor will abort
// the other thread using the E:Abort step.
template <typename E>
class ThreadSynchronization
{
public:
ThreadSynchronization(E *currentStep, std::mutex *mutex, std::condition_variable *condVar)
: mCurrentStep(currentStep), mMutex(mutex), mCondVar(condVar)
{}
~ThreadSynchronization()
{
bool isAborting = false;
{
// If the other thread isn't finished, cause it to abort.
std::unique_lock<std::mutex> lock(*mMutex);
isAborting = *mCurrentStep != E::Finish;
if (isAborting)
{
*mCurrentStep = E::Abort;
}
}
mCondVar->notify_all();
}
// Helper functions to synchronize the threads so that the operations are executed in the
// specific order the test is written for.
bool waitForStep(E waitStep)
{
std::unique_lock<std::mutex> lock(*mMutex);
while (*mCurrentStep != waitStep)
{
// If necessary, abort execution as the other thread has encountered a GL error.
if (*mCurrentStep == E::Abort)
{
return false;
}
// Expect increasing order to reduce risk of race conditions / deadlocks.
if (*mCurrentStep > waitStep)
{
FATAL() << "waitForStep requires increasing order. mCurrentStep="
<< (int)*mCurrentStep << ", waitStep=" << (int)waitStep;
}
mCondVar->wait(lock);
}
return true;
}
void nextStep(E newStep)
{
{
std::unique_lock<std::mutex> lock(*mMutex);
*mCurrentStep = newStep;
}
mCondVar->notify_all();
}
private:
E *mCurrentStep;
std::mutex *mMutex;
std::condition_variable *mCondVar;
};
} // anonymous namespace
using LockStepThreadFunc = std::function<void(EGLDisplay, EGLSurface, EGLContext)>;
void RunLockStepThreads(EGLWindow *window, size_t threadCount, LockStepThreadFunc threadFuncs[]);
} // namespace angle