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