1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2005, Haiku 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: Messenger.h 23 // Author: Ingo Weinhold (bonefish@users.sf.net) 24 // Description: BMessenger delivers messages to local or remote targets. 25 //------------------------------------------------------------------------------ 26 27 // debugging 28 //#define DBG(x) x 29 #define DBG(x) 30 #define OUT printf 31 32 // Standard Includes ----------------------------------------------------------- 33 #include <new> 34 #include <stdio.h> 35 #include <string.h> 36 37 // System Includes ------------------------------------------------------------- 38 #include <Application.h> 39 #include <Handler.h> 40 #include <Looper.h> 41 #include <LooperList.h> 42 #include <Message.h> 43 #include <Messenger.h> 44 #include <OS.h> 45 #include <Roster.h> 46 #include <TokenSpace.h> 47 48 // Project Includes ------------------------------------------------------------ 49 #include <AppMisc.h> 50 #include <MessageUtils.h> 51 #include "ObjectLocker.h" 52 #include "TokenSpace.h" 53 54 #ifdef USING_MESSAGE4 55 #include <MessagePrivate.h> 56 #endif 57 58 // Local Includes -------------------------------------------------------------- 59 60 // Local Defines --------------------------------------------------------------- 61 62 // Globals --------------------------------------------------------------------- 63 64 using BPrivate::gDefaultTokens; 65 using BPrivate::gLooperList; 66 using BPrivate::BLooperList; 67 using BPrivate::BObjectLocker; 68 69 enum { 70 NOT_IMPLEMENTED = B_ERROR, 71 }; 72 73 74 /*! \brief Creates an unitialized BMessenger. 75 */ 76 BMessenger::BMessenger() 77 : 78 fPort(-1), 79 fHandlerToken(B_NULL_TOKEN), 80 fTeam(-1) 81 { 82 } 83 84 85 /*! \brief Creates a BMessenger and initializes it to target the already 86 running application identified by its signature and/or team ID. 87 88 When only a signature is given, and multiple instances of the application 89 are running it is undeterminate which one is chosen as the target. In case 90 only a team ID is passed, the target application is identified uniquely. 91 If both are supplied, the application identified by the team ID must have 92 a matching signature, otherwise the initilization fails. 93 94 \param signature The target application's signature. May be \c NULL. 95 \param team The target application's team ID. May be < 0. 96 \param result An optional pointer to a pre-allocated status_t into which 97 the result of the initialization is written. 98 */ 99 BMessenger::BMessenger(const char *signature, team_id team, status_t *result) 100 : 101 fPort(-1), 102 fHandlerToken(B_NULL_TOKEN), 103 fTeam(-1) 104 { 105 _InitData(signature, team, result); 106 } 107 108 109 /*! \brief Creates a BMessenger and initializes it to target the local 110 BHandler and/or BLooper. 111 112 When a \c NULL handler is supplied, the preferred handler in the given 113 looper is targeted. If no looper is supplied the looper the given handler 114 belongs to is used -- that means in particular, that the handler must 115 already belong to a looper. If both are supplied the handler must actually 116 belong to looper. 117 118 \param handler The target handler. May be \c NULL. 119 \param looper The target looper. May be \c NULL. 120 \param result An optional pointer to a pre-allocated status_t into which 121 the result of the initialization is written. 122 */ 123 BMessenger::BMessenger(const BHandler* handler, const BLooper* looper, 124 status_t* _result) 125 : 126 fPort(-1), 127 fHandlerToken(B_NULL_TOKEN), 128 fTeam(-1) 129 { 130 status_t error = (handler || looper ? B_OK : B_BAD_VALUE); 131 if (error == B_OK) { 132 if (handler) { 133 // BHandler is given, check/retrieve the looper. 134 if (looper) { 135 if (handler->Looper() != looper) 136 error = B_MISMATCHED_VALUES; 137 } else { 138 looper = handler->Looper(); 139 if (looper == NULL) 140 error = B_MISMATCHED_VALUES; 141 } 142 } 143 // set port, token,... 144 if (error == B_OK) { 145 BObjectLocker<BLooperList> locker(gLooperList); 146 if (locker.IsLocked() && gLooperList.IsLooperValid(looper)) { 147 fPort = looper->fMsgPort; 148 fHandlerToken = (handler 149 ? _get_object_token_(handler) : B_PREFERRED_TOKEN); 150 fTeam = looper->Team(); 151 } else 152 error = B_BAD_VALUE; 153 } 154 } 155 if (_result) 156 *_result = error; 157 } 158 159 160 /*! \brief Creates a BMessenger and initializes it to have the same target 161 as the supplied messemger. 162 163 \param from The messenger to be copied. 164 */ 165 BMessenger::BMessenger(const BMessenger& from) 166 : 167 fPort(from.fPort), 168 fHandlerToken(from.fHandlerToken), 169 fTeam(from.fTeam) 170 { 171 } 172 173 174 /*! \brief Frees all resources associated with this object. 175 */ 176 BMessenger::~BMessenger() 177 { 178 } 179 180 181 // #pragma mark - Target 182 183 184 /*! \brief Returns whether or not the messenger's target lives within the team 185 of the caller. 186 187 \return \c true, if the object is properly initialized and its target 188 lives within the caller's team, \c false otherwise. 189 */ 190 bool 191 BMessenger::IsTargetLocal() const 192 { 193 thread_info info; 194 return get_thread_info(find_thread(NULL), &info) == B_OK 195 && fTeam == info.team; 196 } 197 198 199 /*! \brief Returns the handler and looper targeted by the messenger, if the 200 target is local. 201 202 The handler is returned directly, the looper by reference. If both are 203 \c NULL, the object is either not properly initialized, the target 204 objects have been deleted or the target is remote. If only the returned 205 handler is \c NULL, either the looper's preferred handler is targeted or 206 the handler has been deleted. 207 208 \param looper A pointer to a pre-allocated BLooper pointer into which 209 the pointer to the targeted looper is written. 210 \return The BHandler targeted by the messenger. 211 */ 212 BHandler * 213 BMessenger::Target(BLooper** _looper) const 214 { 215 BHandler *handler = NULL; 216 if (IsTargetLocal() && fHandlerToken > B_NULL_TOKEN) { 217 gDefaultTokens.GetToken(fHandlerToken, B_HANDLER_TOKEN, 218 (void**)&handler); 219 if (_looper) 220 *_looper = BPrivate::gLooperList.LooperForPort(fPort); 221 } else if (_looper) 222 *_looper = NULL; 223 224 return handler; 225 } 226 227 228 /*! \brief Locks the BLooper targeted by the messenger, if the target is local. 229 230 This method is a shorthand for retrieving the targeted looper via 231 Target() and calling BLooper::Lock() on the looper afterwards. 232 233 \see BLooper::Lock() for details. 234 235 \return \c true, if the looper could be locked sucessfully, \c false, if 236 the messenger is not properly initialized, the target is remote, 237 or the targeted looper is invalid. 238 */ 239 bool 240 BMessenger::LockTarget() const 241 { 242 BLooper *looper = NULL; 243 Target(&looper); 244 return looper && looper->Lock(); 245 } 246 247 248 /*! \brief Locks the BLooper targeted by the messenger, if the target is local. 249 250 This method is a shorthand for retrieving the targeted looper via 251 Target() and calling BLooper::LockWithTimeout() on the looper afterwards. 252 253 \see BLooper::LockWithTimeout() for details. 254 255 \return 256 - \c B_OK, if the looper could be locked sucessfully, 257 - \c B_BAD_VALUE, if the messenger is not properly initialized, 258 the target is remote, or the targeted looper is invalid, 259 - other error codes returned by BLooper::LockWithTimeout(). 260 */ 261 status_t 262 BMessenger::LockTargetWithTimeout(bigtime_t timeout) const 263 { 264 BLooper *looper = NULL; 265 Target(&looper); 266 status_t error = looper ? B_OK : B_BAD_VALUE; 267 if (error == B_OK) 268 error = looper->LockWithTimeout(timeout); 269 270 return error; 271 } 272 273 274 // #pragma mark - Message sending 275 276 // SendMessage 277 /*! \brief Delivers a BMessage synchronously to the messenger's target. 278 279 The method does not wait for a reply. The message is sent asynchronously. 280 281 \param command The what field of the message to deliver. 282 \param replyTo The handler to which a reply to the message shall be sent. 283 May be \c NULL. 284 \return 285 - \c B_OK: Everything went fine. 286 - \c B_BAD_PORT_ID: The messenger is not properly initialized or its 287 target doesn't exist anymore. 288 */ 289 status_t 290 BMessenger::SendMessage(uint32 command, BHandler *replyTo) const 291 { 292 BMessage message(command); 293 return SendMessage(&message, replyTo); 294 } 295 296 // SendMessage 297 /*! \brief Delivers a BMessage synchronously to the messenger's target. 298 299 A copy of the supplied message is sent and the caller retains ownership 300 of \a message. 301 302 The method does not wait for a reply. The message is sent asynchronously. 303 304 \param message The message to be sent. 305 \param replyTo The handler to which a reply to the message shall be sent. 306 May be \c NULL. 307 \param timeout A timeout for the delivery of the message. 308 \return 309 - \c B_OK: Everything went fine. 310 - \c B_BAD_PORT_ID: The messenger is not properly initialized or its 311 target doesn't exist anymore. 312 - \c B_WOULD_BLOCK: A delivery timeout of 0 was supplied and the target 313 port was full when trying to deliver the message. 314 - \c B_TIMED_OUT: The timeout expired while trying to deliver the 315 message. 316 */ 317 status_t 318 BMessenger::SendMessage(BMessage *message, BHandler *replyTo, 319 bigtime_t timeout) const 320 { 321 DBG(OUT("BMessenger::SendMessage2(%.4s)\n", (char*)&message->what)); 322 status_t error = (message ? B_OK : B_BAD_VALUE); 323 if (error == B_OK) { 324 BMessenger replyMessenger(replyTo); 325 error = SendMessage(message, replyMessenger, timeout); 326 } 327 DBG(OUT("BMessenger::SendMessage2() done: %lx\n", error)); 328 return error; 329 } 330 331 // SendMessage 332 /*! \brief Delivers a BMessage synchronously to the messenger's target. 333 334 A copy of the supplied message is sent and the caller retains ownership 335 of \a message. 336 337 The method does not wait for a reply. The message is sent asynchronously. 338 339 \param message The message to be sent. 340 \param replyTo A messenger specifying the target for a reply to \a message. 341 \param timeout A timeout for the delivery of the message. 342 \return 343 - \c B_OK: Everything went fine. 344 - \c B_BAD_PORT_ID: The messenger is not properly initialized or its 345 target doesn't exist anymore. 346 - \c B_WOULD_BLOCK: A delivery timeout of 0 was supplied and the target 347 port was full when trying to deliver the message. 348 - \c B_TIMED_OUT: The timeout expired while trying to deliver the 349 message. 350 */ 351 status_t 352 BMessenger::SendMessage(BMessage *message, BMessenger replyTo, 353 bigtime_t timeout) const 354 { 355 if (!message) 356 return B_BAD_VALUE; 357 358 #ifndef USING_MESSAGE4 359 return message->_send_(fPort, fHandlerToken, timeout, false, replyTo); 360 #else 361 return BMessage::Private(message).SendMessage(fPort, fHandlerToken, 362 timeout, false, replyTo); 363 #endif 364 } 365 366 // SendMessage 367 /*! \brief Delivers a BMessage synchronously to the messenger's target and 368 waits for a reply. 369 370 The method does wait for a reply. The reply message is copied into 371 \a reply. If the target doesn't send a reply, the \c what field of 372 \a reply is set to \c B_NO_REPLY. 373 374 \param command The what field of the message to deliver. 375 \param reply A pointer to a pre-allocated BMessage into which the reply 376 message will be copied. 377 \return 378 - \c B_OK: Everything went fine. 379 - \c B_BAD_PORT_ID: The messenger is not properly initialized or its 380 target doesn't exist anymore. 381 - \c B_NO_MORE_PORTS: All reply ports are in use. 382 */ 383 status_t 384 BMessenger::SendMessage(uint32 command, BMessage *reply) const 385 { 386 BMessage message(command); 387 return SendMessage(&message, reply); 388 } 389 390 // SendMessage 391 /*! \brief Delivers a BMessage synchronously to the messenger's target and 392 waits for a reply. 393 394 A copy of the supplied message is sent and the caller retains ownership 395 of \a message. 396 397 The method does wait for a reply. The reply message is copied into 398 \a reply. If the target doesn't send a reply or if a reply timeout occurs, 399 the \c what field of \a reply is set to \c B_NO_REPLY. 400 401 \param message The message to be sent. 402 \param reply A pointer to a pre-allocated BMessage into which the reply 403 message will be copied. 404 \param deliveryTimeout A timeout for the delivery of the message. 405 \param replyTimeout A timeout for waiting for the reply. 406 \return 407 - \c B_OK: Everything went fine. 408 - \c B_BAD_PORT_ID: The messenger is not properly initialized or its 409 target doesn't exist anymore. 410 - \c B_WOULD_BLOCK: A delivery timeout of 0 was supplied and the target 411 port was full when trying to deliver the message. 412 - \c B_TIMED_OUT: The timeout expired while trying to deliver the 413 message. 414 - \c B_NO_MORE_PORTS: All reply ports are in use. 415 */ 416 status_t 417 BMessenger::SendMessage(BMessage *message, BMessage *reply, 418 bigtime_t deliveryTimeout, bigtime_t replyTimeout) const 419 { 420 status_t error = (message && reply ? B_OK : B_BAD_VALUE); 421 if (error == B_OK) { 422 #ifndef USING_MESSAGE4 423 error = message->send_message(fPort, fTeam, fHandlerToken, 424 reply, deliveryTimeout, replyTimeout); 425 #else 426 error = BMessage::Private(message).SendMessage(fPort, fTeam, 427 fHandlerToken, reply, deliveryTimeout, replyTimeout); 428 #endif 429 // Map this error for now: 430 if (error == B_BAD_TEAM_ID) 431 error = B_BAD_PORT_ID; 432 } 433 return error; 434 } 435 436 437 // #pragma mark - Operators and misc 438 439 440 /*! \brief Makes this BMessenger a copy of the supplied one. 441 442 \param from the messenger to be copied. 443 \return A reference to this object. 444 */ 445 BMessenger & 446 BMessenger::operator=(const BMessenger &from) 447 { 448 if (this != &from) { 449 fPort = from.fPort; 450 fHandlerToken = from.fHandlerToken; 451 fTeam = from.fTeam; 452 } 453 return *this; 454 } 455 456 457 /*! \brief Returns whether this and the supplied messenger have the same 458 target. 459 460 \param other The other messenger. 461 \return \c true, if the messengers have the same target or if both aren't 462 properly initialzed, \c false otherwise. 463 */ 464 bool 465 BMessenger::operator==(const BMessenger &other) const 466 { 467 // Note: The fTeam fields are not compared. 468 return fPort == other.fPort 469 && fHandlerToken == other.fHandlerToken; 470 } 471 472 473 /*! \brief Returns whether the messenger's target looper does still exist. 474 475 It is not checked whether the target handler is also still existing. 476 477 \return \c true, if the messenger's target looper does still exist, 478 \c false otherwise. 479 */ 480 bool 481 BMessenger::IsValid() const 482 { 483 port_info info; 484 return fPort >= 0 && get_port_info(fPort, &info) == B_OK; 485 } 486 487 488 /*! \brief Returns the ID of the team the messenger's target lives in. 489 490 \return The team of the messenger's target. 491 */ 492 team_id 493 BMessenger::Team() const 494 { 495 return fTeam; 496 } 497 498 499 // #pragma mark - Private or reserved 500 501 502 /*! \brief Sets the messenger's team, target looper port and handler token. 503 504 To target the preferred handler, use B_PREFERRED_TOKEN as token. 505 506 \param team The target's team. 507 \param port The target looper port. 508 \param token The target handler token. 509 */ 510 void 511 BMessenger::_SetTo(team_id team, port_id port, int32 token) 512 { 513 fTeam = team; 514 fPort = port; 515 fHandlerToken = token; 516 } 517 518 519 /*! \brief Initializes the BMessenger object's data given the signature and/or 520 team ID of a target. 521 522 When only a signature is given, and multiple instances of the application 523 are running it is undeterminate which one is chosen as the target. In case 524 only a team ID is passed, the target application is identified uniquely. 525 If both are supplied, the application identified by the team ID must have 526 a matching signature, otherwise the initilization fails. 527 528 \param signature The target application's signature. May be \c NULL. 529 \param team The target application's team ID. May be < 0. 530 \param result An optional pointer to a pre-allocated status_t into which 531 the result of the initialization is written. 532 */ 533 void 534 BMessenger::_InitData(const char* signature, team_id team, status_t* _result) 535 { 536 status_t error = B_OK; 537 // get an app_info 538 app_info info; 539 if (team < 0) { 540 // no team ID given 541 if (signature) { 542 error = be_roster->GetAppInfo(signature, &info); 543 team = info.team; 544 // B_ERROR means that no application with the given signature 545 // is running. But we are supposed to return B_BAD_VALUE. 546 if (error == B_ERROR) 547 error = B_BAD_VALUE; 548 } else 549 error = B_BAD_TYPE; 550 } else { 551 // a team ID is given 552 error = be_roster->GetRunningAppInfo(team, &info); 553 // Compare the returned signature with the supplied one. 554 if (error == B_OK && signature && strcasecmp(signature, info.signature)) 555 error = B_MISMATCHED_VALUES; 556 } 557 // check whether the app flags say B_ARGV_ONLY 558 if (error == B_OK && (info.flags & B_ARGV_ONLY)) { 559 error = B_BAD_TYPE; 560 // Set the team ID nevertheless -- that's what Be's implementation 561 // does. Don't know, if that is a bug, but at least it doesn't harm. 562 fTeam = team; 563 } 564 // init our members 565 if (error == B_OK) { 566 fTeam = team; 567 fPort = info.port; 568 fHandlerToken = B_PREFERRED_TOKEN; 569 } 570 571 // return the error 572 if (_result) 573 *_result = error; 574 } 575 576 577 /*! \brief Returns whether the first one of two BMessengers is less than the 578 second one. 579 580 This method defines an order on BMessengers based on their member 581 variables \c fPort, \c fHandlerToken and \c fPreferredTarget. 582 583 \param a The first messenger. 584 \param b The second messenger. 585 \return \c true, if \a a is less than \a b, \c false otherwise. 586 */ 587 bool 588 operator<(const BMessenger &_a, const BMessenger &_b) 589 { 590 BMessenger::Private a(const_cast<BMessenger&>(_a)); 591 BMessenger::Private b(const_cast<BMessenger&>(_b)); 592 593 // significance: 594 // 1. fPort 595 // 2. fHandlerToken 596 // 3. fPreferredTarget 597 // fTeam is insignificant 598 return (a.Port() < b.Port() 599 || a.Port() == b.Port() 600 && (a.Token() < b.Token() 601 || a.Token() == b.Token() 602 && !a.IsPreferredTarget() 603 && b.IsPreferredTarget())); 604 } 605 606 607 /*! \brief Returns whether two BMessengers have not the same target. 608 609 \param a The first messenger. 610 \param b The second messenger. 611 \return \c false, if \a a and \a b have the same targets or are both not 612 properly initialized, \c true otherwise. 613 */ 614 bool 615 operator!=(const BMessenger &a, const BMessenger &b) 616 { 617 return !(a == b); 618 } 619 620