Fawkes API  Fawkes Development Version
message_queue.cpp
1 
2 /***************************************************************************
3  * message_queue.cpp - BlackBoard Interface message queue
4  *
5  * Created: Tue Oct 18 15:43:29 2006
6  * Copyright 2006-2009 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 <interface/message_queue.h>
25 #include <interface/message.h>
26 
27 #include <core/threading/mutex.h>
28 #include <core/exceptions/software.h>
29 
30 #include <cstddef>
31 #include <cstdlib>
32 
33 namespace fawkes {
34 
35 /** @class MessageAlreadyQueuedException <interface/message_queue.h>
36  * Message already enqueued exception.
37  * This exception is thrown if you try to enqueue a message that has already
38  * been enqueued in another message queue. This is an illegal operation. If you
39  * need to enqueue a message multiple times use the copy constructor to do this.
40  */
41 
42 
43 /** Constructor. */
45  : Exception("Message already enqueued in another MessageQueue.")
46 {
47 }
48 
49 
50 
51 /** @class MessageQueue <interface/message_queue.h>
52  * Message queue used in interfaces.
53  * This message queue handles the basic messaging operations. The methods the
54  * Interface provides for handling message queues are forwarded to a
55  * MessageQueue instance.
56  * @see Interface
57  */
58 
59 
60 /** Constructor. */
62 {
63  __list = NULL;
64  __end_el = NULL;
65  __mutex = new Mutex();
66 }
67 
68 
69 /** Destructor */
71 {
72  flush();
73  delete __mutex;
74 }
75 
76 
77 /** Delete all messages from queue.
78  * This method deletes all messages from the queue.
79  */
80 void
82 {
83  __mutex->lock();
84  // free list elements
85  msg_list_t *l = __list;
86  msg_list_t *next;
87  while ( l ) {
88  next = l->next;
89  l->msg->unref();
90  free(l);
91  l = next;
92  }
93  __list = NULL;
94  __mutex->unlock();
95 }
96 
97 
98 /** Append message to queue.
99  * @param msg Message to append
100  * @exception MessageAlreadyQueuedException thrown if the message has already been
101  * enqueued to an interface.
102  */
103 void
105 {
106  if ( msg->enqueued() != 0 ) {
108  }
109  __mutex->lock();
110  msg->mark_enqueued();
111  if ( __list == NULL ) {
112  __list = (msg_list_t *)malloc(sizeof(msg_list_t));
113  __list->next = NULL;
114  __list->msg = msg;
115  __list->msg_id = msg->id();
116  __end_el = __list;
117  } else {
118  msg_list_t *l = (msg_list_t *)malloc(sizeof(msg_list_t));
119  l->next = NULL;
120  l->msg = msg;
121  l->msg_id = msg->id();
122  __end_el->next = l;
123  __end_el = l;
124  }
125 
126  __mutex->unlock();
127 }
128 
129 
130 /** Enqueue message after given iterator.
131  * @param it Iterator
132  * @param msg Message to enqueue
133  * @return message queue id of the appended message.
134  * @exception NullPointerException thrown if iterator is end iterator.
135  * @exception NotLockedException thrown if message queue is not locked during this operation.
136  * @exception MessageAlreadyQueuedException thrown if the message has already been
137  * enqueued to an interface.
138  */
139 void
141 {
142  if ( __mutex->try_lock() ) {
143  __mutex->unlock();
144  throw NotLockedException("Message queue must be locked to insert messages after iterator.");
145  }
146  if ( it.cur == NULL ) {
147  throw NullPointerException("Cannot append message at end element.");
148  }
149  if ( msg->enqueued() != 0 ) {
151  }
152  msg->mark_enqueued();
153  msg_list_t *l = (msg_list_t *)malloc(sizeof(msg_list_t));
154  l->next = it.cur->next;
155  l->msg = msg;
156  l->msg_id = msg->id();
157  it.cur->next = l;
158  if ( l->next == NULL ) {
159  __end_el = l;
160  }
161 }
162 
163 
164 /** Remove message from queue.
165  * @param msg message to remove
166  */
167 void
169 {
170  __mutex->lock();
171  msg_list_t *l = __list;
172  msg_list_t *p = NULL;
173  while ( l ) {
174  if ( l->msg == msg ) {
175  remove(l, p);
176  break;
177  } else {
178  p = l;
179  l = l->next;
180  }
181  }
182  __mutex->unlock();
183 }
184 
185 
186 /** Remove message from queue by message id.
187  * @param msg_id id of message to remove
188  */
189 void
190 MessageQueue::remove(const unsigned int msg_id)
191 {
192  __mutex->lock();
193  msg_list_t *l = __list;
194  msg_list_t *p = NULL;
195  while ( l ) {
196  if ( l->msg_id == msg_id ) {
197  remove(l, p);
198  break;
199  } else {
200  p = l;
201  l = l->next;
202  }
203  }
204  __mutex->unlock();
205 }
206 
207 
208 /** Remove message from list.
209  * @param l list item to remove
210  * @param p predecessor of element, may be NULL if there is none
211  */
212 void
213 MessageQueue::remove(msg_list_t *l, msg_list_t *p)
214 {
215  if ( __mutex->try_lock() ) {
216  __mutex->unlock();
217  throw NotLockedException("Protected remove must be made safe by locking.");
218  }
219  if ( p ) {
220  p->next = l->next;
221  } else {
222  // was first element
223  __list = l->next;
224  }
225  l->msg->unref();
226  free(l);
227 }
228 
229 
230 /** Get number of messages in queue.
231  * @return number of messages in queue.
232  */
233 unsigned int
235 {
236  __mutex->lock();
237  unsigned int rv = 0;
238  msg_list_t *l = __list;
239  while ( l ) {
240  ++rv;
241  l = l->next;
242  }
243 
244  __mutex->unlock();
245  return rv;
246 }
247 
248 
249 /** Check if message queue is empty.
250  * @return true if message queue is empty, false otherwise
251  */
252 bool
254 {
255  __mutex->lock();
256  bool rv = ( __list == NULL );
257  __mutex->unlock();
258  return rv;
259 }
260 
261 
262 /** Lock message queue.
263  * No operations can be performed on the message queue after locking it.
264  * Note that you cannot call any method of the message queue as long as
265  * the queue is locked. Use lock() only to have a secure run-through with
266  * the MessageIterator.
267  */
268 void
270 {
271  __mutex->lock();
272 }
273 
274 
275 /** Try to lock message queue.
276  * No operations can be performed on the message queue after locking it.
277  * Note that you cannot call any method of the message queue as long as
278  * the queue is locked. Use try_lock() only to have a secure run-through with
279  * the MessageIterator.
280  * @return true, if the lock has been aquired, false otherwise.
281  */
282 bool
284 {
285  return __mutex->try_lock();
286 }
287 
288 
289 /** Unlock message queue.
290  */
291 void
293 {
294  __mutex->unlock();
295 }
296 
297 
298 /** Get first message from queue.
299  * @return first message from queue
300  */
301 Message *
303 {
304  if ( __list ) {
305  return __list->msg;
306  } else {
307  return NULL;
308  }
309 }
310 
311 
312 /** Erase first message from queue.
313  */
314 void
316 {
317  __mutex->lock();
318  if ( __list ) {
319  remove(__list, NULL);
320  }
321  __mutex->unlock();
322 }
323 
324 
325 /** Get iterator to first element in message queue.
326  * @return iterator to first element in message queue
327  * @exception NotLockedException thrown if message queue is not locked during this operation.
328  */
331 {
332  if ( __mutex->try_lock() ) {
333  __mutex->unlock();
334  throw NotLockedException("Message queue must be locked to get begin iterator.");
335  }
336  return MessageIterator(__list);
337 }
338 
339 
340 /** Get iterator to element beyond end of message queue list.
341  * @return iterator to element beyond end of message queue list
342  * @exception NotLockedException thrown if message queue is not locked during this operation.
343  */
346 {
347  if ( __mutex->try_lock() ) {
348  __mutex->unlock();
349  throw NotLockedException("Message queue must be locked to get end iterator.");
350  }
351  return MessageIterator();
352 }
353 
354 
355 /** @class MessageQueue::MessageIterator message_queue.h <interface/message_queue.h>
356  * Message iterator.
357  * Use this iterator to iterate over messages in a message queue.
358  * Use MessageQueue::begin() to get the iterator.
359  * @author Tim Niemueller
360  */
361 
362 /** Constructor
363  * @param cur Current element for message list
364  */
366 {
367  this->cur = cur;
368 }
369 
370 
371 /** Constructor */
373 {
374  cur = NULL;
375 }
376 
377 
378 /** Copy constructor.
379  * @param it Iterator to copy
380  */
382 {
383  cur = it.cur;
384 }
385 
386 
387 /** Increment iterator.
388  * Advances to the next element. This is the infix-operator. It may be used
389  * like this:
390  * @code
391  * for (MessageIterator cit = msgq->begin(); cit != msgq->end(); ++cit) {
392  * // your code here
393  * }
394  * @endcode
395  * @return Reference to instance itself after advancing to the next element.
396  */
399 {
400  if ( cur != NULL )
401  cur = cur->next;
402 
403  return *this;
404 }
405 
406 
407 /** Increment iterator.
408  * Advances to the next element in allocated chunk list. This is the postfix-operator.
409  * It may be used like this:
410  * @code
411  * for (MessageIterator cit = memmgr->begin(); cit != memmgr->end(); cit++) {
412  * // your code here
413  * }
414  * @endcode
415  * Note that since a copy of the original iterator has to be created an returned it
416  * the postfix operation takes both, more CPU time and more memory. If possible (especially
417  * if used in a for loop like the example) use the prefix operator!
418  * @see operator++()
419  * @param inc ignored
420  * @return copy of the current instance before advancing to the next element.
421  */
424 {
425  MessageIterator rv(cur);
426  if ( cur != NULL )
427  cur = cur->next;
428 
429  return rv;
430 }
431 
432 
433 /** Advance by a certain amount.
434  * Can be used to add an integer to the iterator to advance many steps in one go.
435  * This operation takes linear time depending on i.
436  * @param i steps to advance in list. If i is bigger than the number of remaining
437  * elements in the list will stop beyond list.
438  * @return reference to current instance after advancing i steps or after reaching
439  * end of list.
440  */
443 {
444  for (unsigned int j = 0; (cur != NULL) && (j < i); ++j) {
445  cur = cur->next;
446  }
447  return *this;
448 }
449 
450 
451 /** Advance by a certain amount.
452  * Works like operator+(unsigned int i), provided for convenience.
453  * @param i steps to advance in list
454  * @return reference to current instance after advancing i steps or after reaching
455  * end of list.
456  */
459 {
460  for (unsigned int j = 0; (cur != NULL) && (j < i); ++j) {
461  cur = cur->next;
462  }
463  return *this;
464 }
465 
466 
467 /** Check equality of two iterators.
468  * Can be used to determine if two iterators point to the same chunk.
469  * @param c iterator to compare current instance to
470  * @return true, if iterators point to the same chunk, false otherwise
471  */
472 bool
474 {
475  return (cur == c.cur);
476 }
477 
478 
479 /** Check inequality of two iterators.
480  * Can be used to determine if two iterators point to different chunks.
481  * @param c iterator to compare current instance to
482  * @return true, if iterators point to different chunks of memory, false otherwise
483  */
484 bool
486 {
487  return (cur != c.cur);
488 }
489 
490 
491 /** Get memory pointer of chunk.
492  * Use this operator to get the pointer to the chunk of memory that this iterator
493  * points to.
494  * @return pointer to memory
495  */
496 Message *
498 {
499  return ( cur != NULL ) ? cur->msg : NULL;
500 }
501 
502 
503 /** Act on current message.
504  * Node that you have to make sure that this is not called on the end node!
505  * @return current message
506  */
507 Message *
509 {
510  return cur->msg;
511 }
512 
513 
514 /** Assign iterator.
515  * Makes the current instance to point to the same memory element as c.
516  * @param c assign value
517  * @return reference to current instance
518  */
521 {
522  this->cur = c.cur;
523  return *this;
524 }
525 
526 
527 /** Get ID of current element or 0 if element is end.
528  * @return ID of current element or 0 if element is end.
529  */
530 unsigned int
532 {
533  if ( cur == NULL ) return 0;
534  return cur->msg_id;
535 }
536 
537 } // end namespace fawkes
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:44
unsigned int id() const
Get message ID.
Definition: message.cpp:197
void mark_enqueued()
Mark message as being enqueued.
Definition: message.cpp:235
void insert_after(const MessageIterator &it, Message *msg)
Enqueue message after given iterator.
unsigned int id() const
Get ID of current element or 0 if element is end.
MessageIterator & operator+=(unsigned int i)
Advance by a certain amount.
MessageIterator end()
Get iterator to element beyond end of message queue list.
Fawkes library namespace.
MessageQueue()
Constructor.
Message * operator*() const
Get memory pointer of chunk.
A NULL pointer was supplied where not allowed.
Definition: software.h:34
bool enqueued() const
Check is message has been enqueued.
Definition: message.cpp:251
MessageIterator & operator+(unsigned int i)
Advance by a certain amount.
MessageIterator & operator++()
Increment iterator.
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual ~MessageQueue()
Destructor.
void flush()
Delete all messages from queue.
bool operator!=(const MessageIterator &c) const
Check inequality of two iterators.
unsigned int size() const
Get number of messages in queue.
bool try_lock()
Try to lock message queue.
bool operator==(const MessageIterator &c) const
Check equality of two iterators.
Message * operator->() const
Act on current message.
MessageIterator begin()
Get iterator to first element in message queue.
void append(Message *msg)
Append message to queue.
bool empty() const
Check if message queue is empty.
Operation on unlocked object.
Definition: software.h:64
void remove(const Message *msg)
Remove message from queue.
void pop()
Erase first message from queue.
void unlock()
Unlock message queue.
Message * first()
Get first message from queue.
Mutex mutual exclusion lock.
Definition: mutex.h:32
void lock()
Lock message queue.
MessageIterator & operator=(const MessageIterator &c)
Assign iterator.