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