Logo AND Algorithmique Numérique Distribuée

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