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