QTMainSynchroSpace.h

См. документацию.
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

SourceForge.net Logo
© Овсеевич Р.В. Документация по CntmLib 1.1.4 от 28 May 2008. Создано системой  doxygen 1.5.3