Skip to main content

haiku/app/
application.rs

1//
2// Copyright 2019-2020, Niels Sascha Reedijk <niels.reedijk@gmail.com>
3// All rights reserved. Distributed under the terms of the MIT License.
4//
5
6use std::collections::{HashMap, VecDeque};
7use std::env::args;
8use std::mem;
9use std::sync::{Arc, Mutex, atomic};
10
11use haiku_sys::{thread_info, thread_id, find_thread, get_thread_info, port_id, team_id};
12
13use ::app::{Handler, Message, Messenger};
14use ::app::looper::{HandlerType, Looper, LooperDelegate, NEXT_HANDLER_TOKEN};
15use ::app::roster::{ROSTER, ApplicationRegistrationStatus};
16use ::app::serverlink::{ServerLink, server_protocol};
17use ::app::sys::{B_ARGV_RECEIVED, B_READY_TO_RUN, B_QUIT_REQUESTED, QUIT, B_PREFERRED_TOKEN, get_app_path};
18use ::kernel::INFINITE_TIMEOUT;
19use ::kernel::ports::Port;
20use ::storage::MimeType;
21use ::storage::sys::entry_ref;
22use ::support::Result;
23
24const LOOPER_PORT_DEFAULT_CAPACITY: i32 = 200;
25
26/// Main entrypoint into a Haiku Application
27///
28/// Each Haiku application will create one Application instance. The function
29/// of this object is to connect to all the various servers that provide
30/// functionality and integration on Haiku. As a programmer, you use the object
31/// to set up your internal application. 
32///
33/// In general, you create a new application using the `new()` function. You
34/// then set up additional loopers using the `create_looper()` function, and
35/// when you are ready, you call the `run()` function to start the message
36/// loop. Control is returned to you when the message loop is quit. 
37pub struct Application<A> where A: ApplicationHooks + Send + 'static {
38	state: Arc<Mutex<A>>,
39	inner_looper: Looper<A>,
40	link: ServerLink
41}
42
43impl<A> Application<A> where A: ApplicationHooks + Send + 'static {
44	/// Create a new application object
45	///
46	/// This constructor will create a new application object. The required
47	/// parameters are a signature, and the initial state. The signature should
48	/// be a mime type with the supertype application, and a unique application
49	/// identifier.
50	///
51	/// The initial_state has two roles. First of all, as it implements the
52	/// `ApplicationHooks`, it will act as a `Handler` for messages that are
53	/// sent to the application. Secondly, the state is shared among all the
54	/// loopers (and handlers) through the `Context<A>` instances that are
55	/// passed as arguments to the message processors.
56	pub fn new(signature: &str, initial_state: A) -> Self {
57		// Check the signature
58		let mime_type =  match MimeType::new(signature) {
59			Some(t) => t,
60			None => panic!("Invalid MimeType")
61		};
62
63		if mime_type.is_supertype_only() || (mime_type.get_supertype() != MimeType::new("application").unwrap()) {
64			panic!("Invalid MimeType");
65		}
66
67		// Get an entry_ref for this path
68		let path = get_app_path(0).expect("Cannot get the path for this executable");
69		let entry = entry_ref::from_path(&path).expect("Cannot get the entry_ref for this executable");
70
71		// To do: see if the application file has any attributes set
72		let app_flags: u32 = 1; //B_MULTIPLE_LAUNCH as B_REG_DEFAULT_APP_FLAGS
73
74		// Register at the app server
75		let port = Port::create("application", LOOPER_PORT_DEFAULT_CAPACITY).unwrap();
76		let (team, thread) = get_current_team_and_thread();
77		let registration = match ROSTER.is_application_registered(&entry, team, 0) {
78			Ok(r) => r,
79			Err(_) => panic!("Error communicating with the registrar about the registration status")
80		};
81		match registration {
82			ApplicationRegistrationStatus::Registered(_) => (), //Ignored by the C++ implementation as well
83			ApplicationRegistrationStatus::PreRegistered(_) => panic!("Pre registered applications are not implemented"),
84			ApplicationRegistrationStatus::NotRegistered => (), //Ignored, now register
85		};
86		
87		match ROSTER.add_application(&String::from(signature), &entry, app_flags,
88			team, thread, port.get_port_id(), true) {
89				Ok(_) => (),
90				Err(_) => panic!("Error registering with the registrar")
91		};
92
93		// Set up some defaults
94		let state = Arc::new(Mutex::new(initial_state));
95		let default_looper_state = Box::new(ApplicationLooperState{});
96		let context = Context {
97			handler_messenger: Messenger::from_port(&port).unwrap(),
98			looper: LooperDelegate{ messenger: Messenger::from_port(&port).unwrap() },
99			application: ApplicationDelegate{ messenger: Messenger::from_port(&port).unwrap() },
100			application_state: state.clone()
101		};
102		let mut handlers = HashMap::new();
103		let handler_token = NEXT_HANDLER_TOKEN.fetch_add(1, atomic::Ordering::Relaxed);
104		handlers.insert(handler_token, HandlerType::LooperState);
105		let mut inner_looper = Looper {
106			name: String::from("application"),
107			port: port,
108			message_queue: VecDeque::new(),
109			handlers: handlers,
110			preferred_handler: handler_token,
111			context: context,
112			state: default_looper_state,
113			terminating: false
114		};
115
116		// Add the ARGV_RECEIVED message to the queue
117		let mut argv_message = Message::new(B_ARGV_RECEIVED);
118		argv_message.header.target = B_PREFERRED_TOKEN;
119		argv_message.add_data("_internal", &true).unwrap();
120		inner_looper.message_queue.push_back(argv_message);
121
122		// Add the READY_TO_RUN message to the queue
123		let mut ready_message = Message::new(B_READY_TO_RUN);
124		ready_message.header.target = B_PREFERRED_TOKEN;
125		inner_looper.message_queue.push_back(ready_message);
126
127		// Connect to the app_server
128		let mut link = ServerLink::create_desktop_connection().unwrap();
129		// AS_CREATE_APP:
130		// Data: 1) port_id - receiver port of the serverlink
131		//       2) port_id - looper port for this BApplication
132		//       3) team_id - the team id for this application
133		//       4) i32 - the handler ID token of this app
134		//       5) &str - signature of this app
135		link.sender.start_message(server_protocol::AS_CREATE_APP, 0).unwrap();
136		link.sender.attach(&link.receiver.port.get_port_id()).unwrap();
137		link.sender.attach(&inner_looper.port.get_port_id()).unwrap();
138		link.sender.attach(&team).unwrap();
139		link.sender.attach(&handler_token).unwrap();
140		link.sender.attach_string(signature).unwrap();
141		link.sender.flush(true).unwrap();
142		let message = link.receiver.get_next_message(INFINITE_TIMEOUT).unwrap();
143		if message.0 != 0 {
144			panic!("Cannot register the application at the app_server");
145		}
146		let server_port: port_id = link.receiver.read(0).unwrap();
147		let _: i32 = link.receiver.read(0).unwrap(); // area id, ignore for now
148		let _: i32 = link.receiver.read(0).unwrap(); // team id, ignore for now
149		link.sender.set_port(Port::from_id(server_port).unwrap());
150
151		Self {
152			state: state,
153			inner_looper: inner_looper,
154			link: link
155		}
156	}
157
158	/// Create a new looper for this application
159	///
160	/// This method creates a new looper. Each looper has a name, and a state.
161	/// The state of the looper should implement the `Handler<A>` trait, which
162	/// makes sure that the state can process messages. The initial_state will
163	/// be set as the preferred handler once it is provided.
164	///
165	/// The created loopers will not automatically start running; instead they
166	/// will be in a suspended state. See the Looper documentation on how to
167	/// start running them.
168	pub fn create_looper(&mut self, name: &str, initial_state: Box<dyn Handler<A> + Send>) -> Looper<A>
169	{
170		let port = Port::create(name, LOOPER_PORT_DEFAULT_CAPACITY).unwrap();
171		let mut handlers = HashMap::new();
172		let token = NEXT_HANDLER_TOKEN.fetch_add(1, atomic::Ordering::Relaxed);
173		handlers.insert(token, HandlerType::LooperState);
174		let context = Context {
175			handler_messenger: Messenger::from_port(&port).unwrap(),
176			looper: LooperDelegate{ messenger: Messenger::from_port(&port).unwrap() },
177			application: ApplicationDelegate{ messenger: self.inner_looper.get_messenger() },
178			application_state: self.state.clone()
179		};
180		Looper {
181			name: String::from(name),
182			port: port,
183			message_queue: VecDeque::new(),
184			handlers: handlers,
185			preferred_handler: token,
186			context: context,
187			state: initial_state,
188			terminating: false
189		}
190	}
191
192	/// Run the application
193	///
194	/// Calling this method will start the application's main message loop. The
195	/// method returns once all the messages are processed.
196	///
197	/// This method consumes the application instance, meaning that you won't be
198	/// able to use it after the loop has finished.
199	pub fn run(mut self) -> Result<()> {
200		self.inner_looper.looper_task();
201		Ok(())
202	}
203
204	/// Get a messenger to the application
205	///
206	/// The messenger will point to the preferred handler, which usually is the
207	/// state you provide.
208	pub fn get_messenger(&self) -> Messenger {
209		self.inner_looper.get_messenger()
210	}
211}
212
213impl<A> Drop for Application<A> where A: ApplicationHooks + Send + 'static {
214	fn drop(&mut self) {
215		// Unregister from Registrar
216		let (team, _) = get_current_team_and_thread();
217		let _ = ROSTER.remove_application(team);
218
219		// Unregister from the app_server
220		self.link.sender.start_message(B_QUIT_REQUESTED as i32, 0).unwrap();
221		self.link.sender.flush(false).unwrap();
222	}
223}
224
225/// Interact with the application object
226pub struct ApplicationDelegate {
227	/// A messenger that targets the preferred handler of the application.
228	pub messenger: Messenger
229}
230
231impl ApplicationDelegate {
232	/// Send a message to the application to quit
233	///
234	/// This message will inform the application's Looper that you want to end
235	/// the message loop. In effect this means that the application will no
236	/// longer process messages.
237	///
238	/// Note that this request does not clean up any of the existing Loopers.
239	pub fn quit(&self) {
240		let message = Message::new(QUIT);
241		self.messenger.send(message, &self.messenger).unwrap();
242	}
243}
244
245/// Execution context for a Handler
246///
247/// All Handers execute in the context of a Looper. The Context object is
248/// passed to your Handler in the message_received() method, so that you can
249/// interact with other parts of the system.
250///
251/// The context contains three messengers, that may be used to send messages
252/// to certain destinations, or to use as reply addresses while sending
253/// messages. Additionally, the Context gives access to the current
254/// application state.
255pub struct Context<A> where A: Send {
256	/// The messenger to the current Handler
257	///
258	/// This Messenger is most useful as a reply address for any messages you
259	/// are sending out.
260	pub handler_messenger: Messenger,
261	/// Interact with the current Looper
262	///
263	/// This is the Looper that is the context for the current message
264	///
265	/// Note that in some cases, this will be the same message queue as the
266	/// application delegate.
267	pub looper: LooperDelegate,
268	/// Interact with the current Application object
269	///
270	/// This gives access to the global application struct
271	pub application: ApplicationDelegate,
272	/// Access the global Application state
273	///
274	/// This state is shared among the whole application. It is locked behind
275	/// a Mutex, to allow thread-safe access. Note that using the application
276	/// state is 'dangerous', in the sense that it may lead to deadlocks when
277	/// two interdependent threads are waiting for access to it.
278	/// Imagine this scenario:
279	///   1. Looper A gets a reference to the application state, locking it.
280	///   2. Looper A sends a Message to Looper B and waits for a reply
281	///   3. Looper B receives the message. While handling that message, it
282	///      tries to get a reference to the application state. Since the
283	///      application state is already locks, both threads will be waiting
284	///      for each other.
285	///
286	/// There are two best practises:
287	///   1. Do not use synchronous messaging, unless you know what you are
288	///      doing.
289	///   2. If you do need access to the application state, use the lock()
290	///      method of the Mutex, and drop the Guard as soon as you are done.
291	pub application_state: Arc<Mutex<A>>,
292}
293
294/// Callbacks to be implemented by the ApplicationState
295///
296/// In order to create an Application object, you will need to provide an
297/// application state. Each application state will need to implement this
298/// trait. Currently there are three hook methods, all of which have a
299/// default implementation (which does nothing).
300///
301/// The application state acts like a `Handler<A>`, and can be targeted by
302/// messages. There is a variety of the `Handler<A>::message_received()`
303/// method available as a hook.
304pub trait ApplicationHooks {
305	/// Called when the messaging loop is started
306	///
307	/// This hook is called when the message loop starts running. It is
308	/// guaranteed to be the second hook called, after the `argv_received()`
309	/// hook.
310	fn ready_to_run(&mut self, _application: &ApplicationDelegate) {
311	}
312
313	/// Called when a message is received
314	///
315	/// This hook method is similar to the `Handler<A>::message_received()`
316	/// function. It is called when the application message loop receives a
317	/// message that either directly targets the state as a handler, or when
318	/// the state is the preferred handler of the application (which it is
319	/// by default).
320	///
321	/// Unlike the `Handler<A>` callback, this hook function does not receive
322	/// a `Context<A>`. Instead, you only get the `ApplicationDelegate`. The
323	/// reason is that you don't need the `LooperDelegate`, as the application
324	/// is running the relevant message loop. Additionally, you don't need
325	/// access to the mutex-protected application state, since this is already
326	/// available as the `self` argument.
327	fn message_received(&mut self, _application: &ApplicationDelegate, _message: &Message) {
328	}
329
330	/// Called when your application receives arguments
331	///
332	/// This hook is guaranteed to be called as the first hook when the message
333	/// loop starts. It contains the command line arguments, including the
334	/// application name.
335	///
336	/// Additionally, this hook may be called when you set your application as
337	/// Single Launch, and the user tried to launch another instance. In that
338	/// case the arguments will be sent to this instance.
339	fn argv_received(&mut self, _application: &ApplicationDelegate, _argv: Vec<String>) {
340	}
341}
342
343struct ApplicationLooperState {}
344
345impl<A> Handler<A> for ApplicationLooperState 
346	where A: ApplicationHooks + Send + 'static 
347{
348	fn message_received(&mut self, context: &Context<A>, message: &Message) {
349		let mut application_state = context.application_state.lock().unwrap();
350		// Dispatch specific messages to particular application hooks
351		match message.what() {
352			B_ARGV_RECEIVED => {
353				let argv = parse_argv(message);
354				if argv.len() > 0 {
355					application_state.argv_received(&context.application, argv);
356				}
357			},
358			B_READY_TO_RUN => application_state.ready_to_run(&context.application),
359			_ => application_state.message_received(&context.application, message)
360		}
361	}
362}
363
364// Convert a B_ARGV_RECEIVED message into a Vector with strings
365fn parse_argv(message: &Message) -> Vec<String> {
366	let internal = message.find_data::<bool>("_internal", 0).unwrap_or(false);
367	let mut argv: Vec<String> = Vec::new();
368	if internal {
369		// parse argv
370		for arg in args() {
371			argv.push(arg);
372		}
373	} else {
374		for i in 0.. {
375			let arg = match message.find_data::<String>("argv", i) {
376				Ok(arg) => arg,
377				Err(_) => break
378			};
379			argv.push(arg);
380		}
381	}
382	argv
383}
384		
385
386/// Get the current team id and thread id
387// TODO: some caching
388pub(crate) fn get_current_team_and_thread() -> (team_id, thread_id) {
389	let mut info: thread_info = unsafe { mem::zeroed() };
390	let (team, thread) = unsafe {
391		if get_thread_info(find_thread(0 as *const i8), &mut info) == 0 {
392			(info.team, info.thread)
393		} else {
394			(-1, -1)
395		}
396	};
397	(team, thread)
398}
399
400
401#[cfg(test)]
402mod tests {
403	use super::*;
404	use app::{Message};
405	use app::sys::QUIT;
406	
407	const ADD_TO_COUNTER: u32 = haiku_constant!('C','O','+','+');
408	const INFORM_APP_ABOUT_COUNTER: u32 = haiku_constant!('I','A','A','C');
409	
410	struct CountLooperState {
411		count: u32
412	}
413	
414	impl Handler<ApplicationState> for CountLooperState {
415		fn message_received(&mut self, context: &Context<ApplicationState>, message: &Message) {
416			match message.what() {
417				ADD_TO_COUNTER => {
418					self.count += 1;
419					let mut response = Message::new(INFORM_APP_ABOUT_COUNTER);
420					response.add_data("count", &self.count).unwrap();
421					context.application.messenger.send_and_ask_reply(response, &context.looper.messenger).unwrap();
422				},
423				_ => panic!("We are not supposed to receive messages other than ADD_TO_COUNTER"),
424			}
425		}
426	}
427	
428	struct ApplicationState {
429		total_count: u32
430	}
431	
432	impl ApplicationHooks for ApplicationState {
433		fn ready_to_run(&mut self, _application: &ApplicationDelegate) {
434			println!("ready_to_run()");
435		}
436		
437		fn message_received(&mut self, application: &ApplicationDelegate, message: &Message) {
438			match message.what() {
439				INFORM_APP_ABOUT_COUNTER => {
440					self.total_count += 1;
441					let count = message.find_data::<u32>("count", 0).unwrap();
442					if count == 2 {
443						// Quit the looper when the count hits 2
444						let messenger = message.get_return_address().unwrap();
445						// TODO:  We should not be using QUIT here, this is an internal detail
446						//        In general, it should be resolved how we do inter-looper
447						//        management
448						messenger.send_and_ask_reply(Message::new(QUIT), &messenger).unwrap();
449					}
450					println!("total count: {}", self.total_count);
451				},
452				_ => println!("application: {}", message.what())
453			}
454			
455			// Check if we are done now
456			if self.total_count == 4 {
457				application.quit();
458			}
459		}
460	}
461	
462	#[test]
463	fn looper_test() {
464		let looper_state_1 = Box::new(CountLooperState{ count: 0 });
465		let looper_state_2 = Box::new(CountLooperState{ count: 0 });
466		let application_state = ApplicationState{ total_count: 0 };
467
468		let mut application = Application::new("application/looper_test", application_state);
469
470		let looper_1 = application.create_looper("looper 1", looper_state_1);
471		let messenger_1 = looper_1.get_messenger();
472		let looper_2 = application.create_looper("looper 2", looper_state_2);
473		let messenger_2 = looper_2.get_messenger();
474		assert!(looper_1.run().is_ok());
475		assert!(looper_2.run().is_ok());
476		
477		// Create four count messages, two for each counter
478		let app_messenger = application.get_messenger();
479		let message = Message::new(ADD_TO_COUNTER);
480		messenger_1.send_and_ask_reply(message, &app_messenger).unwrap();
481		let message = Message::new(ADD_TO_COUNTER);
482		messenger_2.send_and_ask_reply(message, &app_messenger).unwrap();
483		let message = Message::new(ADD_TO_COUNTER);
484		messenger_1.send_and_ask_reply(message, &app_messenger).unwrap();
485		let message = Message::new(ADD_TO_COUNTER);
486		messenger_2.send_and_ask_reply(message, &app_messenger).unwrap();
487
488		application.run().unwrap(); 
489	}
490}