xref: /haiku/src/kits/app/Messenger.cpp (revision 60a6f1d5d7a8715cd3897dd0b626f2e4a64984a8)
1 /*
2  * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold (bonefish@users.sf.net)
7  */
8 
9 
10 #include <Messenger.h>
11 
12 #include <new>
13 #include <stdio.h>
14 #include <strings.h>
15 
16 #include <Application.h>
17 #include <AutoLocker.h>
18 #include <Handler.h>
19 #include <Looper.h>
20 #include <Message.h>
21 #include <OS.h>
22 #include <Roster.h>
23 
24 #include <AppMisc.h>
25 #include <LaunchRoster.h>
26 #include <LooperList.h>
27 #include <MessagePrivate.h>
28 #include <MessageUtils.h>
29 #include <TokenSpace.h>
30 
31 
32 // debugging
33 //#define DBG(x) x
34 #define DBG(x)
35 #define OUT	printf
36 
37 using BPrivate::gDefaultTokens;
38 using BPrivate::gLooperList;
39 using BPrivate::BLooperList;
40 
41 
42 BMessenger::BMessenger()
43 	:
44 	fPort(-1),
45 	fHandlerToken(B_NULL_TOKEN),
46 	fTeam(-1)
47 {
48 }
49 
50 
51 BMessenger::BMessenger(const char* signature, team_id team, status_t* result)
52 	:
53 	fPort(-1),
54 	fHandlerToken(B_NULL_TOKEN),
55 	fTeam(-1)
56 {
57 	_InitData(signature, team, result);
58 }
59 
60 
61 BMessenger::BMessenger(const BHandler* handler, const BLooper* looper,
62 	status_t* _result)
63 	:
64 	fPort(-1),
65 	fHandlerToken(B_NULL_TOKEN),
66 	fTeam(-1)
67 {
68 	_InitData(handler, looper, _result);
69 }
70 
71 
72 BMessenger::BMessenger(const BMessenger& other)
73 	:
74 	fPort(other.fPort),
75 	fHandlerToken(other.fHandlerToken),
76 	fTeam(other.fTeam)
77 {
78 }
79 
80 
81 BMessenger::~BMessenger()
82 {
83 }
84 
85 
86 //	#pragma mark - Target
87 
88 
89 bool
90 BMessenger::IsTargetLocal() const
91 {
92 	return BPrivate::current_team() == fTeam;
93 }
94 
95 
96 BHandler*
97 BMessenger::Target(BLooper** _looper) const
98 {
99 	BHandler* handler = NULL;
100 	if (IsTargetLocal()
101 		&& (fHandlerToken > B_NULL_TOKEN
102 			|| fHandlerToken == B_PREFERRED_TOKEN)) {
103 		gDefaultTokens.GetToken(fHandlerToken, B_HANDLER_TOKEN,
104 			(void**)&handler);
105 		if (_looper)
106 			*_looper = BPrivate::gLooperList.LooperForPort(fPort);
107 	} else if (_looper)
108 		*_looper = NULL;
109 
110 	return handler;
111 }
112 
113 
114 bool
115 BMessenger::LockTarget() const
116 {
117 	BLooper* looper = NULL;
118 	Target(&looper);
119 	if (looper != NULL && looper->Lock()) {
120 		if (looper->fMsgPort == fPort)
121 			return true;
122 
123 		looper->Unlock();
124 		return false;
125 	}
126 
127 	return false;
128 }
129 
130 
131 status_t
132 BMessenger::LockTargetWithTimeout(bigtime_t timeout) const
133 {
134 	BLooper* looper = NULL;
135 	Target(&looper);
136 	if (looper == NULL)
137 		return B_BAD_VALUE;
138 
139 	status_t result = looper->LockWithTimeout(timeout);
140 
141 	if (result == B_OK && looper->fMsgPort != fPort) {
142 		looper->Unlock();
143 		return B_BAD_PORT_ID;
144 	}
145 
146 	return result;
147 }
148 
149 
150 //	#pragma mark - Message sending
151 
152 
153 status_t
154 BMessenger::SendMessage(uint32 command, BHandler* replyTo) const
155 {
156 	BMessage message(command);
157 	return SendMessage(&message, replyTo);
158 }
159 
160 
161 status_t
162 BMessenger::SendMessage(BMessage* message, BHandler* replyTo,
163 	bigtime_t timeout) const
164 {
165 	DBG(OUT("BMessenger::SendMessage2(%.4s)\n", (char*)&message->what));
166 
167 	status_t result = message != NULL ? B_OK : B_BAD_VALUE;
168 	if (result == B_OK) {
169 		BMessenger replyMessenger(replyTo);
170 		result = SendMessage(message, replyMessenger, timeout);
171 	}
172 
173 	DBG(OUT("BMessenger::SendMessage2() done: %lx\n", result));
174 
175 	return result;
176 }
177 
178 
179 status_t
180 BMessenger::SendMessage(BMessage* message, BMessenger replyTo,
181 	bigtime_t timeout) const
182 {
183 	if (message == NULL)
184 		return B_BAD_VALUE;
185 
186 	return BMessage::Private(message).SendMessage(fPort, fTeam, fHandlerToken,
187 		timeout, false, replyTo);
188 }
189 
190 
191 status_t
192 BMessenger::SendMessage(uint32 command, BMessage* reply) const
193 {
194 	BMessage message(command);
195 
196 	return SendMessage(&message, reply);
197 }
198 
199 
200 status_t
201 BMessenger::SendMessage(BMessage* message, BMessage* reply,
202 	bigtime_t deliveryTimeout, bigtime_t replyTimeout) const
203 {
204 	if (message == NULL || reply == NULL)
205 		return B_BAD_VALUE;
206 
207 	status_t result = BMessage::Private(message).SendMessage(fPort, fTeam,
208 		fHandlerToken, reply, deliveryTimeout, replyTimeout);
209 
210 	// map this result for now
211 	if (result == B_BAD_TEAM_ID)
212 		result = B_BAD_PORT_ID;
213 
214 	return result;
215 }
216 
217 
218 //	#pragma mark - Operators and misc
219 
220 
221 status_t
222 BMessenger::SetTo(const char* signature, team_id team)
223 {
224 	status_t result = B_OK;
225 	_InitData(signature, team, &result);
226 
227 	return result;
228 }
229 
230 
231 status_t
232 BMessenger::SetTo(const BHandler* handler, const BLooper* looper)
233 {
234 	status_t result = B_OK;
235 	_InitData(handler, looper, &result);
236 
237 	return result;
238 }
239 
240 
241 BMessenger&
242 BMessenger::operator=(const BMessenger& other)
243 {
244 	if (this != &other) {
245 		fPort = other.fPort;
246 		fHandlerToken = other.fHandlerToken;
247 		fTeam = other.fTeam;
248 	}
249 
250 	return *this;
251 }
252 
253 
254 bool
255 BMessenger::operator==(const BMessenger& other) const
256 {
257 	// Note: The fTeam fields are not compared.
258 	return fPort == other.fPort && fHandlerToken == other.fHandlerToken;
259 }
260 
261 
262 bool
263 BMessenger::IsValid() const
264 {
265 	port_info info;
266 	return fPort >= 0 && get_port_info(fPort, &info) == B_OK;
267 }
268 
269 
270 team_id
271 BMessenger::Team() const
272 {
273 	return fTeam;
274 }
275 
276 
277 uint32
278 BMessenger::HashValue() const
279 {
280 	return fPort * 19 + fHandlerToken;
281 }
282 
283 
284 //	#pragma mark - Private or reserved
285 
286 
287 /*!	Sets the messenger's team, target looper port and handler token.
288 
289 	To target the preferred handler, use \c B_PREFERRED_TOKEN as token.
290 
291 	\param team The target's team.
292 	\param port The target looper port.
293 	\param token The target handler token.
294 */
295 void
296 BMessenger::_SetTo(team_id team, port_id port, int32 token)
297 {
298 	fTeam = team;
299 	fPort = port;
300 	fHandlerToken = token;
301 }
302 
303 
304 /*!	Initializes the BMessenger object's data given the signature and/or
305 	team ID of a target.
306 
307 	When only a signature is given, and multiple instances of the application
308 	are running it is undeterminate which one is chosen as the target. In case
309 	only a team ID is passed, the target application is identified uniquely.
310 	If both are supplied, the application identified by the team ID must have
311 	a matching signature, otherwise the initilization fails.
312 
313 	\param signature The target application's signature. May be \c NULL.
314 	\param team The target application's team ID. May be < 0.
315 	\param result An optional pointer to a pre-allocated status_t into which
316 		   the result of the initialization is written.
317 */
318 void
319 BMessenger::_InitData(const char* signature, team_id team, status_t* _result)
320 {
321 	status_t result = B_OK;
322 
323 	// get an app_info
324 	app_info info;
325 	if (team < 0) {
326 		// no team ID given
327 		if (signature != NULL) {
328 			// Try existing launch communication data first
329 			BMessage data;
330 			if (BLaunchRoster().GetData(signature, data) == B_OK) {
331 				info.port = data.GetInt32("port", -1);
332 				team = data.GetInt32("team", -5);
333 			}
334 			if (info.port < 0) {
335 				result = be_roster->GetAppInfo(signature, &info);
336 				team = info.team;
337 				// B_ERROR means that no application with the given signature
338 				// is running. But we are supposed to return B_BAD_VALUE.
339 				if (result == B_ERROR)
340 					result = B_BAD_VALUE;
341 			} else
342 				info.flags = 0;
343 		} else
344 			result = B_BAD_TYPE;
345 	} else {
346 		// a team ID is given
347 		result = be_roster->GetRunningAppInfo(team, &info);
348 		// Compare the returned signature with the supplied one.
349 		if (result == B_OK && signature != NULL
350 			&& strcasecmp(signature, info.signature) != 0) {
351 			result = B_MISMATCHED_VALUES;
352 		}
353 	}
354 	// check whether the app flags say B_ARGV_ONLY
355 	if (result == B_OK && (info.flags & B_ARGV_ONLY) != 0) {
356 		result = B_BAD_TYPE;
357 		// Set the team ID nevertheless -- that's what Be's implementation
358 		// does. Don't know, if that is a bug, but at least it doesn't harm.
359 		fTeam = team;
360 	}
361 	// init our members
362 	if (result == B_OK) {
363 		fTeam = team;
364 		fPort = info.port;
365 		fHandlerToken = B_PREFERRED_TOKEN;
366 	}
367 
368 	// return the result
369 	if (_result != NULL)
370 		*_result = result;
371 }
372 
373 
374 /*!	Initializes the BMessenger to target the local BHandler and/or BLooper.
375 
376 	When a \c NULL handler is supplied, the preferred handler in the given
377 	looper is targeted. If no looper is supplied the looper the given handler
378 	belongs to is used -- that means in particular, that the handler must
379 	already belong to a looper. If both are supplied the handler must actually
380 	belong to looper.
381 
382 	\param handler The target handler. May be \c NULL.
383 	\param looper The target looper. May be \c NULL.
384 	\param result An optional pointer to a pre-allocated status_t into which
385 	       the result of the initialization is written.
386 */
387 void
388 BMessenger::_InitData(const BHandler* handler, const BLooper* looper,
389 	status_t* _result)
390 {
391 	status_t result = (handler != NULL || looper != NULL) ? B_OK : B_BAD_VALUE;
392 	if (result == B_OK) {
393 		if (handler != NULL) {
394 			// BHandler is given, check/retrieve the looper.
395 			if (looper != NULL) {
396 				if (handler->Looper() != looper)
397 					result = B_MISMATCHED_VALUES;
398 			} else {
399 				looper = handler->Looper();
400 				if (looper == NULL)
401 					result = B_MISMATCHED_VALUES;
402 			}
403 		}
404 
405 		// set port, token,...
406 		if (result == B_OK) {
407 			AutoLocker<BLooperList> locker(gLooperList);
408 			if (locker.IsLocked() && gLooperList.IsLooperValid(looper)) {
409 				fPort = looper->fMsgPort;
410 				fHandlerToken = handler != NULL
411 					? _get_object_token_(handler)
412 					: B_PREFERRED_TOKEN;
413 				fTeam = looper->Team();
414 			} else
415 				result = B_BAD_VALUE;
416 		}
417 	}
418 
419 	if (_result != NULL)
420 		*_result = result;
421 }
422 
423 
424 //	#pragma mark - Operator functions
425 
426 
427 bool
428 operator<(const BMessenger& _a, const BMessenger& _b)
429 {
430 	BMessenger::Private a(const_cast<BMessenger&>(_a));
431 	BMessenger::Private b(const_cast<BMessenger&>(_b));
432 
433 	// significance:
434 	// 1. fPort
435 	// 2. fHandlerToken
436 	// 3. fPreferredTarget
437 	// fTeam is insignificant
438 	return (a.Port() < b.Port()
439 			|| (a.Port() == b.Port()
440 				&& (a.Token() < b.Token()
441 					|| (a.Token() == b.Token()
442 						&& !a.IsPreferredTarget()
443 						&& b.IsPreferredTarget()))));
444 }
445 
446 
447 bool
448 operator!=(const BMessenger& a, const BMessenger& b)
449 {
450 	return !(a == b);
451 }
452