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