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