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