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