/* PIP - Platform Independent Primitives Object, base class of some PIP classes, provide EVENT -> EVENT_HANDLER mechanism Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piobject.h" #include "piconditionvar.h" #include "pithread.h" #ifndef MICRO_PIP # include "pifile.h" # include "piiostream.h" # include "pisysteminfo.h" #endif //! \~\class PIObject piobject.h //! \~\details //! \~english \section PIObject_sec0 Events and Event handlers //! \~russian \section PIObject_sec0 События и Обработчики событий //! //! \~english //! %PIObject provide notification mechanism similar Qt but implemented //! on language capabilities without any special preprocessors or compilers. //! Any class inherits %PIObject should use macro \a PIOBJECT() immediate //! after declaration to proper compile. //! //! Event is a some abstract event that can be raised at any time as common method. //! Event is a function but declared with special macro \a EVENT() and don`t need definition. //! To raise event simply execute event function. //! //! Event handler is a function but declared with special macro //! \a EVENT_HANDLER(). It need definition as common method. //! You can use event handlers as ordinary functions. //! //! Main goal of this mechanism is perform abstract connections between //! various objects. This functionality provide macro \a CONNECT(), \a CONNECTU() and \a CONNECTL() which //! connect some event of first object to some event handler or event of //! second object. Each event can be connected any times to any event handlers. //! //! * \a CONNECT() macros family work with explicit subclasses of %PIObject with compile-time check of events and handlers //! * \a CONNECTU() macro can work implicit subclasses of %PIObject with run-time check of events and handlers //! * \a CONNECTU_QUEUED() macro similar to \a CONNECTU() macro but queue execution with performer object instead of handler direct call //! * \a CONNECTL() macro work with implicit subclasses of %PIObject and lambda-function //! //! \~russian //! %PIObject предоставляет механизм событий и их обработчиков, реализованный без //! дополнительного препроцессора или метакомпилятора. Любой класс, наследованный //! от %PIObject должен использовать макрос \a PIOBJECT() сразу после объявления //! класса для корректной работы. //! //! Событием является сигнал, который может быть вызван как обычный метод в любое время. //! Это метод, объявленный с помощью макроса \a EVENT() и не требует описания. //! Для его вызова просто вызывается метод события. //! //! Обработчик события это метод, объявленный с помощью макроса \a EVENT_HANDLER() //! и он требует описания, как и обычный метод. Можно его использовать как обычный метод. //! //! Основной функцией этого механизма является реализация соединений между различными объектами. //! Её предоставляют макросы \a CONNECT(), \a CONNECTU() и \a CONNECTL(), которые соединяют //! события одних объектов с обработчиками или событиями других объектов. Каждое событие //! может быть присоеденино неограниченное количество раз к любым обработчикам. //! //! * \a CONNECT() семейство макросов работает с явными наследниками %PIObject, и проверяет соединение во время компиляции //! * \a CONNECTU() макрос может работать с неявными наследниками %PIObject, и проверяет соединение во время исполнения //! * \a CONNECTU_QUEUED() макрос подобен \a CONNECTU(), но планирует вызов обработчика у объекта performer вместо прямого вызова //! * \a CONNECTL() макрос может работать с неявными наследниками %PIObject и лямбда-функцией //! //! \~\image html events_handlers.png //! //! \~english Example: //! \~russian Пример: //! //! \~\snippet piobject.cpp main //! \~english Result: //! \~russian Результат: //! \~\code{.cpp} //! handler B: 2 , 0.5 //! handler A: event to handler //! handler A: event to event //! event to lambda //! \endcode //! //! \~\class PIObject::Connection piobject.h //! \~\details //! PIObject::__MetaFunc::__MetaFunc() { for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) { types[i] = names[i] = nullptr; types_id[i] = 0; } } int PIObject::__MetaFunc::argumentsCount() const { for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) if (!types[i]) return i; return __PIOBJECT_MAX_ARGS__; } PIString PIObject::__MetaFunc::arguments() const { PIString ret; for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) { if (!types[i]) break; if (!ret.isEmpty()) ret += ','; ret += PIStringAscii(types[i]); } return ret; } PIString PIObject::__MetaFunc::fullFormat() const { PIString ret = PIStringAscii(type_ret) + " " + PIStringAscii(scope) + "::" + PIStringAscii(func_name) + "("; for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) { if (!types[i]) break; if (i > 0) ret += ", "; ret += PIStringAscii(types[i]) + " " + PIStringAscii(names[i]); } ret += ")"; return ret; } void PIObject::__MetaFunc::__setFuncName(const char * n) { func_name = n; func_name_id = PIStringAscii(n).hash(); } void PIObject::__MetaFunc::__addArgument(const char * t, const char * n) { for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) { if (types[i]) continue; types[i] = t; names[i] = n; types_id[i] = PIObject::simplifyType(t, false).hash(); break; } // PICout(PICoutManipulators::DefaultControls | PICoutManipulators::AddQuotes) // << "__addArgument" << t << n << PIObject::simplifyType(t) << types_id.back(); } bool PIObject::__MetaFunc::canConnectTo(const __MetaFunc & dst, int & args_count) const { for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) { // piCout << "canConnectTo" << i << types[i] << dst.types[i]; args_count = i; if (!dst.types[i]) break; if (!types[i]) return false; if (types_id[i] != dst.types_id[i]) return false; } return true; } PIObject::PIObject(const PIString & name): _signature_(__PIOBJECT_SIGNATURE__), emitter_(0), thread_safe_(false), proc_event_queue(false) { in_event_cnt = 0; setName(name); setDebug(true); mutexObjects().lock(); objects() << this; mutexObjects().unlock(); // piCout << "new" << this; } PIObject::~PIObject() { in_event_cnt = 0; // piCout << "delete" << this; mutexObjects().lock(); objects().removeAll(this); mutexObjects().unlock(); deleted(this); piDisconnectAll(); _signature_ = 0; } bool PIObject::execute(const PIString & method, const PIVector & vl) { if (method.isEmpty()) return false; if (!isPIObject()) { piCout << "Error: \"execute(" << method << ")\":" << (void *)this << "is not PIObject!"; return false; } int ac = 0; __MetaFunc func; bool ok = findSuitableMethodV(method, vl.size_s(), ac, func); if (!ok) return false; callAddrV(func.addrV, toThis(), ac, vl); return true; } bool PIObject::executeQueued(PIObject * performer, const PIString & method, const PIVector & vl) { if (!isPIObject()) { piCout << "Error: \"executeQueued(" << method << ")\": this(" << (void *)this << ") is not PIObject!"; return false; } if (!performer->isPIObject()) { piCout << "Error: \"executeQueued(" << method << ")\": performer(" << (void *)performer << ") is not PIObject!"; return false; } int ac = 0; __MetaFunc func; bool ok = findSuitableMethodV(method, vl.size_s(), ac, func); if (!ok) return false; performer->postQueuedEvent(__QueuedEvent(func.addrV, toThis(), this, performer, vl)); performer->proc_event_queue = true; return true; } PIStringList PIObject::scopeList() const { PIStringList ret; ret.reserve(2); PIMutexLocker ml(__meta_mutex()); const PIVector & scope(__meta_data()[classNameID()].scope_list); for (const char * c: scope) ret << PIStringAscii(c); return ret; } PIStringList PIObject::methodsEH() const { PIMutexLocker ml(__meta_mutex()); PIStringList ret; const __MetaData & ehd(__meta_data()[classNameID()]); for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) ret << eh.value().fullFormat(); return ret; } bool PIObject::isMethodEHContains(const PIString & name) const { uint search_id = name.hash(); PIMutexLocker ml(__meta_mutex()); const __MetaData & ehd(__meta_data()[classNameID()]); for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) { if (eh.value().func_name_id == search_id) return true; } return false; } PIString PIObject::methodEHArguments(const PIString & name) const { uint search_id = name.hash(); PIMutexLocker ml(__meta_mutex()); const __MetaData & ehd(__meta_data()[classNameID()]); for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) { if (eh.value().func_name_id == search_id) return eh.value().arguments(); } return PIString(); } PIString PIObject::methodEHFullFormat(const PIString & name) const { uint search_id = name.hash(); PIMutexLocker ml(__meta_mutex()); const __MetaData & ehd(__meta_data()[classNameID()]); for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) { if (eh.value().func_name_id == search_id) return eh.value().fullFormat(); } return PIString(); } PIString PIObject::methodEHFromAddr(const void * addr) const { return methodEH(addr).func_name; } PIVector PIObject::findEH(const PIString & name) const { uint search_id = name.hash(); PIVector<__MetaFunc> ret; const __MetaData & ehd(__meta_data()[classNameID()]); for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) { if (eh.value().func_name_id == search_id) ret << eh.value(); } return ret; } PIObject::__MetaFunc PIObject::methodEH(const void * addr) const { PIMutexLocker ml(__meta_mutex()); return __meta_data()[classNameID()].eh_func.value(addr); } PIObject::Connection PIObject::piConnect(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, void * ev_h, void * e_h, int args, const char * loc) { // piCout << "piConnect ..."; // piCout << "piConnect" << src << (void*)(dest) << sig; // piCout << "piConnect" << src->className() << "->" << ((PIObject*)dest)->className(); PIMutexLocker _ml(src->mutex_connect); PIMutexLocker _mld(dest_o->mutex_connect, src != dest_o); Connection conn(ev_h, e_h, sig, src, dest_o, dest, args); src->connections << conn; // piCout << "piConnect" << ((PIObject*)dest) << sig << ((PIObject*)dest)->connectors.size_s() << "..."; // piCout << "addConnector" << dest_o << src; dest_o->connectors << src; // piCout << "piConnect" << ((PIObject*)dest) << sig << ((PIObject*)dest)->connectors.size_s(); // piCout << "piConnect ok"; return conn; } PIObject::Connection PIObject::piConnectU(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, const PIString & hname, const char * loc, PIObject * performer) { if (src == 0 || dest_o == 0 || dest == 0) return Connection(); if (!src->isPIObject()) { piCout << "[piConnectU] \"" << sig << "\" -> \"" << hname << "\" error: source object is not PIObject! (" << loc << ")"; return Connection(); } if (!dest_o->isPIObject()) { piCout << "[piConnectU] \"" << sig << "\" -> \"" << hname << "\" error: destination object is not PIObject! (" << loc << ")"; return Connection(); } PIMutexLocker ml(__meta_mutex()); PIMutexLocker mls(src->mutex_connect); PIMutexLocker mld(dest_o->mutex_connect, src != dest_o); PIVector<__MetaFunc> m_src = src->findEH(sig), m_dest = dest_o->findEH(hname); if (m_src.isEmpty()) { piCout << "[piConnectU] Error: can`t find event \"" << sig << "\" in class \"" << src->className() << "\"! (" << loc << ")"; return Connection(); } if (m_dest.isEmpty()) { piCout << "[piConnectU] Error: can`t find handler \"" << hname << "\" in class \"" << dest_o->className() << "\"! (" << loc << ")"; return Connection(); } void *addr_src(0), *addr_dest(0); int args(0); bool que = (performer != 0); piForeachC(__MetaFunc & fs, m_src) { if (addr_src != 0) break; piForeachC(__MetaFunc & fd, m_dest) { if (addr_src != 0) break; if (fs.canConnectTo(fd, args)) { addr_src = fs.addr; addr_dest = que ? fd.addrV : fd.addr; } } } if (addr_src == 0) { piCout << "[piConnectU] Error: can`t find suitable pair of event \"" << sig << "\" in class \"" << src->className() << "\" and handler \"" << hname << "\" in class \"" << dest_o->className() << "\"! (" << loc << ")"; return Connection(); } Connection conn(addr_dest, addr_src, sig, src, dest_o, dest, args, performer); src->connections << conn; if (que) performer->proc_event_queue = true; dest_o->connectors << src; // piCout << cc << cq << _ol.size();//"connect" << src << "->" << dest_o << ", dest.connectors.size() =" << dest_o->connectors.size(); return conn; } PIObject::Connection PIObject::piConnectLS(PIObject * src, const PIString & sig, std::function * f, const char * loc) { if (src == 0) { delete f; return Connection(); } if (!src->isPIObject()) { piCout << "[piConnectLS] \"" << sig << "\" -> [lambda] error: source object is not PIObject! (" << loc << ")"; delete f; return Connection(); } PIMutexLocker ml(__meta_mutex()); PIMutexLocker mls(src->mutex_connect); // piCout << "locked"; PIVector<__MetaFunc> m_src = src->findEH(sig); if (m_src.isEmpty()) { piCout << "[piConnectLS] Error: can`t find event \"" << sig << "\" in class \"" << src->className() << "\"! (" << loc << ")"; delete f; return Connection(); } if (m_src.size() != 1) { piCout << "[piConnectLS] Error: can`t connect overloaded event \"" << sig << "\" in class \"" << src->className() << "\"! (" << loc << ")"; delete f; return Connection(); } PIObject::Connection conn(0, m_src[0].addr, sig, src); // piCout << "found"; conn.functor = f; src->connections << conn; // piCout << "finished"; return conn; } void PIObject::piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h) { PIMutexLocker _ml(src->mutex_connect); PIMutexLocker _mld(dest->mutex_connect, src != dest); for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); if (cc.event == sig && cc.dest_o == dest && cc.slot == ev_h) { src->connections[i].destroy(); src->connections.remove(i); i--; } } dest->updateConnectors(); } void PIObject::piDisconnect(PIObject * src, const PIString & sig, PIObject * dest) { PIMutexLocker _ml(src->mutex_connect); PIMutexLocker _mld(dest->mutex_connect, src != dest); for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); if (cc.event == sig && cc.dest_o == dest) { src->connections[i].destroy(); src->connections.remove(i); i--; } } dest->updateConnectors(); } void PIObject::piDisconnect(PIObject * src, const PIString & sig) { PIMutexLocker _ml(src->mutex_connect); for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); if (cc.event == sig) { PIObject * dest = cc.dest_o; src->connections[i].destroy(); src->connections.remove(i); i--; if (dest) { #if !defined(ANDROID) && !defined(MAC_OS) && !defined(MICRO_PIP) PIMutexLocker _mld(dest->mutex_connect, src != dest); #endif dest->updateConnectors(); } } } } void PIObject::piDisconnectAll() { PIMutexLocker _ml(mutex_connect); PIVector cv = connectors.toVector(); // piCout << "disconnect connectors =" << connectors.size(); piForeach(PIObject * o, cv) { // piCout << "disconnect"<< src << o; if (!o || (o == this)) continue; if (!o->isPIObject()) continue; #if !defined(ANDROID) && !defined(MAC_OS) && !defined(MICRO_PIP) PIMutexLocker _mld(o->mutex_connect, this != o); #endif PIVector & oc(o->connections); for (int i = 0; i < oc.size_s(); ++i) { if (oc[i].functor) continue; // piCout << " check" << (void*)(oc[i].dest_o) << "==" << (void*)(src); if (oc[i].dest_o == this) { oc[i].destroy(); oc.remove(i); --i; } } } // piCout << "disconnect connections =" << connections.size(); piForeachC(PIObject::Connection & c, connections) { if (c.functor) continue; if (!c.dest_o) continue; if (!c.dest_o->isPIObject()) continue; c.dest_o->connectors.remove(this); } for (int i = 0; i < connections.size_s(); ++i) connections[i].destroy(); connections.clear(); } void PIObject::updateConnectors() { // piCout << "*** updateConnectors" << this; connectors.clear(); PIMutexLocker _ml(mutexObjects()); piForeach(PIObject * o, objects()) { if (o == this) continue; PIVector & oc(o->connections); piForeach(Connection & c, oc) if (c.dest == this) connectors << o; } } void PIObject::postQueuedEvent(const PIObject::__QueuedEvent & e) { mutex_queue.lock(); events_queue << e; mutex_queue.unlock(); } void * PIObject::toThis() const { // piCout << ptrOffset() << (void*)this << (void*)((char*)this - ptrOffset()); return (void *)((char *)this - ptrOffset()); } PIMutex & PIObject::__meta_mutex() { static PIMutex ret; return ret; } PIMap & PIObject::__meta_data() { static PIMap ret; return ret; } void PIObject::callQueuedEvents() { mutex_queue.lock(); PIVector<__QueuedEvent> qe = events_queue; events_queue.clear(); mutex_queue.unlock(); piForeachC(__QueuedEvent & e, qe) { if (e.dest_o->thread_safe_) e.dest_o->mutex_.lock(); e.dest_o->emitter_ = e.src; callAddrV(e.slot, e.dest, e.values.size_s(), e.values); e.dest_o->emitter_ = 0; if (e.dest_o->thread_safe_) e.dest_o->mutex_.unlock(); } } //! \details //! \~english //! On first call background thread started to delete objects. //! Each object deletes when it`s outside from any events and hadlers. //! \~russian //! При первом вызове стартует фоновый поток для удаления объектов. //! Каждый объект из очереди удаляется только когда выйдет из всех //! событий и обработок. void PIObject::deleteLater() { Deleter::instance()->post(this); } bool PIObject::findSuitableMethodV(const PIString & method, int args, int & ret_args, PIObject::__MetaFunc & ret) { PIVector<__MetaFunc> ml = findEH(method); if (ml.isEmpty()) { piCoutObj << "Error: no such method \"" << method << "\"!"; return false; } int mfi = -1, ac = -1, mac = -1; for (int i = 0; i < ml.size_s(); ++i) { __MetaFunc & m(ml[i]); int j = m.argumentsCount(); if (mac < 0 || mac > j) mac = j; if ((j <= args) && (ac < j)) { ac = j; mfi = i; } } if (mfi < 0) { piCoutObj << "Error: no such suitable method \"" << method << "\", need at least" << mac << "arguments!"; return false; } ret_args = ac; ret = ml[mfi]; return true; } PIVector & PIObject::objects() { static PIVector * ret = new PIVector(); return *ret; } PIMutex & PIObject::mutexObjects() { static PIMutex ret; return ret; } void PIObject::callAddrV(void * slot, void * obj, int args, const PIVector & vl) { args = piMini(args, vl.size_s()); switch (args) { case 0: ((void (*)(void *))slot)(obj); break; case 1: ((void (*)(void *, const PIVariantSimple &))slot)(obj, vl[0]); break; case 2: ((void (*)(void *, const PIVariantSimple &, const PIVariantSimple &))slot)(obj, vl[0], vl[1]); break; case 3: ((void (*)(void *, const PIVariantSimple &, const PIVariantSimple &, const PIVariantSimple &))slot)(obj, vl[0], vl[1], vl[2]); break; case 4: ((void (*)(void *, const PIVariantSimple &, const PIVariantSimple &, const PIVariantSimple &, const PIVariantSimple &)) slot)(obj, vl[0], vl[1], vl[2], vl[3]); break; default: break; } } PIString PIObject::simplifyType(const char * a, bool readable) { PIString ret = PIStringAscii(a).trim(); if (readable) { int white = -1; for (int i = 0; i < ret.size_s(); ++i) { bool iw = ret[i] == ' ' || ret[i] == '\t' || ret[i] == '\r' || ret[i] == '\n'; // piCout << i << iw << white; if (white < 0) { if (iw) { white = i; continue; } } else { if (!iw) { ret.replace(white, i - white, " "); i = white; white = -1; // piCout << i; } } } ret.replaceAll(" [", '['); ret.replaceAll(" ]", ']'); ret.replaceAll(" <", '<'); ret.replaceAll(" >", '>'); ret.replaceAll(" &", '&'); ret.replaceAll(" *", '*'); ret.replaceAll("[ ", '['); ret.replaceAll("] ", ']'); ret.replaceAll("< ", '<'); ret.replaceAll("> ", '>'); ret.replaceAll("& ", '&'); ret.replaceAll("* ", '*'); if (ret.startsWith("const ") && ret.endsWith("&")) ret.cutLeft(6).cutRight(1).trim(); } else { if (ret.startsWith("const ") && ret.endsWith("&")) ret.cutLeft(6).cutRight(1).trim(); ret.removeAll(' ').removeAll('\t').removeAll('\r').removeAll('\n'); } return ret; } bool PIObject::isPIObject(const PIObject * o) { if (!o) return false; return o->_signature_ == __PIOBJECT_SIGNATURE__; } void PIObject::dump(const PIString & line_prefix) const { // printf("dump %s \"%s\"\n", className(), name().data()); PICout(PICoutManipulators::AddNewLine) << line_prefix << "class " << className() << " (" << (const void *)this << ", \"" << name() << "\") {"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " scope: " << scopeList().join(" -> "); PICout(PICoutManipulators::AddNewLine) << line_prefix << " properties {"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << properties_.size_s(); // printf("dump %d properties\n", properties_.size()); const char * o_name = "name"; auto it = properties_.makeIterator(); while (it.next()) { if (it.key() != piHashData((const uchar *)o_name, strlen(o_name))) PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << it.key() << ": " << it.value(); } // printf("dump %d properties ok\n", properties_.size()); PICout(PICoutManipulators::AddNewLine) << line_prefix << " }"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " methods {"; const __MetaData & ehd(__meta_data()[classNameID()]); PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << ehd.eh_func.size_s(); // printf("dump %d methods\n", ehd.eh_func.size()); for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) { PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << eh.value().fullFormat(); } // printf("dump %d methods ok\n", ehd.eh_func.size()); PICout(PICoutManipulators::AddNewLine) << line_prefix << " }"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " connections {"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << connections.size_s(); // printf("dump %d connections\n",connections.size()); for (const Connection & c: connections) { PIObject * dst = c.dest_o; __MetaFunc ef = methodEH(c.signal); PIString src(c.event); if (ef.func_name) src = PIStringAscii(ef.func_name) + "(" + ef.arguments() + ")"; if (dst) { __MetaFunc hf = dst->methodEH(c.slot); PIString hf_fn; if (!hf.func_name) hf_fn = "[BROKEN]"; else hf_fn = PIStringAscii(hf.func_name) + "(" + hf.arguments() + ")"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << src << " -> " << dst->className() << " (" << c.dest << ", \"" << dst->name() << "\")::" << hf_fn; } else { PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << src << " -> " << "[lambda]"; } } // printf("dump %d connections ok\n",connections.size()); PICout(PICoutManipulators::AddNewLine) << line_prefix << " }"; PICout(PICoutManipulators::AddNewLine) << line_prefix << "}"; } #ifndef MICRO_PIP void dumpApplication(bool with_objects) { PIMutexLocker _ml(PIObject::mutexObjects()); // printf("dump application ...\n"); PIDateTime cd = PIDateTime::current(); PISystemInfo * pi = PISystemInfo::instance(); PICout(PICoutManipulators::AddNewLine) << "application {"; PICout(PICoutManipulators::AddNewLine) << " PIP version: " << PIPVersion(); PICout(PICoutManipulators::AddNewLine) << " OS name: \"" << pi->OS_name << "\""; PICout(PICoutManipulators::AddNewLine) << " OS version: \"" << pi->OS_version << "\""; PICout(PICoutManipulators::AddNewLine) << " processors: " << pi->processorsCount; PICout(PICoutManipulators::AddNewLine) << " architecture: \"" << pi->architecture << "\""; PICout(PICoutManipulators::AddNewLine) << " hostname: \"" << pi->hostname << "\""; PICout(PICoutManipulators::AddNewLine) << " username: \"" << pi->user << "\""; PICout(PICoutManipulators::AddNewLine) << " exec command: \"" << pi->execCommand << "\""; PICout(PICoutManipulators::AddNewLine) << " started: " << pi->execDateTime.toString(); PICout(PICoutManipulators::AddNewLine) << " uptime: " << PITime::fromSystemTime(cd.toSystemTime() - pi->execDateTime.toSystemTime()).toString(); PICout(PICoutManipulators::AddNewLine) << " PIObjects {"; PICout(PICoutManipulators::AddNewLine) << " count: " << PIObject::objects().size_s(); if (with_objects) { for (const PIObject * o: PIObject::objects()) o->dump(" "); } PICout(PICoutManipulators::AddNewLine) << " }"; PICout(PICoutManipulators::AddNewLine) << "}"; // printf("dump application done\n"); } bool dumpApplicationToFile(const PIString & path, bool with_objects) { PIFile f(path + "_tmp"); f.setName("__S__DumpFile"); f.clear(); if (!f.open(PIIODevice::WriteOnly)) return false; auto out_devs = PICout::currentOutputDevices(); PICout::setOutputDevices(PICout::Buffer); PICout::clearBuffer(); dumpApplication(with_objects); PIIOTextStream ts(&f); ts << PICout::getBuffer(); f.close(); PICout::setOutputDevices(out_devs); PIFile::rename(path + "_tmp", path); return true; } #endif void PIObject::__MetaData::addScope(const char * s, uint shash) { if (!scope_id.contains(shash)) { scope_list << s; scope_id << shash; } } void PIObject::Connection::destroy() { if (functor) delete functor; functor = nullptr; } PIObject::Connection::Connection() { slot = signal = dest = nullptr; src_o = dest_o = performer = nullptr; functor = nullptr; eventID = 0; args_count = 0; } bool PIObject::Connection::disconnect() { if (!isValid() || !src_o) return false; if (!src_o->isPIObject()) return false; bool ndm = dest_o && (src_o != dest_o), ret = false, found = false; if (dest_o) { if (!dest_o->isPIObject()) ndm = false; } PIMutexLocker _ml(src_o->mutex_connect); if (ndm) dest_o->mutex_connect.lock(); for (int i = 0; i < src_o->connections.size_s(); ++i) { Connection & cc(src_o->connections[i]); if (cc.eventID == eventID) { if (dest_o && (cc.dest_o == dest_o)) { if (cc.slot == slot) found = true; } if (functor && (cc.functor == functor)) found = true; } if (found) { src_o->connections[i].destroy(); src_o->connections.remove(i); ret = true; break; } } if (dest_o) { if (dest_o->isPIObject()) dest_o->updateConnectors(); } if (ndm) dest_o->mutex_connect.unlock(); return ret; } PRIVATE_DEFINITION_START(PIObject::Deleter) PIThread thread; PIConditionVariable cond_var; PIVector obj_queue; PRIVATE_DEFINITION_END(PIObject::Deleter) PIObject::Deleter::Deleter() { // piCout << "Deleter start ..."; PRIVATE->thread.setSlot([this]() { PIVector oq; PRIVATE->thread.lock(); if (PRIVATE->obj_queue.isEmpty()) PRIVATE->cond_var.wait(PRIVATE->thread.mutex()); oq.swap(PRIVATE->obj_queue); PRIVATE->thread.unlock(); for (PIObject * o: oq) deleteObject(o); }); PRIVATE->thread.start(); } PIObject::Deleter::~Deleter() { // piCout << "~Deleter ..."; PRIVATE->thread.stop(); PRIVATE->cond_var.notifyAll(); PRIVATE->thread.waitForFinish(); for (PIObject * o: PRIVATE->obj_queue) deleteObject(o); // piCout << "~Deleter ok"; } PIObject::Deleter * PIObject::Deleter::instance() { static Deleter ret; return &ret; } void PIObject::Deleter::post(PIObject * o) { if (!o->isPIObject()) return; // piCout << "[Deleter] post" << o << "..."; PRIVATE->thread.lock(); if (!PRIVATE->obj_queue.contains(o)) { PRIVATE->obj_queue << o; PRIVATE->cond_var.notifyAll(); } PRIVATE->thread.unlock(); // piCout << "[Deleter] post" << o << "done"; } void PIObject::Deleter::deleteObject(PIObject * o) { // piCout << "[Deleter] delete" << (uintptr_t)o << "..."; if (o->isPIObject()) { // piCout << "[Deleter] delete" << (uintptr_t)o << "wait atomic ..."; while (o->isInEvent()) piMinSleep(); // piCout << "[Deleter] delete" << (uintptr_t)o << "wait atomic done"; delete o; } // piCout << "[Deleter] delete" << (uintptr_t)o << "done"; }