Commit 3be517fb authored by Pavel Vainerman's avatar Pavel Vainerman

(IOController, IONotifyController): рефакторинг работы с userdata,

а также попытка оптимизировать работу с mutex-ами в INC, правки комментариев.
parent 3338255d
......@@ -762,15 +762,10 @@ namespace uniset
if( hist.empty() )
return;
if( usi->userdata[udataHistory] == nullptr )
HistoryItList* lst = static_cast<HistoryItList*>(usi->getUserData(udataHistory));
if( !lst )
return;
HistoryItList& lst = *(static_cast<HistoryItList*>(usi->userdata[udataHistory]));
// auto i = histmap.find(s_it->si.id);
// if( i == histmap.end() )
// return;
long value = 0;
unsigned long sm_tv_sec = 0;
unsigned long sm_tv_nsec = 0;
......@@ -786,7 +781,7 @@ namespace uniset
<< " value=" << value
<< endl;
for( auto && it1 : lst)
for( auto&& it1 : (*lst) )
{
History::iterator it = it1;
......
......@@ -33,7 +33,15 @@
//---------------------------------------------------------------------------
namespace uniset
{
/*! Реализация интерфейса IOController-а */
/*! Реализация интерфейса IOController-а
* Важной особенностью данной реализации является то, что
* список входов/выходов (ioList) формируется один раз во время создания объекта
* и не меняется (!) в процессе работы. На этом построены некоторые оптимизации!
* Поэтому неизменность ioList во время всей жизни объекта должна гарантироваться.
* В частности, очень важной является структура USensorInfo, а также userdata,
* которые используются для "кэширования" (сохранения) указателей на специальные данные.
* (см. также IONotifyContoller).
*/
class IOController:
public UniSetManager,
public POA_IOController_i
......@@ -298,8 +306,13 @@ namespace uniset
// Дополнительные (вспомогательные поля)
uniset::uniset_rwmutex val_lock; /*!< флаг блокирующий работу со значением */
// userdata (универасльный, небезопасный способ расширения информации связанной с датчиком)
static const size_t MaxUserData = 4;
void* userdata[MaxUserData] = { nullptr, nullptr, nullptr, nullptr }; /*!< расширение для возможности хранения своей информации */
uniset::uniset_rwmutex userdata_lock; /*!< mutex для работы с userdata */
void* getUserData( size_t index );
void setUserData( size_t index, void* data );
// сигнал для реализации механизма зависимостией..
// (все зависимые датчики подключаются к нему (см. NCRestorer::init_depends_signals)
......@@ -328,7 +341,7 @@ namespace uniset
return std::move(s);
}
inline uniset::SensorMessage makeSensorMessage()
inline uniset::SensorMessage makeSensorMessage( bool with_lock = false )
{
uniset::SensorMessage sm;
sm.id = si.id;
......@@ -337,6 +350,7 @@ namespace uniset
sm.priority = (uniset::Message::Priority)priority;
// лочим только изменяемые поля
if( with_lock )
{
uniset::uniset_rwmutex_rlock lock(val_lock);
sm.value = value;
......@@ -346,6 +360,15 @@ namespace uniset
sm.supplier = supplier;
sm.undefined = undefined;
}
else
{
sm.value = value;
sm.sm_tv.tv_sec = tv_sec;
sm.sm_tv.tv_nsec = tv_nsec;
sm.ci = ci;
sm.supplier = supplier;
sm.undefined = undefined;
}
return std::move(sm);
}
......
......@@ -111,23 +111,25 @@ namespace uniset
\note Следует иметь ввиду, что для \b ЗАВИСИМОГО датчика функция setValue(..) действует как обычно и
даже если он "заблокирован", значение в него можно сохранять. Оно "появиться" как только сниметься блокировка.
\section sec_NC_Optimization Оптимизация работы со списком "заказчиков"
Для оптимизации поиска списка заказчиков для конкретного датчика используется поле userdata (void*) у USensorInfo!
Это опасный и "некрасивый" хак, но который позволяет избавиться от одного лишнего поиска по unordered_map<SensorID,ConsumerList>.
Суть в том что к датчику через usedata мы привязываем указатель на список заказчиков. Сделано через userdata,
т.к. сам map "хранится" в IOController и IONotifyController не может поменять тип (в текущей реализации по крайней мере).
В userdata задействованы два места (см. UserDataID) для списка заказчиков и для списка порогов.
\section sec_NC_Optimization Оптимизация работы со списком "заказчиков"
Для оптимизации поиска списка заказчиков для конкретного датчика используется поле userdata (void*) у USensorInfo!
Это опасный и "некрасивый" хак, но он позволяет избавиться от одного лишнего поиска по unordered_map<SensorID,ConsumerList>.
Суть в том что к датчику через usedata мы привязываем указатель на список заказчиков. Сделано через userdata,
т.к. сам map "хранится" в IOController и IONotifyController не может поменять тип (в текущей реализации по крайней мере).
В userdata задействованы два места (см. UserDataID) для списка заказчиков и для списка порогов.
ри этом, чтобы гарантировать корректность работы, cписки заказчиков по тому или иному датчику,
создаются (см. функцию ask()), но никогда не удаляются, даже если остаются пустыми.
Это сделано, чтобы сохранённые указатели в userdata, оставались всегда валидными
(т.к. используются из разных потоков).
*/
//---------------------------------------------------------------------------
/*! \class IONotifyController
\section AskSensors Заказ датчиков
....
ConsumerMaxAttempts - максимальное число неудачных
попыток послать сообщение "заказчику". Настраивается в
конфигурационном файле. По умолчанию = 5.
*/
*
* \section AskSensors Заказ датчиков
* ....
* \b ConsumerMaxAttempts - максимальное число неудачных попыток послать сообщение "заказчику".
* Настраивается в конфигурационном файле. По умолчанию = 5.
*/
class IONotifyController:
public IOController,
public POA_IONotifyController_i
......@@ -179,7 +181,7 @@ namespace uniset
ref(ref), attempt(maxAttemtps) {}
UniSetObject_i_var ref;
size_t attempt;
size_t attempt = { 10 };
size_t lostEvents = { 0 }; // количество потерянных сообщений (не смогли послать)
size_t smCount = { 0 }; // количество посланных SensorMessage
......@@ -283,7 +285,7 @@ namespace uniset
protected:
IONotifyController();
virtual bool activateObject() override;
virtual void initItem(std::shared_ptr<USensorInfo>& usi, IOController* ic );
virtual void initItem( std::shared_ptr<USensorInfo>& usi, IOController* ic );
//! посылка информации об изменении состояния датчика (всем или указанному заказчику)
virtual void send( ConsumerListInfo& lst, const uniset::SensorMessage& sm, const uniset::ConsumerInfo* ci = nullptr );
......@@ -351,6 +353,7 @@ namespace uniset
/*! добавить новый порог для датчика */
bool addThreshold(ThresholdExtList& lst, ThresholdInfoExt&& ti, const uniset::ConsumerInfo& ci);
/*! удалить порог для датчика */
bool removeThreshold(ThresholdExtList& lst, ThresholdInfoExt& ti, const uniset::ConsumerInfo& ci);
......@@ -380,8 +383,6 @@ namespace uniset
/*! map для хранения информации о заказчиках с которыми была потеряна связь
* и которые были удалены из списка заказчиков
* size_t - количество раз
* ObjectId - id заказчика
*/
std::unordered_map<uniset::ObjectId, LostConsumerInfo> lostConsumers;
};
......
......@@ -620,7 +620,25 @@ IOController::USensorInfo::operator=(IOController_i::SensorIOInfo* r)
(*this) = (*r);
return *this;
}
// ----------------------------------------------------------------------------------------
void* IOController::USensorInfo::getUserData( size_t index )
{
if( index > MaxUserData )
return nullptr;
uniset::uniset_rwmutex_rlock ulock(userdata_lock);
return userdata[index];
}
void IOController::USensorInfo::setUserData( size_t index, void* data )
{
if( index > MaxUserData )
return;
uniset::uniset_rwmutex_wrlock ulock(userdata_lock);
userdata[index] = data;
}
// ----------------------------------------------------------------------------------------
const IOController::USensorInfo&
IOController::USensorInfo::operator=(const IOController_i::SensorIOInfo& r)
{
......@@ -628,7 +646,7 @@ IOController::USensorInfo::operator=(const IOController_i::SensorIOInfo& r)
// any=0;
return *this;
}
// ----------------------------------------------------------------------------------------
void IOController::USensorInfo::init( const IOController_i::SensorIOInfo& s )
{
IOController::USensorInfo r(s);
......
......@@ -513,27 +513,25 @@ void IONotifyController::askSensor(const uniset::ObjectId sid,
}
catch( IOController_i::Undefined& ex ) {}
// Чтобы не было гонки между текущей функцией (askSensor)
// и setValue(), которая может происходить параллельно с этой
// держим askIOMutex до конца функции, а также для посылки пользуемся функцией send()...
uniset_rwmutex_wrlock lock(askIOMutex);
ask(askIOList, sid, ci, cmd);
{
uniset_rwmutex_wrlock lock(askIOMutex);
ask(askIOList, sid, ci, cmd);
}
auto usi = li->second;
// посылка первый раз состояния
if( cmd == UniversalIO::UIONotify || (cmd == UIONotifyFirstNotNull && usi->value) )
{
if( usi->userdata[udataConsumerList] != nullptr )
ConsumerListInfo* lst = static_cast<ConsumerListInfo*>(usi->getUserData(udataConsumerList));
if( lst )
{
ConsumerListInfo& lst = *(static_cast<ConsumerListInfo*>(usi->userdata[udataConsumerList]));
SensorMessage smsg( std::move(usi->makeSensorMessage()) );
send(lst,smsg,&ci);
uniset::uniset_rwmutex_rlock lock(usi->val_lock);
SensorMessage smsg( std::move(usi->makeSensorMessage(false)) );
send(*lst,smsg,&ci);
}
}
}
// ------------------------------------------------------------------------------------------
void IONotifyController::ask( AskMap& askLst, const uniset::ObjectId sid,
const uniset::ConsumerInfo& cons, UniversalIO::UIOCommand cmd)
......@@ -549,18 +547,23 @@ void IONotifyController::ask( AskMap& askLst, const uniset::ObjectId sid,
{
if( askIterator == askLst.end() )
{
ConsumerListInfo lst; // создаем новый список
addConsumer(lst, cons);
// более оптимальный способ(при условии вставки первый раз)
askLst.emplace(sid, std::move(lst));
try
ConsumerListInfo newlst; // создаем новый список
addConsumer(newlst, cons);
askLst.emplace(sid, std::move(newlst));
// т.к. мы делали move
// то теперь надо достучаться до списка..
auto i = askLst.find(sid);
if( i != askLst.end() )
{
dumpOrdersList(sid, lst);
}
catch( const uniset::Exception& ex )
{
uwarn << myname << " не смогли сделать dump: " << ex << endl;
try
{
dumpOrdersList(sid, i->second);
}
catch( const uniset::Exception& ex )
{
uwarn << myname << " не смогли сделать dump: " << ex << endl;
}
}
}
else
......@@ -595,9 +598,9 @@ void IONotifyController::ask( AskMap& askLst, const uniset::ObjectId sid,
if( askIterator->second.clst.empty() )
{
// не удаляем, т.к. могут поломаться итераторы
// используемые в это время в других потоках..
// askLst.erase(askIterator);
// не удаляем, т.к. могут поломаться итераторы
// используемые в это время в других потоках..
// askLst.erase(askIterator);
}
else
{
......@@ -629,13 +632,11 @@ void IONotifyController::ask( AskMap& askLst, const uniset::ObjectId sid,
if( askIterator != askLst.end() )
{
//! \warning Оптимизация использует userdata! Это опасно, если кто-то ещё захочет его использовать!
auto s = myiofind(sid);
if( s != myioEnd() )
s->second->userdata[udataConsumerList] = &(askIterator->second);
s->second->setUserData(udataConsumerList,&(askIterator->second));
else
s->second->userdata[udataConsumerList] = nullptr;
s->second->setUserData(udataConsumerList,nullptr);
}
}
// ------------------------------------------------------------------------------------------
......@@ -658,24 +659,26 @@ long IONotifyController::localSetValue( std::shared_ptr<IOController::USensorInf
if( prevValue == curValue )
return curValue;
SensorMessage sm(std::move(usi->makeSensorMessage()));
try
{
if( !usi->dbignore )
logging(sm);
}
catch(...) {}
// с учётом того, что параллельно с этой функцией может
// выполняться askSensor, то
// посылать сообщение надо "заблокировав" доступ к value...
{
uniset_rwmutex_rlock lock(askIOMutex);
uniset::uniset_rwmutex_rlock lock(usi->val_lock);
//! \warning Оптимизация использует userdata! Это опасно, если кто-то ещё захочет его использовать!
if( usi->userdata[udataConsumerList] != nullptr )
SensorMessage sm(std::move(usi->makeSensorMessage(false)));
try
{
ConsumerListInfo& lst = *(static_cast<ConsumerListInfo*>(usi->userdata[udataConsumerList]));
send(lst, sm);
if( !usi->dbignore )
logging(sm);
}
catch(...) {}
ConsumerListInfo* lst = static_cast<ConsumerListInfo*>(usi->getUserData(udataConsumerList));
if( lst )
send(*lst, sm);
}
// проверка порогов
......@@ -789,7 +792,7 @@ void IONotifyController::send( ConsumerListInfo& lst, const uniset::SensorMessag
// --------------------------------------------------------------------------------------------------------------
bool IONotifyController::activateObject()
{
// сперва вычитаем датчиков и заказчиков..
// сперва загружаем датчики и заказчиков..
readDump();
// а потом уже собственно активация..
return IOController::activateObject();
......@@ -805,9 +808,8 @@ void IONotifyController::readDump()
catch( const std::exception& ex )
{
// Если дамп не удалось считать, значит что-то не то в configure.xml
// и безопаснее "вылететь", чем запустится, но половина датчиков работать не будет
// и безопаснее "вылететь", чем запустится, т.к. часть датчиков не будет работать
// как ожидается.
ucrit << myname << "(IONotifyController::readDump): " << ex.what() << endl;
std::terminate(); // std::abort();
}
......@@ -991,15 +993,9 @@ void IONotifyController::askThreshold(uniset::ObjectId sid, const uniset::Consum
it = askTMap.find(sid);
if( li != myioEnd() )
{
//! \warning Оптимизация использует userdata! Это опасно, если кто-то ещё захочет его использовать!
if( it == askTMap.end() )
li->second->userdata[udataThresholdList] = nullptr;
else
li->second->userdata[udataThresholdList] = (void*)(&(it->second));
}
li->second->setUserData(udataThresholdList, &(it->second));
} // unlock
} // unlock trshMutex
}
// --------------------------------------------------------------------------------------------------------------
bool IONotifyController::addThreshold( ThresholdExtList& lst, ThresholdInfoExt&& ti, const uniset::ConsumerInfo& ci )
......@@ -1063,24 +1059,17 @@ void IONotifyController::checkThreshold( IOController::IOStateList::iterator& li
// --------------------------------------------------------------------------------------------------------------
void IONotifyController::checkThreshold( std::shared_ptr<IOController::USensorInfo>& usi, bool send_msg )
{
// поиск списка порогов
ThresholdsListInfo* ti = nullptr;
{
uniset_rwmutex_rlock lock(trshMutex);
uniset_rwmutex_rlock lock(trshMutex);
//! \warning Оптимизация использует userdata! Это опасно, если кто-то ещё захочет его использовать!
if( usi->userdata[udataThresholdList] == nullptr )
return;
ThresholdsListInfo* ti = static_cast<ThresholdsListInfo*>(usi->getUserData(udataThresholdList));
if( !ti || ti->list.empty() )
return;
//! \warning Оптимизация использует userdata! Это опасно, если кто-то ещё захочет его использовать!
ti = static_cast<ThresholdsListInfo*>(usi->userdata[udataThresholdList]);
// обрабатываем текущее состояние датчика обязательно "залочив" значение..
if( ti->list.empty() )
return;
}
uniset_rwmutex_rlock vlock(usi->val_lock);
SensorMessage sm(std::move(usi->makeSensorMessage()));
SensorMessage sm(std::move(usi->makeSensorMessage(false)));
// текущее время
struct timespec tm = uniset::now_to_timespec();
......@@ -1316,7 +1305,8 @@ IONotifyController_i::ThresholdsListSeq* IONotifyController::getThresholdsList()
// -----------------------------------------------------------------------------
void IONotifyController::onChangeUndefinedState( std::shared_ptr<USensorInfo>& usi, IOController* ic )
{
SensorMessage sm( std::move(usi->makeSensorMessage()) );
uniset_rwmutex_rlock vlock(usi->val_lock);
SensorMessage sm( std::move(usi->makeSensorMessage(false)) );
try
{
......@@ -1325,17 +1315,9 @@ void IONotifyController::onChangeUndefinedState( std::shared_ptr<USensorInfo>& u
}
catch(...) {}
{
// lock
uniset_rwmutex_rlock lock(askIOMutex);
//! \warning Оптимизация использует userdata! Это опасно, если кто-то ещё захочет его использовать!
if( usi->userdata[udataConsumerList] != nullptr )
{
ConsumerListInfo& lst = *(static_cast<ConsumerListInfo*>(usi->userdata[udataConsumerList]));
send(lst, sm);
}
} // unlock
ConsumerListInfo* lst = static_cast<ConsumerListInfo*>(usi->getUserData(udataConsumerList));
if( lst )
send(*lst, sm);
}
// -----------------------------------------------------------------------------
......
......@@ -75,7 +75,7 @@ void NCRestorer::addlist( IONotifyController* ic, std::shared_ptr<IOController::
case UniversalIO::AO:
{
ic->askIOList[inf->si.id] = std::move(lst);
inf->userdata[IONotifyController::udataConsumerList] = &(ic->askIOList[inf->si.id]);
inf->setUserData(IONotifyController::udataConsumerList, &(ic->askIOList[inf->si.id]) );
break;
}
......@@ -151,29 +151,15 @@ NCRestorer::SInfo& NCRestorer::SInfo::operator=( const IOController_i::SensorIOI
this->blocked = inf.blocked;
this->dbignore = inf.dbignore;
for( size_t i = 0; i < IOController::USensorInfo::MaxUserData; i++ )
this->userdata[i] = nullptr;
{
uniset_rwmutex_wrlock l(this->userdata_lock);
for( size_t i = 0; i < IOController::USensorInfo::MaxUserData; i++ )
this->userdata[i] = nullptr;
}
return *this;
}
// ------------------------------------------------------------------------------------------
#if 0
NCRestorer::SInfo& NCRestorer::SInfo::operator=( const std::shared_ptr<IOController_i::SensorIOInfo>& inf )
{
this->si = inf->si;
this->type = inf->type;
this->priority = inf->priority;
this->default_val = inf->default_val;
this->real_value = inf->real_value;
this->ci = inf->ci;
this->undefined = inf->undefined;
this->blocked = inf->blocked;
this->dbignore = inf->dbignore;
this->any = 0;
return *this;
}
#endif
// ------------------------------------------------------------------------------------------
void NCRestorer::init_depends_signals( IONotifyController* ic )
{
for( auto it = ic->ioList.begin(); it != ic->ioList.end(); ++it )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment