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  * @exception NullPointerException thrown if iterator is end iterator.
125  * @exception NotLockedException thrown if message queue is not locked during this operation.
126  * @exception MessageAlreadyQueuedException thrown if the message has already been
127  * enqueued to an interface.
128  */
129 void
131 {
132  if (mutex_->try_lock()) {
133  mutex_->unlock();
134  throw NotLockedException("Message queue must be locked to insert messages after iterator.");
135  }
136  if (it.cur == NULL) {
137  throw NullPointerException("Cannot append message at end element.");
138  }
139  if (msg->enqueued() != 0) {
141  }
142  msg->mark_enqueued();
143  msg_list_t *l = (msg_list_t *)malloc(sizeof(msg_list_t));
144  l->next = it.cur->next;
145  l->msg = msg;
146  l->msg_id = msg->id();
147  it.cur->next = l;
148  if (l->next == NULL) {
149  end_el_ = l;
150  }
151 }
152 
153 /** Remove message from queue.
154  * @param msg message to remove
155  */
156 void
158 {
159  mutex_->lock();
160  msg_list_t *l = list_;
161  msg_list_t *p = NULL;
162  while (l) {
163  if (l->msg == msg) {
164  remove(l, p);
165  break;
166  } else {
167  p = l;
168  l = l->next;
169  }
170  }
171  mutex_->unlock();
172 }
173 
174 /** Remove message from queue by message id.
175  * @param msg_id id of message to remove
176  */
177 void
178 MessageQueue::remove(const unsigned int msg_id)
179 {
180  mutex_->lock();
181  msg_list_t *l = list_;
182  msg_list_t *p = NULL;
183  while (l) {
184  if (l->msg_id == msg_id) {
185  remove(l, p);
186  break;
187  } else {
188  p = l;
189  l = l->next;
190  }
191  }
192  mutex_->unlock();
193 }
194 
195 /** Remove message from list.
196  * @param l list item to remove
197  * @param p predecessor of element, may be NULL if there is none
198  */
199 void
200 MessageQueue::remove(msg_list_t *l, msg_list_t *p)
201 {
202  if (mutex_->try_lock()) {
203  mutex_->unlock();
204  throw NotLockedException("Protected remove must be made safe by locking.");
205  }
206  if (p) {
207  p->next = l->next;
208  } else {
209  // was first element
210  list_ = l->next;
211  }
212  l->msg->unref();
213  free(l);
214 }
215 
216 /** Get number of messages in queue.
217  * @return number of messages in queue.
218  */
219 unsigned int
221 {
222  mutex_->lock();
223  unsigned int rv = 0;
224  msg_list_t * l = list_;
225  while (l) {
226  ++rv;
227  l = l->next;
228  }
229 
230  mutex_->unlock();
231  return rv;
232 }
233 
234 /** Check if message queue is empty.
235  * @return true if message queue is empty, false otherwise
236  */
237 bool
239 {
240  mutex_->lock();
241  bool rv = (list_ == NULL);
242  mutex_->unlock();
243  return rv;
244 }
245 
246 /** Lock message queue.
247  * No operations can be performed on the message queue after locking it.
248  * Note that you cannot call any method of the message queue as long as
249  * the queue is locked. Use lock() only to have a secure run-through with
250  * the MessageIterator.
251  */
252 void
254 {
255  mutex_->lock();
256 }
257 
258 /** Try to lock message queue.
259  * No operations can be performed on the message queue after locking it.
260  * Note that you cannot call any method of the message queue as long as
261  * the queue is locked. Use try_lock() only to have a secure run-through with
262  * the MessageIterator.
263  * @return true, if the lock has been aquired, false otherwise.
264  */
265 bool
267 {
268  return mutex_->try_lock();
269 }
270 
271 /** Unlock message queue.
272  */
273 void
275 {
276  mutex_->unlock();
277 }
278 
279 /** Get first message from queue.
280  * @return first message from queue
281  */
282 Message *
284 {
285  if (list_) {
286  return list_->msg;
287  } else {
288  return NULL;
289  }
290 }
291 
292 /** Erase first message from queue.
293  */
294 void
296 {
297  mutex_->lock();
298  if (list_) {
299  remove(list_, NULL);
300  }
301  mutex_->unlock();
302 }
303 
304 /** Get iterator to first element in message queue.
305  * @return iterator to first element in message queue
306  * @exception NotLockedException thrown if message queue is not locked during this operation.
307  */
310 {
311  if (mutex_->try_lock()) {
312  mutex_->unlock();
313  throw NotLockedException("Message queue must be locked to get begin iterator.");
314  }
315  return MessageIterator(list_);
316 }
317 
318 /** Get iterator to element beyond end of message queue list.
319  * @return iterator to element beyond end of message queue list
320  * @exception NotLockedException thrown if message queue is not locked during this operation.
321  */
324 {
325  if (mutex_->try_lock()) {
326  mutex_->unlock();
327  throw NotLockedException("Message queue must be locked to get end iterator.");
328  }
329  return MessageIterator();
330 }
331 
332 /** @class MessageQueue::MessageIterator message_queue.h <interface/message_queue.h>
333  * Message iterator.
334  * Use this iterator to iterate over messages in a message queue.
335  * Use MessageQueue::begin() to get the iterator.
336  * @author Tim Niemueller
337  */
338 
339 /** Constructor
340  * @param cur Current element for message list
341  */
343 {
344  this->cur = cur;
345 }
346 
347 /** Constructor */
349 {
350  cur = NULL;
351 }
352 
353 /** Copy constructor.
354  * @param it Iterator to copy
355  */
357 {
358  cur = it.cur;
359 }
360 
361 /** Increment iterator.
362  * Advances to the next element. This is the infix-operator. It may be used
363  * like this:
364  * @code
365  * for (MessageIterator cit = msgq->begin(); cit != msgq->end(); ++cit) {
366  * // your code here
367  * }
368  * @endcode
369  * @return Reference to instance itself after advancing to the next element.
370  */
373 {
374  if (cur != NULL)
375  cur = cur->next;
376 
377  return *this;
378 }
379 
380 /** Increment iterator.
381  * Advances to the next element in allocated chunk list. This is the postfix-operator.
382  * It may be used like this:
383  * @code
384  * for (MessageIterator cit = memmgr->begin(); cit != memmgr->end(); cit++) {
385  * // your code here
386  * }
387  * @endcode
388  * Note that since a copy of the original iterator has to be created an returned it
389  * the postfix operation takes both, more CPU time and more memory. If possible (especially
390  * if used in a for loop like the example) use the prefix operator!
391  * @see operator++()
392  * @param inc ignored
393  * @return copy of the current instance before advancing to the next element.
394  */
397 {
398  MessageIterator rv(cur);
399  if (cur != NULL)
400  cur = cur->next;
401 
402  return rv;
403 }
404 
405 /** Advance by a certain amount.
406  * Can be used to add an integer to the iterator to advance many steps in one go.
407  * This operation takes linear time depending on i.
408  * @param i steps to advance in list. If i is bigger than the number of remaining
409  * elements in the list will stop beyond list.
410  * @return reference to current instance after advancing i steps or after reaching
411  * end of list.
412  */
415 {
416  for (unsigned int j = 0; (cur != NULL) && (j < i); ++j) {
417  cur = cur->next;
418  }
419  return *this;
420 }
421 
422 /** Advance by a certain amount.
423  * Works like operator+(unsigned int i), provided for convenience.
424  * @param i steps to advance in list
425  * @return reference to current instance after advancing i steps or after reaching
426  * end of list.
427  */
430 {
431  for (unsigned int j = 0; (cur != NULL) && (j < i); ++j) {
432  cur = cur->next;
433  }
434  return *this;
435 }
436 
437 /** Check equality of two iterators.
438  * Can be used to determine if two iterators point to the same chunk.
439  * @param c iterator to compare current instance to
440  * @return true, if iterators point to the same chunk, false otherwise
441  */
442 bool
444 {
445  return (cur == c.cur);
446 }
447 
448 /** Check inequality of two iterators.
449  * Can be used to determine if two iterators point to different chunks.
450  * @param c iterator to compare current instance to
451  * @return true, if iterators point to different chunks of memory, false otherwise
452  */
453 bool
455 {
456  return (cur != c.cur);
457 }
458 
459 /** Get memory pointer of chunk.
460  * Use this operator to get the pointer to the chunk of memory that this iterator
461  * points to.
462  * @return pointer to memory
463  */
464 Message *
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  */
474 Message *
476 {
477  return cur->msg;
478 }
479 
480 /** Assign iterator.
481  * Makes the current instance to point to the same memory element as c.
482  * @param c assign value
483  * @return reference to current instance
484  */
487 {
488  this->cur = c.cur;
489  return *this;
490 }
491 
492 /** Get ID of current element or 0 if element is end.
493  * @return ID of current element or 0 if element is end.
494  */
495 unsigned int
497 {
498  if (cur == NULL)
499  return 0;
500  return cur->msg_id;
501 }
502 
503 } // end namespace fawkes
Base class for exceptions in Fawkes.
Definition: exception.h:36
Message already enqueued exception.
Definition: message_queue.h:36
Message * operator->() const
Act on current message.
MessageIterator & operator+(unsigned int i)
Advance by a certain amount.
MessageIterator & operator++()
Increment iterator.
bool operator==(const MessageIterator &c) const
Check equality of two iterators.
bool operator!=(const MessageIterator &c) const
Check inequality of two iterators.
Message * operator*() const
Get memory pointer of chunk.
MessageIterator & operator+=(unsigned int i)
Advance by a certain amount.
MessageIterator & operator=(const MessageIterator &c)
Assign iterator.
unsigned int id() const
Get ID of current element or 0 if element is end.
MessageIterator end()
Get iterator to element beyond end of message queue list.
void append(Message *msg)
Append message to queue.
MessageQueue()
Constructor.
void unlock()
Unlock message queue.
void pop()
Erase first message from queue.
void flush()
Delete all messages from queue.
void lock()
Lock message queue.
void remove(const Message *msg)
Remove message from queue.
bool empty() const
Check if message queue is empty.
MessageIterator begin()
Get iterator to first element in message queue.
virtual ~MessageQueue()
Destructor.
bool try_lock()
Try to lock message queue.
void insert_after(const MessageIterator &it, Message *msg)
Enqueue message after given iterator.
unsigned int size() const
Get number of messages in queue.
Message * first()
Get first message from queue.
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:45
void mark_enqueued()
Mark message as being enqueued.
Definition: message.cpp:214
bool enqueued() const
Check is message has been enqueued.
Definition: message.cpp:229
unsigned int id() const
Get message ID.
Definition: message.cpp:180
Mutex mutual exclusion lock.
Definition: mutex.h:33
bool try_lock()
Tries to lock the mutex.
Definition: mutex.cpp:117
void lock()
Lock this mutex.
Definition: mutex.cpp:87
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
Operation on unlocked object.
Definition: software.h:62
A NULL pointer was supplied where not allowed.
Definition: software.h:32
Fawkes library namespace.