Fawkes API  Fawkes Development Version
interface_manager.cpp
00001  
00002 /***************************************************************************
00003  *  interface_manager.cpp - BlackBoard interface manager
00004  *
00005  *  Created: Mon Oct 09 19:08:29 2006
00006  *  Copyright  2006-2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <blackboard/internal/interface_manager.h>
00025 
00026 #include <blackboard/blackboard.h>
00027 #include <blackboard/internal/memory_manager.h>
00028 #include <blackboard/internal/message_manager.h>
00029 #include <blackboard/exceptions.h>
00030 #include <blackboard/internal/interface_mem_header.h>
00031 #include <blackboard/interface_listener.h>
00032 #include <blackboard/interface_observer.h>
00033 #include <blackboard/internal/instance_factory.h>
00034 #include <blackboard/internal/notifier.h>
00035 
00036 #include <interface/interface.h>
00037 #include <interface/interface_info.h>
00038 
00039 #include <core/threading/mutex.h>
00040 #include <core/threading/refc_rwlock.h>
00041 #include <core/exceptions/system.h>
00042 #include <utils/system/dynamic_module/module.h>
00043 
00044 #include <cstdlib>
00045 #include <cstring>
00046 #include <fnmatch.h>
00047 
00048 namespace fawkes {
00049 
00050 /** @class BlackBoardInterfaceManager <blackboard/internal/interface_manager.h>
00051  * BlackBoard interface manager.
00052  * This class is used by the BlackBoard to manage interfaces stored in the
00053  * shared memory.
00054  *
00055  * @author Tim Niemueller
00056  */
00057 
00058 
00059 /** Constructor.
00060  * The shared memory segment is created with data from bbconfig.h.
00061  * @param bb_memmgr BlackBoard memory manager to use
00062  * @param bb_msgmgr BlackBoard message manager to use
00063  * @param bb_notifier BlackBoard notifier to all for events
00064  * @see bbconfig.h
00065  */
00066 BlackBoardInterfaceManager::BlackBoardInterfaceManager(BlackBoardMemoryManager *bb_memmgr,
00067                                                        BlackBoardMessageManager *bb_msgmgr,
00068                                                        BlackBoardNotifier *bb_notifier)
00069 {
00070   memmgr = bb_memmgr;
00071   msgmgr = bb_msgmgr;
00072   notifier = bb_notifier;
00073 
00074   instance_serial = 1;
00075   instance_factory = new BlackBoardInstanceFactory();
00076   mutex = new Mutex();
00077 
00078   writer_interfaces.clear();
00079   rwlocks.clear();
00080 }
00081 
00082 
00083 /** Destructor */
00084 BlackBoardInterfaceManager::~BlackBoardInterfaceManager()
00085 {
00086   delete mutex;
00087   delete instance_factory;
00088 }
00089 
00090 
00091 /** Creates a new interface instance.
00092  * This method will look in the libinterfaces shared object for a factory function
00093  * for the interface of the given type. If this was found a new instance of the
00094  * interface is returned.
00095  * @param type type of the interface
00096  * @param identifier identifier of the interface
00097  * @return a new instance of the requested interface type
00098  * @exception BlackBoardInterfaceNotFoundException thrown if the factory function
00099  * for the given interface type could not be found
00100  */
00101 Interface *
00102 BlackBoardInterfaceManager::new_interface_instance(const char *type, const char *identifier)
00103 {
00104   Interface *iface = instance_factory->new_interface_instance(type, identifier);
00105 
00106   iface->set_instance_serial(next_instance_serial());
00107   iface->set_mediators(this, msgmgr);
00108   return iface;
00109 }
00110 
00111 
00112 /** Destroy an interface instance.
00113  * The destroyer function for the given interface is called to destroy the given
00114  * interface instance.
00115  * @param interface to destroy
00116  * @exception BlackBoardInterfaceNotFoundException thrown if the destroyer function
00117  * for the given interface could not be found. The interface will not be freed.
00118  */
00119 void
00120 BlackBoardInterfaceManager::delete_interface_instance(Interface *interface)
00121 {
00122   instance_factory->delete_interface_instance(interface);
00123 }
00124 
00125 
00126 /** search memory chunks if the desired interface has been allocated already.
00127  * @param type type of the interface to look for
00128  * @param identifier identifier of the interface to look for
00129  * @return a pointer to the memory of the interface or NULL if not found
00130  */
00131 void *
00132 BlackBoardInterfaceManager::find_interface_in_memory(const char *type, const char *identifier)
00133 {
00134   interface_header_t *ih;
00135   BlackBoardMemoryManager::ChunkIterator cit;
00136   for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
00137     ih = (interface_header_t *)*cit;
00138     if ( (strncmp(ih->type, type, __INTERFACE_TYPE_SIZE) == 0) &&
00139          (strncmp(ih->id, identifier, __INTERFACE_ID_SIZE) == 0)
00140          ) {
00141       // found it!
00142       return *cit;
00143     }
00144   }
00145 
00146   return NULL;
00147 }
00148 
00149 
00150 /** Get next mem serial.
00151  * @return next unique memory serial
00152  */
00153 unsigned int
00154 BlackBoardInterfaceManager::next_mem_serial()
00155 {
00156   unsigned int serial = 1;
00157   interface_header_t *ih;
00158   BlackBoardMemoryManager::ChunkIterator cit;
00159   for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
00160     ih = (interface_header_t *)*cit;
00161     if ( ih->serial >= serial ) {
00162       serial = ih->serial + 1;
00163     }
00164   }
00165 
00166   return serial;
00167 }
00168 
00169 
00170 /** Get next instance serial.
00171  * @return next unique instance serial
00172  */
00173 unsigned int
00174 BlackBoardInterfaceManager::next_instance_serial()
00175 {
00176   if ( memmgr->is_master() ) {
00177     // simple, just increment value and return it
00178     return instance_serial++;
00179   } else {
00180     throw BBNotMasterException("Instance serial can only be requested by BB Master");
00181   }
00182 }
00183 
00184 
00185 /** Create an interface instance.
00186  * This will create a new interface instance. Storage in the shared memory
00187  * is allocated to hold the interface data.
00188  * @param type type of the interface
00189  * @param identifier identifier of the interface
00190  * @param interface reference to a pointer where the interface will be created
00191  * @param ptr reference to pointer of interface memory
00192  * @exception OutOfMemoryException thrown if there is not enough memory in the
00193  * BlackBoard to create the interface
00194  */
00195 void
00196 BlackBoardInterfaceManager::create_interface(const char *type, const char *identifier,
00197                                              Interface* &interface, void* &ptr)
00198 {
00199   interface_header_t *ih;
00200 
00201   // create new interface and allocate appropriate chunk
00202   interface = new_interface_instance(type, identifier);
00203   try {
00204     ptr = memmgr->alloc_nolock(interface->datasize() + sizeof(interface_header_t));
00205     ih  = (interface_header_t *)ptr;
00206   } catch (OutOfMemoryException &e) {
00207     e.append("BlackBoardInterfaceManager::createInterface: interface of type %s could not be created", type);
00208     memmgr->unlock();
00209     mutex->unlock();
00210     throw;
00211   }
00212   memset(ptr, 0, interface->datasize() + sizeof(interface_header_t));
00213 
00214   strncpy(ih->type, type, __INTERFACE_TYPE_SIZE);
00215   strncpy(ih->id, identifier, __INTERFACE_ID_SIZE);
00216   memcpy(ih->hash, interface->hash(), __INTERFACE_HASH_SIZE);
00217 
00218   ih->refcount           = 0;
00219   ih->serial             = next_mem_serial();
00220   ih->flag_writer_active = 0;
00221   ih->num_readers        = 0;
00222   rwlocks[ih->serial] = new RefCountRWLock();
00223 
00224   interface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
00225 }
00226 
00227 
00228 /** Open interface for reading.
00229  * This will create a new interface instance of the given type. The result can be
00230  * casted to the appropriate type.
00231  * @param type type of the interface
00232  * @param identifier identifier of the interface
00233  * @return new fully initialized interface instance of requested type
00234  * @exception OutOfMemoryException thrown if there is not enough free space for
00235  * the requested interface.
00236  */
00237 Interface *
00238 BlackBoardInterfaceManager::open_for_reading(const char *type, const char *identifier)
00239 {
00240   mutex->lock();
00241   Interface *iface = NULL;
00242   void *ptr = NULL;
00243   interface_header_t *ih;
00244   bool created = false;
00245 
00246   memmgr->lock();
00247 
00248   ptr = find_interface_in_memory(type, identifier);
00249 
00250   try {
00251     if ( ptr != NULL ) {
00252       // found, instantiate new interface for given memory chunk
00253       iface = new_interface_instance(type, identifier);
00254       ih  = (interface_header_t *)ptr;
00255       if ( (iface->hash_size() != __INTERFACE_HASH_SIZE ) ||
00256            (memcmp(iface->hash(), ih->hash, __INTERFACE_HASH_SIZE) != 0) ) {
00257         throw BlackBoardInterfaceVersionMismatchException();
00258       }
00259       iface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
00260       rwlocks[ih->serial]->ref();
00261     } else {
00262       created = true;
00263       create_interface(type, identifier, iface, ptr);
00264       ih  = (interface_header_t *)ptr;
00265     }
00266 
00267     iface->set_readwrite(false, rwlocks[ih->serial]);
00268     ih->refcount++;
00269     ih->num_readers++;
00270 
00271     memmgr->unlock();
00272     mutex->unlock();
00273 
00274     if ( created ) {
00275       notifier->notify_of_interface_created(type, identifier);
00276     }
00277     notifier->notify_of_reader_added(iface, iface->serial());
00278 
00279   } catch (Exception &e) {
00280     if (iface)  delete_interface_instance(iface);
00281     memmgr->unlock();
00282     mutex->unlock();
00283     throw;
00284   }
00285 
00286   return iface;
00287 }
00288 
00289 
00290 /** Open all interfaces of the given type for reading.
00291  * This will create interface instances for all currently registered interfaces of
00292  * the given type. The result can be casted to the appropriate type.
00293  * @param type_pattern pattern of interface types to open, supports wildcards
00294  * similar to filenames (*, ?, []), see "man fnmatch" for all supported.
00295  * @param id_pattern pattern of interface IDs to open, supports wildcards similar
00296  * to filenames (*, ?, []), see "man fnmatch" for all supported.
00297  * @return list of new fully initialized interface instances of requested type. The
00298  * is allocated using new and you have to free it using delete after you are done
00299  * with it!
00300  */
00301 std::list<Interface *>
00302 BlackBoardInterfaceManager::open_multiple_for_reading(const char *type_pattern,
00303                                                       const char *id_pattern)
00304 {
00305   mutex->lock();
00306   memmgr->lock();
00307 
00308   std::list<Interface *> rv;
00309 
00310   Interface *iface = NULL;
00311   interface_header_t *ih;
00312   BlackBoardMemoryManager::ChunkIterator cit;
00313 
00314   try {
00315     for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
00316       iface = NULL;
00317       ih = (interface_header_t *)*cit;
00318 
00319       // ensure 0-termination
00320       char type[__INTERFACE_TYPE_SIZE + 1];
00321       char id[__INTERFACE_ID_SIZE + 1];
00322       type[__INTERFACE_TYPE_SIZE] = 0;
00323       id[__INTERFACE_TYPE_SIZE] = 0;
00324       strncpy(type, ih->type, __INTERFACE_TYPE_SIZE);
00325       strncpy(id, ih->id, __INTERFACE_ID_SIZE);
00326 
00327       if ((fnmatch(type_pattern, type, 0) == FNM_NOMATCH) ||
00328           (fnmatch(id_pattern, id, 0) == FNM_NOMATCH) ) {
00329         // type or ID prefix does not match, go on
00330         continue;
00331       }
00332 
00333       void *ptr = *cit;
00334       iface = new_interface_instance(ih->type, ih->id);
00335       iface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
00336 
00337       if ( (iface->hash_size() != __INTERFACE_HASH_SIZE ) ||
00338            (memcmp(iface->hash(), ih->hash, __INTERFACE_HASH_SIZE) != 0) ) {
00339         throw BlackBoardInterfaceVersionMismatchException();
00340       }
00341 
00342       rwlocks[ih->serial]->ref();
00343 
00344       iface->set_readwrite(false, rwlocks[ih->serial]);
00345       ih->refcount++;
00346       ih->num_readers++;
00347 
00348       rv.push_back(iface);
00349     }
00350 
00351     mutex->unlock();
00352     memmgr->unlock();
00353 
00354     for (std::list<Interface *>::iterator j = rv.begin(); j != rv.end(); ++j) {
00355       notifier->notify_of_reader_added(*j, (*j)->serial());
00356     }
00357 
00358 
00359   } catch (Exception &e) {
00360     if (iface)  delete_interface_instance( iface );
00361     for (std::list<Interface *>::iterator i = rv.begin(); i != rv.end(); ++i) {
00362       delete_interface_instance(*i);
00363     }
00364     memmgr->unlock();
00365     mutex->unlock();
00366     throw;
00367   }
00368 
00369   return rv;
00370 }
00371 
00372 
00373 /** Open interface for writing.
00374  * This will create a new interface instance of the given type. The result can be
00375  * casted to the appropriate type. This will only succeed if there is not already
00376  * a writer for the given interface type/id!
00377  * @param type type of the interface
00378  * @param identifier identifier of the interface
00379  * @return new fully initialized interface instance of requested type
00380  * @exception OutOfMemoryException thrown if there is not enough free space for
00381  * the requested interface.
00382  * @exception BlackBoardWriterActiveException thrown if there is already a writing
00383  * instance with the same type/id
00384  */
00385 Interface *
00386 BlackBoardInterfaceManager::open_for_writing(const char *type, const char *identifier)
00387 {
00388   mutex->lock();
00389   memmgr->lock();
00390 
00391   Interface *iface = NULL;
00392   void *ptr = NULL;
00393   interface_header_t *ih;
00394   bool created = false;
00395 
00396   try {
00397     ptr = find_interface_in_memory(type, identifier);
00398 
00399     if ( ptr != NULL ) {
00400       // found, check if there is already a writer
00401       //instantiate new interface for given memory chunk
00402       ih  = (interface_header_t *)ptr;
00403       if ( ih->flag_writer_active ) {
00404         throw BlackBoardWriterActiveException(identifier, type);
00405       }
00406       iface = new_interface_instance(type, identifier);
00407       if ( (iface->hash_size() != __INTERFACE_HASH_SIZE ) ||
00408            (memcmp(iface->hash(), ih->hash, __INTERFACE_HASH_SIZE) != 0) ) {
00409         throw BlackBoardInterfaceVersionMismatchException();
00410       }
00411       iface->set_memory(ih->serial, ptr, (char *)ptr + sizeof(interface_header_t));
00412       rwlocks[ih->serial]->ref();
00413     } else {
00414       created = true;
00415       create_interface(type, identifier, iface, ptr);
00416       ih = (interface_header_t *)ptr;
00417     }
00418 
00419     iface->set_readwrite(true, rwlocks[ih->serial]);
00420     ih->flag_writer_active = 1;
00421     ih->refcount++;
00422 
00423     memmgr->unlock();
00424     writer_interfaces[ih->serial] = iface;
00425 
00426     mutex->unlock();
00427 
00428     if ( created ) {
00429       notifier->notify_of_interface_created(type, identifier);
00430     }
00431     notifier->notify_of_writer_added(iface, iface->serial());
00432   } catch (Exception &e) {
00433     if (iface)  delete_interface_instance(iface);
00434     memmgr->unlock();
00435     mutex->unlock();
00436     throw;
00437   }
00438 
00439   return iface;
00440 }
00441 
00442 
00443 /** Close interface.
00444  * @param interface interface to close
00445  */
00446 void
00447 BlackBoardInterfaceManager::close(Interface *interface)
00448 {
00449   if ( interface == NULL ) return;
00450   mutex->lock();
00451   bool destroyed = false;
00452 
00453   // reduce refcount and free memory if refcount is zero
00454   interface_header_t *ih = (interface_header_t *)interface->__mem_real_ptr;
00455   bool killed_writer = interface->__write_access;
00456   if ( --(ih->refcount) == 0 ) {
00457     // redeem from memory
00458     if ( interface->__write_access ) {
00459       writer_interfaces.erase( interface->__mem_serial );
00460     }
00461     memmgr->free( interface->__mem_real_ptr );
00462     destroyed = true;
00463   } else {
00464     if ( interface->__write_access ) {
00465       ih->flag_writer_active = 0;
00466       writer_interfaces.erase( interface->__mem_serial );
00467     } else {
00468       ih->num_readers--;
00469     }
00470   }
00471 
00472   mutex->unlock();
00473   if (killed_writer) {
00474     notifier->notify_of_writer_removed(interface, interface->serial());
00475   } else {
00476     notifier->notify_of_reader_removed(interface, interface->serial());
00477   }
00478   if ( destroyed ) {
00479     notifier->notify_of_interface_destroyed(interface->__type, interface->__id);
00480   }
00481 
00482   mutex->lock();
00483   delete_interface_instance( interface );
00484   mutex->unlock();
00485 }
00486 
00487 
00488 /** Get a list of interfaces.
00489  * @return list of currently existing interfaces. List may be outdated on
00490  * return since there maybe concurrent actions.
00491  */
00492 InterfaceInfoList *
00493 BlackBoardInterfaceManager::list_all() const
00494 {
00495   InterfaceInfoList *infl = new InterfaceInfoList();
00496 
00497   memmgr->lock();
00498   interface_header_t *ih;
00499   BlackBoardMemoryManager::ChunkIterator cit;
00500   for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
00501     ih = (interface_header_t *)*cit;
00502     infl->append(ih->type, ih->id, ih->hash, ih->serial,
00503                  ih->flag_writer_active, ih->num_readers);
00504   }
00505 
00506   memmgr->unlock();
00507 
00508   return infl;
00509 }
00510 
00511 
00512 /** Get a constrained list of interfaces.
00513  * @param type_pattern tyoe pattern, may contain shell-like wildcards * (any number
00514  * of characters) and ? (one character), cf. man fnmatch().
00515  * @param id_pattern ID pattern, may contain shell-like wildcards * (any number
00516  * of characters) and ? (one character), cf. man fnmatch().
00517  * @return list of currently existing interfaces matching the given type and
00518  * ID patterns. List may be outdated on return since there maybe concurrent
00519  * actions.
00520  */
00521 InterfaceInfoList *
00522 BlackBoardInterfaceManager::list(const char *type_pattern,
00523                                  const char *id_pattern) const
00524 {
00525   InterfaceInfoList *infl = new InterfaceInfoList();
00526 
00527   memmgr->lock();
00528   interface_header_t *ih;
00529   BlackBoardMemoryManager::ChunkIterator cit;
00530   for ( cit = memmgr->begin(); cit != memmgr->end(); ++cit ) {
00531     ih = (interface_header_t *)*cit;
00532     char type[__INTERFACE_TYPE_SIZE + 1];
00533     char id[__INTERFACE_ID_SIZE + 1];
00534     // ensure NULL-termination
00535     type[__INTERFACE_TYPE_SIZE] = 0;
00536     id[__INTERFACE_ID_SIZE] = 0;
00537     strncpy(type, ih->type, __INTERFACE_TYPE_SIZE);
00538     strncpy(id, ih->id, __INTERFACE_ID_SIZE);
00539     if ((fnmatch(type_pattern, type, FNM_NOESCAPE) == 0) &&
00540         (fnmatch(id_pattern, id, FNM_NOESCAPE) == 0))
00541     {
00542       infl->append(ih->type, ih->id, ih->hash, ih->serial,
00543                    ih->flag_writer_active, ih->num_readers);
00544     }
00545   }
00546 
00547   memmgr->unlock();
00548 
00549   return infl;
00550 }
00551 
00552 
00553 /** Get the writer interface for the given mem serial.
00554  * @param mem_serial memory serial to get writer for
00555  * @return writer interface for given mem serial, or NULL if non exists
00556  * @exception BlackBoardNoWritingInstanceException thrown if no writer
00557  * was found for the given interface.
00558  */
00559 Interface *
00560 BlackBoardInterfaceManager::writer_for_mem_serial(unsigned int mem_serial)
00561 {
00562   if ( writer_interfaces.find(mem_serial) != writer_interfaces.end() ) {
00563     return writer_interfaces[mem_serial];
00564   } else {
00565     throw BlackBoardNoWritingInstanceException();
00566   }
00567 }
00568 
00569 
00570 void
00571 BlackBoardInterfaceManager::notify_of_data_change(const Interface *interface)
00572 {
00573   notifier->notify_of_data_change(interface);
00574 }
00575 
00576 
00577 bool
00578 BlackBoardInterfaceManager::exists_writer(const Interface *interface) const
00579 {
00580   return (writer_interfaces.find(interface->__mem_serial) != writer_interfaces.end());
00581 }
00582 
00583 
00584 unsigned int
00585 BlackBoardInterfaceManager::num_readers(const Interface *interface) const
00586 {
00587   const interface_header_t *ih = (interface_header_t *)interface->__mem_real_ptr;
00588   return ih->num_readers;
00589 }
00590 
00591 } // end namespace fawkes