Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
d5e10d690b987c40c24b34d57d82d56e94175563
[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     /**
72      *
73      */
74     public Hashtable<String,String> properties;
75
76         /**
77          * The name of the process.                                                     
78          */
79         protected String name;
80         /**
81           * The PID of the process
82           */
83         protected int pid = -1;
84         /**
85          * The PPID of the process 
86          */
87         protected int ppid = -1;
88         /**
89          * The host of the process
90          */
91         protected Host host = null;
92     /**
93      *
94      * @return
95      */
96     public String msgName() {
97                 return this.name;
98         }
99         /** The arguments of the method function of the process. */     
100         public Vector<String> args;
101
102         /* process synchronization tools */
103     /**
104      *
105      */
106     /**
107      *
108      */
109     protected Semaphore schedBegin, schedEnd;
110     private boolean nativeStop = false;
111
112         /**
113          * Default constructor (used in ApplicationHandler to initialize it)
114          */
115         protected Process() {
116                 super();
117                 this.id = nextProcessId++;
118                 this.name = null;
119                 this.bind = 0;
120                 this.args = new Vector<String>();
121                 this.properties = null;
122                 schedBegin = new Semaphore(0);
123                 schedEnd = new Semaphore(0);
124         }
125
126
127         /**
128          * Constructs a new process from the name of a host and his name. The method
129          * function of the process doesn't have argument.
130          *
131          * @param hostname              The name of the host of the process to create.
132          * @param name                  The name of the process.
133          *
134          * @exception                   HostNotFoundException  if no host with this name exists.
135          *                      
136          *
137          */
138         public Process(String hostname, String name) throws HostNotFoundException {
139                 this(Host.getByName(hostname), name, null);
140         }
141         /**
142          * Constructs a new process from the name of a host and his name. The arguments
143          * of the method function of the process are specified by the parameter args.
144          *
145          * @param hostname              The name of the host of the process to create.
146          * @param name                  The name of the process.
147          * @param args                  The arguments of the main function of the process.
148          *
149          * @exception                   HostNotFoundException  if no host with this name exists.
150      *                      NativeException
151      * @throws NativeException
152          *
153          */ 
154         public Process(String hostname, String name, String args[]) throws HostNotFoundException, NativeException {
155                 this(Host.getByName(hostname), name, args);
156         }
157         /**
158          * Constructs a new process from a host and his name. The method function of the 
159          * process doesn't have argument.
160          *
161          * @param host                  The host of the process to create.
162          * @param name                  The name of the process.
163          *
164          */
165         public Process(Host host, String name) {
166                 this(host, name, null);
167         }
168         /**
169          * Constructs a new process from a host and his name, the arguments of here method function are
170          * specified by the parameter args.
171          *
172          * @param host                  The host of the process to create.
173          * @param name                  The name of the process.
174          * @param args                  The arguments of main method of the process.
175          *
176          */
177         public Process(Host host, String name, String[]args) {
178                 /* This is the constructor called by all others */
179                 this();
180                 
181                 if (name == null)
182                         throw new NullPointerException("Process name cannot be NULL");
183                 this.name = name;
184
185                 this.args = new Vector<String>();
186                 if (null != args)
187                         this.args.addAll(Arrays.asList(args));
188
189                 try {
190                         MsgNative.processCreate(this, host.getName());
191                 } catch (HostNotFoundException e) {
192                         throw new RuntimeException("The impossible happend (yet again): the host that I have were not found",e);
193                 }
194                 
195         }
196
197
198         /**
199          * This method kills all running process of the simulation.
200          *
201          * @param resetPID              Should we reset the PID numbers. A negative number means no reset
202          *                                              and a positive number will be used to set the PID of the next newly
203          *                                              created process.
204          *
205          * @return                              The function returns the PID of the next created process.
206          *                      
207          */ 
208         public static int killAll(int resetPID) {
209                 return MsgNative.processKillAll(resetPID);
210         }
211
212         /**
213          * This method sets a flag to indicate that this thread must be killed. End user must use static method kill
214          *
215          * @return                              
216          *                      
217          */ 
218         public void nativeStop()
219         {
220         nativeStop = true;
221         }
222         /**
223          * getter for the flag that indicates that this thread must be killed
224          *
225          * @return                              
226          *                      
227          */ 
228         public boolean getNativeStop()
229         {
230                 return nativeStop;
231         }
232
233         /**
234          * This method kill a process.
235          * @param process  the process to be killed.
236          *
237          */
238         public void kill() {
239                  nativeStop();
240                  Msg.info("Process " + msgName() + " will be killed.");                         
241                                  
242         }
243
244         /**
245          * Suspends the process by suspending the task on which it was
246          * waiting for the completion.
247          *
248          */
249         public void pause() {
250                 MsgNative.processSuspend(this);
251         }
252         /**
253          * Resumes a suspended process by resuming the task on which it was
254          * waiting for the completion.
255          *
256          *
257          */ 
258         public void restart()  {
259                 MsgNative.processResume(this);
260         }
261         /**
262          * Tests if a process is suspended.
263          *
264          * @return                              The method returns true if the process is suspended.
265          *                                              Otherwise the method returns false.
266          */ 
267         public boolean isSuspended() {
268                 return MsgNative.processIsSuspended(this);
269         }
270         /**
271          * Returns the host of a process.
272          *
273          * @return                              The host instance of the process.
274          *
275          *
276          */ 
277         public Host getHost() {
278                 if (this.host == null) {
279                         this.host = MsgNative.processGetHost(this);
280                 }
281                 return this.host;
282         }
283         /**
284          * This static method gets a process from a PID.
285          *
286          * @param PID                   The process identifier of the process to get.
287          *
288          * @return                              The process with the specified PID.
289          *
290          * @exception                   NativeException on error in the native SimGrid code
291          */ 
292         public static Process fromPID(int PID) throws NativeException {
293                 return MsgNative.processFromPID(PID);
294         }
295         /**
296          * This method returns the PID of the process.
297          *
298          * @return                              The PID of the process.
299          *
300          */ 
301         public int getPID()  {
302                 if (pid == -1) {
303                         pid = MsgNative.processGetPID(this);
304                 }
305                 return pid;
306         }
307         /**
308          * This method returns the PID of the parent of a process.
309          *
310          * @return                              The PID of the parent of the process.
311          *
312          */ 
313         public int getPPID()  {
314                 if (ppid == -1) {
315                         ppid = MsgNative.processGetPPID(this);
316                 }
317                 return ppid;
318         }
319         /**
320          * This static method returns the currently running process.
321          *
322          * @return                              The current process.
323          *
324          */ 
325         public static Process currentProcess()  {
326                 return MsgNative.processSelf();
327         }
328         /**
329          * Migrates a process to another host.
330          *
331          * @param process               The process to migrate.
332          * @param host                  The host where to migrate the process.
333          *
334          */
335         public static void migrate(Process process, Host host)  {
336                 MsgNative.processMigrate(process, host);
337                 process.host = null;
338         }
339         /**
340          * Makes the current process sleep until time seconds have elapsed.
341          *
342          * @param seconds               The time the current process must sleep.
343          *
344          * @exception                   HostFailureException on error in the native SimGrid code
345          */ 
346         public static void waitFor(double seconds) throws HostFailureException {
347                 MsgNative.processWaitFor(seconds);
348         } 
349     /**
350      *
351      */
352     public void showArgs() {
353                 Msg.info("[" + this.name + "/" + this.getHost().getName() + "] argc=" +
354                                 this.args.size());
355                 for (int i = 0; i < this.args.size(); i++)
356                         Msg.info("[" + this.msgName() + "/" + this.getHost().getName() +
357                                         "] args[" + i + "]=" + (String) (this.args.get(i)));
358         }
359     /**
360      * Let the simulated process sleep for the given amount of millisecond in the simulated world.
361      * 
362      *  You don't want to use sleep instead, because it would freeze your simulation 
363      *  run without any impact on the simulated world.
364      * @param millis
365      */
366     public native void simulatedSleep(double seconds);
367
368         /**
369          * This method runs the process. Il calls the method function that you must overwrite.
370          */
371         public void run() {
372
373                 String[]args = null;      /* do not fill it before the signal or this.args will be empty */
374
375                 //waitSignal(); /* wait for other people to fill the process in */
376
377
378                 try {
379                         schedBegin.acquire();
380                 } catch(InterruptedException e) {
381                 }
382
383                 try {
384                         args = new String[this.args.size()];
385                         if (this.args.size() > 0) {
386                                 this.args.toArray(args);
387                         }
388
389                         this.main(args);
390                         MsgNative.processExit(this);
391                         schedEnd.release();
392                 } catch(MsgException e) {
393                         e.printStackTrace();
394                         Msg.info("Unexpected behavior. Stopping now");
395                         System.exit(1);
396                 }
397                  catch(ProcessKilled pk) {
398                         if (nativeStop) {
399                                 try {
400                                         MsgNative.processExit(this);
401                                 } catch (ProcessKilled pk2) {
402                                         /* Ignore that other exception that *will* occur all the time. 
403                                          * This is because the C mechanic gives the control to the now-killed process 
404                                          * so that it does some garbage collecting on its own. When it does so here, 
405                                          * the Java thread checks when starting if it's supposed to be killed (to inform 
406                                          * the C world). To avoid the infinite loop or anything similar, we ignore that 
407                                          * exception now. This should be ok since we ignore only a very specific exception 
408                                          * class and not a generic (such as any RuntimeException).
409                                          */
410                                         System.err.println(currentThread().getName()+": I ignore that other exception");                                        
411                                 }
412                         Msg.info(" Process " + ((Process) Thread.currentThread()).msgName() + " has been killed.");                                             
413                         schedEnd.release();                     
414                         }
415                         else {
416                         pk.printStackTrace();
417                         Msg.info("Unexpected behavior. Stopping now");
418                         System.exit(1);
419                         }
420                 }       
421         }
422
423         /**
424          * The main function of the process (to implement).
425      *
426      * @param args
427      * @throws MsgException
428      */
429         public abstract void main(String[]args) throws MsgException;
430
431
432     /** @brief Gives the control from the given user thread back to the maestro 
433      * 
434      * schedule() and unschedule() are the basis of interactions between the user threads 
435      * (executing the user code), and the maestro thread (executing the platform models to decide 
436      * which user thread should get executed when. Once it decided which user thread should be run 
437      * (because the blocking action it were blocked onto are terminated in the simulated world), the 
438      * maestro passes the control to this uthread by calling uthread.schedule() in the maestro thread 
439      * (check its code for the simple semaphore-based synchronization schema). 
440      * 
441      * The uthread executes (while the maestro is blocked), until it starts another blocking 
442      * action, such as a communication or so. In that case, uthread.unschedule() gets called from 
443      * the user thread.    
444      *
445      * As other complications, these methods are called directly by the C through a JNI upcall in 
446      * response to the JNI downcalls done by the Java code. For example, you have this (simplified) 
447      * execution path: 
448      *   - a process calls the Task.send() method in java
449      *   - this calls Java_org_simgrid_msg_MsgNative_taskSend() in C through JNI
450      *   - this ends up calling jprocess_unschedule(), still in C
451      *   - this calls the java method "org/simgrid/msg/Process/unschedule()V" through JNI
452      *   - that is to say, the unschedule() method that you are reading the documentation of.
453      *   
454      * To understand all this, you must keep in mind that there is no difference between the C thread 
455      * describing a process, and the Java thread doing the same. Most of the time, they are system 
456      * threads from the kernel anyway. In the other case (such as when using green java threads when 
457      * the OS does not provide any thread feature), I'm unsure of what happens: it's a very long time 
458      * that I didn't see any such OS. 
459      * 
460      * The synchronization itself is implemented using simple semaphores in Java, as you can see by
461      * checking the code of these functions (and run() above). That's super simple, and thus welcome
462      * given the global complexity of the synchronization architecture: getting C and Java cooperate
463      * with regard to thread handling in a portable manner is very uneasy. A simple and straightforward 
464      * implementation of each synchronization point is precious. 
465      *  
466      * But this kinda limits the system scalability. It may reveal difficult to simulate dozens of 
467      * thousands of processes this way, both for memory limitations and for hard limits pushed by the 
468      * system on the amount of threads and semaphores (we have 2 semaphores per user process).
469      * 
470      * At time of writing, the best source of information on how to simulate large systems within the 
471      * Java bindings of simgrid is here: http://tomp2p.net/dev/simgrid/
472      * 
473      */
474     public void unschedule() {
475         /* this function is called from the user thread only */
476                 try {     
477                         
478                         /* unlock the maestro before going to sleep */
479                         schedEnd.release();
480                         /* Here, the user thread is locked, waiting for the semaphore, and maestro executes instead */
481                         schedBegin.acquire();
482                         /* now that the semaphore is acquired, it means that maestro gave us the control back */
483                         
484                         /* the user thread is starting again after giving the control to maestro. 
485                          * Let's check if we were asked to die in between */
486                         if ( (Thread.currentThread() instanceof Process) &&((Process) Thread.currentThread()).getNativeStop()) {                                
487                                 throw new ProcessKilled();
488                         }
489                         
490                 } catch (InterruptedException e) {
491                         /* ignore this exception because this is how we get killed on process.kill or end of simulation.
492                          * I don't like hiding exceptions this way, but fail to see any other solution 
493                          */
494                 }
495                 
496         }
497
498     /** @brief Gives the control from the maestro back to the given user thread 
499      * 
500      * Must be called from the maestro thread -- see unschedule() for details.
501      *
502      */
503     public void schedule() {
504                 try {
505                         /* unlock the user thread before going to sleep */
506                         schedBegin.release();
507                         /* Here, maestro is locked, waiting for the schedEnd semaphore to get signaled by used thread, that executes instead */
508                         schedEnd.acquire();
509                         /* Maestro now has the control back and the user thread went to sleep gently */
510                         
511                 } catch(InterruptedException e) {
512                         throw new RuntimeException("The impossible did happend once again: I got interrupted in schedEnd.acquire()",e);
513                 }
514         }
515
516         /** Send the given task in the mailbox associated with the specified alias  (waiting at most given time) 
517      * @param mailbox
518      * @param task 
519      * @param timeout
520      * @throws TimeoutException
521          * @throws HostFailureException 
522          * @throws TransferFailureException */
523         public void taskSend(String mailbox, Task task, double timeout) throws TransferFailureException, HostFailureException, TimeoutException {
524                 MsgNative.taskSend(mailbox, task, timeout);
525         }
526
527         /** Send the given task in the mailbox associated with the specified alias
528      * @param mailbox
529      * @param task
530      * @throws TimeoutException
531          * @throws HostFailureException 
532          * @throws TransferFailureException */
533         public void taskSend(String mailbox, Task task) throws  TransferFailureException, HostFailureException, TimeoutException {
534                 MsgNative.taskSend(mailbox, task, -1);
535         }
536
537     /** Receive a task on mailbox associated with the specified mailbox
538      * @param mailbox
539      * @return
540      * @throws TransferFailureException
541      * @throws HostFailureException
542      * @throws TimeoutException
543      */
544         public Task taskReceive(String mailbox) throws TransferFailureException, HostFailureException, TimeoutException {
545                 return MsgNative.taskReceive(mailbox, -1.0, null);
546         }
547
548     /** Receive a task on mailbox associated with the specified alias (waiting at most given time)
549      * @param mailbox
550      * @param timeout
551      * @return
552      * @throws TransferFailureException
553      * @throws HostFailureException
554      * @throws TimeoutException
555      */
556         public Task taskReceive(String mailbox, double timeout) throws  TransferFailureException, HostFailureException, TimeoutException {
557                 return MsgNative.taskReceive(mailbox, timeout, null);
558         }
559
560     /** Receive a task on mailbox associated with the specified alias from given sender
561      * @param mailbox
562      * @param host
563      * @param timeout
564      * @return
565      * @throws TransferFailureException
566      * @throws HostFailureException
567      * @throws TimeoutException
568      */
569         public Task taskReceive(String mailbox, double timeout, Host host) throws  TransferFailureException, HostFailureException, TimeoutException {
570                 return MsgNative.taskReceive(mailbox, timeout, host);
571         }
572
573     /** Receive a task on mailbox associated with the specified alias from given sender
574      * @param mailbox
575      * @param host
576      * @return
577      * @throws TransferFailureException
578      * @throws HostFailureException
579      * @throws TimeoutException
580      */
581         public Task taskReceive(String mailbox, Host host) throws  TransferFailureException, HostFailureException, TimeoutException {
582                 return MsgNative.taskReceive(mailbox, -1.0, host);
583         }
584 }