remove global variable for barriers, move into controller
[oweals/gnunet.git] / src / testbed / testbed_api_barriers.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2008--2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18   Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file testbed/testbed_api_barriers.c
23  * @brief API implementation for testbed barriers
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25  */
26
27 #include "platform.h"
28 #include "gnunet_testbed_service.h"
29 #include "testbed_api.h"
30 #include "testbed_api_barriers.h"
31
32 /**
33  * Logging shorthand
34  */
35 #define LOG(type, ...)                          \
36   GNUNET_log_from (type, "testbed-api-barriers", __VA_ARGS__);
37
38 /**
39  * Debug logging shorthand
40  */
41 #define LOG_DEBUG(...)                          \
42   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__);
43
44
45 /**
46  * Remove a barrier and it was the last one in the barrier hash map, destroy the
47  * hash map
48  *
49  * @param barrier the barrier to remove
50  */
51 void
52 GNUNET_TESTBED_barrier_remove_ (struct GNUNET_TESTBED_Barrier *barrier)
53 {
54   struct GNUNET_TESTBED_Controller *c = barrier->c;
55
56   GNUNET_assert (NULL != c->barrier_map); /* No barriers present */
57   GNUNET_assert (GNUNET_OK ==
58                  GNUNET_CONTAINER_multihashmap_remove (c->barrier_map,
59                                                        &barrier->key,
60                                                        barrier));
61   GNUNET_free (barrier->name);
62   GNUNET_free (barrier);
63   if (0 == GNUNET_CONTAINER_multihashmap_size (c->barrier_map))
64   {
65     GNUNET_CONTAINER_multihashmap_destroy (c->barrier_map);
66     c->barrier_map = NULL;
67   }
68 }
69
70
71 /**
72  * Initialise a barrier and call the given callback when the required percentage
73  * of peers (quorum) reach the barrier OR upon error.
74  *
75  * @param controller the handle to the controller
76  * @param name identification name of the barrier
77  * @param quorum the percentage of peers that is required to reach the barrier.
78  *   Peers signal reaching a barrier by calling
79  *   GNUNET_TESTBED_barrier_reached().
80  * @param cb the callback to call when the barrier is reached or upon error.
81  *   Cannot be NULL.
82  * @param cls closure for the above callback
83  * @param echo GNUNET_YES to echo the barrier crossed status message back to the
84  *   controller
85  * @return barrier handle; NULL upon error
86  */
87 struct GNUNET_TESTBED_Barrier *
88 GNUNET_TESTBED_barrier_init_ (struct GNUNET_TESTBED_Controller *controller,
89                               const char *name,
90                               unsigned int quorum,
91                               GNUNET_TESTBED_barrier_status_cb cb, void *cls,
92                               int echo)
93 {
94   struct GNUNET_TESTBED_BarrierInit *msg;
95   struct GNUNET_TESTBED_Barrier *barrier;
96   struct GNUNET_HashCode key;
97   size_t name_len;
98   uint16_t msize;
99
100   GNUNET_assert (quorum <= 100);
101   GNUNET_assert (NULL != cb);
102   name_len = strlen (name);
103   GNUNET_assert (0 < name_len);
104   GNUNET_CRYPTO_hash (name, name_len, &key);
105   if (NULL == controller->barrier_map)
106     controller->barrier_map = GNUNET_CONTAINER_multihashmap_create (3, GNUNET_YES);
107   if (GNUNET_YES ==
108       GNUNET_CONTAINER_multihashmap_contains (controller->barrier_map,
109                                               &key))
110   {
111     GNUNET_break (0);
112     return NULL;
113   }
114   LOG_DEBUG ("Initialising barrier `%s'\n", name);
115   barrier = GNUNET_new (struct GNUNET_TESTBED_Barrier);
116   barrier->c = controller;
117   barrier->name = GNUNET_strdup (name);
118   barrier->cb = cb;
119   barrier->cls = cls;
120   barrier->echo = echo;
121   (void) memcpy (&barrier->key, &key, sizeof (struct GNUNET_HashCode));
122   GNUNET_assert (GNUNET_OK ==
123                  GNUNET_CONTAINER_multihashmap_put (controller->barrier_map,
124                                                     &barrier->key,
125                                                     barrier,
126                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
127   msize = name_len + sizeof (struct GNUNET_TESTBED_BarrierInit);
128   msg = GNUNET_malloc (msize);
129   msg->header.size = htons (msize);
130   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_INIT);
131   msg->quorum = (uint8_t) quorum;
132   (void) memcpy (msg->name, barrier->name, name_len);
133   GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
134   return barrier;
135 }
136
137
138 /**
139  * Initialise a barrier and call the given callback when the required percentage
140  * of peers (quorum) reach the barrier OR upon error.
141  *
142  * @param controller the handle to the controller
143  * @param name identification name of the barrier
144  * @param quorum the percentage of peers that is required to reach the barrier.
145  *   Peers signal reaching a barrier by calling
146  *   GNUNET_TESTBED_barrier_reached().
147  * @param cb the callback to call when the barrier is reached or upon error.
148  *   Cannot be NULL.
149  * @param cls closure for the above callback
150  * @return barrier handle; NULL upon error
151  */
152 struct GNUNET_TESTBED_Barrier *
153 GNUNET_TESTBED_barrier_init (struct GNUNET_TESTBED_Controller *controller,
154                              const char *name,
155                              unsigned int quorum,
156                              GNUNET_TESTBED_barrier_status_cb cb, void *cls)
157 {
158   return GNUNET_TESTBED_barrier_init_ (controller,
159                                        name, quorum, cb, cls, GNUNET_YES);
160 }
161
162
163 /**
164  * Cancel a barrier.
165  *
166  * @param barrier the barrier handle
167  */
168 void
169 GNUNET_TESTBED_barrier_cancel (struct GNUNET_TESTBED_Barrier *barrier)
170 {
171   struct GNUNET_TESTBED_BarrierCancel *msg;
172   uint16_t msize;
173
174   msize = sizeof (struct GNUNET_TESTBED_BarrierCancel) + strlen (barrier->name);
175   msg = GNUNET_malloc (msize);
176   msg->header.size = htons (msize);
177   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_CANCEL);
178   (void) memcpy (msg->name, barrier->name, strlen (barrier->name));
179   GNUNET_TESTBED_queue_message_ (barrier->c, &msg->header);
180   GNUNET_TESTBED_barrier_remove_ (barrier);
181 }
182
183
184 /**
185  * Barrier wait handle
186  */
187 struct GNUNET_TESTBED_BarrierWaitHandle
188 {
189   /**
190    * The name of the barrier
191    */
192   char *name;
193
194   /**
195    * Then configuration used for the client connection
196    */
197   struct GNUNET_CONFIGURATION_Handle *cfg;
198
199   /**
200    * The client connection
201    */
202   struct GNUNET_CLIENT_Connection *conn;
203
204   /**
205    * Transmit handle
206    */
207   struct GNUNET_CLIENT_TransmitHandle *tx;
208
209   /**
210    * The message to transmit with tx
211    */
212   struct GNUNET_MessageHeader *msg;
213
214   /**
215    * The barrier wait callback
216    */
217   GNUNET_TESTBED_barrier_wait_cb cb;
218
219   /**
220    * The closure for the above callback
221    */
222   void *cls;
223 };
224
225
226 /**
227  * Function to destroy barrier wait handle
228  *
229  * @param h the handle to destroy
230  */
231 static void
232 destroy_handle (struct GNUNET_TESTBED_BarrierWaitHandle *h)
233 {
234   GNUNET_free (h->name);
235   if (NULL != h->tx)
236     GNUNET_CLIENT_notify_transmit_ready_cancel (h->tx);
237   if (NULL != h->conn)
238     GNUNET_CLIENT_disconnect (h->conn);
239   if (NULL != h->msg)
240     GNUNET_free (h->msg);
241   GNUNET_CONFIGURATION_destroy (h->cfg);
242   GNUNET_free (h);
243 }
244
245
246 /**
247  * Type of a function to call when we receive a message
248  * from the service.
249  *
250  * @param cls closure
251  * @param message received message; NULL on timeout or fatal error
252  */
253 static void
254 receive_handler (void *cls,
255                  const struct GNUNET_MessageHeader *message)
256 {
257   struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
258   const struct GNUNET_TESTBED_BarrierStatusMsg *msg;
259   uint16_t msize;
260
261   if (NULL == message)
262   {
263     GNUNET_break_op (0);
264     goto fail;
265   }
266   if (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS != ntohs (message->type))
267   {
268     GNUNET_break_op (0);
269     goto fail;
270   }
271   msize = ntohs (message->size);
272   if (msize <= sizeof (struct GNUNET_TESTBED_BarrierStatusMsg))
273   {
274     GNUNET_break_op (0);
275     goto fail;
276   }
277   msg = (const struct GNUNET_TESTBED_BarrierStatusMsg *) message;
278   switch (ntohs (msg->status))
279   {
280   case GNUNET_TESTBED_BARRIERSTATUS_ERROR:
281     goto fail;
282   case GNUNET_TESTBED_BARRIERSTATUS_INITIALISED:
283     GNUNET_break (0);           /* FIXME */
284     goto destroy;
285   case GNUNET_TESTBED_BARRIERSTATUS_CROSSED:
286     h->cb (h->cls, h->name, GNUNET_OK);
287     goto destroy;
288   default:
289     GNUNET_break_op (0);
290   }
291
292  fail:
293   h->cb (h->cls, h->name, GNUNET_SYSERR);
294
295  destroy:
296   destroy_handle (h);
297 }
298
299
300 /**
301  * Function called to notify a client about the connection
302  * begin ready to queue more data.  "buf" will be
303  * NULL and "size" zero if the connection was closed for
304  * writing in the meantime.
305  *
306  * @param cls closure
307  * @param size number of bytes available in buf
308  * @param buf where the callee should write the message
309  * @return number of bytes written to buf
310  */
311 static size_t
312 transmit_notify (void *cls, size_t size, void *buf)
313 {
314   struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
315   uint16_t msize;
316
317   h->tx = NULL;
318   if ((0 == size) || (NULL == buf))
319   {
320     destroy_handle (h);
321     return 0;
322   }
323   msize = htons (h->msg->size);
324   GNUNET_assert (msize <= size);
325   (void) memcpy (buf, h->msg, msize);
326   GNUNET_free (h->msg);
327   h->msg = NULL;
328   GNUNET_CLIENT_receive (h->conn, &receive_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
329   return msize;
330 }
331
332
333 /**
334  * Wait for a barrier to be crossed.  This function should be called by the
335  * peers which have been started by the testbed.  If the peer is not started by
336  * testbed this function may return error
337  *
338  * @param name the name of the barrier
339  * @param cb the barrier wait callback
340  * @param cls the closure for the above callback
341  * @return barrier wait handle which can be used to cancel the waiting at
342  *   anytime before the callback is called.  NULL upon error.
343  */
344 struct GNUNET_TESTBED_BarrierWaitHandle *
345 GNUNET_TESTBED_barrier_wait (const char *name,
346                              GNUNET_TESTBED_barrier_wait_cb cb,
347                              void *cls)
348 {
349   struct GNUNET_TESTBED_BarrierWait *msg;
350   struct GNUNET_CONFIGURATION_Handle *cfg;
351   struct GNUNET_TESTBED_BarrierWaitHandle *h;
352   char *cfg_filename;
353   size_t name_len;
354   uint16_t msize;
355
356   GNUNET_assert (NULL != cb);
357   GNUNET_assert (NULL != name);
358   cfg_filename = getenv (ENV_TESTBED_CONFIG);
359   if (NULL == cfg_filename)
360   {
361     LOG (GNUNET_ERROR_TYPE_ERROR, "Are you running under testbed?\n");
362     return NULL;
363   }
364   cfg = GNUNET_CONFIGURATION_create ();
365   if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_filename))
366   {
367     LOG (GNUNET_ERROR_TYPE_ERROR, "Unable to load configuration from file `%s'\n",
368          cfg_filename);
369     GNUNET_CONFIGURATION_destroy (cfg);
370     return NULL;
371   }
372   h = GNUNET_new (struct GNUNET_TESTBED_BarrierWaitHandle);
373   h->name = GNUNET_strdup (name);
374   h->cfg = cfg;
375   h->conn = GNUNET_CLIENT_connect ("testbed-barrier", h->cfg);
376   h->cb = cb;
377   h->cls = cls;
378   if (NULL == h->conn)
379   {
380     LOG (GNUNET_ERROR_TYPE_ERROR,
381          "Unable to connect to local testbed-barrier service\n");
382     destroy_handle (h);
383     return NULL;
384   }
385   name_len = strlen (name);
386   msize = sizeof (struct GNUNET_TESTBED_BarrierWait) + name_len;
387   msg = GNUNET_malloc (msize);
388   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT);
389   msg->header.size = htons (msize);
390   (void) memcpy (msg->name, name, name_len);
391   h->msg = &msg->header;
392   h->tx =
393       GNUNET_CLIENT_notify_transmit_ready (h->conn, msize,
394                                            GNUNET_TIME_UNIT_FOREVER_REL,
395                                            GNUNET_NO,
396                                            &transmit_notify,
397                                            h);
398   return h;
399 }
400
401
402 /**
403  * Cancel a barrier wait handle
404  *
405  * @param h the barrier wait handle
406  */
407 void
408 GNUNET_TESTBED_barrier_wait_cancel (struct GNUNET_TESTBED_BarrierWaitHandle *h)
409 {
410   destroy_handle (h);
411 }
412
413 /* end of testbed_api_barriers.c */