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