xref: /haiku/src/kits/app/Messenger.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2  * Copyright 2001-2007, 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 sucessfully, \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 	return looper && looper->Lock();
216 }
217 
218 
219 /*!	\brief Locks the BLooper targeted by the messenger, if the target is local.
220 
221 	This method is a shorthand for retrieving the targeted looper via
222 	Target() and calling BLooper::LockWithTimeout() on the looper afterwards.
223 
224 	\see BLooper::LockWithTimeout() for details.
225 
226 	\return
227 	- \c B_OK, if the looper could be locked sucessfully,
228 	- \c B_BAD_VALUE, if the messenger is not properly initialized,
229 	  the target is remote, or the targeted looper is invalid,
230 	- other error codes returned by BLooper::LockWithTimeout().
231 */
232 status_t
233 BMessenger::LockTargetWithTimeout(bigtime_t timeout) const
234 {
235 	BLooper *looper = NULL;
236 	Target(&looper);
237 	status_t error = looper ? B_OK : B_BAD_VALUE;
238 	if (error == B_OK)
239 		error = looper->LockWithTimeout(timeout);
240 
241 	return error;
242 }
243 
244 
245 //	#pragma mark - Message sending
246 
247 // SendMessage
248 /*! \brief Delivers a BMessage synchronously to the messenger's target.
249 
250 	The method does not wait for a reply. The message is sent asynchronously.
251 
252 	\param command The what field of the message to deliver.
253 	\param replyTo The handler to which a reply to the message shall be sent.
254 		   May be \c NULL.
255 	\return
256 	- \c B_OK: Everything went fine.
257 	- \c B_BAD_PORT_ID: The messenger is not properly initialized or its
258 	  target doesn't exist anymore.
259 */
260 status_t
261 BMessenger::SendMessage(uint32 command, BHandler *replyTo) const
262 {
263 	BMessage message(command);
264 	return SendMessage(&message, replyTo);
265 }
266 
267 // SendMessage
268 /*! \brief Delivers a BMessage synchronously to the messenger's target.
269 
270 	A copy of the supplied message is sent and the caller retains ownership
271 	of \a message.
272 
273 	The method does not wait for a reply. The message is sent asynchronously.
274 
275 	\param message The message to be sent.
276 	\param replyTo The handler to which a reply to the message shall be sent.
277 		   May be \c NULL.
278 	\param timeout A timeout for the delivery of the message.
279 	\return
280 	- \c B_OK: Everything went fine.
281 	- \c B_BAD_PORT_ID: The messenger is not properly initialized or its
282 	  target doesn't exist anymore.
283 	- \c B_WOULD_BLOCK: A delivery timeout of 0 was supplied and the target
284 	  port was full when trying to deliver the message.
285 	- \c B_TIMED_OUT: The timeout expired while trying to deliver the
286 	  message.
287 */
288 status_t
289 BMessenger::SendMessage(BMessage *message, BHandler *replyTo,
290 						bigtime_t timeout) const
291 {
292 DBG(OUT("BMessenger::SendMessage2(%.4s)\n", (char*)&message->what));
293 	status_t error = (message ? B_OK : B_BAD_VALUE);
294 	if (error == B_OK) {
295 		BMessenger replyMessenger(replyTo);
296 		error = SendMessage(message, replyMessenger, timeout);
297 	}
298 DBG(OUT("BMessenger::SendMessage2() done: %lx\n", error));
299 	return error;
300 }
301 
302 // SendMessage
303 /*! \brief Delivers a BMessage synchronously to the messenger's target.
304 
305 	A copy of the supplied message is sent and the caller retains ownership
306 	of \a message.
307 
308 	The method does not wait for a reply. The message is sent asynchronously.
309 
310 	\param message The message to be sent.
311 	\param replyTo A messenger specifying the target for a reply to \a message.
312 	\param timeout A timeout for the delivery of the message.
313 	\return
314 	- \c B_OK: Everything went fine.
315 	- \c B_BAD_PORT_ID: The messenger is not properly initialized or its
316 	  target doesn't exist anymore.
317 	- \c B_WOULD_BLOCK: A delivery timeout of 0 was supplied and the target
318 	  port was full when trying to deliver the message.
319 	- \c B_TIMED_OUT: The timeout expired while trying to deliver the
320 	  message.
321 */
322 status_t
323 BMessenger::SendMessage(BMessage *message, BMessenger replyTo,
324 						bigtime_t timeout) const
325 {
326 	if (!message)
327 		return B_BAD_VALUE;
328 
329 	return BMessage::Private(message).SendMessage(fPort, fTeam, fHandlerToken,
330 		timeout, false, replyTo);
331 }
332 
333 // SendMessage
334 /*! \brief Delivers a BMessage synchronously to the messenger's target and
335 	waits for a reply.
336 
337 	The method does wait for a reply. The reply message is copied into
338 	\a reply. If the target doesn't send a reply, the \c what field of
339 	\a reply is set to \c B_NO_REPLY.
340 
341 	\param command The what field of the message to deliver.
342 	\param reply A pointer to a pre-allocated BMessage into which the reply
343 		   message will be copied.
344 	\return
345 	- \c B_OK: Everything went fine.
346 	- \c B_BAD_PORT_ID: The messenger is not properly initialized or its
347 	  target doesn't exist anymore.
348 	- \c B_NO_MORE_PORTS: All reply ports are in use.
349 */
350 status_t
351 BMessenger::SendMessage(uint32 command, BMessage *reply) const
352 {
353 	BMessage message(command);
354 	return SendMessage(&message, reply);
355 }
356 
357 // SendMessage
358 /*! \brief Delivers a BMessage synchronously to the messenger's target and
359 	waits for a reply.
360 
361 	A copy of the supplied message is sent and the caller retains ownership
362 	of \a message.
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 or if a reply timeout occurs,
366 	the \c what field of \a reply is set to \c B_NO_REPLY.
367 
368 	\param message The message to be sent.
369 	\param reply A pointer to a pre-allocated BMessage into which the reply
370 		   message will be copied.
371 	\param deliveryTimeout A timeout for the delivery of the message.
372 	\param replyTimeout A timeout for waiting for the reply.
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_WOULD_BLOCK: A delivery timeout of 0 was supplied and the target
378 	  port was full when trying to deliver the message.
379 	- \c B_TIMED_OUT: The timeout expired while trying to deliver the
380 	  message.
381 	- \c B_NO_MORE_PORTS: All reply ports are in use.
382 */
383 status_t
384 BMessenger::SendMessage(BMessage *message, BMessage *reply,
385 	bigtime_t deliveryTimeout, bigtime_t replyTimeout) const
386 {
387 	if (message == NULL || reply == NULL)
388 		return B_BAD_VALUE;
389 
390 	status_t error = BMessage::Private(message).SendMessage(fPort, fTeam,
391 		fHandlerToken, reply, deliveryTimeout, replyTimeout);
392 
393 	// Map this error for now:
394 	if (error == B_BAD_TEAM_ID)
395 		error = B_BAD_PORT_ID;
396 
397 	return error;
398 }
399 
400 
401 //	#pragma mark - Operators and misc
402 
403 
404 /*!	\brief Makes this BMessenger a copy of the supplied one.
405 
406 	\param from the messenger to be copied.
407 	\return A reference to this object.
408 */
409 BMessenger &
410 BMessenger::operator=(const BMessenger &from)
411 {
412 	if (this != &from) {
413 		fPort = from.fPort;
414 		fHandlerToken = from.fHandlerToken;
415 		fTeam = from.fTeam;
416 	}
417 	return *this;
418 }
419 
420 
421 /*!	\brief Returns whether this and the supplied messenger have the same
422 	target.
423 
424 	\param other The other messenger.
425 	\return \c true, if the messengers have the same target or if both aren't
426 			properly initialzed, \c false otherwise.
427 */
428 bool
429 BMessenger::operator==(const BMessenger &other) const
430 {
431 	// Note: The fTeam fields are not compared.
432 	return fPort == other.fPort
433 		&& fHandlerToken == other.fHandlerToken;
434 }
435 
436 
437 /*!	\brief Returns whether the messenger's target looper does still exist.
438 
439 	It is not checked whether the target handler is also still existing.
440 
441 	\return \c true, if the messenger's target looper does still exist,
442 			\c false otherwise.
443 */
444 bool
445 BMessenger::IsValid() const
446 {
447 	port_info info;
448 	return fPort >= 0 && get_port_info(fPort, &info) == B_OK;
449 }
450 
451 
452 /*!	\brief Returns the ID of the team the messenger's target lives in.
453 
454 	\return The team of the messenger's target.
455 */
456 team_id
457 BMessenger::Team() const
458 {
459 	return fTeam;
460 }
461 
462 
463 //	#pragma mark - Private or reserved
464 
465 
466 /*!	\brief Sets the messenger's team, target looper port and handler token.
467 
468 	To target the preferred handler, use B_PREFERRED_TOKEN as token.
469 
470 	\param team The target's team.
471 	\param port The target looper port.
472 	\param token The target handler token.
473 */
474 void
475 BMessenger::_SetTo(team_id team, port_id port, int32 token)
476 {
477 	fTeam = team;
478 	fPort = port;
479 	fHandlerToken = token;
480 }
481 
482 
483 /*!	\brief Initializes the BMessenger object's data given the signature and/or
484 	team ID of a target.
485 
486 	When only a signature is given, and multiple instances of the application
487 	are running it is undeterminate which one is chosen as the target. In case
488 	only a team ID is passed, the target application is identified uniquely.
489 	If both are supplied, the application identified by the team ID must have
490 	a matching signature, otherwise the initilization fails.
491 
492 	\param signature The target application's signature. May be \c NULL.
493 	\param team The target application's team ID. May be < 0.
494 	\param result An optional pointer to a pre-allocated status_t into which
495 		   the result of the initialization is written.
496 */
497 void
498 BMessenger::_InitData(const char* signature, team_id team, status_t* _result)
499 {
500 	status_t error = B_OK;
501 	// get an app_info
502 	app_info info;
503 	if (team < 0) {
504 		// no team ID given
505 		if (signature) {
506 			error = be_roster->GetAppInfo(signature, &info);
507 			team = info.team;
508 			// B_ERROR means that no application with the given signature
509 			// is running. But we are supposed to return B_BAD_VALUE.
510 			if (error == B_ERROR)
511 				error = B_BAD_VALUE;
512 		} else
513 			error = B_BAD_TYPE;
514 	} else {
515 		// a team ID is given
516 		error = be_roster->GetRunningAppInfo(team, &info);
517 		// Compare the returned signature with the supplied one.
518 		if (error == B_OK && signature && strcasecmp(signature, info.signature))
519 			error = B_MISMATCHED_VALUES;
520 	}
521 	// check whether the app flags say B_ARGV_ONLY
522 	if (error == B_OK && (info.flags & B_ARGV_ONLY)) {
523 		error = B_BAD_TYPE;
524 		// Set the team ID nevertheless -- that's what Be's implementation
525 		// does. Don't know, if that is a bug, but at least it doesn't harm.
526 		fTeam = team;
527 	}
528 	// init our members
529 	if (error == B_OK) {
530 		fTeam = team;
531 		fPort = info.port;
532 		fHandlerToken = B_PREFERRED_TOKEN;
533 	}
534 
535 	// return the error
536 	if (_result)
537 		*_result = error;
538 }
539 
540 
541 /*!	\brief Returns whether the first one of two BMessengers is less than the
542 	second one.
543 
544 	This method defines an order on BMessengers based on their member
545 	variables \c fPort, \c fHandlerToken and \c fPreferredTarget.
546 
547 	\param a The first messenger.
548 	\param b The second messenger.
549 	\return \c true, if \a a is less than \a b, \c false otherwise.
550 */
551 bool
552 operator<(const BMessenger &_a, const BMessenger &_b)
553 {
554 	BMessenger::Private a(const_cast<BMessenger&>(_a));
555 	BMessenger::Private b(const_cast<BMessenger&>(_b));
556 
557 	// significance:
558 	// 1. fPort
559 	// 2. fHandlerToken
560 	// 3. fPreferredTarget
561 	// fTeam is insignificant
562 	return (a.Port() < b.Port()
563 			|| a.Port() == b.Port()
564 				&& (a.Token() < b.Token()
565 					|| a.Token() == b.Token()
566 						&& !a.IsPreferredTarget()
567 						&& b.IsPreferredTarget()));
568 }
569 
570 
571 /*!	\brief Returns whether two BMessengers have not the same target.
572 
573 	\param a The first messenger.
574 	\param b The second messenger.
575 	\return \c false, if \a a and \a b have the same targets or are both not
576 			properly initialized, \c true otherwise.
577 */
578 bool
579 operator!=(const BMessenger &a, const BMessenger &b)
580 {
581 	return !(a == b);
582 }
583 
584