EnglishУкраїнськаmRussian
Login/New
Topic with no new replies

[BugFixed] Баг с мютексом


Author Message
Written on: 16. 11. 2017 [23:48]
aleax
Alexey Bondarchuk
Topic creator
registered since: 27.01.2010
Posts: 73
В отличии от предыдущего бага, тут ситуация была сложнее, и на поиск потрачено больше времени.

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

Был реализован шаблонный параметр контроллера modbus (назовем его условно «smart»), в «конструкторе» ( if(f_start) {} ) которого конструировался другой, соседний, параметр std, назовем его «data». Smart контроллер по определенным правилам программно устанавливал перечень регистров, которые подлежат чтению (тут я боролся с ручным вбиванием кучи однотипных данных, либо возможных ошибок последующей копипасты). Следующим xml запросом выставлял галочку включения. После чего рядом других xml запросов доступался к полученным данным.

Все это запускалось в продакшин на версии 0.8.12 и прекрасно работает по сей день.

История.

В новых LTS версиях (а так же в транке 0.9) данный механизм начал давать сбой. Обнаружено было давно, но никак не доходили руки локализировать проблему.

Поведение наблюдается следующие:
Отлично создается std атрибут data, но программное включение приводит к неработоспособности всего modbus контроллера. Регистры просто перестают (или не начинают) опрашиваться. Хотя в интерфейсе галочка «Ввімкнено» показывается установленной.
Попытка снять галочку вручную приводит к бесконечному ожиданию «Очікування відповіді від хосту ...»
При выключении же опенскады в лог бесконечно выводится
JAVASCRIPT
2017-11-16T23:24:30 1[/Machine/] SDKT: Очікування події завдання 'sub_DAQ.mod_ModBus.cntr_MB2' …


На первом этапе удалось выяснить что если в коде smart атрибута убрать программное включение std атрибута data (но оставить его создание, и последующие снятие данных), а вместо этого вручную выставить галочку «Ввімкнено», то описанное поведение не воспроизводится.

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

Локализация проблемы в коде.
Сначала прошелся по LTS версиям начиная с 0.8.12 по текущую 0.8.16, и обнаружил что проблема появилась в версии, где был произведен большой бекпорт кода модуля modbus из транка.

Затем прошелся по транку, и методом половинного деления определил, что в ревизии 2323 все работает, а в ревизии 2325 уже нет (2324 просто не собирается).
Между данными ревизиями произошел переход от прямого использования phthread mutex к внутриннему ResMtx, и в конструкторе TMdContr::TMdContr (файл modbus_daq.cpp, строки начиная с 110) была убрана установка атрибута PTHREAD_MUTEX_RECURSIVE:

JAVASCRIPT
--- a/src/moduls/daq/ModBus/modbus_daq.cpp
+++ b/src/moduls/daq/ModBus/modbus_daq.cpp
@@ -107,16 +107,9 @@ TMdContr::TMdContr(string name_c, const string &daq_db, TElem *cfgelem) :
        mSched(cfg("SCHEDULE")), mPrt(cfg("PROT")), mAddr(cfg("ADDR")),
        mMerge(cfg("FRAG_MERGE").getBd()), mMltWr(cfg("WR_MULTI").getBd()), mAsynchWr(cfg("WR_ASYNCH").getBd()),
        reqTm(cfg("TM_REQ").getId()), restTm(cfg("TM_REST").getId()), connTry(cfg("REQ_TRY").getId()),
-       prc_st(false), call_st(false), endrun_req(false), isReload(false), alSt(-1),
+       prcSt(false), callSt(false), endrunReq(false), isReload(false), alSt(-1),
        tmDelay(0), numRReg(0), numRRegIn(0), numRCoil(0), numRCoilIn(0), numWReg(0), numWCoil(0), numErrCon(0), numErrResp(0)
 {
-    pthread_mutexattr_t attrM;
-    pthread_mutexattr_init(&attrM);
-    pthread_mutexattr_settype(&attrM, PTHREAD_MUTEX_RECURSIVE);
-    pthread_mutex_init(&enRes, &attrM);
-    pthread_mutex_init(&dataRes, &attrM);
-    pthread_mutexattr_destroy(&attrM);


В итоге я пришел к следующему однострочному патчу, который восстанавливает работоспособность моего кода как в транк ветке, так и в 0.8.18:

JAVASCRIPT
diff --git a/src/moduls/daq/ModBus/modbus_daq.cpp b/src/moduls/daq/ModBus/modbus_daq.cpp
index b060dc0..473c071 100644
--- a/src/moduls/daq/ModBus/modbus_daq.cpp
+++ b/src/moduls/daq/ModBus/modbus_daq.cpp
@@ -103,6 +103,7 @@ TController *TTpContr::ContrAttach( const string &name, const string &daq_db )      {
 //******************************************************
 TMdContr::TMdContr(string name_c, const string &daq_db, TElem *cfgelem) :
        TController(name_c, daq_db, cfgelem),
+       enRes(true),   // Set mutex attribute PTHREAD_MUTEX_RECURSIVE
        mPrior(cfg("PRIOR").getId()), mNode(cfg("NODE").getId()), blkMaxSz(cfg("MAX_BLKSZ").getId()),
        mSched(cfg("SCHEDULE")), mPrt(cfg("PROT")), mAddr(cfg("ADDR")),
        mMerge(cfg("FRAG_MERGE").getBd()), mMltWr(cfg("WR_MULTI").getBd()), mAsynchWr(cfg("WR_ASYNCH").getBd()),


P.S.: не создавал отдельный проект с тестовым кодом, а проверял на рабочем, но если есть необходимость воспроизвести, то могу подготовить тестовый проект.
Written on: 17. 11. 2017 [09:48]
roman
Roman Savochenko
Moderator
Contributor
Developer
registered since: 12.12.2007
Posts: 3742
"aleax" wrote:

Был реализован шаблонный параметр контроллера modbus (назовем его условно «smart»), в «конструкторе» ( if(f_start) {} ) которого конструировался другой, соседний, параметр std, назовем его «data». Smart контроллер по определенным правилам программно устанавливал перечень регистров, которые подлежат чтению (тут я боролся с ручным вбиванием кучи однотипных данных, либо возможных ошибок последующей копипасты). Следующим xml запросом выставлял галочку включения. После чего рядом других xml запросов доступался к полученным данным.

Похожее сейчас практикуется на логическом уровне, да и актуально для ModBus, для пассивных объектов параметров с динамически созданными атрибутами, если данные получаются через расширенные функции или искажённый ModBus.

"aleax" wrote:

Локализация проблемы в коде.
Сначала прошелся по LTS версиям начиная с 0.8.12 по текущую 0.8.16, и обнаружил что проблема появилась в версии, где был произведен большой бекпорт кода модуля modbus из транка.

Текущая 0.8.18! :)

"aleax" wrote:

Затем прошелся по транку, и методом половинного деления определил, что в ревизии 2323 все работает, а в ревизии 2325 уже нет (2324 просто не собирается).
Между данными ревизиями произошел переход от прямого использования phthread mutex к внутриннему ResMtx, и в конструкторе TMdContr::TMdContr (файл modbus_daq.cpp, строки начиная с 110) была убрана установка атрибута PTHREAD_MUTEX_RECURSIVE:

Собственно, забыта.
Вообще respect за такое!
Хотя наверное проще было "убить" сигналом SIGSEGV(11), как написано в документации, и в отчёте это блокирование можно разглядеть.

"aleax" wrote:

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

Достаточно, тут всё очевидно.

Исправил, за одно и у соседних модулей пересмотрел, сегодня выгружу!

Learn, learn and learn better than work, work and work.



2892