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