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