- more barrier code
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_barriers.c
1 /*
2   This file is part of GNUnet.
3   (C) 2008--2013 Christian Grothoff (and other contributing authors)
4
5   GNUnet is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published
7   by the Free Software Foundation; either version 3, or (at your
8   option) any later version.
9
10   GNUnet is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with GNUnet; see the file COPYING.  If not, write to the
17   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18   Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file testbed/gnunet-service-testbed_barriers.c
23  * @brief barrier handling at the testbed controller
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in> 
25  */
26
27 #include "gnunet-service-testbed.h"
28 #include "gnunet-service-testbed_barriers.h"
29
30
31 /**
32  * timeout for outgoing message transmissions in seconds
33  */
34 #define MESSAGE_SEND_TIMEOUT(s) \
35   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, s)
36
37
38 /**
39  * Test to see if local peers have reached the required quorum of a barrier
40  */
41 #define LOCAL_QUORUM_REACHED(barrier)           \
42   ((barrier->quorum * GST_num_local_peers) <= (barrier->nreached * 100))
43
44
45 /**
46  * Barrier
47  */
48 struct Barrier;
49
50
51 /**
52  * Message queue for transmitting messages
53  */
54 struct MessageQueue
55 {
56   /**
57    * next pointer for DLL
58    */
59   struct MessageQueue *next;
60
61   /**
62    * prev pointer for DLL
63    */
64   struct MessageQueue *prev;
65
66   /**
67    * The message to be sent
68    */
69   struct GNUNET_MessageHeader *msg;
70 };
71
72
73 /**
74  * Context to be associated with each client
75  */
76 struct ClientCtx
77 {
78   /**
79    * The barrier this client is waiting for
80    */
81   struct Barrier *barrier;
82
83   /**
84    * DLL next ptr
85    */
86   struct ClientCtx *next;
87
88   /**
89    * DLL prev ptr
90    */
91   struct ClientCtx *prev;
92
93   /**
94    * The client handle
95    */
96   struct GNUNET_SERVER_Client *client;
97
98   /**
99    * the transmission handle
100    */
101   struct GNUNET_SERVER_TransmitHandle *tx;
102
103   /**
104    * message queue head
105    */
106   struct MessageQueue *mq_head;
107
108   /**
109    * message queue tail
110    */
111   struct MessageQueue *mq_tail;
112 };
113
114
115 /**
116  * Wrapper around Barrier handle
117  */
118 struct WBarrier
119 {
120   /**
121    * DLL next pointer
122    */
123   struct WBarrier *next;
124
125   /**
126    * DLL prev pointer
127    */
128   struct WBarrier *prev;
129
130   /**
131    * The local barrier associated with the creation of this wrapper
132    */
133   struct Barrier *barrier;
134
135   /**
136    * The barrier handle from API
137    */
138   struct GNUNET_TESTBED_Barrier *hbarrier;
139
140   /**
141    * Has this barrier been crossed?
142    */
143   uint8_t reached;
144 };
145
146
147 /**
148  * Barrier
149  */
150 struct Barrier
151 {
152   /**
153    * The hashcode of the barrier name
154    */
155   struct GNUNET_HashCode hash;
156
157   /**
158    * The client handle to the master controller
159    */
160   struct GNUNET_SERVER_Client *client;
161
162   /**
163    * The name of the barrier
164    */
165   char *name;
166
167   /**
168    * DLL head for the list of clients waiting for this barrier
169    */
170   struct ClientCtx *head;
171
172   /**
173    * DLL tail for the list of clients waiting for this barrier
174    */
175   struct ClientCtx *tail;
176
177   /**
178    * DLL head for the list of barrier handles
179    */
180   struct WBarrier *whead;
181
182   /**
183    * DLL tail for the list of barrier handles
184    */
185   struct WBarrier *wtail;
186
187   /**
188    * Identifier for the timeout task
189    */
190   GNUNET_SCHEDULER_TaskIdentifier tout_task;
191   
192   /**
193    * The status of this barrier
194    */
195   enum GNUNET_TESTBED_BarrierStatus status;
196   
197   /**
198    * Number of barriers wrapped in the above DLL
199    */
200   unsigned int num_wbarriers;
201
202   /**
203    * Number of wrapped barriers reached so far
204    */
205   unsigned int num_wbarriers_reached;
206
207   /**
208    * Number of wrapped barrier initialised so far
209    */
210   unsigned int num_wbarriers_inited;
211
212   /**
213    * Number of peers which have reached this barrier
214    */
215   unsigned int nreached;
216
217   /**
218    * Number of slaves we have initialised this barrier
219    */
220   unsigned int nslaves;
221
222   /**
223    * Quorum percentage to be reached
224    */
225   uint8_t quorum;
226   
227   /**
228    * Was there a timeout while propagating initialisation
229    */
230   uint8_t timedout;
231 };
232
233
234 /**
235  * Hashtable handle for storing initialised barriers
236  */
237 static struct GNUNET_CONTAINER_MultiHashMap *barrier_map;
238
239 /**
240  * Service context
241  */
242 static struct GNUNET_SERVICE_Context *ctx;
243
244
245 /**
246  * Function called to notify a client about the connection
247  * begin ready to queue more data.  "buf" will be
248  * NULL and "size" zero if the connection was closed for
249  * writing in the meantime.
250  *
251  * @param cls client context
252  * @param size number of bytes available in buf
253  * @param buf where the callee should write the message
254  * @return number of bytes written to buf
255  */
256 static size_t 
257 transmit_ready_cb (void *cls, size_t size, void *buf)
258 {
259   struct ClientCtx *ctx = cls;
260   struct GNUNET_SERVER_Client *client = ctx->client;
261   struct MessageQueue *mq;
262   struct GNUNET_MessageHeader *msg;
263   size_t wrote;
264
265   ctx->tx = NULL;
266   wrote = 0;
267   if ((0 == size) || (NULL == buf))
268   {
269     GNUNET_assert (NULL != ctx->client);
270     GNUNET_SERVER_client_drop (ctx->client);
271     ctx->client = NULL;    
272     return 0;
273   }
274   mq = ctx->mq_head;
275   msg = mq->msg;
276   wrote = ntohs (msg->size);
277   GNUNET_assert (size >= wrote);
278   (void) memcpy (buf, msg, wrote);
279   GNUNET_CONTAINER_DLL_remove (ctx->mq_head, ctx->mq_tail, mq);
280   GNUNET_free (mq->msg);
281   GNUNET_free (mq);
282   if (NULL != (mq = ctx->mq_head))
283     ctx->tx = GNUNET_SERVER_notify_transmit_ready (client, ntohs (msg->size),
284                                                   MESSAGE_SEND_TIMEOUT (30),
285                                                   &transmit_ready_cb, ctx);
286   return wrote;
287 }
288
289
290 /**
291  * Queue a message into a clients message queue
292  *
293  * @param ctx the context associated with the client
294  * @param msg the message to queue.  Will be consumed
295  */
296 static void
297 queue_message (struct ClientCtx *ctx, struct GNUNET_MessageHeader *msg)
298 {
299   struct MessageQueue *mq;
300   struct GNUNET_SERVER_Client *client = ctx->client;
301   
302   mq = GNUNET_malloc (sizeof (struct MessageQueue));
303   mq->msg = msg;
304   GNUNET_CONTAINER_DLL_insert_tail (ctx->mq_head, ctx->mq_tail, mq);
305   if (NULL == ctx->tx)
306    ctx->tx = GNUNET_SERVER_notify_transmit_ready (client, ntohs (msg->size),
307                                                   MESSAGE_SEND_TIMEOUT (30),
308                                                   &transmit_ready_cb, ctx);
309 }
310
311
312 /**
313  * Function to remove a barrier from the barrier map and cleanup resources
314  * occupied by a barrier
315  *
316  * @param barrier the barrier handle
317  */
318 static void
319 remove_barrier (struct Barrier *barrier)
320 {
321   GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (barrier_map,
322                                                                     &barrier->hash,
323                                                                     barrier));
324   GNUNET_free (barrier->name);
325   GNUNET_SERVER_client_drop (barrier->client);
326   GNUNET_free (barrier);
327 }
328
329
330 /**
331  * Cancels all subcontroller barrier handles
332  *
333  * @param barrier the local barrier
334  */
335 static void
336 cancel_wrappers (struct Barrier *barrier)
337 {
338   struct WBarrier *wrapper;
339
340   while (NULL != (wrapper = barrier->whead))
341   {
342     GNUNET_TESTBED_barrier_cancel (wrapper->hbarrier);
343     GNUNET_CONTAINER_DLL_remove (barrier->whead, barrier->wtail, wrapper);
344     GNUNET_free (wrapper);
345   }
346 }
347
348
349 /**
350  * Send a status message about a barrier to the given client
351  *
352  * @param client the client to send the message to
353  * @param name the barrier name
354  * @param status the status of the barrier
355  * @param emsg the error message; should be non-NULL for
356  *   status=BARRIER_STATUS_ERROR 
357  */
358 static void
359 send_client_status_msg (struct GNUNET_SERVER_Client *client,
360                         const char *name,
361                         enum GNUNET_TESTBED_BarrierStatus status,
362                         const char *emsg)
363 {
364   struct GNUNET_TESTBED_BarrierStatusMsg *msg;
365   size_t name_len;
366   uint16_t msize;
367
368   GNUNET_assert ((NULL == emsg) || (BARRIER_STATUS_ERROR == status));
369   name_len = strlen (name) + 1;
370   msize = sizeof (struct GNUNET_TESTBED_BarrierStatusMsg)
371       + name_len
372       + (NULL == emsg) ? 0 : strlen (emsg) + 1;
373   msg = GNUNET_malloc (msize);
374   msg->status = htons (status);
375   msg->name_len = htons ((uint16_t) name_len);
376   (void) memcpy (msg->data, name, name_len);
377   if (NULL != emsg)
378     (void) memcpy (msg->data + name_len, emsg, strlen (emsg) + 1);
379   GST_queue_message (client, &msg->header);
380 }
381
382
383 /**
384  * Sends a barrier failed message
385  *
386  * @param barrier the corresponding barrier
387  * @param emsg the error message; should be non-NULL for
388  *   status=BARRIER_STATUS_ERROR 
389  */
390 static void
391 send_barrier_status_msg (struct Barrier *barrier, const char *emsg)
392 {
393   GNUNET_assert (0 != barrier->status);
394   send_client_status_msg (barrier->client, barrier->name,
395                           barrier->status, emsg);
396 }
397
398
399
400 /**
401  * Task for sending barrier crossed notifications to waiting client
402  *
403  * @param cls the barrier which is crossed
404  * @param tc scheduler task context
405  */
406 static void
407 notify_task_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
408 {
409   struct Barrier *barrier = cls;
410   struct ClientCtx *client_ctx;
411   struct GNUNET_TESTBED_BarrierStatusMsg *msg;
412   struct GNUNET_MessageHeader *dup_msg;
413   uint16_t name_len;
414   uint16_t msize;
415
416   name_len = strlen (barrier->name) + 1;
417   msize = sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len;  
418   msg = GNUNET_malloc (msize);
419   msg->header.size = htons (msize);
420   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS);
421   msg->status = 0;
422   msg->name_len = htons (name_len);
423   (void) memcpy (msg->data, barrier->name, name_len);
424   msg->data[name_len] = '\0';
425   while (NULL != (client_ctx = barrier->head))
426   {
427     dup_msg = GNUNET_copy_message (&msg->header);
428     queue_message (client_ctx, dup_msg);
429     GNUNET_CONTAINER_DLL_remove (barrier->head, barrier->tail, client_ctx);
430     GNUNET_SERVER_client_set_user_context_ (client_ctx->client, NULL, 0);
431     GNUNET_free (client_ctx);
432   }
433 }
434
435
436 /**
437  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT messages.  This
438  * message should come from peers or a shared helper service using the
439  * testbed-barrier client API (@see gnunet_testbed_barrier_service.h)
440  *
441  * This handler is queued in the main service and will handle the messages sent
442  * either from the testbed driver or from a high level controller
443  *
444  * @param cls NULL
445  * @param client identification of the client
446  * @param message the actual message
447  */
448 static void
449 handle_barrier_wait (void *cls, struct GNUNET_SERVER_Client *client,
450                      const struct GNUNET_MessageHeader *message)
451 {
452   const struct GNUNET_TESTBED_BarrierWait *msg;
453   struct Barrier *barrier;
454   char *name;
455   struct ClientCtx *client_ctx;
456   struct GNUNET_HashCode key;
457   size_t name_len;
458   uint16_t msize;
459   
460   msize = ntohs (message->size);
461   if (msize <= sizeof (struct GNUNET_TESTBED_BarrierWait))
462   {
463     GNUNET_break_op (0);
464     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
465     return;
466   }
467   if (NULL == barrier_map)
468   {
469     GNUNET_break (0);
470     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
471     return;
472   }
473   msg = (const struct GNUNET_TESTBED_BarrierWait *) message;
474   name_len = msize - sizeof (struct GNUNET_TESTBED_BarrierWait);
475   name = GNUNET_malloc (name_len + 1);
476   name[name_len] = '\0';
477   (void) memcpy (name, msg->name, name_len);
478   GNUNET_CRYPTO_hash (name, name_len - 1, &key);
479   if (NULL == (barrier = GNUNET_CONTAINER_multihashmap_get (barrier_map, &key)))
480   {
481     GNUNET_break (0);
482     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
483     GNUNET_free (name);
484     return;
485   }
486   client_ctx = GNUNET_SERVER_client_get_user_context (client, struct ClientCtx);
487   if (NULL == client_ctx)
488   {
489     client_ctx = GNUNET_malloc (sizeof (struct ClientCtx));
490     client_ctx->client = client;
491     GNUNET_SERVER_client_keep (client);
492     client_ctx->barrier = barrier;
493     GNUNET_CONTAINER_DLL_insert_tail (barrier->head, barrier->tail, client_ctx);
494     barrier->nreached++;
495     if (LOCAL_QUORUM_REACHED (barrier))
496       notify_task_cb (barrier, NULL);
497   }
498   GNUNET_SERVER_receive_done (client, GNUNET_OK);
499 }
500
501
502 /**
503  * Functions with this signature are called whenever a client
504  * is disconnected on the network level.
505  *
506  * @param cls closure
507  * @param client identification of the client; NULL
508  *        for the last call when the server is destroyed
509  */
510 static void
511 disconnect_cb (void *cls, struct GNUNET_SERVER_Client *client)
512 {
513   struct ClientCtx *client_ctx;
514   struct Barrier *barrier;
515   
516   client_ctx = GNUNET_SERVER_client_get_user_context (client, struct ClientCtx);
517   if (NULL == client_ctx)
518     return;
519   barrier = client_ctx->barrier;
520   GNUNET_CONTAINER_DLL_remove (barrier->head, barrier->tail, client_ctx);
521   if (NULL != client_ctx->tx)
522     GNUNET_SERVER_notify_transmit_ready_cancel (client_ctx->tx);
523 }
524
525
526 /**
527  * Function to initialise barrriers component
528  */
529 void
530 GST_barriers_init (struct GNUNET_CONFIGURATION_Handle *cfg)
531 {
532   static const struct GNUNET_SERVER_MessageHandler message_handlers[] = {
533     {&handle_barrier_wait, NULL, GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT, 0},
534     {NULL, NULL, 0, 0}
535   };
536   struct GNUNET_SERVER_Handle *srv;
537
538   barrier_map = GNUNET_CONTAINER_multihashmap_create (3, GNUNET_YES);
539   ctx = GNUNET_SERVICE_start ("testbed-barrier", cfg,
540                               GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN);
541   srv = GNUNET_SERVICE_get_server (ctx);
542   GNUNET_SERVER_add_handlers (srv, message_handlers);
543   GNUNET_SERVER_disconnect_notify (srv, &disconnect_cb, NULL);  
544 }
545
546
547 /**
548  * Function to stop the barrier service
549  */
550 void
551 GST_barriers_stop ()
552 {
553   GNUNET_assert (NULL != barrier_map);
554   GNUNET_CONTAINER_multihashmap_destroy (barrier_map);
555   GNUNET_assert (NULL != ctx);
556   GNUNET_SERVICE_stop (ctx);
557 }
558
559
560 /**
561  * Functions of this type are to be given as callback argument to
562  * GNUNET_TESTBED_barrier_init().  The callback will be called when status
563  * information is available for the barrier.
564  *
565  * @param cls the closure given to GNUNET_TESTBED_barrier_init()
566  * @param name the name of the barrier
567  * @param barrier the barrier handle
568  * @param status status of the barrier; GNUNET_OK if the barrier is crossed;
569  *   GNUNET_SYSERR upon error
570  * @param emsg if the status were to be GNUNET_SYSERR, this parameter has the
571  *   error messsage
572  */
573 static void 
574 wbarrier_status_cb (void *cls, const char *name,
575                     struct GNUNET_TESTBED_Barrier *b_,
576                     enum GNUNET_TESTBED_BarrierStatus status,
577                     const char *emsg)
578 {
579   struct WBarrier *wrapper = cls;
580   struct Barrier *barrier = wrapper->barrier;
581
582   GNUNET_assert (b_ == wrapper->hbarrier);
583   wrapper->hbarrier = NULL;
584   GNUNET_CONTAINER_DLL_remove (barrier->whead, barrier->wtail, wrapper);
585   GNUNET_free (wrapper);
586   if (BARRIER_STATUS_ERROR == status)
587   {
588     LOG (GNUNET_ERROR_TYPE_ERROR,
589          "Initialising barrier `%s' failed at a sub-controller: %s\n",
590          barrier->name, (NULL != emsg) ? emsg : "NULL");
591     cancel_wrappers (barrier);
592     if (NULL == emsg)
593       emsg = "Initialisation failed at a sub-controller";
594     barrier->status = BARRIER_STATUS_ERROR;
595     send_barrier_status_msg (barrier, emsg);
596     return;
597   }
598   switch (status)
599   {
600   case BARRIER_STATUS_CROSSED:
601     if (BARRIER_STATUS_INITIALISED != barrier->status)
602     {
603       GNUNET_break_op (0);
604       return;
605     }
606     barrier->num_wbarriers_reached++;
607     if ((barrier->num_wbarriers_reached == barrier->num_wbarriers)
608         && (LOCAL_QUORUM_REACHED (barrier)))
609     {
610       barrier->status = BARRIER_STATUS_CROSSED;
611       send_barrier_status_msg (barrier, NULL);
612     }
613     break;
614   case BARRIER_STATUS_INITIALISED:
615     if (0 != barrier->status)
616     {
617       GNUNET_break_op (0);
618       return;
619     }
620     barrier->num_wbarriers_inited++;
621     if (barrier->num_wbarriers_inited == barrier->num_wbarriers)
622     {
623       barrier->status = BARRIER_STATUS_INITIALISED;
624       send_barrier_status_msg (barrier, NULL);
625     }
626     break;
627   case BARRIER_STATUS_ERROR:
628     GNUNET_assert (0);
629   }
630   return;
631 }
632
633
634 /**
635  * Function called upon timeout while waiting for a response from the
636  * subcontrollers to barrier init message
637  *
638  * @param 
639  * @return 
640  */
641 static void
642 fwd_tout_barrier_init (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
643 {
644   struct Barrier *barrier = cls;
645   
646   barrier->nslaves--;
647   barrier->timedout = GNUNET_YES;
648   cancel_wrappers (barrier);
649   barrier->status = BARRIER_STATUS_ERROR;
650   send_barrier_status_msg (barrier,
651                            "Timedout while propagating barrier initialisation\n");
652   remove_barrier (barrier);
653 }
654
655
656 /**
657  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_INIT messages.  This
658  * message should always come from a parent controller or the testbed API if we
659  * are the root controller.
660  *
661  * This handler is queued in the main service and will handle the messages sent
662  * either from the testbed driver or from a high level controller
663  *
664  * @param cls NULL
665  * @param client identification of the client
666  * @param message the actual message
667  */
668 void
669 GST_handle_barrier_init (void *cls, struct GNUNET_SERVER_Client *client,
670                          const struct GNUNET_MessageHeader *message)
671 {
672   const struct GNUNET_TESTBED_BarrierInit *msg;
673   char *name;
674   struct Barrier *barrier;
675   struct Slave *slave;
676   struct WBarrier *wrapper;
677   struct GNUNET_HashCode hash;
678   size_t name_len;
679   unsigned int cnt;
680   uint16_t msize;
681   
682   if (NULL == GST_context)
683   {
684     GNUNET_break_op (0);
685     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
686     return;
687   }
688   if (client != GST_context->client)
689   {
690     GNUNET_break_op (0);
691     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
692     return;
693   }
694   msize = ntohs (message->size);
695   if (msize <= sizeof (struct GNUNET_TESTBED_BarrierInit))
696   {
697     GNUNET_break_op (0);
698     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
699     return;
700   }
701   msg = (const struct GNUNET_TESTBED_BarrierInit *) message;
702   name_len = (size_t) msize - sizeof (struct GNUNET_TESTBED_BarrierInit);
703   name = GNUNET_malloc (name_len + 1);
704   (void) memcpy (name, msg->name, name_len);
705   name[name_len] = '\0';
706   GNUNET_CRYPTO_hash (name, name_len, &hash);
707   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (barrier_map, &hash))
708   {
709   
710     send_client_status_msg (client, name, BARRIER_STATUS_ERROR,
711                             "A barrier with the same name already exists");
712     GNUNET_free (name);
713     GNUNET_SERVER_receive_done (client, GNUNET_OK);
714     return;
715   }
716   barrier = GNUNET_malloc (sizeof (struct Barrier));
717   (void) memcpy (&barrier->hash, &hash, sizeof (struct GNUNET_HashCode));
718   barrier->quorum = msg->quorum;
719   barrier->name = name;
720   barrier->client = client;
721   GNUNET_SERVER_client_keep (client);
722   GNUNET_assert (GNUNET_OK ==
723                  GNUNET_CONTAINER_multihashmap_put (barrier_map,
724                                                     &barrier->hash,
725                                                     barrier,
726                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
727   GNUNET_SERVER_receive_done (client, GNUNET_OK);
728   /* Propagate barrier init to subcontrollers */
729   for (cnt = 0; cnt < GST_slave_list_size; cnt++)
730   {
731     if (NULL == (slave = GST_slave_list[cnt]))
732       continue;
733     if (NULL == slave->controller)
734     {
735       GNUNET_break (0);/* May happen when we are connecting to the controller */
736       continue;
737     }
738     wrapper = GNUNET_malloc (sizeof (struct WBarrier));
739     wrapper->barrier = barrier;
740     GNUNET_CONTAINER_DLL_insert_tail (barrier->whead, barrier->wtail, wrapper);
741     wrapper->hbarrier = GNUNET_TESTBED_barrier_init (slave->controller,
742                                                      barrier->name,
743                                                      barrier->quorum,
744                                                      &wbarrier_status_cb,
745                                                      wrapper);    
746   }
747   if (NULL == barrier->whead)   /* No further propagation */
748   {
749     barrier->status = BARRIER_STATUS_INITIALISED;
750     send_barrier_status_msg (barrier, NULL);
751   }else
752     barrier->tout_task = GNUNET_SCHEDULER_add_delayed (MESSAGE_SEND_TIMEOUT (30),
753                                                        &fwd_tout_barrier_init,
754                                                        barrier);
755 }
756
757
758 /**
759  * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_CANCEL messages.  This
760  * message should always come from a parent controller or the testbed API if we
761  * are the root controller.
762  *
763  * This handler is queued in the main service and will handle the messages sent
764  * either from the testbed driver or from a high level controller
765  *
766  * @param cls NULL
767  * @param client identification of the client
768  * @param message the actual message
769  */
770 void
771 GST_handle_barrier_cancel (void *cls, struct GNUNET_SERVER_Client *client,
772                            const struct GNUNET_MessageHeader *message)
773 {
774   GNUNET_break (0);
775 }
776
777 /* end of gnunet-service-testbed_barriers.c */