#205 closed баг (fixed)
Гонки при использовании XMLConfig::ESLConferences
Reported by: | alx | Owned by: | dimag |
---|---|---|---|
Priority: | blocker | Milestone: | 2 очередь |
Component: | ПО MC04-Dispatcher. Пульт диспетчера/техника | Keywords: | потоки |
Cc: | san |
Description
XMLConfig::ESLConferences используется в GUI потоке, например через функцию XMLConfig::getConferenceIndex(), и при этом модифицируется в Events::EventsTimerTick, то есть в другом потоке. Таким образом, возможна модификация одним потоком в процессе обращения из другого потока.
Change History (16)
comment:1 by , 8 years ago
comment:2 by , 8 years ago
Priority: | major → critical |
---|
comment:3 by , 8 years ago
Keywords: | потоки added |
---|---|
Resolution: | → fixed |
Status: | new → closed |
r249.
Просмотрел исходный код, поставил мьютексы везде, где используются совместно используемые разными потоками ресурсы.
comment:6 by , 8 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Хм... В коммите r248 я не вижу добавления никаких мьютексов. Добавлен параметр в XMLConfig::ParseURI() и изменен фрагмент в StartupDlg.cpp, отвечающий, насколько я понял, за выбор логина по умолчанию при старте программы. Как это может устранить гонки при использовании XMLConfig::ESLConferences? По-моему, никак не может...
comment:7 by , 8 years ago
r248 исправляет баг из тикета #201.
Пишите, пожалуйста, комментарии для каждого коммита, как мы договаривались, в соответствии с TracEffectiveWork. Был бы у коммита r248 комментарий, не было бы сейчас этой путаницы, в каком коммите что исправлено...
comment:9 by , 8 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
comment:10 by , 8 years ago
Я так и не вижу в коде, что помешает потоку EventThread модифицировать XMLConfig::ESLConferences например по такой цепочке: Events::EventsTimerTick() -> Events::RefreshConferenceState(), в то время как выполняется обращение к XMLConfig::ESLConferences например по такой цепочке: MainAppFrameWindow3::StarEndConferenceButtonPressed() -> XMLConfig::getConferenceIndex(). По-моему гонки не устранены. Поправьте меня, если я неправ.
comment:11 by , 8 years ago
Priority: | critical → blocker |
---|---|
Resolution: | fixed |
Status: | closed → reopened |
follow-up: 13 comment:12 by , 8 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
Все вызовы RefreshConferenceState вызываются в во время действия мутекса, в коде аналогичным данному
QMutex mutex;
QMutexLocker mutexlocker(&mutex);
std::string sCallerChannel = EventParamValueMapCaller-Channel-Name;
RefreshConferenceState(sConference, sAffectedConferenceUserID, sAction);
Соотвественно, если пользователь нажмёт на кнопку вызвать/остановить конференцию во время выполнения функции StartEndConferenceButtonPressed, то функция выполниться до первого мьютекса, далее будет ждать разблокировки мьютекса в потоке EventThread.
В основном потоке и в остальных потоках в r249 и позднее, все обращения к разделяемым данным осуществляется через взятие мьютекса, то есть гонок быть не должно.
comment:13 by , 8 years ago
Replying to dimag:
Все вызовы RefreshConferenceState вызываются в во время действия мутекса,
Какого именно? Уточните, пожалуйста, его имя и место, где этот мьютекс определен в программе.
Соотвественно, если пользователь нажмёт на кнопку вызвать/остановить конференцию во время выполнения функции StartEndConferenceButtonPressed, то функция выполниться до первого мьютекса, далее будет ждать разблокировки мьютекса в потоке EventThread.
Извините, но здесь Вы пишете ерунду (поправьте если я ошибаюсь).
Во-первых, StartEndConferenceButtonPressed - это слот объекта mainWindow, к которому подключен сигнал pressed виджета кнопки. Код слота выполняется в основном (гуишном) потоке. Иными словами, StartEndConferenceButtonPressed() выполняется в том же потоке, который генерирует сигнал. Сигналы обрабатываются последовательно в цикле прямым вызовом кода слота, к которому сигнал подключен. Таким образом, описанная Вами ситуация принципиально невозможна - новый сигнал pressed не начнет обрабатываться до тех пор, пока не завершится StartEndConferenceButtonPressed(), вызванная в результате обработки предыдущего сигнала pressed. Здесь нет и не может быть никаких гонок, так как все выполняется в одном потоке.
Во-вторых, в приведенном примере кода:
{ QMutex mutex; QMutexLocker mutexlocker(&mutex); ... делаем какую-то работу ... }
никакой блокировки при захвате мьютекса быть не может. В приведенном коде Вы создаете объект мьютекса mutex
с автоматической длительностью хранения. Исходным состоянием мьютекса является "свободен" (не захвачен). В следующей строке Вы захватываете mutex в конструкторе QMutexLocker. Так как на данный момент известно, что mutex свободен, при выполнении QMutexLocker mutexlocker(&mutex);
никакой блокировки потока не будет. Далее по коду никаких других обращений к mutex нет. Таким образом, две первые строки этого примера, фактически, не имеют никакого эффекта.
Я же в comment:10 привел пример совсем другой ситуации - когда во время выполнения XMLConfig::getConferenceIndex(), вызванной из StarEndConferenceButtonPressed() выполнится Events::RefreshConferenceState(), вызванная из Events::EventsTimerTick() например в результате получения события add-mmber.
В основном потоке и в остальных потоках в r249 и позднее, все обращения к разделяемым данным осуществляется через взятие мьютекса,
Еще раз - уточните, пожалуйста, какого именно мьютекса. Возможно, Вам стоит освежить в памяти понятия "storage duration", "scope" и "linkage"...
то есть гонок быть не должно.
comment:14 by , 8 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Я везде там где идёт обращение к объекту currentConfig ставлю
QMutex mutex;
QMutexLocker mutexlocker(&mutex);
Это должно предотвратить модификацию состояние объекта currentConfig из другого потока.