Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
interface_manager.cpp
1 
2 /***************************************************************************
3  * interface_manager.cpp - BlackBoard interface manager
4  *
5  * Created: Mon Oct 09 19:08:29 2006
6  * Copyright 2006-2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <blackboard/internal/interface_manager.h>
25 
26 #include <blackboard/blackboard.h>
27 #include <blackboard/internal/memory_manager.h>
28 #include <blackboard/internal/message_manager.h>
29 #include <blackboard/exceptions.h>
30 #include <blackboard/internal/interface_mem_header.h>
31 #include <blackboard/interface_listener.h>
32 #include <blackboard/interface_observer.h>
33 #include <blackboard/internal/instance_factory.h>
34 #include <blackboard/internal/notifier.h>
35 
36 #include <interface/interface.h>
37 #include <interface/interface_info.h>
38 
39 #include <core/threading/mutex.h>
40 #include <core/threading/refc_rwlock.h>
41 #include <core/exceptions/system.h>
42 #include <utils/system/dynamic_module/module.h>
43 
44 #include <cstdlib>
45 #include <cstring>
46 #include <fnmatch.h>
47 
48 namespace fawkes {
49 
50 /** @class BlackBoardInterfaceManager <blackboard/internal/interface_manager.h>
51  * BlackBoard interface manager.
52  * This class is used by the BlackBoard to manage interfaces stored in the
53  * shared memory.
54  *
55  * @author Tim Niemueller
56  */
57 
58 
59 /** Constructor.
60  * The shared memory segment is created with data from bbconfig.h.
61  * @param bb_memmgr BlackBoard memory manager to use
62  * @param bb_msgmgr BlackBoard message manager to use
63  * @param bb_notifier BlackBoard notifier to all for events
64  * @see bbconfig.h
65  */
67  BlackBoardMessageManager *bb_msgmgr,
68  BlackBoardNotifier *bb_notifier)
69 {
70  memmgr = bb_memmgr;
71  msgmgr = bb_msgmgr;
72  notifier = bb_notifier;
73 
74  instance_serial = 1;
75  instance_factory = new BlackBoardInstanceFactory();
76  mutex = new Mutex();
77 
78  writer_interfaces.clear();
79  rwlocks.clear();
80 }
81 
82 
83 /** Destructor */
85 {
86  delete mutex;
87  delete instance_factory;
88 }
89 
90 
91 /** Creates a new interface instance.
92  * This method will look in the libinterfaces shared object for a factory function
93  * for the interface of the given type. If this was found a new instance of the
94  * interface is returned.
95  * @param type type of the interface
96  * @param identifier identifier of the interface
97  * @return a new instance of the requested interface type
98  * @exception BlackBoardInterfaceNotFoundException thrown if the factory function
99  * for the given interface type could not be found
100  */
101 Interface *
102 BlackBoardInterfaceManager::new_interface_instance(const char *type, const char *identifier)
103 {
104  Interface *iface = instance_factory->new_interface_instance(type, identifier);
105 
106  iface->set_instance_serial(next_instance_serial());
107  iface->set_mediators(this, msgmgr);
108  return iface;
109 }
110 
111 
112 /** Destroy an interface instance.
113  * The destroyer function for the given interface is called to destroy the given
114  * interface instance.
115  * @param interface to destroy
116  * @exception BlackBoardInterfaceNotFoundException thrown if the destroyer function
117  * for the given interface could not be found. The interface will not be freed.
118  */
119 void
120 BlackBoardInterfaceManager::delete_interface_instance(Interface *interface)
121 {
122  instance_factory->delete_interface_instance(interface);
123 }
124 
125 
126 /** search memory chunks if the desired interface has been allocated already.
127  * @param type type of the interface to look for
128  * @param identifier identifier of the interface to look for
129  * @return a pointer to the memory of the interface or NULL if not found
130  */
131 void *
132 BlackBoardInterfaceManager::find_interface_in_memory(const char *type, const char *identifier)
133 {
134  interface_header_t *ih;
135  BlackBoardMemoryManager::ChunkIterator cit;
136  for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
137  ih = (interface_header_t *)*cit;
138  if ( (strncmp(ih->type, type, __INTERFACE_TYPE_SIZE) == 0) &&
139  (strncmp(ih->id, identifier, __INTERFACE_ID_SIZE) == 0)
140  ) {
141  // found it!
142  return *cit;
143  }
144  }
145 
146  return NULL;
147 }
148 
149 
150 /** Get next mem serial.
151  * @return next unique memory serial
152  */
153 unsigned int
154 BlackBoardInterfaceManager::next_mem_serial()
155 {
156  unsigned int serial = 1;
157  interface_header_t *ih;
158  BlackBoardMemoryManager::ChunkIterator cit;
159  for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
160  ih = (interface_header_t *)*cit;
161  if ( ih->serial >= serial ) {
162  serial = ih->serial + 1;
163  }
164  }
165 
166  return serial;
167 }
168 
169 
170 /** Get next instance serial.
171  * @return next unique instance serial
172  */
173 unsigned int
174 BlackBoardInterfaceManager::next_instance_serial()
175 {
176  if ( memmgr->is_master() ) {
177  // simple, just increment value and return it
178  return instance_serial++;
179  } else {
180  throw BBNotMasterException("Instance serial can only be requested by BB Master");
181  }
182 }
183 
184 
185 /** Create an interface instance.
186  * This will create a new interface instance. Storage in the shared memory
187  * is allocated to hold the interface data.
188  * @param type type of the interface
189  * @param identifier identifier of the interface
190  * @param interface reference to a pointer where the interface will be created
191  * @param ptr reference to pointer of interface memory
192  * @exception OutOfMemoryException thrown if there is not enough memory in the
193  * BlackBoard to create the interface
194  */
195 void
196 BlackBoardInterfaceManager::create_interface(const char *type, const char *identifier,
197  Interface* &interface, void* &ptr)
198 {
199  interface_header_t *ih;
200 
201  // create new interface and allocate appropriate chunk
202  interface = new_interface_instance(type, identifier);
203  try {
204  ptr = memmgr->alloc_nolock(interface->datasize() + sizeof(interface_header_t));
205  ih = (interface_header_t *)ptr;
206  } catch (OutOfMemoryException &e) {
207  e.append("BlackBoardInterfaceManager::createInterface: interface of type %s could not be created", type);
208  memmgr->unlock();
209  mutex->unlock();
210  throw;
211  }
212  memset(ptr, 0, interface->datasize() + sizeof(interface_header_t));
213 
214  strncpy(ih->type, type, __INTERFACE_TYPE_SIZE);
215  strncpy(ih->id, identifier, __INTERFACE_ID_SIZE);
216  memcpy(ih->hash, interface->hash(), __INTERFACE_HASH_SIZE);
217 
218  ih->refcount = 0;
219  ih->serial = next_mem_serial();
220  ih->flag_writer_active = 0;
221  ih->num_readers = 0;
222  rwlocks[ih->serial] = new RefCountRWLock();
223 
224  interface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
225 }
226 
227 
228 /** Open interface for reading.
229  * This will create a new interface instance of the given type. The result can be
230  * casted to the appropriate type.
231  * @param type type of the interface
232  * @param identifier identifier of the interface
233  * @return new fully initialized interface instance of requested type
234  * @exception OutOfMemoryException thrown if there is not enough free space for
235  * the requested interface.
236  */
237 Interface *
238 BlackBoardInterfaceManager::open_for_reading(const char *type, const char *identifier)
239 {
240  mutex->lock();
241  Interface *iface = NULL;
242  void *ptr = NULL;
243  interface_header_t *ih;
244  bool created = false;
245 
246  memmgr->lock();
247 
248  ptr = find_interface_in_memory(type, identifier);
249 
250  try {
251  if ( ptr != NULL ) {
252  // found, instantiate new interface for given memory chunk
253  iface = new_interface_instance(type, identifier);
254  ih = (interface_header_t *)ptr;
255  if ( (iface->hash_size() != __INTERFACE_HASH_SIZE ) ||
256  (memcmp(iface->hash(), ih->hash, __INTERFACE_HASH_SIZE) != 0) ) {
258  }
259  iface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
260  rwlocks[ih->serial]->ref();
261  } else {
262  created = true;
263  create_interface(type, identifier, iface, ptr);
264  ih = (interface_header_t *)ptr;
265  }
266 
267  iface->set_readwrite(false, rwlocks[ih->serial]);
268  ih->refcount++;
269  ih->num_readers++;
270 
271  memmgr->unlock();
272  mutex->unlock();
273 
274  if ( created ) {
275  notifier->notify_of_interface_created(type, identifier);
276  }
277  notifier->notify_of_reader_added(iface, iface->serial());
278 
279  } catch (Exception &e) {
280  if (iface) delete_interface_instance(iface);
281  memmgr->unlock();
282  mutex->unlock();
283  throw;
284  }
285 
286  return iface;
287 }
288 
289 
290 /** Open all interfaces of the given type for reading.
291  * This will create interface instances for all currently registered interfaces of
292  * the given type. The result can be casted to the appropriate type.
293  * @param type_pattern pattern of interface types to open, supports wildcards
294  * similar to filenames (*, ?, []), see "man fnmatch" for all supported.
295  * @param id_pattern pattern of interface IDs to open, supports wildcards similar
296  * to filenames (*, ?, []), see "man fnmatch" for all supported.
297  * @return list of new fully initialized interface instances of requested type. The
298  * is allocated using new and you have to free it using delete after you are done
299  * with it!
300  */
301 std::list<Interface *>
303  const char *id_pattern)
304 {
305  mutex->lock();
306  memmgr->lock();
307 
308  std::list<Interface *> rv;
309 
310  Interface *iface = NULL;
311  interface_header_t *ih;
313 
314  try {
315  for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
316  iface = NULL;
317  ih = (interface_header_t *)*cit;
318 
319  // ensure 0-termination
320  char type[__INTERFACE_TYPE_SIZE + 1];
321  char id[__INTERFACE_ID_SIZE + 1];
322  type[__INTERFACE_TYPE_SIZE] = 0;
323  id[__INTERFACE_TYPE_SIZE] = 0;
324  strncpy(type, ih->type, __INTERFACE_TYPE_SIZE);
325  strncpy(id, ih->id, __INTERFACE_ID_SIZE);
326 
327  if ((fnmatch(type_pattern, type, 0) == FNM_NOMATCH) ||
328  (fnmatch(id_pattern, id, 0) == FNM_NOMATCH) ) {
329  // type or ID prefix does not match, go on
330  continue;
331  }
332 
333  void *ptr = *cit;
334  iface = new_interface_instance(ih->type, ih->id);
335  iface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
336 
337  if ( (iface->hash_size() != __INTERFACE_HASH_SIZE ) ||
338  (memcmp(iface->hash(), ih->hash, __INTERFACE_HASH_SIZE) != 0) ) {
340  }
341 
342  rwlocks[ih->serial]->ref();
343 
344  iface->set_readwrite(false, rwlocks[ih->serial]);
345  ih->refcount++;
346  ih->num_readers++;
347 
348  rv.push_back(iface);
349  }
350 
351  mutex->unlock();
352  memmgr->unlock();
353 
354  for (std::list<Interface *>::iterator j = rv.begin(); j != rv.end(); ++j) {
355  notifier->notify_of_reader_added(*j, (*j)->serial());
356  }
357 
358 
359  } catch (Exception &e) {
360  if (iface) delete_interface_instance( iface );
361  for (std::list<Interface *>::iterator i = rv.begin(); i != rv.end(); ++i) {
362  delete_interface_instance(*i);
363  }
364  memmgr->unlock();
365  mutex->unlock();
366  throw;
367  }
368 
369  return rv;
370 }
371 
372 
373 /** Open interface for writing.
374  * This will create a new interface instance of the given type. The result can be
375  * casted to the appropriate type. This will only succeed if there is not already
376  * a writer for the given interface type/id!
377  * @param type type of the interface
378  * @param identifier identifier of the interface
379  * @return new fully initialized interface instance of requested type
380  * @exception OutOfMemoryException thrown if there is not enough free space for
381  * the requested interface.
382  * @exception BlackBoardWriterActiveException thrown if there is already a writing
383  * instance with the same type/id
384  */
385 Interface *
386 BlackBoardInterfaceManager::open_for_writing(const char *type, const char *identifier)
387 {
388  mutex->lock();
389  memmgr->lock();
390 
391  Interface *iface = NULL;
392  void *ptr = NULL;
393  interface_header_t *ih;
394  bool created = false;
395 
396  try {
397  ptr = find_interface_in_memory(type, identifier);
398 
399  if ( ptr != NULL ) {
400  // found, check if there is already a writer
401  //instantiate new interface for given memory chunk
402  ih = (interface_header_t *)ptr;
403  if ( ih->flag_writer_active ) {
404  throw BlackBoardWriterActiveException(identifier, type);
405  }
406  iface = new_interface_instance(type, identifier);
407  if ( (iface->hash_size() != __INTERFACE_HASH_SIZE ) ||
408  (memcmp(iface->hash(), ih->hash, __INTERFACE_HASH_SIZE) != 0) ) {
410  }
411  iface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
412  rwlocks[ih->serial]->ref();
413  } else {
414  created = true;
415  create_interface(type, identifier, iface, ptr);
416  ih = (interface_header_t *)ptr;
417  }
418 
419  iface->set_readwrite(true, rwlocks[ih->serial]);
420  ih->flag_writer_active = 1;
421  ih->refcount++;
422 
423  memmgr->unlock();
424  writer_interfaces[ih->serial] = iface;
425 
426  mutex->unlock();
427 
428  if ( created ) {
429  notifier->notify_of_interface_created(type, identifier);
430  }
431  notifier->notify_of_writer_added(iface, iface->serial());
432  } catch (Exception &e) {
433  if (iface) delete_interface_instance(iface);
434  memmgr->unlock();
435  mutex->unlock();
436  throw;
437  }
438 
439  return iface;
440 }
441 
442 
443 /** Close interface.
444  * @param interface interface to close
445  */
446 void
448 {
449  if ( interface == NULL ) return;
450  mutex->lock();
451  bool destroyed = false;
452 
453  // reduce refcount and free memory if refcount is zero
454  interface_header_t *ih = (interface_header_t *)interface->__mem_real_ptr;
455  bool killed_writer = interface->__write_access;
456  if ( --(ih->refcount) == 0 ) {
457  // redeem from memory
458  if ( interface->__write_access ) {
459  writer_interfaces.erase( interface->__mem_serial );
460  }
461  memmgr->free( interface->__mem_real_ptr );
462  destroyed = true;
463  } else {
464  if ( interface->__write_access ) {
465  ih->flag_writer_active = 0;
466  writer_interfaces.erase( interface->__mem_serial );
467  } else {
468  ih->num_readers--;
469  }
470  }
471 
472  mutex->unlock();
473  if (killed_writer) {
474  notifier->notify_of_writer_removed(interface, interface->serial());
475  } else {
476  notifier->notify_of_reader_removed(interface, interface->serial());
477  }
478  if ( destroyed ) {
479  notifier->notify_of_interface_destroyed(interface->__type, interface->__id);
480  }
481 
482  mutex->lock();
483  delete_interface_instance( interface );
484  mutex->unlock();
485 }
486 
487 
488 /** Get a list of interfaces.
489  * @return list of currently existing interfaces. List may be outdated on
490  * return since there maybe concurrent actions.
491  */
494 {
495  InterfaceInfoList *infl = new InterfaceInfoList();
496 
497  memmgr->lock();
498  interface_header_t *ih;
500  for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
501  ih = (interface_header_t *)*cit;
502  infl->append(ih->type, ih->id, ih->hash, ih->serial,
504  }
505 
506  memmgr->unlock();
507 
508  return infl;
509 }
510 
511 
512 /** Get a constrained list of interfaces.
513  * @param type_pattern tyoe pattern, may contain shell-like wildcards * (any number
514  * of characters) and ? (one character), cf. man fnmatch().
515  * @param id_pattern ID pattern, may contain shell-like wildcards * (any number
516  * of characters) and ? (one character), cf. man fnmatch().
517  * @return list of currently existing interfaces matching the given type and
518  * ID patterns. List may be outdated on return since there maybe concurrent
519  * actions.
520  */
522 BlackBoardInterfaceManager::list(const char *type_pattern,
523  const char *id_pattern) const
524 {
525  InterfaceInfoList *infl = new InterfaceInfoList();
526 
527  memmgr->lock();
528  interface_header_t *ih;
530  for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
531  ih = (interface_header_t *)*cit;
532  char type[__INTERFACE_TYPE_SIZE + 1];
533  char id[__INTERFACE_ID_SIZE + 1];
534  // ensure NULL-termination
535  type[__INTERFACE_TYPE_SIZE] = 0;
536  id[__INTERFACE_ID_SIZE] = 0;
537  strncpy(type, ih->type, __INTERFACE_TYPE_SIZE);
538  strncpy(id, ih->id, __INTERFACE_ID_SIZE);
539  if ((fnmatch(type_pattern, type, FNM_NOESCAPE) == 0) &&
540  (fnmatch(id_pattern, id, FNM_NOESCAPE) == 0))
541  {
542  infl->append(ih->type, ih->id, ih->hash, ih->serial,
544  }
545  }
546 
547  memmgr->unlock();
548 
549  return infl;
550 }
551 
552 
553 /** Get the writer interface for the given mem serial.
554  * @param mem_serial memory serial to get writer for
555  * @return writer interface for given mem serial, or NULL if non exists
556  * @exception BlackBoardNoWritingInstanceException thrown if no writer
557  * was found for the given interface.
558  */
559 Interface *
560 BlackBoardInterfaceManager::writer_for_mem_serial(unsigned int mem_serial)
561 {
562  if ( writer_interfaces.find(mem_serial) != writer_interfaces.end() ) {
563  return writer_interfaces[mem_serial];
564  } else {
565  throw BlackBoardNoWritingInstanceException();
566  }
567 }
568 
569 
570 void
572 {
573  notifier->notify_of_data_change(interface);
574 }
575 
576 
577 bool
579 {
580  return (writer_interfaces.find(interface->__mem_serial) != writer_interfaces.end());
581 }
582 
583 
584 unsigned int
586 {
587  const interface_header_t *ih = (interface_header_t *)interface->__mem_real_ptr;
588  return ih->num_readers;
589 }
590 
591 } // end namespace fawkes