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