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