xref: /haiku/src/kits/app/Messenger.cpp (revision d7e489f80a82a0dc5974df1e780d7a908129bab4)
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