xref: /haiku/src/kits/app/MessageQueue.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 //
2 //  $Id: MessageQueue.cpp,v 1.3 2003/05/14 17:21:46 haydentech Exp $
3 //
4 //  This file contains the implementation of the BMessageQueue class
5 //  for the OpenBeOS project.
6 //
7 
8 
9 #include <MessageQueue.h>
10 #include <Autolock.h>
11 #include <Message.h>
12 
13 #ifdef USE_OPENBEOS_NAMESPACE
14 namespace OpenBeOS {
15 #endif
16 
17 
18 /*
19  *  Method: BMessageQueue::BMessageQueue()
20  *   Descr: This method is the only constructor for a BMessageQueue.  Once the
21  *          constructor completes, the BMessageQueue is created with no BMessages
22  *          in it.
23  *
24  */
25 BMessageQueue::BMessageQueue() :
26 	fTheQueue(NULL), fQueueTail(NULL), fMessageCount(0)
27 {
28 }
29 
30 
31 /*
32  *  Method: BMessageQueue::~BMessageQueue()
33  *   Descr: This is the desctructor for the BMessageQueue.  It iterates over
34  *          any messages left on the queue and deletes them.
35  *
36  *		    The implementation is careful not to release the lock when the
37  *          BMessageQueue is deconstructed.  If the lock is released, it is
38  *          possible another thread will start an AddMessage() operation before
39  *          the BLocker is deleted.  The safe thing to do is not to unlock the
40  *          BLocker from the destructor once it is acquired. That way, any thread
41  *          waiting to do a AddMessage() will fail to acquire the lock since the
42  *          BLocker will be deleted before they can acquire it.
43  *
44  */
45 BMessageQueue::~BMessageQueue()
46 {
47 	if (fLocker.Lock()) {
48 		BMessage *theMessage = fTheQueue;
49 		while (theMessage != NULL) {
50 			BMessage *messageToDelete = theMessage;
51 			theMessage = theMessage->link;
52 			delete messageToDelete;
53 		}
54 	}
55 }
56 
57 
58 /*
59  *  Method: BMessageQueue::AddMessage()
60  *   Descr: This method adds a BMessage to the queue.  It makes a couple of
61  *          assumptions:
62  *             - The BMessage was allocated on the heap with new.  Since the
63  *               destructor delete's BMessages left on the queue, this must be
64  *               true.  The same assumption is made with Be's implementation.
65  *             - The BMessage is not already on this or any other BMessageQueue.
66  *               If it is, the queue it is already on will be corrupted.  Be's
67  *               implementation makes this assumption also and does corrupt
68  *               BMessageQueues where this is violated.
69  *
70  */
71 void
72 BMessageQueue::AddMessage(BMessage *message)
73 {
74 	if (message == NULL) {
75 		return;
76 	}
77 
78 	// The Be implementation does not seem to check that the lock acquisition
79 	// was successful.  This will specifically cause problems when the
80 	// message queue is deleted.  On delete, any thread waiting for the lock
81 	// will be notified that the lock failed.  Be's implementation, because
82 	// they do not check proceeds with the operation, potentially corrupting
83 	// memory.  This implementation is different, but I can't imagine that
84 	// Be's implementation is worth emulating.
85 	//
86 	BAutolock theAutoLocker(fLocker);
87 
88 	if (theAutoLocker.IsLocked()) {
89 
90 		// The message passed in will be the last message on the queue so its
91 		// link member should be set to null.
92 		message->link = NULL;
93 
94 		// We now have one more BMessage on the queue.
95 		fMessageCount++;
96 
97 		// If there are no BMessages on the queue.
98 		if (fQueueTail == NULL) {
99 			// Then this message is both the start and the end of the queue.
100 			fTheQueue = message;
101 			fQueueTail = message;
102 		} else {
103 			// If there are already messages on the queue, then the put this
104 			// BMessage at the end.  The last BMessage prior to this AddMessage()
105 			// is fQueueTail.  The BMessage at fQueueTail needs to point to the
106 			// new last message, the one being added.
107 			fQueueTail->link = message;
108 
109 			// Now update the fQueueTail to point to this new last message.
110 			fQueueTail = message;
111 		}
112 	}
113 }
114 
115 
116 /*
117  *  Method: BMessageQueue::RemoveMessage()
118  *   Descr: This method searches the queue for a particular BMessage.  If
119  *          it is found, it is removed from the queue.
120  *
121  */
122 void
123 BMessageQueue::RemoveMessage(BMessage *message)
124 {
125 	if (message == NULL) {
126 		return;
127 	}
128 
129 	BAutolock theAutoLocker(fLocker);
130 
131 	// The Be implementation does not seem to check that the lock acquisition
132 	// was successful.  This will specifically cause problems when the
133 	// message queue is deleted.  On delete, any thread waiting for the lock
134 	// will be notified that the lock failed.  Be's implementation, because
135 	// they do not check proceeds with the operation, potentially corrupting
136 	// memory.  This implementation is different, but I can't imagine that
137 	// Be's implementation is worth emulating.
138 	//
139 	if (theAutoLocker.IsLocked()) {
140 
141 		// If the message to be removed is at the front of the queue.
142 		if (fTheQueue == message) {
143 			// We need to special case the handling of removing the first element.
144 			// First, the new front element will be the next one.
145 			fTheQueue = fTheQueue->link;
146 
147 			// Must decrement the count of elements since the front one is being
148 			// removed.
149 			fMessageCount--;
150 
151 			// If the new front element is NULL, then that means that the queue
152 			// is now empty.  That means that fQueueTail must be set to NULL.
153 			if (fTheQueue == NULL) {
154 				fQueueTail = NULL;
155 			}
156 
157 			// We have found the message and removed it in this case.  We can
158 			// bail out now.  The autolocker will take care of releasing the
159 			// lock for us.
160 			return;
161 		}
162 
163 		// The message to remove is not the first one, so we need to scan the
164 		// queue.  Get a message iterator and set it to the first element.
165 		BMessage *messageIter = fTheQueue;
166 
167 		// While we are not at the end of the list.
168 		while (messageIter != NULL) {
169 			// If the next message after this (ie second, then third etc) is
170 			// the one we are looking for.
171 			if (messageIter->link == message) {
172 				// At this point, this is what we have:
173 				//    messageIter - the BMessage in the queue just before the
174 				//                  match
175 				//    messageIter->link - the BMessage which matches message
176 				//    message - the same as messageIter->link
177 				//    message->link - the element after the match
178 				//
179 				// The next step is to link the BMessage just before the match
180 				// to the one just after the match.  This removes the match from
181 				// the queue.
182 				messageIter->link = message->link;
183 
184 				// One less element on the queue.
185 				fMessageCount--;
186 
187 				// If there is no BMessage after the match is the
188 				if (message->link == NULL) {
189 					// That means that we just removed the last element from the
190 					// queue.  The new last element then must be messageIter.
191 					fQueueTail = messageIter;
192 				}
193 
194 				// We can return now because we have a match and removed it.
195 				return;
196 			}
197 
198 			// No match yet, go to the next element in the list.
199 			messageIter = messageIter->link;
200 		}
201 	}
202 }
203 
204 
205 /*
206  *  Method: BMessageQueue::CountMessages()
207  *   Descr: This method just returns the number of BMessages on the queue.
208  */
209 int32
210 BMessageQueue::CountMessages(void) const
211 {
212     return fMessageCount;
213 }
214 
215 
216 /*
217  *  Method: BMessageQueue::IsEmpty()
218  *   Descr: This method just returns true if there are no BMessages on the queue.
219  */
220 bool
221 BMessageQueue::IsEmpty(void) const
222 {
223     return (fMessageCount == 0);
224 }
225 
226 
227 /*
228  *  Method: BMessageQueue::FindMessage()
229  *   Descr: This method searches the queue for the index'th BMessage.  The first
230  *          BMessage is at index 0, the second at index 1 etc.  The BMessage
231  *          is returned if it is found.  If no BMessage exists at that index
232  *          (ie the queue is not that long or the index is invalid) NULL is
233  *          returned.
234  *
235  *          This method does not lock the BMessageQueue so there is risk that
236  *          the queue could change in the course of the search.  Be's
237  *          implementation must do the same, unless they do some funky casting.
238  *          The method is declared const which means it cannot modify the data
239  *          members.  Because it cannot modify the data members, it cannot
240  *          acquire a lock.  So unless they are casting away the const-ness
241  *          of the this pointer, this member in Be's implementation does no
242  *          locking either.
243  */
244 BMessage *
245 BMessageQueue::FindMessage(int32 index) const
246 {
247 	// If the index is negative or larger than the number of messages on the
248 	// queue.
249 	if ((index < 0) || (index >= fMessageCount)) {
250 		// No match is possible, bail out now.
251 		return NULL;
252 	}
253 
254 	// Get a message iterator and initialize it to the start of the queue.
255 	BMessage *messageIter = fTheQueue;
256 
257 	// While this is not the end of the queue.
258 	while (messageIter != NULL) {
259 		// If the index reaches zero, then we have found a match.
260 		if (index == 0) {
261 			// Because this is a match, break out of the while loop so we can
262 			// return the message pointed to messageIter.
263 			break;
264 		}
265 
266 		// No match yet, decrement the index.  We will have a match once index
267 		// reaches zero.
268 		index--;
269 		// Increment the messageIter to the next BMessage on the queue.
270 		messageIter = messageIter->link;
271 	}
272 
273 	// If no match was found, messageIter will be NULL since that is the only
274 	// way out of the loop.  If a match was found, the messageIter will point
275 	// to that match.
276     return messageIter;
277 }
278 
279 
280 /*
281  *  Method: BMessageQueue::FindMessage()
282  *   Descr: This method searches the queue for the index'th BMessage that has a
283  *          particular what code.  The first BMessage with that what value is at
284  *          index 0, the second at index 1 etc.  The BMessage is returned if it
285  *          is found.  If no matching BMessage exists at that index NULL is
286  *          returned.
287  *
288  *          This method does not lock the BMessageQueue so there is risk that
289  *          the queue could change in the course of the search.  Be's
290  *          implementation must do the same, unless they do some funky casting.
291  *          The method is declared const which means it cannot modify the data
292  *          members.  Because it cannot modify the data members, it cannot
293  *          acquire a lock.  So unless they are casting away the const-ness
294  *          of the this pointer, this member in Be's implementation does no
295  *          locking either.
296  */
297 BMessage *
298 BMessageQueue::FindMessage(uint32 what,
299                            int32 index) const
300 {
301 	// If the index is negative or larger than the number of messages on the
302 	// queue.
303 	if ((index < 0) || (index >= fMessageCount)) {
304 		// No match is possible, bail out now.
305 		return NULL;
306 	}
307 
308 	// Get a message iterator and initialize it to the start of the queue.
309 	BMessage *messageIter = fTheQueue;
310 
311 	// While this is not the end of the queue.
312 	while (messageIter != NULL) {
313 		// If the messageIter points to a BMessage with the what code we are
314 		// looking for.
315 		if (messageIter->what == what) {
316 			// If the index reaches zero, then we have found a match.
317 			if (index == 0) {
318 				// Because this is a match, break out of the while loop so we can
319 				// return the message pointed to messageIter.
320 				break;
321 			}
322 			// No match yet, decrement the index.  We will have a match once index
323 			// reaches zero.
324 			index--;
325 		}
326 		// Increment the messageIter to the next BMessage on the queue.
327 		messageIter = messageIter->link;
328 	}
329 
330 	// If no match was found, messageIter will be NULL since that is the only
331 	// way out of the loop.  If a match was found, the messageIter will point
332 	// to that match.
333     return messageIter;
334 }
335 
336 
337 /*
338  *  Method: BMessageQueue::Lock()
339  *   Descr: This member just locks the BMessageQueue so no other thread can acquire
340  *          the lock nor make changes to the queue through members like
341  *          AddMessage(), RemoveMessage(), NextMessage() or ~BMessageQueue().
342  */
343 bool
344 BMessageQueue::Lock(void)
345 {
346     return fLocker.Lock();
347 }
348 
349 
350 /*
351  *  Method: BMessageQueue::Unlock()
352  *   Descr: This member releases the lock which was acquired by Lock().
353  */
354 void
355 BMessageQueue::Unlock(void)
356 {
357 	fLocker.Unlock();
358 }
359 
360 
361 /*
362  *  Method: BMessageQueue::IsLocked()
363  *   Descr: This member returns whether or not the queue is locked
364  */
365 bool
366 BMessageQueue::IsLocked(void)
367 {
368 	return fLocker.IsLocked();
369 }
370 
371 
372 /*
373  *  Method: BMessageQueue::NextMessage()
374  *   Descr: This member removes the first BMessage on the queue and returns
375  *          it to the caller.  If the queue is empty, NULL is returned.
376  */
377 BMessage *
378 BMessageQueue::NextMessage(void)
379 {
380 	// By default, we will assume that no BMessage is on the queue.
381 	BMessage *result = NULL;
382 	BAutolock theAutoLocker(fLocker);
383 
384 	// The Be implementation does not seem to check that the lock acquisition
385 	// was successful.  This will specifically cause problems when the
386 	// message queue is deleted.  On delete, any thread waiting for the lock
387 	// will be notified that the lock failed.  Be's implementation, because
388 	// they do not check proceeds with the operation, potentially corrupting
389 	// memory.  This implementation is different, but I can't imagine that
390 	// Be's implementation is worth emulating.
391 	//
392 	if (theAutoLocker.IsLocked()) {
393 		// Store the first BMessage in the queue in result.
394 		result = fTheQueue;
395 
396 		// If the queue is not empty.
397 		if (fTheQueue != NULL) {
398 			// Decrement the message count since we are removing an element.
399 			fMessageCount--;
400 			// The new front of the list is moved forward thereby removing the
401 			// first element from the queue.
402 			fTheQueue = fTheQueue->link;
403 			// If the queue is empty after removing the front element.
404 			if (fTheQueue == NULL) {
405 				// We need to set the tail of the queue to NULL since the queue
406 				// is now empty.
407 				fQueueTail = NULL;
408 			}
409 		}
410 	}
411     return result;
412 }
413 
414 
415 void
416 BMessageQueue::_ReservedMessageQueue1(void)
417 {
418 }
419 
420 
421 void
422 BMessageQueue::_ReservedMessageQueue2(void)
423 {
424 }
425 
426 
427 void
428 BMessageQueue::_ReservedMessageQueue3(void)
429 {
430 }
431 
432 #ifdef USE_OPENBEOS_NAMESPACE
433 }
434 #endif
435