Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Change the way the objects are created in ApplicationHandler: they are
[simgrid.git] / org / simgrid / msg / Process.java
1 /*
2  * $Id$
3  *
4  * Copyright 2006,2007 Martin Quinson, Malek Cherier           
5  * All right reserved. 
6  *
7  * This program is free software; you can redistribute 
8  * it and/or modify it under the terms of the license 
9  *(GNU LGPL) which comes with this package. 
10  */
11
12 package org.simgrid.msg;
13  
14 import java.util.Arrays;
15 import java.util.Hashtable;
16 import java.util.Vector;
17 import java.util.concurrent.Semaphore;
18
19 /**
20  * A process may be defined as a code, with some private data, executing 
21  * in a location (host). All the process used by your simulation must be
22  * declared in the deployment file (XML format).
23  * To create your own process you must inherit your own process from this 
24  * class and override the method "main()". For example if you want to use 
25  * a process named Slave proceed as it :
26  *
27  * (1) import the class Process of the package simgrid.msg
28  * import simgrid.msg.Process;
29  * 
30  * public class Slave extends simgrid.msg.Process {
31  *
32  *  (2) Override the method function
33  * 
34  *  \verbatim
35  *      public void main(String[] args) {
36  *              System.out.println("Hello MSG");
37  *      }
38  *  \endverbatim
39  * }
40  * The name of your process must be declared in the deployment file of your simulation.
41  * For the example, for the previous process Slave this file must contains a line :
42  * <process host="Maxims" function="Slave"/>, where Maxims is the host of the process
43  * Slave. All the process of your simulation are automatically launched and managed by Msg.
44  * A process use tasks to simulate communications or computations with another process. 
45  * For more information see Task. For more information on host concept 
46  * see Host.
47  * 
48  */
49
50 public abstract class Process extends Thread {
51         /**
52          * This attribute represents a bind between a java process object and
53          * a native process. Even if this attribute is public you must never
54          * access to it. It is set automatically during the build of the object.
55          */
56         public long bind;
57
58         /**
59          * Even if this attribute is public you must never access to it.
60          * It is used to compute the id of an MSG process.
61          */
62         public static long nextProcessId = 0;
63
64         /**
65          * Even if this attribute is public you must never access to it.
66          * It is compute automatically during the creation of the object. 
67          * The native functions use this identifier to synchronize the process.
68          */
69         public long id;
70
71     public Hashtable<String,String> properties;
72
73         /**
74          * The name of the process.                                                     
75          */
76         protected String name;
77         /**
78           * The PID of the process
79           */
80         protected int pid = -1;
81         /**
82          * The PPID of the process 
83          */
84         protected int ppid = -1;
85         /**
86          * The host of the process
87          */
88         protected Host host = null;
89
90         /** The arguments of the method function of the process. */     
91         public Vector<String> args;
92
93         /* process synchronization tools */
94         
95         /* give the full path to semaphore to ensure that our own implementation don't get selected */
96     protected java.util.concurrent.Semaphore schedBegin, schedEnd;
97     private boolean nativeStop = false;
98
99         /**
100          * Default constructor (used in ApplicationHandler to initialize it)
101          */
102         protected Process() {
103                 super();
104                 this.id = nextProcessId++;
105                 this.name = null;
106                 this.bind = 0;
107                 this.args = new Vector<String>();
108                 this.properties = null;
109                 schedBegin = new java.util.concurrent.Semaphore(0);
110                 schedEnd = new java.util.concurrent.Semaphore(0);
111         }
112
113
114         /**
115          * Constructs a new process from the name of a host and his name. The method
116          * function of the process doesn't have argument.
117          *
118          * @param hostname              The name of the host of the process to create.
119          * @param name                  The name of the process.
120          *
121          * @exception                   HostNotFoundException  if no host with this name exists.
122          *                      
123          *
124          */
125         public Process(String hostname, String name) throws HostNotFoundException {
126                 this(Host.getByName(hostname), name, null);
127         }
128         /**
129          * Constructs a new process from the name of a host and his name. The arguments
130          * of the method function of the process are specified by the parameter args.
131          *
132          * @param hostname              The name of the host of the process to create.
133          * @param name                  The name of the process.
134          * @param args                  The arguments of the main function of the process.
135          *
136          * @exception                   HostNotFoundException  if no host with this name exists.
137      *                      NativeException
138      * @throws NativeException
139          *
140          */ 
141         public Process(String hostname, String name, String args[]) throws HostNotFoundException, NativeException {
142                 this(Host.getByName(hostname), name, args);
143         }
144         /**
145          * Constructs a new process from a host and his name. The method function of the 
146          * process doesn't have argument.
147          *
148          * @param host                  The host of the process to create.
149          * @param name                  The name of the process.
150          *
151          */
152         public Process(Host host, String name) {
153                 this(host, name, null);
154         }
155         /**
156          * Constructs a new process from a host and his name, the arguments of here method function are
157          * specified by the parameter args.
158          *
159          * @param host                  The host of the process to create.
160          * @param name                  The name of the process.
161          * @param args                  The arguments of main method of the process.
162          *
163          */
164         public Process(Host host, String name, String[]args) {
165                 /* This is the constructor called by all others */
166                 this();
167                 
168                 if (name == null)
169                         throw new NullPointerException("Process name cannot be NULL");
170                 this.name = name;
171
172                 this.args = new Vector<String>();
173                 if (null != args)
174                         this.args.addAll(Arrays.asList(args));
175
176                 try {
177                         create(host.getName());
178                 } catch (HostNotFoundException e) {
179                         throw new RuntimeException("The impossible happened (yet again): the host that I have were not found",e);
180                 }
181                 
182                 this.properties = new Hashtable<String,String>();
183                 
184         }
185         /**
186          * The natively implemented method to create an MSG process.
187          * @param host    A valid (binded) host where create the process.
188          */
189         protected native void create(String hostName) throws HostNotFoundException;
190         /**
191          * This method kills all running process of the simulation.
192          *
193          * @param resetPID              Should we reset the PID numbers. A negative number means no reset
194          *                                              and a positive number will be used to set the PID of the next newly
195          *                                              created process.
196          *
197          * @return                              The function returns the PID of the next created process.
198          *                      
199          */ 
200         public static native int killAll(int resetPID);
201         /**
202          * This method sets a flag to indicate that this thread must be killed. End user must use static method kill
203          *
204          * @return                              
205          *                      
206          */ 
207         public void nativeStop() {
208                 nativeStop = true;
209         }
210         /**
211          * getter for the flag that indicates that this thread must be killed
212          *
213          * @return                              
214          *                      
215          */ 
216         public boolean getNativeStop() {
217                 return nativeStop;
218         }
219
220         /**
221          * This method kill a process.
222          * @param process  the process to be killed.
223          *
224          */
225         public void kill() {
226                  nativeStop();
227                  Msg.info("Process " + msgName() + " will be killed.");                                                  
228         }
229
230         /**
231          * Suspends the process by suspending the task on which it was
232          * waiting for the completion.
233          *
234          */
235         public native void pause();
236         /**
237          * Resumes a suspended process by resuming the task on which it was
238          * waiting for the completion.
239          *
240          *
241          */ 
242         public native void restart();
243         /**
244          * Tests if a process is suspended.
245          *
246          * @return                              The method returns true if the process is suspended.
247          *                                              Otherwise the method returns false.
248          */ 
249         public native boolean isSuspended();
250         /**
251          * Returns the name of the process
252          */
253         public String msgName() {
254                 return this.name;
255         }
256         /**
257          * Returns the host of the process.
258          * @return                              The host instance of the process.
259          */ 
260         public Host getHost() {
261                 return this.host;
262         }
263         /**
264          * This static method gets a process from a PID.
265          *
266          * @param PID                   The process identifier of the process to get.
267          *
268          * @return                              The process with the specified PID.
269          *
270          * @exception                   NativeException on error in the native SimGrid code
271          */ 
272         public static native Process fromPID(int PID) throws NativeException;
273         /**
274          * This method returns the PID of the process.
275          *
276          * @return                              The PID of the process.
277          *
278          */ 
279         public int getPID()  {
280                 return pid;
281         }
282         /**
283          * This method returns the PID of the parent of a process.
284          *
285          * @return                              The PID of the parent of the process.
286          *
287          */ 
288         public int getPPID()  {
289                 return ppid;
290         }
291         /**
292          * This static method returns the currently running process.
293          *
294          * @return                              The current process.
295          *
296          */ 
297         public static native Process currentProcess();
298         /**
299          * Kills a MSG process
300          * @param process Valid java process to kill
301          */
302         final static native void kill(Process process); 
303         /**
304          * Migrates a process to another host.
305          *
306          * @param process               The process to migrate.
307          * @param host                  The host where to migrate the process.
308          *
309          */
310         public static void migrate(Process process, Host host)  {
311                 MsgNative.processMigrate(process, host);
312                 process.host = null;
313         }
314         /**
315          * Makes the current process sleep until millis millisecondes have elapsed.
316          * You should note that unlike "waitFor" which takes seconds, this method takes milliseconds.
317          * FIXME: Not optimal, maybe we should have two native functions.
318          * @param millis the length of time to sleep in milliseconds.
319          */
320         public static void sleep(long millis)  {
321                 sleep(millis,0);
322         }
323         /**
324          * Makes the current process sleep until millis milliseconds and nanos nanoseconds 
325          * have elapsed.
326          * You should note that unlike "waitFor" which takes seconds, this method takes milliseconds and nanoseconds.
327          * Overloads Thread.sleep.
328          * @param millis the length of time to sleep in milliseconds.
329          * @param nanos additionnal nanoseconds to sleep.
330          */
331         public native static void sleep(long millis, int nanos);
332         /**
333          * Makes the current process sleep until time seconds have elapsed.
334          * @param seconds               The time the current process must sleep.
335          */ 
336         public native void waitFor(double seconds);    
337         /**
338      *
339      */
340     public void showArgs() {
341                 Msg.info("[" + this.name + "/" + this.getHost().getName() + "] argc=" +
342                                 this.args.size());
343                 for (int i = 0; i < this.args.size(); i++)
344                         Msg.info("[" + this.msgName() + "/" + this.getHost().getName() +
345                                         "] args[" + i + "]=" + (String) (this.args.get(i)));
346         }
347     /**
348      * Exit the process
349      */
350     public native void exit();
351
352         /**
353          * This method runs the process. Il calls the method function that you must overwrite.
354          */
355         public void run() {
356
357                 String[] args = null;      /* do not fill it before the signal or this.args will be empty */
358
359                 //waitSignal(); /* wait for other people to fill the process in */
360
361
362                 try {
363                         schedBegin.acquire();
364                 } catch(InterruptedException e) {
365                 }
366
367                 try {
368                         args = new String[this.args.size()];
369                         if (this.args.size() > 0) {
370                                 this.args.toArray(args);
371                         }
372
373                         this.main(args);
374                         exit();
375                         schedEnd.release();
376                 } catch(MsgException e) {
377                         e.printStackTrace();
378                         Msg.info("Unexpected behavior. Stopping now");
379                         System.exit(1);
380                 }
381                  catch(ProcessKilled pk) {
382                         if (nativeStop) {
383                                 try {
384                                         exit();
385                                 } catch (ProcessKilled pk2) {
386                                         /* Ignore that other exception that *will* occur all the time. 
387                                          * This is because the C mechanic gives the control to the now-killed process 
388                                          * so that it does some garbage collecting on its own. When it does so here, 
389                                          * the Java thread checks when starting if it's supposed to be killed (to inform 
390                                          * the C world). To avoid the infinite loop or anything similar, we ignore that 
391                                          * exception now. This should be ok since we ignore only a very specific exception 
392                                          * class and not a generic (such as any RuntimeException).
393                                          */
394                                         System.err.println(currentThread().getName()+": I ignore that other exception");                                        
395                                 }
396                                 Msg.info(" Process " + ((Process) Thread.currentThread()).msgName() + " has been killed.");                                             
397                                 schedEnd.release();                     
398                         }
399                         else {
400                                 pk.printStackTrace();
401                                 Msg.info("Unexpected behavior. Stopping now");
402                                 System.exit(1);
403                         }
404                 }       
405         }
406
407         /**
408          * The main function of the process (to implement).
409      *
410      * @param args
411      * @throws MsgException
412      */
413         public abstract void main(String[]args) throws MsgException;
414
415
416     /** @brief Gives the control from the given user thread back to the maestro 
417      * 
418      * schedule() and unschedule() are the basis of interactions between the user threads 
419      * (executing the user code), and the maestro thread (executing the platform models to decide 
420      * which user thread should get executed when. Once it decided which user thread should be run 
421      * (because the blocking action it were blocked onto are terminated in the simulated world), the 
422      * maestro passes the control to this uthread by calling uthread.schedule() in the maestro thread 
423      * (check its code for the simple semaphore-based synchronization schema). 
424      * 
425      * The uthread executes (while the maestro is blocked), until it starts another blocking 
426      * action, such as a communication or so. In that case, uthread.unschedule() gets called from 
427      * the user thread.    
428      *
429      * As other complications, these methods are called directly by the C through a JNI upcall in 
430      * response to the JNI downcalls done by the Java code. For example, you have this (simplified) 
431      * execution path: 
432      *   - a process calls the Task.send() method in java
433      *   - this calls Java_org_simgrid_msg_MsgNative_taskSend() in C through JNI
434      *   - this ends up calling jprocess_unschedule(), still in C
435      *   - this calls the java method "org/simgrid/msg/Process/unschedule()V" through JNI
436      *   - that is to say, the unschedule() method that you are reading the documentation of.
437      *   
438      * To understand all this, you must keep in mind that there is no difference between the C thread 
439      * describing a process, and the Java thread doing the same. Most of the time, they are system 
440      * threads from the kernel anyway. In the other case (such as when using green java threads when 
441      * the OS does not provide any thread feature), I'm unsure of what happens: it's a very long time 
442      * that I didn't see any such OS. 
443      * 
444      * The synchronization itself is implemented using simple semaphores in Java, as you can see by
445      * checking the code of these functions (and run() above). That's super simple, and thus welcome
446      * given the global complexity of the synchronization architecture: getting C and Java cooperate
447      * with regard to thread handling in a portable manner is very uneasy. A simple and straightforward 
448      * implementation of each synchronization point is precious. 
449      *  
450      * But this kinda limits the system scalability. It may reveal difficult to simulate dozens of 
451      * thousands of processes this way, both for memory limitations and for hard limits pushed by the 
452      * system on the amount of threads and semaphores (we have 2 semaphores per user process).
453      * 
454      * At time of writing, the best source of information on how to simulate large systems within the 
455      * Java bindings of simgrid is here: http://tomp2p.net/dev/simgrid/
456      * 
457      */
458     public void unschedule() {
459         /* this function is called from the user thread only */
460                 try {     
461                         
462                         /* unlock the maestro before going to sleep */
463                         schedEnd.release();
464                         /* Here, the user thread is locked, waiting for the semaphore, and maestro executes instead */
465                         schedBegin.acquire();
466                         /* now that the semaphore is acquired, it means that maestro gave us the control back */
467                         
468                         /* the user thread is starting again after giving the control to maestro. 
469                          * Let's check if we were asked to die in between */
470                         if ( (Thread.currentThread() instanceof Process) &&((Process) Thread.currentThread()).getNativeStop()) {                                
471                                 throw new ProcessKilled();
472                         }
473                         
474                 } catch (InterruptedException e) {
475                         /* ignore this exception because this is how we get killed on process.kill or end of simulation.
476                          * I don't like hiding exceptions this way, but fail to see any other solution 
477                          */
478                 }
479                 
480         }
481
482     /** @brief Gives the control from the maestro back to the given user thread 
483      * 
484      * Must be called from the maestro thread -- see unschedule() for details.
485      *
486      */
487     public void schedule() {
488                 try {
489                         /* unlock the user thread before going to sleep */
490                         schedBegin.release();
491                         /* Here, maestro is locked, waiting for the schedEnd semaphore to get signaled by used thread, that executes instead */
492                         schedEnd.acquire();
493                         /* Maestro now has the control back and the user thread went to sleep gently */
494                         
495                 } catch(InterruptedException e) {
496                         throw new RuntimeException("The impossible did happend once again: I got interrupted in schedEnd.acquire()",e);
497                 }
498         }
499     
500         /**
501          * Class initializer, to initialize various JNI stuff
502          */
503         public static native void nativeInit();
504         static {
505                 nativeInit();
506         }
507 }