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