message_queue.cpp

00001 
00002 /***************************************************************************
00003  *  message_queue.cpp - BlackBoard Interface message queue
00004  *
00005  *  Created: Tue Oct 18 15:43:29 2006
00006  *  Copyright  2006-2009  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 <interface/message_queue.h>
00025 #include <interface/message.h>
00026 
00027 #include <core/threading/mutex.h>
00028 #include <core/exceptions/software.h>
00029 
00030 #include <cstddef>
00031 #include <cstdlib>
00032 
00033 namespace fawkes {
00034 
00035 /** @class MessageAlreadyQueuedException <interface/message_queue.h>
00036  * Message already enqueued exception.
00037  * This exception is thrown if you try to enqueue a message that has already
00038  * been enqueued in another message queue. This is an illegal operation. If you
00039  * need to enqueue a message multiple times use the copy constructor to do this.
00040  */
00041 
00042 
00043 /** Constructor. */
00044 MessageAlreadyQueuedException::MessageAlreadyQueuedException()
00045   : Exception("Message already enqueued in another MessageQueue.")
00046 {
00047 }
00048 
00049 
00050 
00051 /** @class MessageQueue <interface/message_queue.h>
00052  * Message queue used in interfaces.
00053  * This message queue handles the basic messaging operations. The methods the
00054  * Interface provides for handling message queues are forwarded to a
00055  * MessageQueue instance.
00056  * @see Interface
00057  */
00058 
00059 
00060 /** Constructor. */
00061 MessageQueue::MessageQueue()
00062 {
00063   __list = NULL;
00064   __end_el = NULL;
00065   __mutex = new Mutex();
00066 }
00067 
00068 
00069 /** Destructor */
00070 MessageQueue::~MessageQueue()
00071 {
00072   flush();
00073   delete __mutex;
00074 }
00075 
00076 
00077 /** Delete all messages from queue.
00078  * This method deletes all messages from the queue.
00079  */
00080 void
00081 MessageQueue::flush()
00082 {
00083   __mutex->lock();
00084   // free list elements
00085   msg_list_t *l = __list;
00086   msg_list_t *next;
00087   while ( l ) {
00088     next = l->next;
00089     l->msg->unref();
00090     free(l);
00091     l = next;
00092   }
00093   __list = NULL;
00094   __mutex->unlock();
00095 }
00096 
00097 
00098 /** Append message to queue.
00099  * @param msg Message to append
00100  * @exception MessageAlreadyQueuedException thrown if the message has already been
00101  * enqueued to an interface.
00102  */
00103 void
00104 MessageQueue::append(Message *msg)
00105 {
00106   if ( msg->enqueued() != 0 ) {
00107     throw MessageAlreadyQueuedException();
00108   }
00109   __mutex->lock();
00110   msg->mark_enqueued();
00111   if ( __list == NULL ) {
00112     __list = (msg_list_t *)malloc(sizeof(msg_list_t));
00113     __list->next = NULL;
00114     __list->msg = msg;
00115     __list->msg_id = msg->id();
00116     __end_el = __list;
00117   } else {
00118     msg_list_t *l = (msg_list_t *)malloc(sizeof(msg_list_t));
00119     l->next = NULL;
00120     l->msg = msg;
00121     l->msg_id = msg->id();
00122     __end_el->next = l;
00123     __end_el = l;
00124   }
00125 
00126   __mutex->unlock();
00127 }
00128 
00129 
00130 /** Enqueue message after given iterator.
00131  * @param it Iterator
00132  * @param msg Message to enqueue
00133  * @return message queue id of the appended message.
00134  * @exception NullPointerException thrown if iterator is end iterator.
00135  * @exception NotLockedException thrown if message queue is not locked during this operation.
00136  * @exception MessageAlreadyQueuedException thrown if the message has already been
00137  * enqueued to an interface.
00138  */
00139 void
00140 MessageQueue::insert_after(const MessageIterator &it, Message *msg)
00141 {
00142   if ( __mutex->try_lock() ) {
00143     __mutex->unlock();
00144     throw NotLockedException("Message queue must be locked to insert messages after iterator.");
00145   }
00146   if ( it.cur == NULL ) {
00147     throw NullPointerException("Cannot append message at end element.");
00148   }
00149   if ( msg->enqueued() != 0 ) {
00150     throw MessageAlreadyQueuedException();
00151   }
00152   msg->mark_enqueued();
00153   msg_list_t *l = (msg_list_t *)malloc(sizeof(msg_list_t));
00154   l->next = it.cur->next;
00155   l->msg = msg;
00156   l->msg_id = msg->id();
00157   it.cur->next = l;
00158   if ( l->next == NULL ) {
00159     __end_el = l;
00160   }
00161 }
00162 
00163 
00164 /** Remove message from queue.
00165  * @param msg message to remove
00166  */
00167 void
00168 MessageQueue::remove(const Message *msg)
00169 {
00170   __mutex->lock();
00171   msg_list_t *l = __list;
00172   msg_list_t *p = NULL;
00173   while ( l ) {
00174     if ( l->msg == msg ) {
00175       remove(l, p);
00176       break;
00177     } else {
00178       p = l;
00179       l = l->next;
00180     }
00181   }
00182   __mutex->unlock();
00183 }
00184 
00185 
00186 /** Remove message from queue by message id.
00187  * @param msg_id id of message to remove
00188  */
00189 void
00190 MessageQueue::remove(const unsigned int msg_id)
00191 {
00192   __mutex->lock();
00193   msg_list_t *l = __list;
00194   msg_list_t *p = NULL;
00195   while ( l ) {
00196     if ( l->msg_id == msg_id ) {
00197       remove(l, p);
00198       break;
00199     } else {
00200       p = l;
00201       l = l->next;
00202     }
00203   }
00204   __mutex->unlock();
00205 }
00206 
00207 
00208 /** Remove message from list.
00209  * @param l list item to remove
00210  * @param p predecessor of element, may be NULL if there is none
00211  */
00212 void
00213 MessageQueue::remove(msg_list_t *l, msg_list_t *p)
00214 {
00215   if ( __mutex->try_lock() ) {
00216     __mutex->unlock();
00217     throw NotLockedException("Protected remove must be made safe by locking.");
00218   }
00219   if ( p ) {
00220     p->next = l->next;
00221   } else {
00222     // was first element
00223     __list = l->next;
00224   }
00225   l->msg->unref();
00226   free(l);
00227 }
00228 
00229 
00230 /** Get number of messages in queue.
00231  * @return number of messages in queue.
00232  */
00233 unsigned int
00234 MessageQueue::size() const
00235 {
00236   __mutex->lock();
00237   unsigned int rv = 0;
00238   msg_list_t *l = __list;
00239   while ( l ) {
00240     ++rv;
00241     l = l->next;
00242   }
00243 
00244   __mutex->unlock();
00245   return rv;
00246 }
00247 
00248 
00249 /** Check if message queue is empty.
00250  * @return true if message queue is empty, false otherwise
00251  */
00252 bool
00253 MessageQueue::empty() const
00254 {
00255   __mutex->lock();
00256   bool rv = ( __list == NULL );
00257   __mutex->unlock();
00258   return rv;
00259 }
00260 
00261 
00262 /** Lock message queue.
00263  * No operations can be performed on the message queue after locking it.
00264  * Note that you cannot call any method of the message queue as long as
00265  * the queue is locked. Use lock() only to have a secure run-through with
00266  * the MessageIterator.
00267  */
00268 void
00269 MessageQueue::lock()
00270 {
00271   __mutex->lock();
00272 }
00273 
00274 
00275 /** Try to lock message queue.
00276  * No operations can be performed on the message queue after locking it.
00277  * Note that you cannot call any method of the message queue as long as
00278  * the queue is locked. Use try_lock() only to have a secure run-through with
00279  * the MessageIterator.
00280  * @return true, if the lock has been aquired, false otherwise.
00281  */
00282 bool
00283 MessageQueue::try_lock()
00284 {
00285   return __mutex->try_lock();
00286 }
00287 
00288 
00289 /** Unlock message queue.
00290  */
00291 void
00292 MessageQueue::unlock()
00293 {
00294   __mutex->unlock();
00295 }
00296 
00297 
00298 /** Get first message from queue.
00299  * @return first message from queue
00300  */
00301 Message *
00302 MessageQueue::first()
00303 {
00304   if ( __list ) {
00305     return __list->msg;
00306   } else {
00307     return NULL;
00308   }
00309 }
00310 
00311 
00312 /** Erase first message from queue.
00313  */
00314 void
00315 MessageQueue::pop()
00316 {
00317   __mutex->lock();
00318   if ( __list ) {
00319     remove(__list, NULL);
00320   }
00321   __mutex->unlock();
00322 }
00323 
00324 
00325 /** Get iterator to first element in message queue.
00326  * @return iterator to first element in message queue
00327  * @exception NotLockedException thrown if message queue is not locked during this operation.
00328  */
00329 MessageQueue::MessageIterator
00330 MessageQueue::begin()
00331 {
00332   if ( __mutex->try_lock() ) {
00333     __mutex->unlock();
00334     throw NotLockedException("Message queue must be locked to get begin iterator.");
00335   }
00336   return MessageIterator(__list);
00337 }
00338 
00339 
00340 /** Get iterator to element beyond end of message queue list.
00341  * @return iterator to element beyond end of message queue list
00342  * @exception NotLockedException thrown if message queue is not locked during this operation.
00343  */
00344 MessageQueue::MessageIterator
00345 MessageQueue::end()
00346 {
00347   if ( __mutex->try_lock() ) {
00348     __mutex->unlock();
00349     throw NotLockedException("Message queue must be locked to get end iterator.");
00350   }
00351   return MessageIterator();
00352 }
00353 
00354 
00355 /** @class MessageQueue::MessageIterator message_queue.h <interface/message_queue.h>
00356  * Message iterator.
00357  * Use this iterator to iterate over messages in a message queue.
00358  * Use MessageQueue::begin() to get the iterator.
00359  * @author Tim Niemueller
00360  */
00361 
00362 /** Constructor
00363  * @param cur Current element for message list
00364  */
00365 MessageQueue::MessageIterator::MessageIterator(msg_list_t *cur)
00366 {
00367   this->cur = cur;
00368 }
00369 
00370 
00371 /** Constructor */
00372 MessageQueue::MessageIterator::MessageIterator()
00373 {
00374   cur = NULL;
00375 }
00376 
00377 
00378 /** Copy constructor.
00379  * @param it Iterator to copy
00380  */
00381 MessageQueue::MessageIterator::MessageIterator(const MessageIterator &it)
00382 {
00383   cur = it.cur;
00384 }
00385 
00386 
00387 /** Increment iterator.
00388  * Advances to the next element. This is the infix-operator. It may be used
00389  * like this:
00390  * @code
00391  * for (MessageIterator cit = msgq->begin(); cit != msgq->end(); ++cit) {
00392  *   // your code here
00393  * }
00394  * @endcode
00395  * @return Reference to instance itself after advancing to the next element.
00396  */
00397 MessageQueue::MessageIterator &
00398 MessageQueue::MessageIterator::operator++()
00399 {
00400   if ( cur != NULL )
00401     cur = cur->next;
00402 
00403   return *this;
00404 }
00405 
00406 
00407 /** Increment iterator.
00408  * Advances to the next element in allocated chunk list. This is the postfix-operator.
00409  * It may be used like this:
00410  * @code
00411  * for (MessageIterator cit = memmgr->begin(); cit != memmgr->end(); cit++) {
00412  *   // your code here
00413  * }
00414  * @endcode
00415  * Note that since a copy of the original iterator has to be created an returned it
00416  * the postfix operation takes both, more CPU time and more memory. If possible (especially
00417  * if used in a for loop like the example) use the prefix operator!
00418  * @see operator++()
00419  * @param inc ignored
00420  * @return copy of the current instance before advancing to the next element.
00421  */
00422 MessageQueue::MessageIterator
00423 MessageQueue::MessageIterator::operator++(int inc)
00424 {
00425   MessageIterator rv(cur);
00426   if ( cur != NULL )
00427     cur = cur->next;
00428   
00429   return rv;
00430 }
00431 
00432 
00433 /** Advance by a certain amount.
00434  * Can be used to add an integer to the iterator to advance many steps in one go.
00435  * This operation takes linear time depending on i.
00436  * @param i steps to advance in list. If i is bigger than the number of remaining
00437  * elements in the list will stop beyond list.
00438  * @return reference to current instance after advancing i steps or after reaching
00439  * end of list.
00440  */
00441 MessageQueue::MessageIterator &
00442 MessageQueue::MessageIterator::operator+(unsigned int i)
00443 {
00444   for (unsigned int j = 0; (cur != NULL) && (j < i); ++j) {
00445     cur = cur->next;
00446   }
00447   return *this;
00448 }
00449 
00450 
00451 /** Advance by a certain amount.
00452  * Works like operator+(unsigned int i), provided for convenience.
00453  * @param i steps to advance in list
00454  * @return reference to current instance after advancing i steps or after reaching
00455  * end of list.
00456  */
00457 MessageQueue::MessageIterator &
00458 MessageQueue::MessageIterator::operator+=(unsigned int i)
00459 {
00460   for (unsigned int j = 0; (cur != NULL) && (j < i); ++j) {
00461     cur = cur->next;
00462   }
00463   return *this;
00464 }
00465 
00466 
00467 /** Check equality of two iterators.
00468  * Can be used to determine if two iterators point to the same chunk.
00469  * @param c iterator to compare current instance to
00470  * @return true, if iterators point to the same chunk, false otherwise
00471  */
00472 bool
00473 MessageQueue::MessageIterator::operator==(const MessageIterator & c) const
00474 {
00475   return (cur == c.cur);
00476 }
00477 
00478 
00479 /** Check inequality of two iterators.
00480  * Can be used to determine if two iterators point to different chunks.
00481  * @param c iterator to compare current instance to
00482  * @return true, if iterators point to different chunks of memory, false otherwise
00483  */
00484 bool
00485 MessageQueue::MessageIterator::operator!=(const MessageIterator & c) const
00486 {
00487   return (cur != c.cur);
00488 }
00489 
00490 
00491 /** Get memory pointer of chunk.
00492  * Use this operator to get the pointer to the chunk of memory that this iterator
00493  * points to.
00494  * @return pointer to memory
00495  */
00496 Message *
00497 MessageQueue::MessageIterator::operator*() const
00498 {
00499   return ( cur != NULL ) ? cur->msg : NULL;
00500 }
00501 
00502 
00503 /** Act on current message.
00504  * Node that you have to make sure that this is not called on the end node!
00505  * @return current message
00506  */
00507 Message *
00508 MessageQueue::MessageIterator::operator->() const
00509 {
00510   return cur->msg;
00511 }
00512 
00513 
00514 /** Assign iterator.
00515  * Makes the current instance to point to the same memory element as c.
00516  * @param c assign value
00517  * @return reference to current instance
00518  */
00519 MessageQueue::MessageIterator &
00520 MessageQueue::MessageIterator::operator=(const MessageIterator & c)
00521 {
00522   this->cur = c.cur;
00523   return *this;
00524 }
00525 
00526 
00527 /** Get ID of current element or 0 if element is end.
00528  * @return ID of current element or 0 if element is end.
00529  */
00530 unsigned int
00531 MessageQueue::MessageIterator::id() const
00532 {
00533   if ( cur == NULL ) return 0;
00534   return cur->msg_id;
00535 }
00536 
00537 } // end namespace fawkes

Generated on Tue Feb 22 13:32:27 2011 for Fawkes API by  doxygen 1.4.7