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