Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
e48e5ec399815791053824554f52c2a63f5f48ef
[simgrid.git] / examples / lua / chord / chord.lua
1 -- A SimGrid Lua implementation of the Chord DHT
2
3 -- Copyright (c) 2011-2012, 2014. The SimGrid Team.
4 -- All rights reserved.
5
6 -- This program is free software; you can redistribute it and/or modify it
7 -- under the terms of the license (GNU LGPL) which comes with this package.
8
9 require("simgrid")
10
11 nb_bits                 = 24
12 nb_keys                 = 2^nb_bits
13 comp_size               = 0
14 comm_size               = 10
15 timeout                 = 50
16 max_simulation_time     = 1000
17 stabilize_delay         = 20
18 fix_fingers_delay       = 120
19 check_predecessor_delay = 120
20 lookup_delay            = 10
21
22 -- current node (don't worry, globals are duplicated in each simulated process)
23 my_node = {
24   id = my_id,
25   next_finger_to_fix = 1,
26   fingers = {},
27   predecessor = nil,
28   comm_recv = nil
29 }
30
31 -- Main function of each Chord process
32 -- Arguments:
33 -- - my id
34 -- - the id of a guy I know in the system (except for the first node)
35 function node(...)
36
37   -- TODO simplify the deployment file
38   local known_id
39   local args = {...}
40   my_node.id = tonumber(args[1])
41   if #args == 4 then
42     known_id = tonumber(args[2])
43   end
44
45   -- initialize the node
46   for i = 1, nb_bits do
47     my_node.fingers[i] = my_node.id
48   end
49   my_node.comm_recv = simgrid.task.irecv(my_node.id)
50
51   -- join the ring
52   local join_success = false
53   if known_id == nil then
54     -- first node
55     create()
56     join_success = true
57   else
58     join_success = join(known_id)
59   end
60
61   -- main loop
62   if join_success then
63
64     local now = simgrid.get_clock()
65     local next_stabilize_date = now + stabilize_delay
66     local next_fix_fingers_date = now + fix_fingers_delay
67     local next_check_predecessor_date = now + check_predecessor_delay
68     local next_lookup_date = now + lookup_delay
69
70     local task, err
71
72     while now < max_simulation_time do
73
74       task, err = my_node.comm_recv:test()
75
76       if task then
77         -- I received a task: answer it
78         my_node.comm_recv = simgrid.task.irecv(my_node.id)
79         handle_task(task)
80       elseif err then
81         -- the communication has failed: nevermind
82         my_node.comm_recv = simgrid.task.irecv(my_node.id)
83       else
84         -- no task was received: do periodic calls
85         if now >= next_stabilize_date then
86           stabilize()
87           next_stabilize_date = simgrid.get_clock() + stabilize_delay
88
89         elseif now >= next_fix_fingers_date then
90           fix_fingers()
91           next_fix_fingers_date = simgrid.get_clock() + fix_fingers_delay
92
93         elseif now >= next_check_predecessor_date then
94           check_predecessor()
95           next_check_predecessor_date = simgrid.get_clock() + check_predecessor_delay
96
97         elseif now >= next_lookup_date then
98           random_lookup()
99           next_lookup_date = simgrid.get_clock() + lookup_delay
100
101         else
102           -- nothing to do: sleep for a while
103           simgrid.process.sleep(5)
104         end
105       end
106       now = simgrid.get_clock()
107     end -- while
108
109     -- leave the ring
110     leave()
111   end
112 end
113
114 -- Makes the current node leave the ring
115 function leave()
116
117   simgrid.info("Leaving the ring")
118   -- TODO: notify others
119 end
120
121 -- This function is called when the current node receives a task.
122 -- - task: the task received
123 function handle_task(task)
124
125   local type = task.type
126
127   if type == "find successor" then
128
129     simgrid.info("Received a 'find successor' request from " .. task.answer_to ..
130         " for id " .. task.request_id)
131
132     -- is my successor the successor?
133     if is_in_interval(task.request_id, my_node.id + 1, my_node.fingers[1]) then
134
135       simgrid.info("Sending back a 'find successor answer' to " ..
136           task.answer_to .. ": the successor of " .. task.request_id ..
137           " is " .. my_node.fingers[1])
138
139       task.type = "find successor answer"
140       task.answer = my_node.fingers[1]
141       task:dsend(task.answer_to)
142     else
143       -- forward the request to the closest preceding finger in my table
144
145       simgrid.info("Forwarding the 'find successor' request to my closest preceding finger")
146       task:dsend(closest_preceding_node(task.request_id))
147     end
148
149   elseif type == "get predecessor" then
150     task.type = "get predecessor answer"
151     task.answer = my_node.predecessor
152     task:dsend(task.answer_to)
153
154   elseif type == "notify" then
155     -- someone is telling me that he may be my new predecessor
156     notify(task.request_id)
157
158   elseif type == "predecessor leaving" then
159     -- TODO
160
161   elseif type == "successor_leaving" then
162     -- TODO
163
164   elseif type == "find successor answer" then
165     -- ignoring
166
167   elseif type == "get predecessor answer" then
168     -- ignoring
169
170   else
171     error("Unknown type of task received: " .. task.type)
172   end
173 end
174
175 -- Returns whether an id belongs to the interval [a, b[.
176 -- The parameters are normalized to make sure they are between 0 and nb_keys - 1.
177 -- 1 belongs to [62, 3].
178 -- 1 does not belong to [3, 62].
179 -- 63 belongs to [62, 3].
180 -- 63 does not belong to [3, 62].
181 -- 24 belongs to [21, 29].
182 -- 24 does not belong to [29, 21].
183 function is_in_interval(id, a, b)
184
185   -- normalize the parameters
186   id = id % nb_bits
187   a = a % nb_bits
188   b = b % nb_bits
189  
190   -- make sure a <= b and a <= id
191   if b < a then
192     b = b + nb_keys
193   end
194
195   if id < a then
196     id = id + nb_keys
197   end
198
199   return id <= b
200 end
201
202 -- Returns whether the current node is in the ring.
203 function has_joined()
204
205   return my_node.fingers[1] ~= nil
206 end
207
208 -- Creates a new Chord ring.
209 function create()
210   my_node.predecessor = nil
211   my_node.fingers[1] = my_node.id
212 end
213
214 -- Attemps to join the Chord ring.
215 -- - known_id: id of a node already in the ring
216 -- - return value: true if the join was successful
217 function join(known_id)
218
219   simgrid.info("Joining the ring with id " .. my_node.id .. ", knowing node " .. known_id)
220
221   local successor = remote_find_successor(known_id, my_node.id)
222   if successor == nil then
223     simgrid.info("Cannot join the ring.")
224     return false
225   end
226
227   my_node.predecessor = nil
228   my_node.fingers[1] = successor
229   return true
230 end
231
232 -- Returns the closest preceding finger of an id with respect to the finger
233 -- table of the current node.
234 -- - id: the id to find
235 -- - return value: the closest preceding finger of that id
236 function closest_preceding_node(id)
237
238   for i = nb_bits, 1, -1 do
239     if is_in_interval(my_node.fingers[i], my_node.id + 1, id - 1) then
240       -- finger i is the first one before id
241       return my_node.fingers[i]
242     end
243   end
244 end
245
246 -- Finds the successor of an id
247 -- id: the id to find
248 -- return value: the id of the successor, or nil if the request failed
249 function find_successor(id)
250
251   if is_in_interval(id, my_node.id + 1, my_node.fingers[1]) then
252     -- my successor is the successor
253     return my_node.fingers[1]
254   end
255
256   -- ask to the closest preceding finger in my table
257   local ask_to = closest_preceding_node(id)
258   return remote_find_successor(ask_to, id)
259 end
260
261 -- Asks a remote node the successor of an id.
262 -- ask_to: id of a remote node to ask to
263 -- id: the id to find
264 -- return value: the id of the successor, or nil if the request failed
265 function remote_find_successor(ask_to, id)
266
267   local task = simgrid.task.new("", comp_size, comm_size)
268   task.type = "find successor"
269   task.request_id = id
270   task.answer_to = my_node.id
271
272   simgrid.info("Sending a 'find successor' request to " .. ask_to .. " for id " .. id)
273   if task:send(ask_to, timeout) then
274     -- request successfully sent: wait for an answer
275
276     simgrid.info("Sent the 'find successor' request to " .. ask_to ..
277         " for id " .. id .. ", waiting for the answer")
278
279     while true do
280       task = my_node.comm_recv:wait(timeout)
281       my_node.comm_recv = simgrid.task.irecv(my_node.id)
282     
283       if not task then
284         -- failed to receive the answer
285         simgrid.info("Failed to receive the answer to my 'find successor' request")
286         return nil
287       else
288         -- a task was received: is it the expected answer?
289         if task.type ~= "find successor answer" or task.request_id ~= id then
290           -- this is not our answer
291           simgrid.info("Received another request of type " .. task.type)
292           handle_task(task)
293         else
294           -- this is our answer
295           simgrid.info("Received the answer to my 'find successor' request for id " ..
296               id .. ": the successor is " .. task.answer)
297           return task.answer
298         end
299       end
300     end
301   else
302     simgrid.info("Failed to send the 'find successor' request to " .. ask_to ..
303         " for id " .. id)
304   end
305
306   return successor
307 end
308
309 -- Asks a remote node its predecessor.
310 -- ask_to: id of a remote node to ask to
311 -- return value: the id of its predecessor, or nil if the request failed
312 function remote_get_predecessor(ask_to)
313
314   local task = simgrid.task.new("", comp_size, comm_size)
315   task.type = "get predecessor"
316   task.answer_to = my_node.id
317
318   if task:send(ask_to, timeout) then
319     -- request successfully sent: wait for an answer
320     while true do
321       task = my_node.comm_recv:wait(timeout)
322       my_node.comm_recv = simgrid.task.irecv(my_node.id)
323     
324       if not task then
325         -- failed to receive the answer
326         return nil
327       else
328         -- a task was received: is it the expected answer?
329         if task.type ~= "get predecessor answer" then
330           -- this is not our answer
331           handle_task(task)
332         else
333           -- this is our answer
334           -- FIXME make sure the message answers to this particular request
335           return task.answer
336         end
337       end
338     end
339   end
340
341   return successor
342 end
343
344 -- Checks the immediate successor of the current node.
345 function stabilize()
346
347   local candidate
348   local successor = my_node.fingers[1]
349   if successor ~= my_node.id then
350     candidate = remote_get_predecessor(successor)
351   else
352     candidate = my_node.predecessor
353   end
354
355   -- this node is a candidate to become my new successor
356   if candidate ~= nil and is_in_interval(candidate, my_node.id + 1, successor - 1) then
357     my_node.fingers[1] = candidate
358   end
359
360   if successor ~= my_node.id then
361     remote_notify(successor, my_node.id)
362   end
363 end
364
365 -- Notifies the current node that its predecessor my have changed
366 -- - candidate_predecessor: the possible new predecessor
367 function notify(candidate_predecessor)
368
369   if my_node.predecessor == nil or is_in_interval(candidate_predecessor,
370       my_node.predecessor + 1, my_node.id - 1) then
371     my_node.predecessor = candidate_predecessor
372   end
373 end
374
375 -- Notifies a remote node that its predecessor my have changed.
376 -- - notify_to
377 -- - candidate the possible new predecessor
378 function remote_notify(notify_to, candidate_predecessor)
379
380   local task = simgrid.task.new("", comp_size, comm_size)
381   task.type = "notify"
382   task.request_id = candidate_predecessor
383   task:dsend(notify_to)
384 end
385
386 -- Refreshes the finger table of the current node.
387 function fix_fingers()
388
389   local i = my_node.next_finger_to_fix
390   local id = find_successor(my_node.id + 2^i)
391   if id ~= nil then
392     if id ~= my_node.fingers[i] then
393       my_node.fingers[i] = id
394     end
395     my_node.next_finger_to_fix = (i % nb_bits) + 1
396   end
397 end
398
399 -- Checks whether the predecessor of the current node has failed.
400 function check_predecessor()
401   -- TODO
402 end
403
404 -- Performs a find successor request to an arbitrary id.
405 function random_lookup()
406
407   find_successor(1337)
408 end
409
410 simgrid.platform(arg[1] or "../../msg/msg_platform.xml")
411 simgrid.application(arg[2] or "../../msg/chord/chord90.xml")
412 simgrid.run()
413