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