src/gui/utils/event_handler.cpp

Go to the documentation of this file.
00001 /*  $Id: event_handler.cpp 17781 2008-09-15 21:02:42Z yazhuk $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Vladimir Tereshkov, Andrey Yazhuk
00027  *
00028  * File Description:
00029  *      Event receiver implementation
00030  */
00031 
00032 #include <ncbi_pch.hpp>
00033 #include <corelib/ncbistd.hpp>
00034 
00035 #include <gui/utils/event_handler.hpp>
00036 
00037 #include <algorithm>
00038 
00039 
00040 BEGIN_NCBI_SCOPE
00041 
00042 
00043 ///////////////////////////////////////////////////////////////////////////////
00044 /// CEventHandler
00045 
00046 CEventHandler::CEventHandler()
00047 {
00048 }
00049 
00050 
00051 CEventHandler::~CEventHandler()
00052 {
00053     x_DeclareDead();
00054 
00055 #ifdef _DEBUG
00056     ///
00057     /// disconnect our listeners
00058     ///
00059     TPools::iterator iter = m_Pools.find(ePool_Default);
00060     if (iter != m_Pools.end()) {
00061         NON_CONST_ITERATE (TListeners, it, iter->second) {
00062             if (*it) {
00063                 (*it)->RemoveListener(this);
00064             }
00065         }
00066     }
00067     m_Pools.clear();
00068 #endif
00069 }
00070 
00071 
00072 void CEventHandler::x_DeclareDead()
00073 {
00074     // make sure it not receive any async events after from the queue
00075     if(m_Queue)   {
00076         m_Queue->DeclareDead(this);
00077     }
00078 }
00079 
00080 
00081 SEvtMapEntry defaultUpdateEntry = {CEvent::eEvent_CommandUpdate, "", 0, 0, NULL};
00082 
00083 /// Traverses a list of Command Maps in order to locate a handler corresponding
00084 /// to the given command ID.
00085 const SEvtMapEntry* FindEventMapEntry(const SEvtMap* pMap,
00086                                       CEvent::TEventClass cls,
00087                                       CEvent::TEventTypeInfo tif,
00088                                       CEvent::TEventID CmdID)
00089 {
00090     const SEvtMapEntry* pEntry = 0;
00091     const SEvtMapEntry* pUpdateEntry = 0;
00092 
00093     while(pMap) {
00094         for (pEntry = pMap->entries;  pEntry->id != CEvent::eEvent_InvalidID;  ++pEntry) {
00095 
00096             bool cmd_between = (CmdID >= pEntry->id  &&  CmdID <= pEntry->last_id);
00097             bool typeinfo_safe = (strcmp(tif, pEntry->type_info) == 0);
00098             bool class_safe = (cls == pEntry->evt_class);
00099             bool is_cmd = (cls == CEvent::eEvent_Command)  ||
00100                           (cls == CEvent::eEvent_CommandUpdate);
00101 
00102             if (cmd_between  &&  (typeinfo_safe  ||  is_cmd)  &&  class_safe) {
00103                 return pEntry; // matching handler has been found
00104             }
00105 
00106             // found command handler for an undefined update handler
00107             // so command will be enabled, if not-disabled
00108             if (cmd_between  &&  typeinfo_safe  &&
00109                (cls == CEvent::eEvent_CommandUpdate)  &&  (pEntry->evt_class == CEvent::eEvent_Command)) {
00110                 pUpdateEntry = &defaultUpdateEntry;
00111             }
00112         }
00113         pMap = pMap->base_map; // go to the command map of the base class
00114     }
00115     return pUpdateEntry; // handler not found, or "fake" update handler found
00116 }
00117 
00118 ////////////////////////////////////////////////////////////////////////////////
00119 // class CEventHandler
00120 
00121 const SEvtMap CEventHandler::sm_EvtMap =
00122 {
00123     NULL, // no base class
00124     &CEventHandler::sm_EvtMapEntries[0]
00125 };
00126 
00127 
00128 const SEvtMap* CEventHandler::GetEventMap() const
00129 {
00130     return &CEventHandler::sm_EvtMap;
00131 }
00132 
00133 const SEvtMapEntry CEventHandler::sm_EvtMapEntries[] =
00134 {
00135     {   CEvent::eEvent_Message, typeid(void).name(),
00136         CEvent::eEvent_InvalidID, CEvent::eEvent_InvalidID, 0 } // no entries
00137 };
00138 
00139 
00140 void CEventHandler::AddListener(CEventHandler* listener, int pool)
00141 {
00142     if(listener) {
00143         if(pool != ePool_Default)   {
00144             x_AddListenerToPool(listener, ePool_Default);
00145         }
00146         x_AddListenerToPool(listener, pool);
00147     } else {
00148         _TRACE("CEventHandler::AddListener(): NULL listener");
00149         return;
00150     }
00151 }
00152 
00153 
00154 void CEventHandler::x_AddListenerToPool(CEventHandler* listener, int pool_name)
00155 {
00156     _ASSERT(listener);
00157 
00158     TListeners& pool = m_Pools[pool_name];
00159     TListeners::iterator it = std::find(pool.begin(), pool.end(), listener);
00160     if(it == pool.end()) {
00161         pool.push_back(listener);
00162     }
00163 }
00164 
00165 
00166 void CEventHandler::RemoveListener(CEventHandler* listener)
00167 {
00168     if(listener) {
00169         for(TPools::iterator it = m_Pools.begin(); it != m_Pools.end();  ) {
00170             TListeners& pool = it->second;
00171             TListeners::iterator it_lst = std::find(pool.begin(), pool.end(), listener);
00172             if(it_lst != pool.end())  {   // the listener was found in the pool
00173                 pool.erase(it_lst);
00174             }
00175             if(pool.empty()) {  // delete the pool
00176                 TPools::iterator it_del = it;
00177                 ++it;
00178                 m_Pools.erase(it_del);
00179             } else {
00180                 ++it;
00181             }
00182         }
00183     } else {
00184         _TRACE("CEventHandler::RemoveListener(): NULL listener");
00185         return;
00186     }
00187 }
00188 
00189 
00190 void CEventHandler::RemoveAllListeners(void)
00191 {
00192     m_Pools.clear();
00193 }
00194 
00195 
00196 bool CEventHandler::HasListener(CEventHandler* listener, int pool_name) const
00197 {
00198     if(listener != NULL)    {
00199         TPools::const_iterator it_pool = m_Pools.find(pool_name);
00200         if(it_pool != m_Pools.end())    {
00201             const TListeners& pool = it_pool->second;
00202             TListeners::const_iterator it = std::find(pool.begin(), pool.end(), listener);
00203             return (it != pool.end());
00204         }
00205     }
00206     return false;
00207 }
00208 
00209 
00210 const CEventHandler::TListeners* CEventHandler::GetListeners(int pool_name) const
00211 {
00212     TPools::const_iterator it_pool = m_Pools.find(pool_name);
00213     return (it_pool != m_Pools.end()) ? &it_pool->second : NULL;
00214 }
00215 
00216 
00217 static const char* kOnEventException = "CEventHandler::OnEvent() ";
00218 static const char* kDispatchException = "CEventHandler::Dispatch() ";
00219 
00220 bool CEventHandler::OnEvent(CEvent * evt)
00221 {
00222     _ASSERT(evt);
00223     evt->Visit(this);
00224 
00225     try {
00226         const CEvent::TEventClass cls = evt->GetEventClass();
00227         CEvent::TEventTypeInfo info = evt->GetTypeInfo();
00228         CEvent::TEventID id = evt->GetID();
00229 
00230         const SEvtMapEntry* pEntry =
00231             FindEventMapEntry(GetEventMap(), cls, info, id);
00232 
00233         if (pEntry) {
00234             switch (pEntry->evt_class) {
00235             case CEvent::eEvent_Command:
00236                 if (pEntry->handler){
00237                     if (pEntry->last_id != pEntry->id) {
00238                         FCommandRangeHandler handler =
00239                             (FCommandRangeHandler) pEntry->handler;
00240                         (this->*handler)(evt->GetID());
00241                     } else {
00242                         FCommandHandler handler =
00243                             (FCommandHandler) pEntry->handler;
00244                         (this->*handler)();
00245                     }
00246                     return true;
00247                 }
00248                 break;
00249 
00250             case CEvent::eEvent_CommandUpdate:
00251                 // pEntry != NULL  that means update handler or command handler found
00252                 if (pEntry->handler)  {
00253                     // update handler found - invoke
00254                     FCommandUpdateHandler handler =
00255                         (FCommandUpdateHandler) pEntry->handler;
00256                     (this->*handler)(evt->GetAttachment());
00257                 }
00258                 return true;
00259 
00260             default:
00261                 if (pEntry->handler) {
00262                     FEventHandler handler = pEntry->handler;
00263                     (this->*handler)(evt);
00264                     return true;
00265                 }
00266                 break;
00267             }
00268         }
00269     }
00270     catch(CException& e)  {
00271         ERR_POST(kOnEventException << e.GetMsg());
00272     }
00273     catch(std::exception& e)  {
00274         ERR_POST(kOnEventException << e.what());
00275     }
00276 #ifndef _DEBUG
00277     catch(...)  {
00278         ERR_POST(kOnEventException << " unknown exception");
00279     }
00280 #endif
00281     return false; // not handled
00282 }
00283 
00284 
00285 bool CEventHandler::Dispatch(CEvent* evt, EDispatch disp_how, int pool)
00286 {
00287     _ASSERT(evt);
00288     bool handled = false;
00289 
00290     try {
00291         TPools::iterator iter = m_Pools.find(pool);
00292         if(iter == m_Pools.end()) {
00293             return false;
00294         }
00295 
00296         switch (disp_how) {
00297         case eDispatch_SelfOnly: // does not try to handle - Send() does that
00298             break;
00299 
00300         case eDispatch_AllHandlers:
00301             ITERATE(TListeners, listener, iter->second) {
00302                 CEventHandler*   handler = *listener;
00303                 if(evt->Visit(handler)) {
00304                     handled |= handler->Send(evt, disp_how, pool);
00305                 }
00306             }
00307             break;
00308 
00309         case eDispatch_FirstHandler:
00310             ITERATE(TListeners, listener, iter->second){
00311                 CEventHandler*   handler = *listener;
00312                 if(evt->Visit(handler)) {
00313                     handled = handler->Send(evt, disp_how, pool);
00314                     if(handled) {
00315                         return true;
00316                     }
00317                 }
00318             }
00319             break;
00320         }
00321     }
00322     catch(CException& e)  {
00323         ERR_POST(kDispatchException << e.GetMsg());
00324     }
00325     catch(std::exception& e)  {
00326         ERR_POST(kDispatchException << e.what());
00327     }
00328 #ifndef _DEBUG
00329     catch(...)  {
00330         ERR_POST(kDispatchException << " unknown exception");
00331     }
00332 #endif
00333     return handled;
00334 }
00335 
00336 
00337 bool CEventHandler::Send(CEvent* evt, EDispatch disp_how, int pool_name)
00338 {
00339     _ASSERT(evt);
00340 
00341     bool handled = OnEvent(evt);
00342 
00343     switch (disp_how) {
00344     case eDispatch_SelfOnly: // does not dispatch to the listeners
00345         break;
00346 
00347     case eDispatch_AllHandlers: // dispatch always
00348         handled |= Dispatch(evt, disp_how, pool_name);
00349         break;
00350 
00351     case eDispatch_FirstHandler: // dispatch only if not handled
00352         if( ! handled)  {
00353             handled = Dispatch(evt, disp_how, pool_name);
00354         }
00355         break;
00356     }
00357     return handled;
00358 }
00359 
00360 
00361 bool CEventHandler::Send(CEvent* evt, int pool_name)
00362 {
00363     return Send(evt, eDispatch_Default, pool_name);
00364 }
00365 
00366 
00367 void CEventHandler::Post(CRef<CEvent> evt, EDispatch disp_how, int pool_name)
00368 {
00369     _ASSERT(evt);
00370     if( ! m_Queue) {
00371         m_Queue = CPostQueue::GetInstance();
00372     }
00373 
00374     SPostRequest* req = new SPostRequest();
00375     req->m_Target = this;
00376     req->m_Event = evt;
00377     req->m_DispHow = disp_how;
00378     req->m_PoolName = pool_name;
00379 
00380     m_Queue->Post(req);
00381 }
00382 
00383 
00384 void CEventHandler::FireEvent(CEvent* evt, EDispatch disp_how, int pool_name)
00385 {
00386     LOG_POST(Warning << "CEventHandler::FireEvent() - is deprecated,"
00387                 <<  "use Send(), Dispatch() or Post() instead");
00388     Send(evt, disp_how, pool_name);
00389 }
00390 
00391 
00392 bool CEventHandler::OnCommand(const TCmdID CmdID)
00393 {
00394     CEvent evt(CEvent::eEvent_Command, CmdID);
00395     return Send(&evt, eDispatch_FirstHandler);
00396 }
00397 
00398 
00399 bool CEventHandler::HandlePostRequest()
00400 {
00401     CRef<CPostQueue> queue = CPostQueue::GetInstance();
00402     return queue->ExecuteFirstRequest();
00403 }
00404 
00405 
00406 /// removes all requests from the Queue
00407 void CEventHandler::ClearPostQueue()
00408 {
00409     CRef<CPostQueue> queue = CPostQueue::GetInstance();
00410     queue->Clear();
00411 }
00412 
00413 
00414 void CEventHandler::DestroyPostQueue()
00415 {
00416     CPostQueue::DestroyInstance();
00417 }
00418 
00419 
00420 CEventHandler::FOnPostCallback CEventHandler::sm_PostCallback = NULL;
00421 
00422 
00423 void CEventHandler::SetPostCallback(FOnPostCallback callback)
00424 {
00425     sm_PostCallback = callback;
00426 }
00427 
00428 ///////////////////////////////////////////////////////////////////////////////
00429 /// CPostQueue
00430 
00431 CRef<CEventHandler::CPostQueue>   CEventHandler::CPostQueue::sm_PostQueue;
00432 
00433 CRef<CEventHandler::CPostQueue> CEventHandler::CPostQueue::GetInstance()
00434 {
00435     if( ! sm_PostQueue)    {        // fast check
00436         static CMutex s_CreateQueueMutex;
00437         TMutexGuard lock(s_CreateQueueMutex);
00438 
00439         /// now safe check
00440         if( ! sm_PostQueue)    {
00441             sm_PostQueue.Reset(new CPostQueue()); // create the singleton
00442         }
00443     }
00444     return sm_PostQueue;
00445 }
00446 
00447 
00448 void CEventHandler::CPostQueue::DestroyInstance()
00449 {
00450     sm_PostQueue.Reset();
00451 }
00452 
00453 
00454 CEventHandler::CPostQueue::~CPostQueue()
00455 {
00456     CMutexGuard guard(m_Mutex);
00457 
00458     NON_CONST_ITERATE(THandlerToCount, it, m_AliveTargets)  {
00459         it->first->m_Queue.Reset(); // disconnect from the queue
00460     }
00461 
00462     m_AliveTargets.clear();
00463     m_Queue.clear();
00464 }
00465 
00466 /*static int kSampling = 10000;
00467 static CStopWatch   m_PostTimer;
00468 static int  m_PostCounter = 0;
00469 static CStopWatch   m_ExecTimer;
00470 static int m_ExecCounter = 0;
00471 static int m_ExecEventCounter = 0;
00472 
00473 static int dbg_q_size = 0;
00474 */
00475 
00476 void CEventHandler::CPostQueue::Post(SPostRequest* req)
00477 {
00478     if(req) {
00479         /*m_PostCounter++;
00480         if(m_PostCounter % kSampling == 0)    {
00481             double t = m_PostTimer.Elapsed();
00482             LOG_POST("CPostQueue::Post() - " << m_PostCounter << " events,  " << (kSampling / t) << " calls per second");
00483             m_PostTimer.Restart();
00484         }*/
00485 
00486         {
00487             CMutexGuard guard(m_Mutex);
00488 
00489             CEventHandler* target = req->m_Target;
00490             THandlerToCount::iterator it = m_AliveTargets.find(target);
00491             if(it == m_AliveTargets.end()) {  // this is the first event
00492                 m_AliveTargets[target] = 1; // register itself as "alive" target
00493             } else {
00494                 it->second++; // increment event counter
00495             }
00496 
00497             m_Queue.push_back(req); // post the request
00498             //dbg_q_size++;
00499         }
00500         if(sm_PostCallback) {
00501             (*sm_PostCallback)();
00502         }
00503         /*if(dbg_q_size % 20 == 0)   {
00504             LOG_POST("CEventHandler::CPostQueue::Post() - " << dbg_q_size);
00505         }*/
00506 #ifdef _DEBUG
00507         size_t size = m_Queue.size();
00508         if(size > 1000  &&  (size % 100) == 0) {
00509             LOG_POST(Error << "CEventHandler::CPostQueue::Post() - "
00510                 "queue is too long, size = " << size);
00511         }
00512 #endif
00513     }
00514 }
00515 
00516 
00517 /// extracts a request from the front of the queue and executes it
00518 bool CEventHandler::CPostQueue::ExecuteFirstRequest()
00519 {
00520     CRef<CObject>  target_guard;
00521 
00522     // takes care of deleting the request instance
00523     AutoPtr<SPostRequest> request;
00524 
00525     {{
00526 #if 0
00527          m_ExecCounter++;
00528          if(m_ExecCounter % kSampling == 0)    {
00529              double t = m_ExecTimer.Elapsed();
00530              LOG_POST("CPostQueue::ExecuteFirstRequest() - " << m_ExecCounter << " calls,  "
00531                       << (kSampling / t) << " calls per second,  "
00532                       << (m_ExecEventCounter / t) << "  events executed per second");
00533 
00534              m_ExecTimer.Restart();
00535              m_ExecEventCounter = 0;
00536          }
00537 #endif
00538 
00539          /// limit lifetime of the guard
00540          TMutexGuard guard(m_Mutex);  // synchonize access
00541 
00542          if (!m_Queue.empty() ) {
00543              //m_ExecEventCounter++;
00544              request = m_Queue.front();
00545              m_Queue.pop_front();
00546 
00547              THandlerToCount::iterator it = m_AliveTargets.find(request->m_Target);
00548              if(it != m_AliveTargets.end()) {
00549                  // protect the target if possible
00550                  target_guard.Reset(dynamic_cast<CObject*>(request->m_Target));
00551 
00552                  if(it->second == 1) {
00553                      // this target does not have any other events in the queue
00554                      m_AliveTargets.erase(it);
00555                  } else {
00556                      it->second--;
00557                  }
00558              } else { // the target is dead - delete request to skip execution
00559                  request.reset(NULL);
00560              }
00561          }
00562 
00563          /// at this point the Mutex must be released to avoid potential
00564          /// deadlocks during target->Send() call
00565      }}
00566 
00567     if(request.get()) {
00568         /// For targets running on the same thread as the Queue we have a
00569         /// guarantee that they will not be destroyed between the moment we
00570         /// extract the request and the moment we start executing it.
00571         /// Targets running other treads must inherit from CObject to be
00572         /// guarded by the "target_guard".
00573 
00574         CEventHandler* target = request->m_Target;
00575         CEvent* evt = request->m_Event.GetPointer();
00576 
00577         _ASSERT(target  &&  evt);
00578 
00579         // Send() is supposed to be exception safe
00580         target->Send(evt, request->m_DispHow, request->m_PoolName);
00581         return true;
00582     }
00583     return false; // the Queue is empty
00584 }
00585 
00586 
00587 void CEventHandler::CPostQueue::DeclareDead(CEventHandler* handler)
00588 {
00589     if(handler) {
00590         CMutexGuard guard(m_Mutex);
00591         m_AliveTargets.erase(handler); // delete this from the list of alive Post() targets
00592     }
00593 }
00594 
00595 
00596 void CEventHandler::CPostQueue::Clear()
00597 {
00598     CMutexGuard guard(m_Mutex);
00599     m_Queue.clear();
00600     m_AliveTargets.clear();
00601 }
00602 
00603 
00604 END_NCBI_SCOPE
00605 
00606 

Generated on Wed Dec 9 04:32:31 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Wed Dec 09 08:18:02 2009 by modify_doxy.py rev. 173732