NCBI C++ ToolKit
event_handler.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: event_handler.cpp 40834 2018-04-16 19:16:21Z katargir $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Vladimir Tereshkov, Andrey Yazhuk
27  *
28  * File Description:
29  * Event receiver implementation
30  */
31 
32 #include <ncbi_pch.hpp>
33 #include <corelib/ncbistd.hpp>
34 
36 
37 #include <algorithm>
38 
39 
41 
42 
43 ///////////////////////////////////////////////////////////////////////////////
44 /// CEventHandler
45 
47 {
48 }
49 
50 
52 {
53  x_DeclareDead();
54 
55 #ifdef _DEBUG
56  ///
57  /// disconnect our listeners
58  ///
60  if (iter != m_Pools.end()) {
61  NON_CONST_ITERATE (TListeners, it, iter->second) {
62  if (*it) {
63  (*it)->RemoveListener(this);
64  }
65  }
66  }
67  m_Pools.clear();
68 #endif
69 }
70 
71 
73 {
74  // make sure it not receive any async events after from the queue
75  if(m_Queue) {
76  m_Queue->DeclareDead(this);
77  }
78 }
79 
80 
81 /// Traverses a list of Command Maps in order to locate a handler corresponding
82 /// to the given command ID.
86  CEvent::TEventID CmdID)
87 {
88  const SEvtMapEntry* pEntry = 0;
89 
90  while(pMap) {
91  for (pEntry = pMap->entries; pEntry->id != CEvent::eEvent_InvalidID; ++pEntry) {
92 
93  bool cmd_between = (CmdID >= pEntry->id && CmdID <= pEntry->last_id);
94  bool typeinfo_safe = (strcmp(tif, pEntry->type_info) == 0);
95  bool class_safe = (cls == pEntry->evt_class);
96 
97  if (cmd_between && typeinfo_safe && class_safe) {
98  return pEntry; // matching handler has been found
99  }
100  }
101  pMap = pMap->base_map; // go to the command map of the base class
102  }
103 
104  return pEntry; // handler not found, or "fake" update handler found
105 }
106 
107 ////////////////////////////////////////////////////////////////////////////////
108 // class CEventHandler
109 
111 {
112  NULL, // no base class
114 };
115 
116 
118 {
119  return &CEventHandler::sm_EvtMap;
120 }
121 
123 {
124  { CEvent::eEvent_Message, typeid(void).name(),
126 };
127 
128 
129 void CEventHandler::AddListener(CEventHandler* listener, int pool)
130 {
131  if(listener) {
132  if(pool != ePool_Default) {
134  }
135  x_AddListenerToPool(listener, pool);
136  } else {
137  _TRACE("CEventHandler::AddListener(): NULL listener");
138  return;
139  }
140 }
141 
142 
143 void CEventHandler::x_AddListenerToPool(CEventHandler* listener, int pool_name)
144 {
145  _ASSERT(listener);
146 
147  TListeners& pool = m_Pools[pool_name];
148  TListeners::iterator it = std::find(pool.begin(), pool.end(), listener);
149  if(it == pool.end()) {
150  pool.push_back(listener);
151  }
152 }
153 
154 
156 {
157  if(listener) {
158  for(TPools::iterator it = m_Pools.begin(); it != m_Pools.end(); ) {
159  TListeners& pool = it->second;
160  TListeners::iterator it_lst = std::find(pool.begin(), pool.end(), listener);
161  if(it_lst != pool.end()) { // the listener was found in the pool
162  pool.erase(it_lst);
163  }
164  if(pool.empty()) { // delete the pool
165  TPools::iterator it_del = it;
166  ++it;
167  m_Pools.erase(it_del);
168  } else {
169  ++it;
170  }
171  }
172  } else {
173  _TRACE("CEventHandler::RemoveListener(): NULL listener");
174  return;
175  }
176 }
177 
178 
180 {
181  m_Pools.clear();
182 }
183 
184 
185 bool CEventHandler::HasListener(CEventHandler* listener, int pool_name) const
186 {
187  if(listener != NULL) {
188  TPools::const_iterator it_pool = m_Pools.find(pool_name);
189  if(it_pool != m_Pools.end()) {
190  const TListeners& pool = it_pool->second;
191  TListeners::const_iterator it = std::find(pool.begin(), pool.end(), listener);
192  return (it != pool.end());
193  }
194  }
195  return false;
196 }
197 
198 
200 {
201  TPools::const_iterator it_pool = m_Pools.find(pool_name);
202  return (it_pool != m_Pools.end()) ? &it_pool->second : NULL;
203 }
204 
205 
207 {
208  _ASSERT(evt);
209  evt->Visit(this);
210 
211  try {
212  const CEvent::EEventClass cls = evt->GetEventClass();
214  CEvent::TEventID id = evt->GetID();
215 
216  const SEvtMapEntry* pEntry =
217  FindEventMapEntry(GetEventMap(), cls, info, id);
218 
219  if (pEntry) {
220  if (pEntry->handler) {
221  FEventHandler handler = pEntry->handler;
222  (this->*handler)(evt);
223  return true;
224  }
225  }
226  } NCBI_CATCH("CEventHandler::OnEvent()");
227 
228  return false; // not handled
229 }
230 
231 
232 bool CEventHandler::Dispatch(CEvent* evt, EDispatch disp_how, int pool)
233 {
234  _ASSERT(evt);
235  bool handled = false;
236 
237  try {
238  TPools::iterator iter = m_Pools.find(pool);
239  if(iter == m_Pools.end()) {
240  return false;
241  }
242 
243  switch (disp_how) {
244  case eDispatch_SelfOnly: // does not try to handle - Send() does that
245  break;
246 
248  {
249  TListeners listeners_cpy(iter->second);
250  ITERATE(TListeners, listener, listeners_cpy) {
251  CEventHandler* handler = *listener;
252  if (evt->Visit(handler)) {
253  handled |= handler->Send(evt, disp_how, pool);
254  }
255  }
256  }
257  break;
258 
260  {
261  TListeners listeners_cpy(iter->second);
262  ITERATE(TListeners, listener, listeners_cpy){
263  CEventHandler* handler = *listener;
264  if (evt->Visit(handler)) {
265  handled = handler->Send(evt, disp_how, pool);
266  if (handled) {
267  return true;
268  }
269  }
270  }
271  }
272  break;
273  }
274  } NCBI_CATCH("CEventHandler::Dispatch()");
275 
276  return handled;
277 }
278 
279 
280 bool CEventHandler::Send(CEvent* evt, EDispatch disp_how, int pool_name)
281 {
282  _ASSERT(evt);
283 
284  bool handled = OnEvent(evt);
285 
286  switch (disp_how) {
287  case eDispatch_SelfOnly: // does not dispatch to the listeners
288  break;
289 
290  case eDispatch_AllHandlers: // dispatch always
291  handled |= Dispatch(evt, disp_how, pool_name);
292  break;
293 
294  case eDispatch_FirstHandler: // dispatch only if not handled
295  if( ! handled) {
296  handled = Dispatch(evt, disp_how, pool_name);
297  }
298  break;
299  }
300  return handled;
301 }
302 
303 
304 bool CEventHandler::Send(CEvent* evt, int pool_name)
305 {
306  return Send(evt, eDispatch_Default, pool_name);
307 }
308 
309 
310 void CEventHandler::Post(CRef<CEvent> evt, EDispatch disp_how, int pool_name)
311 {
312  _ASSERT(evt);
313  if( ! m_Queue) {
315  }
316 
317  SPostRequest* req = new SPostRequest();
318  req->m_Target = this;
319  req->m_Event = evt;
320  req->m_DispHow = disp_how;
321  req->m_PoolName = pool_name;
322 
323  m_Queue->Post(req);
324 }
325 
326 
328 {
330  return queue->ExecuteFirstRequest();
331 }
332 
333 
334 /// removes all requests from the Queue
336 {
338  queue->Clear();
339 }
340 
341 
343 {
345 }
346 
347 
348 ///////////////////////////////////////////////////////////////////////////////
349 /// CPostQueue
350 
352 
354 {
355  if( ! sm_PostQueue) { // fast check
356  static CMutex s_CreateQueueMutex;
357  TMutexGuard lock(s_CreateQueueMutex);
358 
359  /// now safe check
360  if( ! sm_PostQueue) {
361  sm_PostQueue.Reset(new CPostQueue()); // create the singleton
362  }
363  }
364  return sm_PostQueue;
365 }
366 
367 
369 {
370  sm_PostQueue.Reset();
371 }
372 
373 
375 {
376  CMutexGuard guard(m_Mutex);
377 
378  NON_CONST_ITERATE(THandlerToCount, it, m_AliveTargets) {
379  it->first->m_Queue.Reset(); // disconnect from the queue
380  }
381 
382  m_AliveTargets.clear();
383  m_Queue.clear();
384 }
385 
387 {
388  if (!req) return;
389 
390  size_t size = 0;
391  {
392  CMutexGuard guard(m_Mutex);
393 
394  CEventHandler* target = req->m_Target;
395  THandlerToCount::iterator it = m_AliveTargets.find(target);
396  if (it == m_AliveTargets.end()) { // this is the first event
397  m_AliveTargets[target] = 1; // register itself as "alive" target
398  }
399  else {
400  it->second++; // increment event counter
401  }
402 
403  m_Queue.push_back(req); // post the request
404  size = m_Queue.size();
405  }
406 
407  if (size >= 1000 && (size % 100) == 0) {
408 #ifdef _DEBUG
409  LOG_POST(Error << "CEventHandler::CPostQueue::Post() - "
410  "queue is too long, size = " << size);
411 #endif
412  }
413 }
414 
415 
416 /// extracts a request from the front of the queue and executes it
418 {
419  CRef<CObject> target_guard; // protect the target if possible
420  // requests will be deleted at the end of the function
421  // this prevents deadlocks if CEvent::~CEvent uses CEventHandler functions
422  TPostRequests toDelete;
423  SPostRequest* request = 0;
424 
425  {{
426  TMutexGuard guard(m_Mutex);
427  for (auto i = m_Queue.begin(); i != m_Queue.end() && !request;) {
428  CEventHandler* target = (*i)->m_Target;
429  auto it = m_AliveTargets.find(target);
430  if (it != m_AliveTargets.end()) {
431  request = i->get();
432  target_guard.Reset(dynamic_cast<CObject*>(target));
433  if (it->second == 1) // target does not have any other events in the queue
434  m_AliveTargets.erase(it);
435  else
436  it->second--;
437  }
438  toDelete.push_back(*i);
439  i = m_Queue.erase(i);
440  }
441  }}
442 
443  if (request) {
444  CEventHandler* target = request->m_Target;
445  CEvent* evt = request->m_Event.GetPointerOrNull();
446  _ASSERT(target && evt);
447  target->Send(evt, request->m_DispHow, request->m_PoolName);
448  return true;
449  }
450 
451  return false; // the Queue is empty
452 }
453 
454 
456 {
457  if(handler) {
458  CMutexGuard guard(m_Mutex);
459  m_AliveTargets.erase(handler); // delete this from the list of alive Post() targets
460  }
461 }
462 
463 
465 {
466  CMutexGuard guard(m_Mutex);
467  m_Queue.clear();
468  m_AliveTargets.clear();
469 }
470 
471 
CPostQueue - singleton queue for asynchronous events all methods are synchronized.
CEventHandler.
CEvent - generic event implementation TODO TODO - Attachments.
Definition: event.hpp:86
CMutex –.
Definition: ncbimtx.hpp:749
CObject –.
Definition: ncbiobj.hpp:180
void erase(iterator pos)
Definition: map.hpp:167
container_type::const_iterator const_iterator
Definition: map.hpp:53
const_iterator begin() const
Definition: map.hpp:151
const_iterator end() const
Definition: map.hpp:152
void clear()
Definition: map.hpp:169
const_iterator find(const key_type &key) const
Definition: map.hpp:153
void(*)(CSeq_entry_Handle seh, IWorkbench *wb, const CSerialObject &obj) handler
Include a standard set of the NCBI C++ Toolkit most basic headers.
const SEvtMapEntry * FindEventMapEntry(const SEvtMap *pMap, CEvent::EEventClass cls, CEvent::TEventTypeInfo tif, CEvent::TEventID CmdID)
Traverses a list of Command Maps in order to locate a handler corresponding to the given command ID.
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
#define NON_CONST_ITERATE(Type, Var, Cont)
Non constant version of ITERATE macro.
Definition: ncbimisc.hpp:822
#define NULL
Definition: ncbistd.hpp:225
#define _TRACE(message)
Definition: ncbidbg.hpp:122
#define LOG_POST(message)
This macro is deprecated and it's strongly recomended to move in all projects (except tests) to macro...
Definition: ncbidiag.hpp:226
void Error(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1197
#define NCBI_CATCH(message)
Catch CExceptions as well This macro is deprecated - use *_X or *_XX variant instead of it.
Definition: ncbiexpt.hpp:580
virtual void RemoveListener(CEventHandler *listener)
Remove a listener.
bool Visit(CEventHandler *handler)
Checks whether this event has been visited by the given handler.
Definition: event.cpp:104
const TEventID GetID(void) const
Inline Implementation.
Definition: event.hpp:164
CEvent::EEventClass evt_class
static void DestroyInstance()
returns Singleton
static bool HandlePostRequest()
CEventHandler()
CEventHandler.
virtual ~CEventHandler()
~CPostQueue()
destroys singleton
void Post(SPostRequest *req)
static const SEvtMapEntry sm_EvtMapEntries[]
vector< CEventHandler * > TListeners
Int4 TEventID
Definition: event.hpp:90
virtual const TListeners * GetListeners(int pool_name=ePool_Default) const
returns a set of listeners fro the specified pool
CEvent::TEventID last_id
Event ID (or ID range start)
void DeclareDead(CEventHandler *handler)
FEventHandler handler
Envent ID range end.
const char * TEventTypeInfo
Definition: event.hpp:88
const SEvtMapEntry * entries
pointer to command map of the base class
void x_DeclareDead()
Removes itself unavailable for async event delivery.
bool ExecuteFirstRequest()
extracts a request from the front of the queue and executes it
void Post(CRef< CEvent > evt, EDispatch disp_how=eDispatch_Default, int pool_name=ePool_Default)
Handles an event asynchronously (process and/or dispatch).
CEvent::TEventID id
Type Information.
TEventTypeInfo GetTypeInfo(void)
Definition: event.hpp:185
static void ClearPostQueue()
erases all events from the queue
void(CEventHandler::* FEventHandler)(CEvent *)
event handler
CEvent::TEventTypeInfo type_info
message or command or update
const SEvtMap * base_map
static void DestroyPostQueue()
const EEventClass GetEventClass(void) const
Definition: event.hpp:171
virtual bool Dispatch(CEvent *evt, EDispatch disp_how=eDispatch_Default, int pool_name=ePool_Default)
Dispatches an event to the listeners (but does not handle it).
std::map< CEventHandler *, int > THandlerToCount
EEventClass
default event classes
Definition: event.hpp:98
EDispatch
enum controlling dispatching strategies
virtual void AddListener(CEventHandler *listener, int pool_name=ePool_Default)
Add a listener.
void x_AddListenerToPool(CEventHandler *listener, int pool_name)
virtual const SEvtMap * GetEventMap() const
virtual void RemoveAllListeners(void)
static CRef< CPostQueue > GetInstance()
virtual bool OnEvent(CEvent *evt)
Processes en event. Returns "true" if event has been processed.
virtual bool HasListener(CEventHandler *listener, int pool_name=ePool_Default) const
returns "true" if the given listener belongs to the specified pool
static const SEvtMap sm_EvtMap
virtual bool Send(CEvent *evt, EDispatch disp_how=eDispatch_Default, int pool_name=ePool_Default)
Sends an event synchronously.
CRef< CPostQueue > m_Queue
static CRef< CPostQueue > sm_PostQueue
CPostQueue.
list< AutoPtr< SPostRequest > > TPostRequests
@ eEvent_InvalidID
Definition: event.hpp:105
@ eEvent_Message
message from one class to another
Definition: event.hpp:99
@ eDispatch_AllHandlers
handle and do not dispatch to listeners
@ eDispatch_FirstHandler
dispatch to all handlers
@ eDispatch_Default
dispatch until handled at least by one handler
void Reset(void)
Reset reference object.
Definition: ncbiobj.hpp:773
TObjectType * GetPointerOrNull(void) THROWS_NONE
Get pointer value.
Definition: ncbiobj.hpp:986
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
int i
static MDB_envinfo info
Definition: mdb_load.c:37
const struct ncbi::grid::netcache::search::fields::SIZE size
int strcmp(const char *str1, const char *str2)
Definition: odbc_utils.hpp:160
Command map entry.
Command Map.
#define _ASSERT
Modified on Mon May 20 04:58:29 2024 by modify_doxy.py rev. 669887