Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
6456b6b3c4e9685b40d44fa094eedfecbf053f32
[simgrid.git] / examples / java / chord / Node.java
1 /* Copyright (c) 2006-2014. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 package chord;
8
9 import org.simgrid.msg.Msg;
10 import org.simgrid.msg.Comm;
11 import org.simgrid.msg.Host;
12 import org.simgrid.msg.Task;
13 import org.simgrid.msg.Process;
14 import org.simgrid.msg.MsgException;
15 import org.simgrid.msg.TimeoutException;
16 public class Node extends Process {
17   protected int id;
18   protected String mailbox;
19   protected int predId;
20   protected String predMailbox;
21   protected int nextFingerToFix;
22   protected Comm commReceive;
23   ///Last time I changed a finger or my predecessor
24   protected double lastChangeDate;
25   int fingers[];
26
27   public Node(Host host, String name, String[] args) {
28     super(host,name,args);
29   }
30
31   @Override
32   public void main(String[] args) throws MsgException {
33     if (args.length != 2 && args.length != 4) {
34       Msg.info("You need to provide 2 or 4 arguments.");
35       return;
36     }
37     double initTime = Msg.getClock();
38     int i;
39     boolean joinSuccess = false;
40     double deadline;
41
42     double nextStabilizeDate = initTime + Common.PERIODIC_STABILIZE_DELAY;
43     double nextFixFingersDate = initTime + Common.PERIODIC_FIX_FINGERS_DELAY;
44     double nextCheckPredecessorDate = initTime + Common.PERIODIC_CHECK_PREDECESSOR_DELAY;
45     double nextLookupDate = initTime + Common.PERIODIC_LOOKUP_DELAY;
46
47     id = Integer.valueOf(args[0]);
48     mailbox = Integer.toString(id);
49
50     fingers = new int[Common.NB_BITS];
51     for (i = 0; i < Common.NB_BITS; i++) {
52       fingers[i] = -1;
53       setFinger(i,this.id);
54     }
55
56     //First node
57     if (args.length == 2) {
58       deadline = Integer.valueOf(args[1]);
59       create();
60       joinSuccess = true;
61     } else {
62       int knownId = Integer.valueOf(args[1]);
63       deadline = Integer.valueOf(args[3]);
64       //Msg.info("Hey! Let's join the system with the id " + id + ".");
65
66       joinSuccess = join(knownId);
67     }
68     if (joinSuccess) {
69       double currentClock = Msg.getClock();
70       while (currentClock < (initTime + deadline) && currentClock < Common.MAX_SIMULATION_TIME) {
71         if (commReceive == null) {
72           commReceive = Task.irecv(this.mailbox);
73         }
74         try {
75           if (!commReceive.test()) {
76             if (currentClock >= nextStabilizeDate) {
77               stabilize();
78               nextStabilizeDate = Msg.getClock() + Common.PERIODIC_STABILIZE_DELAY;
79             } else if (currentClock >= nextFixFingersDate) {
80               fixFingers();
81               nextFixFingersDate = Msg.getClock() + Common.PERIODIC_FIX_FINGERS_DELAY;
82             } else if (currentClock >= nextCheckPredecessorDate) {
83               this.checkPredecessor();
84               nextCheckPredecessorDate = Msg.getClock() + Common.PERIODIC_CHECK_PREDECESSOR_DELAY;
85             } else if (currentClock >= nextLookupDate) {
86               this.randomLookup();
87               nextLookupDate = Msg.getClock() + Common.PERIODIC_LOOKUP_DELAY;
88             } else {
89               waitFor(5);
90             }
91             currentClock = Msg.getClock();
92           } else {
93             handleTask(commReceive.getTask());
94             currentClock = Msg.getClock();
95             commReceive = null;
96           }
97         }
98         catch (Exception e) {
99           currentClock = Msg.getClock();
100           commReceive = null;
101         }
102       }
103       leave();
104       if (commReceive != null) {
105         commReceive = null;
106       }
107     } else {
108       Msg.info("I couldn't join the ring");
109     }
110   }
111
112   void handleTask(Task task) {
113     if (task instanceof FindSuccessorTask) {
114       FindSuccessorTask fTask = (FindSuccessorTask)task;
115       Msg.debug("Receiving a 'Find Successor' request from " + fTask.issuerHostName + " for id " + fTask.requestId);
116       // is my successor the successor?
117       if (isInInterval(fTask.requestId, this.id + 1, fingers[0])) {
118         //Msg.info("Send the request to " + fTask.answerTo + " with answer " + fingers[0]);
119         FindSuccessorAnswerTask answer = new FindSuccessorAnswerTask(getHost().getName(), mailbox, fingers[0]);
120         answer.dsend(fTask.answerTo);
121       } else {
122         // otherwise, forward the request to the closest preceding finger in my table
123         int closest = closestPrecedingNode(fTask.requestId);
124         //Msg.info("Forward the request to " + closest);
125         fTask.dsend(Integer.toString(closest));
126       }
127     } else if (task instanceof GetPredecessorTask) {
128       GetPredecessorTask gTask = (GetPredecessorTask)(task);
129       Msg.debug("Receiving a 'Get Predecessor' request from " + gTask.issuerHostName);
130       GetPredecessorAnswerTask answer = new GetPredecessorAnswerTask(getHost().getName(), mailbox, predId);
131       answer.dsend(gTask.answerTo);
132     } else if (task instanceof NotifyTask) {
133       NotifyTask nTask = (NotifyTask)task;
134       notify(nTask.requestId);
135     } else {
136       Msg.debug("Ignoring unexpected task of type:" + task);
137     }
138   }
139
140   void leave() {
141     Msg.debug("Well Guys! I Think it's time for me to quit ;)");
142     quitNotify(1); //Notify my successor
143     quitNotify(-1); //Notify my predecessor.
144   }
145
146   /**
147    * @brief Notifies the successor or the predecessor of the current node of the departure
148    * @param to 1 to notify the successor, -1 to notify the predecessor
149    */
150   static void quitNotify( int to) {
151     //TODO
152   }
153
154   /**
155    * @brief Initializes the current node as the first one of the system.
156    */
157   void create() {
158     Msg.debug("Create a new Chord ring...");
159     setPredecessor(-1);
160   }
161
162   // Makes the current node join the ring, knowing the id of a node already in the ring 
163   boolean join(int knownId) {
164     Msg.info("Joining the ring with id " + this.id + " knowing node " + knownId);
165     setPredecessor(-1);
166     int successorId = remoteFindSuccessor(knownId, this.id);
167     if (successorId == -1) {
168       Msg.info("Cannot join the ring.");
169     } else {
170       setFinger(0, successorId);
171     }
172     return successorId != -1;
173   }
174
175   void setPredecessor(int predecessorId) {
176     if (predecessorId != predId) {
177       predId = predecessorId;
178       if (predecessorId != -1) {
179         predMailbox = Integer.toString(predId);
180       }
181       lastChangeDate = Msg.getClock();
182     }
183   }
184
185   /**
186    * @brief Asks another node its predecessor.
187    * @param askTo the node to ask to
188    * @return the id of its predecessor node, or -1 if the request failed(or if the node does not know its predecessor)
189    */
190   int remoteGetPredecessor(int askTo) {
191     int predecessorId = -1;
192     boolean stop = false;
193     Msg.debug("Sending a 'Get Predecessor' request to " + askTo);
194     String mailboxTo = Integer.toString(askTo);
195     GetPredecessorTask sendTask = new GetPredecessorTask(getHost().getName(), this.mailbox);
196     try {
197       sendTask.send(mailboxTo, Common.TIMEOUT);
198       try {
199         do {
200           if (commReceive == null) {
201             commReceive = Task.irecv(this.mailbox);
202           }
203           commReceive.waitCompletion(Common.TIMEOUT);
204           Task taskReceived = commReceive.getTask();
205           if (taskReceived instanceof GetPredecessorAnswerTask) {
206             predecessorId = ((GetPredecessorAnswerTask) taskReceived).answerId;
207             stop = true;
208           } else {
209             handleTask(taskReceived);
210           }
211           commReceive = null;
212         } while (!stop);
213       }
214       catch (MsgException e) {
215         commReceive = null;
216         stop = true;
217       }
218     }
219     catch (MsgException e) {
220       Msg.debug("Failed to send the Get Predecessor request");
221     }
222     return predecessorId;
223   }
224
225   /**
226    * @brief Makes the current node find the successor node of an id.
227    * @param node the current node
228    * @param id the id to find
229    * @return the id of the successor node, or -1 if the request failed
230    */
231   int findSuccessor(int id) {
232     if (isInInterval(id, this.id + 1, fingers[0])) {
233       return fingers[0];
234     }
235
236     int closest = this.closestPrecedingNode(id);
237     return remoteFindSuccessor(closest, id);
238   }
239
240   // Asks another node the successor node of an id.
241   int remoteFindSuccessor(int askTo, int id) {
242     int successor = -1;
243     boolean stop = false;
244     String mailbox = Integer.toString(askTo);
245     Task sendTask = new FindSuccessorTask(getHost().getName(), this.mailbox, id);
246     Msg.debug("Sending a 'Find Successor' request to " + mailbox + " for id " + id);
247     try {
248       sendTask.send(mailbox, Common.TIMEOUT);
249       do {
250         if (commReceive == null) {
251           commReceive = Task.irecv(this.mailbox);
252         }
253         try {
254           commReceive.waitCompletion(Common.TIMEOUT);
255           Task task = commReceive.getTask();
256           if (task instanceof FindSuccessorAnswerTask) {
257             //TODO: Check if this this our answer.
258             FindSuccessorAnswerTask fTask = (FindSuccessorAnswerTask) task;
259             stop = true;
260             successor = fTask.answerId;
261           } else {
262             handleTask(task);
263           }
264           commReceive = null;
265         }
266         catch (TimeoutException e) {
267           stop = true;
268           commReceive = null;
269         }
270       } while (!stop);
271     }
272     catch (TimeoutException e) {
273       Msg.debug("Failed to send the 'Find Successor' request");
274     }
275     catch (MsgException e) {
276       Msg.debug("Failed to receive Find Successor");
277     }
278
279     return successor;
280   }
281
282   // This function is called periodically. It checks the immediate successor of the current node.
283   void stabilize() {
284     Msg.debug("Stabilizing node");
285     int candidateId;
286     int successorId = fingers[0];
287     if (successorId != this.id){
288       candidateId = remoteGetPredecessor(successorId);
289     } else {
290       candidateId = predId;
291     }
292     //This node is a candidate to become my new successor
293     if (candidateId != -1 && isInInterval(candidateId, this.id + 1, successorId - 1)) {
294       setFinger(0, candidateId);
295     }
296     if (successorId != this.id) {
297       remoteNotify(successorId, this.id);
298     }
299   }
300
301   /**
302    * @brief Notifies the current node that its predecessor may have changed.
303    * @param candidate_id the possible new predecessor
304    */
305   void notify(int predecessorCandidateId) {
306     if (predId == -1 || isInInterval(predecessorCandidateId, predId + 1, this.id - 1 )) {
307       setPredecessor(predecessorCandidateId);
308     }
309   }
310
311   /**
312    * @brief Notifies a remote node that its predecessor may have changed.
313    * @param notify_id id of the node to notify
314    * @param candidate_id the possible new predecessor
315    */
316   void remoteNotify(int notifyId, int predecessorCandidateId) {
317     Msg.debug("Sending a 'Notify' request to " + notifyId);
318     Task sentTask = new NotifyTask(getHost().getName(), this.mailbox, predecessorCandidateId);
319     sentTask.dsend(Integer.toString(notifyId));
320   }
321
322   // This function is called periodically.
323   // It refreshes the finger table of the current node.
324   void fixFingers() {
325     Msg.debug("Fixing fingers");
326     int i = this.nextFingerToFix;
327     int id = this.findSuccessor(this.id + (int)Math.pow(2,i)); //FIXME: SLOW
328     if (id != -1) {
329       if (id != fingers[i]) {
330         setFinger(i, id);
331       }
332       nextFingerToFix = (i + 1) % Common.NB_BITS;
333     }
334   }
335
336   // This function is called periodically.
337   // It checks whether the predecessor has failed
338   void checkPredecessor() {
339     //TODO
340   }
341
342   // Performs a find successor request to a random id.
343   void randomLookup() {
344     int id = 1337;
345     //Msg.info("Making a lookup request for id " + id);
346     findSuccessor(id);
347   }
348
349   /**
350    * @brief Returns the closest preceding finger of an id with respect to the finger table of the current node.
351    * @param id the id to find
352    * @return the closest preceding finger of that id
353    */
354   int closestPrecedingNode(int id) {
355     int i;
356     for (i = Common.NB_BITS - 1; i >= 0; i--) {
357       if (isInInterval(fingers[i], this.id + 1, id - 1)) {
358         return fingers[i];
359       }
360     }
361     return this.id;
362   }
363
364   /**
365    * @brief Returns whether an id belongs to the interval [start, end].
366    *
367    * The parameters are noramlized to make sure they are between 0 and nb_keys - 1).
368    * 1 belongs to [62, 3]
369    * 1 does not belong to [3, 62]
370    * 63 belongs to [62, 3]
371    * 63 does not belong to [3, 62]
372    * 24 belongs to [21, 29]
373    * 24 does not belong to [29, 21]
374    *
375    * @param id id to check
376    * @param start lower bound
377    * @param end upper bound
378    * @return a non-zero value if id in in [start, end]
379    */
380   static boolean isInInterval(int id, int start, int end) {
381     id = normalize(id);
382     start = normalize(start);
383     end = normalize(end);
384
385     // make sure end >= start and id >= start
386     if (end < start) {
387       end += Common.NB_KEYS;
388     }
389     if (id < start) {
390       id += Common.NB_KEYS;
391     }
392     return (id <= end);
393   }
394
395   /**
396    * @brief Turns an id into an equivalent id in [0, nb_keys).
397    * @param id an id
398    * @return the corresponding normalized id
399    */
400   static int normalize(int id) {
401     return id & (Common.NB_KEYS - 1);
402   }
403
404   /**
405    * @brief Sets a finger of the current node.
406    * @param finger_index index of the finger to set (0 to nb_bits - 1)
407    * @param id the id to set for this finger
408    */
409   void setFinger(int fingerIndex, int id) {
410     if (id != fingers[fingerIndex]) {
411       fingers[fingerIndex] = id;
412       lastChangeDate = Msg.getClock();
413     }
414   }
415 }