Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
fa0f4af469dbc88efa00ef6f5559668d3f161037
[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         }
183         /**
184          * The natively implemented method to create an MSG process.
185          * @param host    A valid (binded) host where create the process.
186          */
187         protected native void create(String hostName) throws HostNotFoundException;
188         /**
189          * This method kills all running process of the simulation.
190          *
191          * @param resetPID              Should we reset the PID numbers. A negative number means no reset
192          *                                              and a positive number will be used to set the PID of the next newly
193          *                                              created process.
194          *
195          * @return                              The function returns the PID of the next created process.
196          *                      
197          */ 
198         public static native int killAll(int resetPID);
199         /**
200          * This method sets a flag to indicate that this thread must be killed. End user must use static method kill
201          *
202          * @return                              
203          *                      
204          */ 
205         public void nativeStop() {
206                 nativeStop = true;
207         }
208         /**
209          * getter for the flag that indicates that this thread must be killed
210          *
211          * @return                              
212          *                      
213          */ 
214         public boolean getNativeStop() {
215                 return nativeStop;
216         }
217
218         /**
219          * This method kill a process.
220          * @param process  the process to be killed.
221          *
222          */
223         public void kill() {
224                  nativeStop();
225                  Msg.info("Process " + msgName() + " will be killed.");                                                  
226         }
227
228         /**
229          * Suspends the process by suspending the task on which it was
230          * waiting for the completion.
231          *
232          */
233         public native void pause();
234         /**
235          * Resumes a suspended process by resuming the task on which it was
236          * waiting for the completion.
237          *
238          *
239          */ 
240         public native void restart();
241         /**
242          * Tests if a process is suspended.
243          *
244          * @return                              The method returns true if the process is suspended.
245          *                                              Otherwise the method returns false.
246          */ 
247         public native boolean isSuspended();
248         /**
249          * Returns the name of the process
250          */
251         public String msgName() {
252                 return this.name;
253         }
254         /**
255          * Returns the host of the process.
256          * @return                              The host instance of the process.
257          */ 
258         public Host getHost() {
259                 return this.host;
260         }
261         /**
262          * This static method gets a process from a PID.
263          *
264          * @param PID                   The process identifier of the process to get.
265          *
266          * @return                              The process with the specified PID.
267          *
268          * @exception                   NativeException on error in the native SimGrid code
269          */ 
270         public static native Process fromPID(int PID) throws NativeException;
271         /**
272          * This method returns the PID of the process.
273          *
274          * @return                              The PID of the process.
275          *
276          */ 
277         public int getPID()  {
278                 return pid;
279         }
280         /**
281          * This method returns the PID of the parent of a process.
282          *
283          * @return                              The PID of the parent of the process.
284          *
285          */ 
286         public int getPPID()  {
287                 return ppid;
288         }
289         /**
290          * This static method returns the currently running process.
291          *
292          * @return                              The current process.
293          *
294          */ 
295         public static native Process currentProcess();
296         /**
297          * Kills a MSG process
298          * @param process Valid java process to kill
299          */
300         final static native void kill(Process process); 
301         /**
302          * Migrates a process to another host.
303          *
304          * @param process               The process to migrate.
305          * @param host                  The host where to migrate the process.
306          *
307          */
308         public static void migrate(Process process, Host host)  {
309                 MsgNative.processMigrate(process, host);
310                 process.host = null;
311         }
312         /**
313          * Makes the current process sleep until time seconds have elapsed.
314          * @param seconds               The time the current process must sleep.
315          */ 
316         public native void waitFor(double seconds);    
317         /**
318      *
319      */
320     public void showArgs() {
321                 Msg.info("[" + this.name + "/" + this.getHost().getName() + "] argc=" +
322                                 this.args.size());
323                 for (int i = 0; i < this.args.size(); i++)
324                         Msg.info("[" + this.msgName() + "/" + this.getHost().getName() +
325                                         "] args[" + i + "]=" + (String) (this.args.get(i)));
326         }
327     /**
328      * Exit the process
329      */
330     public native void exit();
331
332         /**
333          * This method runs the process. Il calls the method function that you must overwrite.
334          */
335         public void run() {
336
337                 String[] args = null;      /* do not fill it before the signal or this.args will be empty */
338
339                 //waitSignal(); /* wait for other people to fill the process in */
340
341
342                 try {
343                         schedBegin.acquire();
344                 } catch(InterruptedException e) {
345                 }
346
347                 try {
348                         args = new String[this.args.size()];
349                         if (this.args.size() > 0) {
350                                 this.args.toArray(args);
351                         }
352
353                         this.main(args);
354                         exit();
355                         schedEnd.release();
356                 } catch(MsgException e) {
357                         e.printStackTrace();
358                         Msg.info("Unexpected behavior. Stopping now");
359                         System.exit(1);
360                 }
361                  catch(ProcessKilled pk) {
362                         if (nativeStop) {
363                                 try {
364                                         exit();
365                                 } catch (ProcessKilled pk2) {
366                                         /* Ignore that other exception that *will* occur all the time. 
367                                          * This is because the C mechanic gives the control to the now-killed process 
368                                          * so that it does some garbage collecting on its own. When it does so here, 
369                                          * the Java thread checks when starting if it's supposed to be killed (to inform 
370                                          * the C world). To avoid the infinite loop or anything similar, we ignore that 
371                                          * exception now. This should be ok since we ignore only a very specific exception 
372                                          * class and not a generic (such as any RuntimeException).
373                                          */
374                                         System.err.println(currentThread().getName()+": I ignore that other exception");                                        
375                                 }
376                                 Msg.info(" Process " + ((Process) Thread.currentThread()).msgName() + " has been killed.");                                             
377                                 schedEnd.release();                     
378                         }
379                         else {
380                                 pk.printStackTrace();
381                                 Msg.info("Unexpected behavior. Stopping now");
382                                 System.exit(1);
383                         }
384                 }       
385         }
386
387         /**
388          * The main function of the process (to implement).
389      *
390      * @param args
391      * @throws MsgException
392      */
393         public abstract void main(String[]args) throws MsgException;
394
395
396     /** @brief Gives the control from the given user thread back to the maestro 
397      * 
398      * schedule() and unschedule() are the basis of interactions between the user threads 
399      * (executing the user code), and the maestro thread (executing the platform models to decide 
400      * which user thread should get executed when. Once it decided which user thread should be run 
401      * (because the blocking action it were blocked onto are terminated in the simulated world), the 
402      * maestro passes the control to this uthread by calling uthread.schedule() in the maestro thread 
403      * (check its code for the simple semaphore-based synchronization schema). 
404      * 
405      * The uthread executes (while the maestro is blocked), until it starts another blocking 
406      * action, such as a communication or so. In that case, uthread.unschedule() gets called from 
407      * the user thread.    
408      *
409      * As other complications, these methods are called directly by the C through a JNI upcall in 
410      * response to the JNI downcalls done by the Java code. For example, you have this (simplified) 
411      * execution path: 
412      *   - a process calls the Task.send() method in java
413      *   - this calls Java_org_simgrid_msg_MsgNative_taskSend() in C through JNI
414      *   - this ends up calling jprocess_unschedule(), still in C
415      *   - this calls the java method "org/simgrid/msg/Process/unschedule()V" through JNI
416      *   - that is to say, the unschedule() method that you are reading the documentation of.
417      *   
418      * To understand all this, you must keep in mind that there is no difference between the C thread 
419      * describing a process, and the Java thread doing the same. Most of the time, they are system 
420      * threads from the kernel anyway. In the other case (such as when using green java threads when 
421      * the OS does not provide any thread feature), I'm unsure of what happens: it's a very long time 
422      * that I didn't see any such OS. 
423      * 
424      * The synchronization itself is implemented using simple semaphores in Java, as you can see by
425      * checking the code of these functions (and run() above). That's super simple, and thus welcome
426      * given the global complexity of the synchronization architecture: getting C and Java cooperate
427      * with regard to thread handling in a portable manner is very uneasy. A simple and straightforward 
428      * implementation of each synchronization point is precious. 
429      *  
430      * But this kinda limits the system scalability. It may reveal difficult to simulate dozens of 
431      * thousands of processes this way, both for memory limitations and for hard limits pushed by the 
432      * system on the amount of threads and semaphores (we have 2 semaphores per user process).
433      * 
434      * At time of writing, the best source of information on how to simulate large systems within the 
435      * Java bindings of simgrid is here: http://tomp2p.net/dev/simgrid/
436      * 
437      */
438     public void unschedule() {
439         /* this function is called from the user thread only */
440                 try {     
441                         
442                         /* unlock the maestro before going to sleep */
443                         schedEnd.release();
444                         /* Here, the user thread is locked, waiting for the semaphore, and maestro executes instead */
445                         schedBegin.acquire();
446                         /* now that the semaphore is acquired, it means that maestro gave us the control back */
447                         
448                         /* the user thread is starting again after giving the control to maestro. 
449                          * Let's check if we were asked to die in between */
450                         if ( (Thread.currentThread() instanceof Process) &&((Process) Thread.currentThread()).getNativeStop()) {                                
451                                 throw new ProcessKilled();
452                         }
453                         
454                 } catch (InterruptedException e) {
455                         /* ignore this exception because this is how we get killed on process.kill or end of simulation.
456                          * I don't like hiding exceptions this way, but fail to see any other solution 
457                          */
458                 }
459                 
460         }
461
462     /** @brief Gives the control from the maestro back to the given user thread 
463      * 
464      * Must be called from the maestro thread -- see unschedule() for details.
465      *
466      */
467     public void schedule() {
468                 try {
469                         /* unlock the user thread before going to sleep */
470                         schedBegin.release();
471                         /* Here, maestro is locked, waiting for the schedEnd semaphore to get signaled by used thread, that executes instead */
472                         schedEnd.acquire();
473                         /* Maestro now has the control back and the user thread went to sleep gently */
474                         
475                 } catch(InterruptedException e) {
476                         throw new RuntimeException("The impossible did happend once again: I got interrupted in schedEnd.acquire()",e);
477                 }
478         }
479     
480         /**
481          * Class initializer, to initialize various JNI stuff
482          */
483         public static native void nativeInit();
484         static {
485                 nativeInit();
486         }
487 }