diff options
author | sskvor <sskvor@yandex-team.com> | 2022-11-10 12:04:05 +0300 |
---|---|---|
committer | sskvor <sskvor@yandex-team.com> | 2022-11-10 12:04:05 +0300 |
commit | 6d5ed9f0166178823f3a752bfe46dd7ef5042df8 (patch) | |
tree | 431a578de9d75353b8e60f85d49e74d2370fedf7 | |
parent | 56418f7c4453670d9fae2c1329fe38c5d81d4563 (diff) | |
download | ydb-6d5ed9f0166178823f3a752bfe46dd7ef5042df8.tar.gz |
[util] Terminate instead of deadlock after fork with running thread pools
Контекст. В древнем странном коде обнаружили, что если создавать `TThreadPool` часто и конкуретно с запуском `TShellCommand`, то иногда последняя дедлочится: если начали выполнять конструктор `TThreadPool` и регистрацию в `TAtforkQueueRestarter`, захватив `TAtforkQueueRestarter::ActionMutex`, а в другом потоке параллельно сделали `fork`, то последний может форкнуться с залоченым мьютексом. Первым делом происходит вызов коллбеков из `pthread_atfork`, где мьютекс лочится заново.
Конечно, форкаться в сложных программах с тредпулами категорически запрещено, но, подозреваю, много кода в аркадии так написано. Давайте как-то защитимся от такого.
-rw-r--r-- | util/thread/pool.cpp | 12 |
1 files changed, 8 insertions, 4 deletions
diff --git a/util/thread/pool.cpp b/util/thread/pool.cpp index a3d4122246..1711048f73 100644 --- a/util/thread/pool.cpp +++ b/util/thread/pool.cpp @@ -289,10 +289,14 @@ private: private: void ChildAction() { - with_lock (ActionMutex) { - for (auto it = RegisteredObjects.Begin(); it != RegisteredObjects.End(); ++it) { - it->AtforkAction(); - } + TTryGuard guard{ActionMutex}; + // If you get an error here, it means you've used fork(2) in multi-threaded environment and probably created thread pools often. + // Don't use fork(2) in multi-threaded programs, don't create thread pools often. + // The mutex is locked after fork iff the fork(2) call was concurrent with RegisterObject / UnregisterObject in another thread. + Y_VERIFY(guard.WasAcquired(), "Failed to acquire ActionMutex after fork"); + + for (auto it = RegisteredObjects.Begin(); it != RegisteredObjects.End(); ++it) { + it->AtforkAction(); } } |