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