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