C ++: убедитесь, что во время компиляции функция вызывается только в указанном потоке - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть метод, который работает со сложной структурой, которая не является потокобезопасной.Как правило, это не проблема, поскольку по сути все вызовы этого метода должны выполняться на этапе инициализации, который должен выполняться в одном потоке (тот же, который мы начали с main()).

Однако, похоже, какой-то вредоносный компонент внутри моей кодовой базы действительно вызывает этот метод из неосновного потока.Я, очевидно, могу добавить мьютекс / блокировку защиты в этот метод в качестве защиты от потенциальных вызовов, но, с моей точки зрения, это обходной путь, а не решение, которое я ищу.

Конечно, я могутакже добавьте assert во время выполнения, то есть что-то вроде:

static std::thread::id s_main_thread_id

int main(...) {
    s_main_thread_id = std::this_thread::get_id();
    // ...
}

void vulnerable_function() {
    ASSERT(std::this_thread::get_id() == s_main_thread_id);
}

... но это не гарантия.

Вопрос: можно ли как-то утверждать в компиляциивремя, когда определенный метод должен запускаться только из определенного (то есть основного) потока?Я хотел бы найти мошеннические вызовы в моей кодовой базе и не допустить, чтобы это повторилось.

Я просмотрел контрактов типа assert в C ++ 20, но если яна правильном пути и / или я могу применить его.

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

... все вызовы этого метода должны выполняться на этапе инициализации ...

Забудьте о потоках.Если функция должна вызываться только во время «инициализации», убедитесь, что она может быть вызвана только во время инициализации:

boolean initialization_is_complete = false;

problematic_function(...) {
    ASSERT(! initialization_is_complete);
    ...
}

initialize_the_system(...) {
    ...
    call_things_that_call_problematic_function(...);
    ...
    initialization_is_complete = true;
}

ASSERT () будет выполняться во время выполнениявремя, верно? ... Я хочу, чтобы какой-нибудь инструмент статического анализа мог [предотвратить] это во время компиляции.

Я не эксперт по C ++.Единственные известные мне во время компиляции способы предотвращения «несанкционированного» использования какой-либо вещи - это сделать объявление private классу или static в модуле компиляции. * Если бы я мог сделать problematic_function() be static в модуле компиляции, публичные символы которого полезны только во время «инициализации», тогда я бы так и сделал.

С другой стороны, если у моих коллег-разработчиков были веские причины, по которым они хотели вызвать problematic_function() в других случаях, из других мест, тогда я бы серьезно подумал о том, чтобы переработать его так, чтобы это не было проблемой.


* Если вы потянете какой-нибудь трюк constexpr или какой-нибудь templateуловка, о которой я не задумывался (я , а не эксперт C ++), поэтому я готов поспорить, что это все равно будет зависеть от объявления что-то , которое находится в области действия для initкод и выход за рамки видимости для всего остального кода.


Возвращаясь к вашей первоначальной идее, которая заключалась в том, чтобы предотвратить вызов "неправильными" потоками функции: на самом деле такой вещи не существует.как нить на языке C ++.Тип данных std::thread предоставляется библиотекой, и сам компилятор не может узнать идентичность потока (ов), который будет выполнять код, который он переводит.

То есть std::this_thread::get_id() не constexpr, и нет времени компиляции способа сравнить его с идентификатором любого std::thread в программе.

Возможно, однажды они определятstd::main_thread ...

0 голосов
/ 08 февраля 2019

Что вы можете сделать, это создать несколько макросов assert.Используйте вашу библиотеку потоков, чтобы получить идентификаторы потоков.Сохраните идентификатор потока в первый раз, когда ваш код строит эту структуру данных.Затем утверждайте, что он совпадает с текущим идентификатором потока каждый раз, когда вы вводите эти функции.

Библиотека CEF (Chromium Embedded Framework) использует это.

Оборачивая эти макросы как утверждения, препроцессор удаляетони в сборках выпуска устанавливают -DNDEBUG.

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

...