Как написать автоматический тест на безопасность потоков

У меня есть класс, который не является потокобезопасным:

class Foo { 
    /* Abstract base class, code which is not thread safe */ 
};

Более того, если у вас есть объекты foo1 и foo2, вы не можете вызывать foo1-> someFunc () до тех пор, пока foo2-> anotherFunc () не вернется (это может произойти с двумя потоками). Это ситуация, и ее нельзя изменить (подкласс Foo фактически является оболочкой для скрипта python).

Чтобы предотвратить нежелательные звонки, я создал следующее -

class FooWrapper {
public:
    FooWrapper(Foo* foo, FooWrappersMutex* mutex);
    /* Wrapped functions from Foo */
};

Внутри FooWrapper оборачивает вызовы функций Foo с помощью общего мьютекса.

Я хочу протестировать FooWrapper на безопасность потоков. Моя самая большая проблема заключается в том, что потоки управляются операционной системой, а это означает, что у меня меньше контроля над их выполнением. Я хотел бы протестировать следующий сценарий:

  • Поток 1 вызывает fooWrapper1-> someFunc () и блокирует внутри функции
  • Поток 2 вызывает fooWrapper2-> anotherFunc () и немедленно возвращается (поскольку someFunc () все еще выполняется)
  • Поток 1 завершает выполнение

Как проще всего протестировать такой сценарий автоматически?

Я использую QT на Win32, хотя я бы предпочел решение, по крайней мере, кроссплатформенное, как и QT.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
9
0
5 609
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Пока что я написал следующий код. Иногда это работает, а иногда тест терпит неудачу, поскольку спящего режима недостаточно для запуска всех потоков.

//! Give some time to the other threads
static void YieldThread()
{
#ifdef _WIN32
    Sleep(10);
#endif //_WIN32
}

class FooWithMutex: public Foo {
public:
    QMutex m_mutex;
    virtual void someFunc()
    {
        QMutexLocker(&m_mutex);
    }
    virtual void anotherFunc()
    {
        QMutexLocker(&m_mutex);
    }
};

class ThreadThatCallsFooFunc1: public QThread {
public:
    ThreadThatCallsFooFunc1( FooWrapper& fooWrapper )
        : m_fooWrapper(fooWrapper) {}

    virtual void run()
    {
        m_fooWrapper.someFunc();
    }
private:
    FooWrapper& m_fooWrapper;
};

class ThreadThatCallsFooFunc2: public QThread {
public:
    ThreadThatCallsFooFunc2( FooWrapper& fooWrapper )
        : m_fooWrapper(fooWrapper) {}

    virtual void run()
    {
        m_fooWrapper.anotherFunc();
    }
private:
    FooWrapper& m_fooWrapper;
};

TEST(ScriptPluginWrapperTest, CallsFromMultipleThreads)
{
    // FooWithMutex inherits the abstract Foo and adds
    // mutex lock/unlock on each function.
    FooWithMutex fooWithMutex;

    FooWrapper fooWrapper( &fooWithMutex );
    ThreadThatCallsFooFunc1 thread1(fooWrapper);
    ThreadThatCallsFooFunc2 thread2(fooWrapper);

    fooWithMutex.m_mutex.lock();
    thread1.start(); // Should block

    YieldThread();
    ASSERT_FALSE( thread1.isFinished() );

    thread2.start(); // Should finish immediately
    YieldThread();
    ASSERT_TRUE( thread2.isFinished() );

    fooWithMutex.m_mutex.unlock();

    YieldThread();
    EXPECT_TRUE( thread1.isFinished() );
}

Возможно, вы захотите проверить ШАХМАТЫ: инструмент для систематического тестирования параллельного программного обеспечения от Microsoft Research. Это среда тестирования многопоточных программ (как .NET, так и машинного кода).

Если я правильно понял, он заменяет библиотеки потоков операционной системы своими собственными, чтобы управлять переключением потоков. Затем он анализирует программу, чтобы выяснить все возможные способы, которыми потоки выполнения потоков могут чередоваться, и повторно запускает набор тестов для каждого возможного чередования.

Ответ принят как подходящий

Вместо того, чтобы просто проверять, завершен ли конкретный поток или нет, почему бы не создать поддельный Foo, который будет вызываться вашей оболочкой, в которой функции записывают время, в которое они были фактически запущены / завершены. Тогда вашему потоку yield нужно только подождать достаточно долго, чтобы можно было различить разницу между записанными временами. В своем тесте вы можете утверждать, что время начала another_func находится после времени начала some_func, а время завершения - до времени завершения some_func. Поскольку ваш поддельный класс записывает только время, этого должно быть достаточно, чтобы гарантировать правильную работу класса-оболочки.

РЕДАКТИРОВАТЬ: Вы, конечно, знаете, что то, что делает ваш объект Foo, может быть антипаттерн, а именно Последовательная связь. В зависимости от того, что он делает, вы можете справиться с этим, просто заставив второй метод ничего не делать, если первый метод еще не был вызван. Используя пример из ссылки Sequential Coupling, это будет похоже на то, что автомобиль ничего не делает при нажатии педали акселератора, если автомобиль еще не был запущен. Если ничего не предпринимать, вы можете подождать и попробовать еще раз позже, инициировать «последовательность запуска» в текущем потоке или обработать ее как ошибку. Все эти вещи могут быть реализованы и вашей оболочкой, и их, вероятно, будет легче протестировать.

Вам также может потребоваться быть осторожным, чтобы убедиться, что один и тот же метод не вызывается дважды подряд, если требуется промежуточный вызов другого метода.

Когда вы начинаете многопоточность, ваш код по определению становится недетерминированным, поэтому тестирование безопасности потоков в общем случае невозможно.

Но что касается вашего очень конкретного вопроса, если вы вставляете длинные задержки внутри Foo, чтобы каждый метод Foo занимал определенное время, тогда вы можете делать то, что просите. То есть вероятность того, что первый поток вернется до того, как второй поток войдет в вызов, становится практически нулевой.

Но чего вы на самом деле пытаетесь достичь? Что должен проверить этот тест? Если вы пытаетесь проверить правильность работы класса FooWrappersMutex, этого не произойдет.

Intel Threadchecker.

Если я правильно помню, инструмент проверяет ваш код на теоретически возможные гонки данных. Дело в том, что вам не нужно запускать свой код, чтобы проверить, правильный он или нет.

Здесь - обновленная ссылка
laurie 04.12.2013 15:42

Джинкс спешит на помощь

http://www.corensic.com/

Другие вопросы по теме