У меня здесь серьезная проблема.
У меня есть две разные задачи (например, процесс), работающие на моем ПЛК X20 формы B&R доступ к одному одноэлементному объекту «VarList». Его цель — разрешить межпроцессное взаимодействие через указатели.
Задача A — создание объекта, который включает в себя std :: map, с использованием статического метода getInstance (). Там указатель, если этот объект назначен глобальной переменной ПЛК, чтобы разрешить доступ каждой задачи к этому конкретному объекту. Эта задача также проверяет каждый цикл задачи на предмет вставки переменной в эту карту. В этом случае он пытается удалить этот вставленный узел снова, просто для целей тестирования.
Задача B получает указатель объекта через метод getInstance и вставляет bool * в карту.
После этого Задача A пытается удалить его снова, и он падает с нарушением прав доступа. Я могу получить доступ к узлам и их значениям. Я могу изменить эти значения. Но удаление узла, созданного в другой задаче, приводит к сбою, и я хотел бы знать, почему!
ПЛК является однопоточным, поэтому не должно быть одновременного доступа к памяти.
Это проблема блокировки? Или это логическая проблема? Проблема STL? Проблема с нулевым указателем? Или, может быть, снова проблема конкретной реализации для библиотеки std поставщика plc? Любая помощь, связанная с нарушением доступа, приветствуется!
Вот фрагменты кода упомянутого объекта, я удалил все, что не связано с проблемой, просто чтобы получить подсказку. извините, скорее всего это не компилируется:
Компилятор: gcc 4.1.2
#include <VarListe.hpp>
VarListe::Ptr VLInstanz;
void _INIT VLErzeugerInit(void)
{
VLInstanz = VarListe::getInstance("VLErzeuger");
}
void _CYCLIC VLErzeugerCyclic(void)
{
VLInstanz->checkNewVars(); // Access Violation here
}
#include <VarListe.hpp>
VarListe::Ptr vals;
bool setPtr = true;void _INIT VarListeTestInit(void)
{
}
// btn_VarTest is a Boolean plc Variable for a button on the Visu
void _CYCLIC VarListeTestCyclic(void)
{
try
{
if(btn_VarTest &&setPtr)
{
vals = VarListe::getInstance("VarListe1");
vals->setVar("btn_VarTest",&btn_VarTest);
//vals->checkNewVars(); // Works perfect if used here. Thats not the point
setPtr = false;
}
catch (...)
{
}
}
#include <map>
#include <deque>
#include <string>
#include <boost/shared_ptr.hpp>
using namespace std;
class VarListe
{
public:
typedef bool* BoolPtr;
typedef boost::shared_ptr<VarListe> Ptr;
static Ptr getInstance(string owner);
static Ptr _alwaysUseGetInstance;
static char owners[200];
void checkNewVars();
private:
typedef map<string, BoolPtr > BoolPtrMap;
typedef deque<BoolPtrMap::iterator> BoolVarQueue;
BoolVarQueue _boolVarQueue;
BoolPtrMap _boolListe;
public:
void setVar(string key, bool* value);
};
#include <VarListe.hpp>
VarListe::Ptr VarListe::_alwaysUseGetInstance; // Singleton static Variable; used olny by the object creator
char VarListe::owners[200]; // just to test which task creats the object
#include <../../Temp/Includes/globalvar.h> // For the global PLC variable 'GlobalVarListe'
// This static Method runs perfectly, no need to check here
VarListe::Ptr VarListe::getInstance(string owner)
{
if(GlobalVarListe == 1337)
{
strcpy(VarListe::owners, "");
owner += "(Builder)";
if (!VarListe::_alwaysUseGetInstance)
VarListe::_alwaysUseGetInstance = VarListe::Ptr(new VarListe);
GlobalVarListe = (UDINT) &VarListe::_alwaysUseGetInstance;
VarListe::_alwaysUseGetInstance->setVar("VarListOwners",VarListe::owners);
}
VarListe::Ptr tempVL = *( (VarListe::Ptr*) GlobalVarListe);
VarListe::CharPtrPair locOwners;
if(tempVL->assertVar("VarListOwners",locOwners))
{
string temp = string(locOwners.first);
temp = temp + owner + " + ";
strcpy(locOwners.first, temp.c_str());
}
return tempVL;
}
// This Method is used in Task B
void VarListe::setVar(string key, bool* value)
{
pair<BoolPtrMap::iterator, bool > eingetragen;
eingetragen = _boolListe.insert(pair<string, BoolPtr>(key, value ));
if(eingetragen.second == false)
{
}
else
_boolVarQueue.push_back(eingetragen.first);
}
// This method is in Task A
void VarListe::checkNewVars()
{
if(!_boolVarQueue.empty())
{
string key = _boolVarQueue.front()->first; //OK,
BoolPtr bp = _boolVarQueue.front()->second; //OK
_boolVarQueue.front()->second = 0; //OK
_boolListe[key] = bp; //OK
BoolPtrMap::iterator fund = _boolListe.find(key); // OK
if (fund != this->_boolListe.end()) // OK
{
_boolListe.erase(key); //Access Violation: Code 9101 only if used by Task A
_boolListe.erase(_boolVarQueue.front()); //Access Violation: Code 9101
_boolListe.erase(fund); //Access Violation: Code 9101
}
_boolVarQueue.pop_front(); //OK
_boolListe[key] = bp; //OK
}
}
Размещение карты STL в любой совместной памяти между процессами (или задачами) с отдельными адресными пространствами невозможно сделать без изменений.
Проблема состоит в том, что, если процесс A вставляет данные в карту, он выделяет любую новую память из своего собственного адресного пространства (а карты действительно выделяют память для новых записей), что недоступно для процесса B. Итак, если процесс B тогда пытается получить доступ к этим недавно вставленным данным, очень вероятно, что получит ошибку (например, нарушение доступа).
К счастью, контейнеры STL позволяют переопределить распределитель, чтобы заменить его своим собственным. Если ваш собственный распределитель затем гарантирует, что он выделяет память из общей памяти, а не из собственного адресного пространства процесса, то все должно работать лучше.
Стандартная реализация не имеет / не требует никакой блокировки:
http://www.sgi.com/tech/stl/thread_safety.html
Разумеется, для реализации допустимо включать блокировки, если она того пожелает.