Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Add sleep in Process, with a Thread.sleep like use (in milliseconds/nanoseconds).
[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 millis millisecondes have elapsed.
314          * You should note that unlike "waitFor" which takes seconds, this method takes milliseconds.
315          * FIXME: Not optimal, maybe we should have two native functions.
316          * @param millis the length of time to sleep in milliseconds.
317          */
318         public static void sleep(long millis) {
319                 sleep(millis,0);
320         }
321         /**
322          * Makes the current process sleep until millis milliseconds and nanos nanoseconds 
323          * have elapsed.
324          * You should note that unlike "waitFor" which takes seconds, this method takes milliseconds and nanoseconds.
325          * Overloads Thread.sleep.
326          * @param millis the length of time to sleep in milliseconds.
327          * @param nanos additionnal nanoseconds to sleep.
328          */
329         public native static void sleep(long millis, int nanos);
330         /**
331          * Makes the current process sleep until time seconds have elapsed.
332          * @param seconds               The time the current process must sleep.
333          */ 
334         public native void waitFor(double seconds);    
335         /**
336      *
337      */
338     public void showArgs() {
339                 Msg.info("[" + this.name + "/" + this.getHost().getName() + "] argc=" +
340                                 this.args.size());
341                 for (int i = 0; i < this.args.size(); i++)
342                         Msg.info("[" + this.msgName() + "/" + this.getHost().getName() +
343                                         "] args[" + i + "]=" + (String) (this.args.get(i)));
344         }
345     /**
346      * Exit the process
347      */
348     public native void exit();
349
350         /**
351          * This method runs the process. Il calls the method function that you must overwrite.
352          */
353         public void run() {
354
355                 String[] args = null;      /* do not fill it before the signal or this.args will be empty */
356
357                 //waitSignal(); /* wait for other people to fill the process in */
358
359
360                 try {
361                         schedBegin.acquire();
362                 } catch(InterruptedException e) {
363                 }
364
365                 try {
366                         args = new String[this.args.size()];
367                         if (this.args.size() > 0) {
368                                 this.args.toArray(args);
369                         }
370
371                         this.main(args);
372                         exit();
373                         schedEnd.release();
374                 } catch(MsgException e) {
375                         e.printStackTrace();
376                         Msg.info("Unexpected behavior. Stopping now");
377                         System.exit(1);
378                 }
379                  catch(ProcessKilled pk) {
380                         if (nativeStop) {
381                                 try {
382                                         exit();
383                                 } catch (ProcessKilled pk2) {
384                                         /* Ignore that other exception that *will* occur all the time. 
385                                          * This is because the C mechanic gives the control to the now-killed process 
386                                          * so that it does some garbage collecting on its own. When it does so here, 
387                                          * the Java thread checks when starting if it's supposed to be killed (to inform 
388                                          * the C world). To avoid the infinite loop or anything similar, we ignore that 
389                                          * exception now. This should be ok since we ignore only a very specific exception 
390                                          * class and not a generic (such as any RuntimeException).
391                                          */
392                                         System.err.println(currentThread().getName()+": I ignore that other exception");                                        
393                                 }
394                                 Msg.info(" Process " + ((Process) Thread.currentThread()).msgName() + " has been killed.");                                             
395                                 schedEnd.release();                     
396                         }
397                         else {
398                                 pk.printStackTrace();
399                                 Msg.info("Unexpected behavior. Stopping now");
400                                 System.exit(1);
401                         }
402                 }       
403         }
404
405         /**
406          * The main function of the process (to implement).
407      *
408      * @param args
409      * @throws MsgException
410      */
411         public abstract void main(String[]args) throws MsgException;
412
413
414     /** @brief Gives the control from the given user thread back to the maestro 
415      * 
416      * schedule() and unschedule() are the basis of interactions between the user threads 
417      * (executing the user code), and the maestro thread (executing the platform models to decide 
418      * which user thread should get executed when. Once it decided which user thread should be run 
419      * (because the blocking action it were blocked onto are terminated in the simulated world), the 
420      * maestro passes the control to this uthread by calling uthread.schedule() in the maestro thread 
421      * (check its code for the simple semaphore-based synchronization schema). 
422      * 
423      * The uthread executes (while the maestro is blocked), until it starts another blocking 
424      * action, such as a communication or so. In that case, uthread.unschedule() gets called from 
425      * the user thread.    
426      *
427      * As other complications, these methods are called directly by the C through a JNI upcall in 
428      * response to the JNI downcalls done by the Java code. For example, you have this (simplified) 
429      * execution path: 
430      *   - a process calls the Task.send() method in java
431      *   - this calls Java_org_simgrid_msg_MsgNative_taskSend() in C through JNI
432      *   - this ends up calling jprocess_unschedule(), still in C
433      *   - this calls the java method "org/simgrid/msg/Process/unschedule()V" through JNI
434      *   - that is to say, the unschedule() method that you are reading the documentation of.
435      *   
436      * To understand all this, you must keep in mind that there is no difference between the C thread 
437      * describing a process, and the Java thread doing the same. Most of the time, they are system 
438      * threads from the kernel anyway. In the other case (such as when using green java threads when 
439      * the OS does not provide any thread feature), I'm unsure of what happens: it's a very long time 
440      * that I didn't see any such OS. 
441      * 
442      * The synchronization itself is implemented using simple semaphores in Java, as you can see by
443      * checking the code of these functions (and run() above). That's super simple, and thus welcome
444      * given the global complexity of the synchronization architecture: getting C and Java cooperate
445      * with regard to thread handling in a portable manner is very uneasy. A simple and straightforward 
446      * implementation of each synchronization point is precious. 
447      *  
448      * But this kinda limits the system scalability. It may reveal difficult to simulate dozens of 
449      * thousands of processes this way, both for memory limitations and for hard limits pushed by the 
450      * system on the amount of threads and semaphores (we have 2 semaphores per user process).
451      * 
452      * At time of writing, the best source of information on how to simulate large systems within the 
453      * Java bindings of simgrid is here: http://tomp2p.net/dev/simgrid/
454      * 
455      */
456     public void unschedule() {
457         /* this function is called from the user thread only */
458                 try {     
459                         
460                         /* unlock the maestro before going to sleep */
461                         schedEnd.release();
462                         /* Here, the user thread is locked, waiting for the semaphore, and maestro executes instead */
463                         schedBegin.acquire();
464                         /* now that the semaphore is acquired, it means that maestro gave us the control back */
465                         
466                         /* the user thread is starting again after giving the control to maestro. 
467                          * Let's check if we were asked to die in between */
468                         if ( (Thread.currentThread() instanceof Process) &&((Process) Thread.currentThread()).getNativeStop()) {                                
469                                 throw new ProcessKilled();
470                         }
471                         
472                 } catch (InterruptedException e) {
473                         /* ignore this exception because this is how we get killed on process.kill or end of simulation.
474                          * I don't like hiding exceptions this way, but fail to see any other solution 
475                          */
476                 }
477                 
478         }
479
480     /** @brief Gives the control from the maestro back to the given user thread 
481      * 
482      * Must be called from the maestro thread -- see unschedule() for details.
483      *
484      */
485     public void schedule() {
486                 try {
487                         /* unlock the user thread before going to sleep */
488                         schedBegin.release();
489                         /* Here, maestro is locked, waiting for the schedEnd semaphore to get signaled by used thread, that executes instead */
490                         schedEnd.acquire();
491                         /* Maestro now has the control back and the user thread went to sleep gently */
492                         
493                 } catch(InterruptedException e) {
494                         throw new RuntimeException("The impossible did happend once again: I got interrupted in schedEnd.acquire()",e);
495                 }
496         }
497     
498         /**
499          * Class initializer, to initialize various JNI stuff
500          */
501         public static native void nativeInit();
502         static {
503                 nativeInit();
504         }
505 }