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