00001 /* 00002 * CntmLib - Подсчет ссылок, потоки, синхронизация, асинхронные процедуры, события 00003 * Copyright (c) 2005, Овсеевич Роман, CntmLib@mail.ru 00004 * _______________________________________________________________________________ 00005 * Разрешено свободное использование, копирование, распространение, изменение 00006 * (изменение сведений об авторских правах запрещено). Запрещена продажа и 00007 * включение всей библиотеки или ее частей в другие библиотеки. В сведениях об 00008 * авторских правах на программу (или сведениях о программе, об авторах, 00009 * использованных средствах разработки и т.д.) должна быть указана информация 00010 * о библиотеке CntmLib, ее авторе и, возможно, сайте или email'е. 00011 * Библиотека поставляется "как есть", без каких-либо гарантий со стороны автора. 00012 */ 00013 00014 #ifdef CNTM_USE_QT_SS 00015 00016 #ifndef CNTM_QTMAINSYNCHROSPACE_H 00017 #define CNTM_QTMAINSYNCHROSPACE_H 00018 #include <qapplication.h> 00019 #include <qevent.h> 00020 #include <qeventloop.h> 00021 #include <boost/thread/recursive_mutex.hpp> 00022 #include <Cntm/Utils/IntTypes.h> 00023 #include <Cntm/Exceptions/IllegalStateException.h> 00024 #include <Cntm/Synchro/SynchroSpace.h> 00025 00026 namespace Cntm 00027 { 00028 00029 /** 00030 * Класс главного синхропространства, использующего средства библиотеки QT для реализации функций главного синхропространства. 00031 * 00032 * Для реализации входа/выхода в синхропространство используется мьютекс (критическая секция) библиотеки QT, которая доступна через методы QApplication::lock(), QApplication::tryLock(), QApplication::unlock(). 00033 * 00034 * Обработка отложенных синхрозадач производится в цикле обработки сообщений QT, т.е. в процессе выполнения метода QApplication::exec() или при отображении модального окна. Т.о. система гарантирует, что деинициализация синхрообъектов, обработка отложенных событий и т.д. будет выполняться в главном потоке и, соответственно, синхронизирована со всеми остальными событиями библиотеки QT (например с событиями от кнопок, от таймера и т.д.). Замечание: очереди синхрозадач и событий QT - 2 разные очереди, поэтому порядок выполнения отложенных задач и событий QT может нарушаться: задача может быть обработана раньше, чем событие QT, хотя поставлена была позже и наоборот. 00035 * 00036 * С точки зрения режима входа в главное синхропространство QT все события библиотеки выполняются внутри синхропространства в реентерабельном режиме. Это означает, что из обработчиков событий QT, как и из обработчиков отложенных событий можно производить отображение модальных окон. 00037 * 00038 * Реентерабельный вход в главное синхропространство QT происходит, когда вызывается метод QApplication::exec() или другие методы, приводящие к входу в цикл обработки событий. При попытке реентерабельного входа не из главного потока будет выдано исключение LoopInNoMainThreadException, т.к. библиотека QT запрещает запускать циклы не из главного потока. Если режим входа нереентерабельный, то будет выдано исключение NoReentrantModeException (т.е. в нереентерабельном режиме отображение модальных окон запрещено). Если обработка исключений не поддерживается библиотекой QT, то в этом случае в стандартный поток ошибок будут выданы соответствующие сообщения и программа аварийно завершит работу. 00039 * 00040 * Обеспечение жизненного цикла приложения. Для обеспечения жизненного цикла применяется метод QApplication::exec(). В общем случае жизненный цикл выглядит следующим образом. 00041 * \code 00042 * int main(args...) 00043 * { 00044 * Cntm::QTMainSynchroSpace::Prepare(); 00045 * QApplication a(args...); 00046 * Cntm::QTMainSynchroSpace::Begin(); 00047 * ... 00048 * Инициализация 00049 * ... 00050 * a.exec(); 00051 * ... 00052 * Деинициализация 00053 * ... 00054 * Cntm::QTMainSynchroSpace::End(); 00055 * return 0; 00056 * } 00057 * \endcode 00058 * 00059 * Верхние три строчки функции main производят создание приложения и подготовку главного синхропространства QT. Далее следует инициализация системы, после чего запускается приложение - это и является жизненным циклом приложения. После завершения работы (например, закрылось главное окно) происходит деинициализация и выход из главного синхропространства - метод End(). Данный метод производит ожидание момента, когда будут уничтожены все синхрообъекты в системе. Для ожидания он использует метод QApplication::exec(). это означает, что во время ожидания цикл обработки сообщений не нарушается. 00060 * Замечание. В интервале от метода Begin() до входа в цикл обработки событий (a.exec()), от выхода из цикла до входа в следующий цикл, от выхода из цикла до метода End() поток выполнения находится внутри синхропространства в реентерабельном режиме. 00061 * 00062 * Существует более простая и удобная структура приложения: 00063 * \code 00064 * int main(args...) 00065 * { 00066 * Cntm::QTMainSynchroSpace::Application a(args...); 00067 * ... 00068 * Инициализация 00069 * ... 00070 * a.exec(); 00071 * ... 00072 * Деинициализация 00073 * ... 00074 * return 0; 00075 * } 00076 * \endcode 00077 * 00078 * Если в приложении требуется использовать специальный класс цикла обработки событий, то его следует унаследовать от QTMainSynchroSpace::EventLoop и создать объект цикла до вызова Prepare() или создания объекта QTMainSynchroSpace::Application. 00079 * 00080 * Для более подробной информации см. докементацию по QT (разделы по QApplication и поддержке потоков в QT). От библиотеки QT требуется поддержка многопоточности, поэтому при компиляции следует определить макрос QT_THREAD_SUPPORT в настройках компилятора. При линковке следует использовать библиотеку QT с поддержкой многопоточности. 00081 * 00082 * Для того, что бы использовать QTMainSynchroSpace при компиляции должен быть определен макрос CNTM_USE_QT_SS в настройках компилятора. 00083 * @author Овсеевич Р. 00084 * \ingroup Synchro 00085 */ 00086 class QTMainSynchroSpace: public SynchroSpace 00087 { 00088 private: 00089 00090 /** 00091 * Класс, обеспечивающий вызов метода Cntm::QTMainsynchroSpace::Prepare() перед созданием объекта класса QApplication. 00092 * @author Овсеевич Р. 00093 */ 00094 class PrepareLauncher 00095 { 00096 public: 00097 00098 /** 00099 * Конструктор. Обеспечивает вызов Cntm::QTMainsynchroSpace::Prepare(). 00100 */ 00101 PrepareLauncher() { QTMainSynchroSpace::Prepare(); } 00102 }; 00103 00104 public: 00105 00106 typedef RefPtr<QTMainSynchroSpace> Ptr; 00107 00108 /** 00109 * Специальный класс цикла обработки событий QT. Если в приложении используется свой собственный класс цикла, то он должен быть унаследован от данного класса и непосредственно создан программистом перед созданием объекта приложения (см. документацию по QT). Если специальный класс цикла обработки сообщений не используется, то никаких дополнительных действий (создание объекта класса Cntm::QTMainSynchroSpace::EventLoop) выполнять не надо. 00110 * @author Овсеевич Р. 00111 */ 00112 class EventLoop: public QEventLoop 00113 { 00114 public: 00115 00116 /** 00117 * Конструктор. 00118 */ 00119 EventLoop(QObject* parent = 0, const char* name = 0): QEventLoop(parent, name) { created = true; } 00120 00121 /** 00122 * Статический метод создания объекта класса Cntm::QTMainSynchroSpace::EventLoop. Если объект цикла обработки (в том числе и производный от Cntm::QTMainSynchroSpace::EventLoop) уже создан, то ни каких действий не выполняется. 00123 */ 00124 static void CreateEventLoop() { if (!created) new EventLoop; } 00125 00126 /** 00127 * Переопределенный метод входа в цикл. Выполняет спец. операции. При перегрузке в потомке необходимо производить его вызов. 00128 */ 00129 int enterLoop(); 00130 00131 protected: 00132 00133 /** 00134 * Флаг: создан объект или нет. 00135 */ 00136 static bool created; 00137 }; 00138 00139 /** 00140 * Класс QT приложения. Наследует класс QApplication. Предназначен для автоматизации вызывов Cntm::QTMainsynchroSpace::Prepare(), Cntm::QTMainsynchroSpace::Begin(), Cntm::QTMainsynchroSpace::End(). Для использования нужно создать объект данного класса вместо объекта класса QApplication. 00141 * @author Овсеевич Р. 00142 * \ingroup Synchro 00143 */ 00144 class Application: private PrepareLauncher, public QApplication 00145 { 00146 public: 00147 00148 /** 00149 * Конструктор. Вызывает соответствующий конструктор класса QApplication. Выполняет операции Cntm::QTMainsynchroSpace::Prepare() и Cntm::QTMainsynchroSpace::Begin(). 00150 */ 00151 Application(int& argc, char** argv): PrepareLauncher(), QApplication(argc, argv), ended(false) 00152 { 00153 QTMainSynchroSpace::Begin(); 00154 } 00155 00156 /** 00157 * Конструктор. Вызывает соответствующий конструктор класса QApplication. Выполняет операции Prepare() и Begin(). 00158 */ 00159 Application(int& argc, char** argv, bool GUIenabled): PrepareLauncher(), QApplication(argc, argv, GUIenabled), ended(false) 00160 { 00161 QTMainSynchroSpace::Begin(); 00162 } 00163 00164 /** 00165 * Конструктор. Вызывает соответствующий конструктор класса QApplication. Выполняет операции Cntm::QTMainsynchroSpace::Prepare() и Cntm::QTMainsynchroSpace::Begin(). 00166 */ 00167 Application(int& argc, char** argv, Type type): PrepareLauncher(), QApplication(argc, argv, type), ended(false) 00168 { 00169 QTMainSynchroSpace::Begin(); 00170 } 00171 00172 /** 00173 * Деструктор. Выполняет операцию Cntm::QTMainsynchroSpace::End() если ранее не был вызван метод End(). 00174 */ 00175 ~Application() { End(); } 00176 00177 /** 00178 * Метод позволяет выполнить операцию Cntm::QTMainsynchroSpace::End() до уничтожения объекта приложения. Повторные вызовы данного метода ничего не делают. 00179 */ 00180 void End() 00181 { 00182 if (ended) return; 00183 QTMainSynchroSpace::End(); 00184 ended = true; 00185 } 00186 00187 private: 00188 00189 /** 00190 * Флаг: был ли уже вызван метод End() или нет. 00191 */ 00192 bool ended; 00193 }; 00194 00195 00196 /** 00197 * Добавить новую задачу в конец очереди. Объект задачи д.б. создан в динамической памяти. После выполнения задача автоматически будет удалена. Класс объекта задачи д.б. унаследован от SynchroSpace::TaskBase. 00198 * 00199 * Подробнее о синхрозадачах см. описание класса SynchroSpace и SynchroSpace::TaskBase. 00200 * 00201 * Система гарантирует, что задача будет выполнена в контексте главного потока синхронно с прочими событиями библиотеки QT. 00202 * 00203 * \throw NullArgException если не указана задача. 00204 * @param Task - задача, которую нужно выполнить. Объект задачи д.б. создан в динамической памяти. 00205 */ 00206 void AddSynchroTask(TaskBase* Task) { implement->AddSynchroTask(Task); } 00207 00208 /** 00209 * Подготовка главного синхропространства. Должен вызываться до создания объекта QApplication. Если используется специальный цикл обработки событий (унаследованный от QTMainSynchroSpace::EventLoop), то он должен быть создан до вызова данного метода. 00210 * 00211 * Вместо вызова этого метода рекомендуется использовать класс QTMainSynchroSpace::Application. 00212 * 00213 * \throw IllegalStateException при попытке повторного создания главного синхропространства (главное синхропространство этого же или другого типа уже создано). 00214 */ 00215 static void Prepare(); 00216 00217 /** 00218 * Вход в главное синхропространство (режим входа реентерабельный). Должен вызываться после создания объекта QApplication. 00219 * 00220 * Вместо вызова этого метода рекомендуется использовать класс QTMainSynchroSpace::Application. 00221 */ 00222 static void Begin() { implement->Start(); } 00223 00224 /** 00225 * Выход из главного синхропространства. Метод производит ожидание уничтожения всех синхрообъектов и синхропространств. В процессе ожидания запускается цикл обработки событий. 00226 */ 00227 static void End() { implement->Finish(); } 00228 00229 protected: 00230 00231 friend class Implement; 00232 00233 /** 00234 * Деструктор. 00235 */ 00236 ~QTMainSynchroSpace() { implement->MainInstanceDeleted(this); } 00237 00238 /** 00239 * Выполнить вход в критическую секцию, связанную с синхропространством. Вызывает QApplication::lock(). 00240 */ 00241 void DoEnter() { implement->DoEnter(); } 00242 00243 /** 00244 * Выполнить вход в критическую секцию, связанную с синхропространством. Вызывает QApplication::tryLock(). 00245 */ 00246 bool DoTryEnter() { return implement->DoTryEnter(); } 00247 00248 /** 00249 * Выполнить вход в критическую секцию, связанную с синхропространством. Вызывает QApplication::unlock(). 00250 */ 00251 void DoLeave() { implement->DoLeave(); } 00252 00253 private: 00254 00255 /** 00256 * Класс, реализующий все функции синхропространства (вход в синхропространство, выполнение синхронных задач), созданием объекта синхропространства и ожидания его уничтожения. 00257 * @author Овсеевич Р. 00258 */ 00259 class Implement: public QObject, public SynchroSpace::IMainSynchroSpaceFactory 00260 { 00261 public: 00262 00263 /** 00264 * Конструктор. 00265 * 00266 * Исключение: IllegalStateException при попытке повторного создания главного синхропространства. 00267 */ 00268 Implement(); 00269 00270 /** 00271 * Переопределенный метод QObject::event(), который вызывается, когда требуется синхронное выполнение задач или выход когда на главное синхропространство QT не осталось ссылок. 00272 */ 00273 bool event(QEvent* e); 00274 00275 /** 00276 * Реализация метода Cntm::SynchroSpace::IMainSynchroSpaceFactory::QueryMainInstance. 00277 */ 00278 SynchroSpace::Ptr QueryMainInstance(); 00279 00280 /** 00281 * Выполнение захвата критической секции объекта QApplication, которая используется как критическая секция для галавного синхропространства QT. 00282 * 00283 * Данный метод для главного потока и так всегда вызывается внутри критической секции, поэтому блокировку можно не выполнять. Это приводит к полному отсутствию рекурсивных блокировок, что, в свою очередь, приводит к тому, что крит. секция не блокируется на длительное время при реентерабельном входе (стандартное поведение QT - обработчик события блокирует крит. секцию, в т.ч. и на время отображения модального окна из обработчика, новое поведение - на время отображения модального окна происходит разблокировка, что позволяет войти в главное синхропространство другим потокам во время отображения модального окна). 00284 * 00285 * Вызывается из QTMainSynchroSpace::DoEnter(). 00286 */ 00287 void DoEnter() { if (!QTMainSynchroSpace::IsMainThread()) qApp->lock(); } 00288 00289 /** 00290 * Выполнение попытки захвата критической секции объекта QApplication, которая используется как критическая секция для галавного синхропространства QT. 00291 * 00292 * Данный метод для главного потока и так всегда вызывается внутри критической секции, поэтому блокировку можно не выполнять. Это приводит к полному отсутствию рекурсивных блокировок, что, в свою очередь, приводит к тому, что крит. секция не блокируется на длительное время при реентерабельном входе (стандартное поведение QT - обработчик события блокирует крит. секцию, в т.ч. и на время отображения модального окна из обработчика, новое поведение - на время отображения модального окна происходит разблокировка, что позволяет войти в главное синхропространство другим потокам во время отображения модального окна). 00293 * 00294 * Вызывается из QTMainSynchroSpace::DoTryEnter(). 00295 */ 00296 bool DoTryEnter() { return QTMainSynchroSpace::IsMainThread()? true : qApp->tryLock(); } 00297 00298 /** 00299 * Выполнение выхода из критической секции объекта QApplication, которая используется как критическая секция для галавного синхропространства QT. 00300 * 00301 * Данный метод для главного потока и так всегда вызывается внутри критической секции, поэтому блокировку можно не выполнять. Это приводит к полному отсутствию рекурсивных блокировок, что, в свою очередь, приводит к тому, что крит. секция не блокируется на длительное время при реентерабельном входе (стандартное поведение QT - обработчик события блокирует крит. секцию, в т.ч. и на время отображения модального окна из обработчика, новое поведение - на время отображения модального окна происходит разблокировка, что позволяет войти в главное синхропространство другим потокам во время отображения модального окна). 00302 * 00303 * Вызывается из QTMainSynchroSpace::DoLeave(). 00304 */ 00305 void DoLeave() { if (!QTMainSynchroSpace::IsMainThread()) qApp->unlock(); } 00306 00307 /** 00308 * Добавить новую синхронную задачу. Добавляет задачу в очередь и выдает событие самому себе через цикл обработки сообщений приложения QT. 00309 * 00310 * Вызывается из QTMainSynchroSpace::AddSynchroTask(). 00311 * 00312 * Исключение: NullArgException если не указана задача. 00313 * @param Task - задача, которую нужно выполнить. Объек задачи д.б. создан в динамической памяти. 00314 */ 00315 void AddSynchroTask(SynchroSpace::TaskBase* Task) 00316 { 00317 if (taskQueue.Add(Task)) 00318 QApplication::postEvent(this, new QEvent(QEvent::Type(QEvent::User + 0))); 00319 } 00320 00321 /** 00322 * Действия синхропространства при реентерабельном входе (перед запуском цикла обработки событий QT). Выполняет обработку отложенных задач и выходит из крит. секции QApplication. 00323 * 00324 * Вызывается из EventLoop::enterLoop() перед входом в цикл обработки событий. 00325 * 00326 * Исключения: 00327 * LoopInNoMainThreadException при попытке запуска цикла обработки сообщений не из главного потока (в QT такой запуск запрещен). 00328 * NoReentrantModeException при попытке запуска цикла обработки событий QT когда вход в главное синхропространство произведен не в REENTRANT режиме. 00329 */ 00330 void ReentrantEnter(int Level); 00331 00332 /** 00333 * Действия синхропространства при реентерабельном выходе (после запуска цикла обработки событий QT). Входит в крит. секцию QApplication. 00334 * 00335 * Вызывается из EventLoop::enterLoop() перед входом в цикл обработки событий. 00336 */ 00337 void ReentrantLeave(int Level); 00338 00339 /** 00340 * Метод запуска. Выполняет вход в крит. секцию QApplication перед началом работы. 00341 * 00342 * Вызывается из QTMainSynchroSpace::Begin(). 00343 */ 00344 void Start(); 00345 00346 /** 00347 * Метод ожидает уничтожения всех синхрообъектов (последним уничтожается главное синхропространство), выходит из крит. секции QApplication. 00348 * 00349 * Вызывается из QTMainSynchroSpace::End(). 00350 */ 00351 void Finish(); 00352 00353 /** 00354 * Метод сбрасывает указатель на текущее синхропространство и посылает сообщение о выходе из цикла обработки событий при условии, что оно (Inst) является текущим. 00355 * 00356 * Вызывается из QTMainSynchroSpace::~QTMainSynchroSpace(). 00357 */ 00358 void MainInstanceDeleted(QTMainSynchroSpace* Inst); 00359 00360 private: 00361 00362 /** 00363 * Крит. секция, защищающая работу с указателем instance (получение из instance ссылочного указателя, создание нового объекта синхропространства, сброс указателя instance). 00364 */ 00365 boost::recursive_mutex instanceMutex; 00366 00367 /** 00368 * Указатель на синхропространство QT. Инициализируется при создании синхропространства QT и сбрасывается в NULL в деструкторе синхропространства. 00369 */ 00370 SynchroSpace* instance; 00371 00372 /** 00373 * Ссылочный указатель на синхропространство. Инициализируется в конструкторе (в начале выполнения программы) и сбрасывается в методе Finish() (в конце выполнения). 00374 * 00375 * Используется для минимизации созданий объектов синхропространств QT, а также, чтобы избежать преждевременного выхода из цикла обработки событий (если во время работы, когда отображено главное окно будут потеряны ссылки на синхропространство, то произойдет преждевременный выход). 00376 */ 00377 SynchroSpace::Ptr holdInstance; 00378 00379 /** 00380 * Очередь синхронных задач. 00381 */ 00382 TaskQueue taskQueue; 00383 }; 00384 00385 00386 /** 00387 * Объект, реализующий функции главного синхропространства. 00388 */ 00389 static Implement* implement; 00390 }; 00391 00392 } 00393 00394 #endif //CNTM_QTMAINSYNCHROSPACE_H 00395 00396 #endif //CNTM_USE_QT_SS
© Овсеевич Р.В. Документация по CntmLib 1.1.4 от 28 May 2008. Создано системой 1.5.3 |