Почему повторный вызов mutex::lock это UB?
От: Максим Рогожин Россия  
Дата: 16.03.18 10:39
Оценка:
Объясните, пожалуйста
1. почему нельзя два раза вызывать std::mutex::lock() (т.е. не вызвав перед вторым вызовом unlock())?
2. почему это UB, а не исключение, например?

В Win API поток может заходить повторно в метод защищенный WinAPI-шным mutex-ом, если я не ошибаюсь.
Отредактировано 16.03.2018 10:40 Максим Рогожин . Предыдущая версия .
Re: Почему повторный вызов mutex::lock это UB?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 16.03.18 10:47
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>1. почему нельзя два раза вызывать std::mutex::lock() (т.е. не вызвав перед вторым вызовом unlock())?

Для рекурсивного мьютекса можно вызывать без UB.
Sic luceat lux!
Re[2]: Почему повторный вызов mutex::lock это UB?
От: Максим Рогожин Россия  
Дата: 16.03.18 10:57
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Для рекурсивного мьютекса можно вызывать без UB.

А зачем отдельный рекурсивный мьютекс? Почему std::mutex не сделали рекурсивным?
И почему называется std::recursive_mutex, а не std::reentrant_mutex?
Отредактировано 16.03.2018 11:00 Максим Рогожин . Предыдущая версия .
Re[3]: Почему повторный вызов mutex::lock это UB?
От: ononim  
Дата: 16.03.18 11:03
Оценка:
K>>Для рекурсивного мьютекса можно вызывать без UB.
МР>А зачем отдельный рекурсивный мьютекс? Почему std::mutex не сделали рекурсивным?
1) Реализация нерекурсивного мутекса может быть легковеснее рекурсивного
2) Зачастую (а точнее почти всегда) логика приложения не предполагает рекурсивного входа в мутекс. В таком случае своеобразный ассерт на рекурсию может быть признаком того что девелопер пытается поломать задуманную архитектуру. Впрочем UB — это так себе ассерт

МР>И почему называется std::recursive_mutex, а не std::reentrant_mutex?

Реентерабельность
Как много веселых ребят, и все делают велосипед...
Отредактировано 16.03.2018 11:05 ononim . Предыдущая версия .
Re[3]: Почему повторный вызов mutex::lock это UB?
От: Mr.Delphist  
Дата: 16.03.18 11:06
Оценка: 2 (1)
Здравствуйте, Максим Рогожин, Вы писали:

МР>Здравствуйте, Kernan, Вы писали:


K>>Для рекурсивного мьютекса можно вызывать без UB.

МР>А зачем отдельный рекурсивный мьютекс? Почему std::mutex не сделали рекурсивным?
МР>И почему называется std::recursive_mutex, а не std::reentrant_mutex?

Термин reentrant — это обычно для многопоточных сценариев применяется.
Для мьютекса получается именно рекурсия: залочил, снова залочил. Тогда надо дважды сделать разлочку.
Re[4]: Почему повторный вызов mutex::lock это UB?
От: Максим Рогожин Россия  
Дата: 16.03.18 11:09
Оценка:
Здравствуйте, ononim, Вы писали:

O>2) Зачастую (а точнее почти всегда) логика приложения не предполагает рекурсивного входа в мутекс. В таком случае своеобразный ассерт на рекурсию может быть признаком того что девелопер пытается поломать задуманную архитектуру. Впрочем UB — это так себе ассерт


Как так? Вот сделали мы класс
class SomeClass {
   Resource resource;
   std::mutex m_mutex;
public:
   void doSomething1() {
      std::lock_guard<mutex> locker(m_mutex);
      // modify resource
   }
   void doSomething2() {
      std::lock_guard<mutex> locker(m_mutex);
      // modify resourse
      doSomething1(); // вот и рекурсивный вход в мьютекс
   }
};

Такая ситуация наоборот часто распространена, мне кажется?
Re[4]: Почему повторный вызов mutex::lock это UB?
От: Максим Рогожин Россия  
Дата: 16.03.18 11:15
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Термин reentrant — это обычно для многопоточных сценариев применяется.

А std::mutex может для чего-то в однопоточном сценарии использоваться что-ли...???
Re[5]: Почему повторный вызов mutex::lock это UB?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 16.03.18 11:16
Оценка: +2
Здравствуйте, Максим Рогожин, Вы писали:

МР>Такая ситуация наоборот часто распространена, мне кажется?

Такое бывает, у меня была задача где мне понадобился рекурсивный мьютекс, хотя я, честно говоря, поленился рефакторить код чтобы этого избежать. ИМХО, такой интерфейс как-то неверно сделан т.к. операция doSomething2 должна делать одно и только одно изменение над объектом, а тут, кмк, нарушение SRP.
В твоём случае выполнения конкретных операций можно сделать в приватных методах не защищённых мьютексом, а в интерфейсных методах делать мьютекс и защищать одну или цепочку операций.
Sic luceat lux!
Отредактировано 16.03.2018 13:05 Kernan . Предыдущая версия . Еще …
Отредактировано 16.03.2018 12:02 Kernan . Предыдущая версия .
Отредактировано 16.03.2018 11:41 Kernan . Предыдущая версия .
Отредактировано 16.03.2018 11:18 Kernan . Предыдущая версия .
Re[3]: Почему повторный вызов mutex::lock это UB?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 16.03.18 11:17
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>Здравствуйте, Kernan, Вы писали:


K>>Для рекурсивного мьютекса можно вызывать без UB.

МР>А зачем отдельный рекурсивный мьютекс? Почему std::mutex не сделали рекурсивным?
МР>И почему называется std::recursive_mutex, а не std::reentrant_mutex?
Ты бы прочитал для начала книгу которую я тебе рекомендовал в другом треде.
Sic luceat lux!
Re[4]: Почему повторный вызов mutex::lock это UB?
От: Максим Рогожин Россия  
Дата: 16.03.18 11:25
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Ты бы прочитал для начала книгу которую я тебе рекомендовал в другом треде.

Прочитаю, спасибо. Мне тут просто по работе немного многопоточности добавить надо.
Re[5]: Почему повторный вызов mutex::lock это UB?
От: uzhas Ниоткуда  
Дата: 16.03.18 11:28
Оценка: :))
Здравствуйте, Максим Рогожин, Вы писали:

МР>Прочитаю, спасибо. Мне тут просто по работе немного многопоточности добавить надо.


создай поток. можно два. мьютекс необязателен
Re[5]: Почему повторный вызов mutex::lock это UB?
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 16.03.18 11:41
Оценка:
Здравствуйте, Максим Рогожин, Вы писали:

МР>Здравствуйте, Kernan, Вы писали:


K>>Ты бы прочитал для начала книгу которую я тебе рекомендовал в другом треде.

МР>Прочитаю, спасибо. Мне тут просто по работе немного многопоточности добавить надо.
Так не проще ли задать конкретый вопрос как лучше реализовать решения для того или иного случая? Созадать пару потоков это не проблема.
Sic luceat lux!
Re[6]: Почему повторный вызов mutex::lock это UB?
От: Максим Рогожин Россия  
Дата: 16.03.18 11:45
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Так не проще ли задать конкретый вопрос как лучше реализовать решения для того или иного случая? Созадать пару потоков это не проблема.

Да, я уже сделал что мне надо было. Просто хочется узнать почему два раза позвать lock() это UB. Что-то не улавливаю пока...

Например, будет кто-то другой мой код редактировать и позовет метод из метода (как в примере, который я привел) и что? UB? Как отлавливать такие ситуации? Все методы класса просматривать и разбираться может или нет получится два вызова lock()?
Отредактировано 16.03.2018 11:49 Максим Рогожин . Предыдущая версия . Еще …
Отредактировано 16.03.2018 11:48 Максим Рогожин . Предыдущая версия .
Re[7]: Почему повторный вызов mutex::lock это UB?
От: Alexander G Украина  
Дата: 16.03.18 12:47
Оценка: 5 (2) +1
Здравствуйте, Максим Рогожин, Вы писали:

K>>Так не проще ли задать конкретый вопрос как лучше реализовать решения для того или иного случая? Созадать пару потоков это не проблема.

МР>Да, я уже сделал что мне надо было. Просто хочется узнать почему два раза позвать lock() это UB. Что-то не улавливаю пока...

Теперь и в WinAPI есть нерекурсивный примитив, это Slim Reader/Writer (SRW) Lock
Отсутствие поддержки рекурсии в нём позволило его сделать действительно slim.
std::mutex в студии работают именно на нём.

МР>Например, будет кто-то другой мой код редактировать и позовет метод из метода (как в примере, который я привел) и что? UB? Как отлавливать такие ситуации? Все методы класса просматривать и разбираться может или нет получится два вызова lock()?


Вообще, желательно контролировать все вхождения в мьютексы.
И контролировать что вызывается из-под мьютекса (стараясь не вызвать из своих мьютексов пользовательские коллбэки).
Хаотичные правки здесь ни к чему хорошему не приведут.
Потому что если хаос из одного мьютекса приведёт к необходимости рекурсии, то на двух мьютексах это выльется в deadlock.
Ну а когда всё под контролем, рекурсивность и не нужна вовсе.

Ну и хорошая реализация в отладочной сборке, скорее всего, будет ругаться на повторный захват нерекурсивного мьютекса.
Русский военный корабль идёт ко дну!
Re[7]: Почему повторный вызов mutex::lock это UB?
От: andrey.desman  
Дата: 16.03.18 12:54
Оценка: +1
Здравствуйте, Максим Рогожин, Вы писали:

K>>Так не проще ли задать конкретый вопрос как лучше реализовать решения для того или иного случая? Созадать пару потоков это не проблема.

МР>Да, я уже сделал что мне надо было. Просто хочется узнать почему два раза позвать lock() это UB. Что-то не улавливаю пока...

Потому что в C++ ты не платишь за то, что не используешь.
Обычный мьютекс — это просто битик. Если тебе удается его атомарно сменить с 0 на 1, то ты его захватил, если нет, то приходится висеть.
Рекурсивный мьютекс сложнее. Там если не захватил, то надо проверить, какой поток им владеет, и если это текущий поток, то увеличить счетчик захватов. В общем, сразу появляются всякие проверки, счетчики, системные вызовы/TLS...

МР>Например, будет кто-то другой мой код редактировать и позовет метод из метода (как в примере, который я привел) и что? UB? Как отлавливать такие ситуации? Все методы класса просматривать и разбираться может или нет получится два вызова lock()?


Если боишься, просто воткни recursive_mutex вместо mutex. Никто же не запрещает... Или сделай четкое разделение на locked/unlocked методы.
Re[8]: Почему повторный вызов mutex::lock это UB?
От: lpd Черногория  
Дата: 16.03.18 12:59
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Обычный мьютекс — это просто битик. Если тебе удается его атомарно сменить с 0 на 1, то ты его захватил, если нет, то приходится висеть.


Ты путаешь mutex и spinlock. Мьютекс может при захвате перейти в режим ядра для ожидания.
У сложных вещей обычно есть и хорошие, и плохие аспекты.
Берегите Родину, мать вашу. (ДДТ)
Re: Почему повторный вызов mutex::lock это UB?
От: Pzz Россия https://github.com/alexpevzner
Дата: 16.03.18 13:01
Оценка: 2 (1) +1
Здравствуйте, Максим Рогожин, Вы писали:

МР>1. почему нельзя два раза вызывать std::mutex::lock() (т.е. не вызвав перед вторым вызовом unlock())?

МР>2. почему это UB, а не исключение, например?

Из экономии.

Если std::mutex'у позволено рассчитывать на то, что с ним будут обращаться правильно, его реализация может быть немного более эффективной.

МР>В Win API поток может заходить повторно в метод защищенный WinAPI-шным mutex-ом, если я не ошибаюсь.


Разные API решают такие вопросы по-разному.
Re[3]: Почему повторный вызов mutex::lock это UB?
От: Pzz Россия https://github.com/alexpevzner
Дата: 16.03.18 13:02
Оценка: 2 (1)
Здравствуйте, Максим Рогожин, Вы писали:

МР>И почему называется std::recursive_mutex, а не std::reentrant_mutex?


Потому что объект с такой семантикой получил название рекурсивного мутекса задолго до того, как его внесли в библиотеку C++. И было бы глупо изобретать новое название взамен общепринятого.
Re[9]: Почему повторный вызов mutex::lock это UB?
От: andrey.desman  
Дата: 16.03.18 13:05
Оценка:
Здравствуйте, lpd, Вы писали:

AD>>Обычный мьютекс — это просто битик. Если тебе удается его атомарно сменить с 0 на 1, то ты его захватил, если нет, то приходится висеть.

lpd>Ты путаешь mutex и spinlock. Мьютекс может при захвате перейти в режим ядра для ожидания.

Я ничего не путаю. Может перейти, а может и не перейти. Зачем переходить, если можно не?
Re[10]: Почему повторный вызов mutex::lock это UB?
От: lpd Черногория  
Дата: 16.03.18 13:08
Оценка:
Здравствуйте, andrey.desman, Вы писали:

lpd>>Ты путаешь mutex и spinlock. Мьютекс может при захвате перейти в режим ядра для ожидания.


AD>Я ничего не путаю. Может перейти, а может и не перейти. Зачем переходить, если можно не?


Это да, может и не перейти. Однако если перейдет, все равно в ядре будет список ждущих потоков. Останется только пройти по нему и поискать себя. Уж не знаю, экономили ли авторы stl на этих затратах, или просто выбрали такое поведение без рекурсивного лока как более простое.
У сложных вещей обычно есть и хорошие, и плохие аспекты.
Берегите Родину, мать вашу. (ДДТ)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.