Logo AND Algorithmique Numérique Distribuée

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