ab468a08877574c6fd06ae0c6b3b00ecd06c69d5
[oweals/gnunet.git] / src / testbed / testbed_api_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/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
31 /**
32  * Handle for barrier
33  */
34 struct GNUNET_TESTBED_Barrier
35 {
36   /**
37    * hashcode identifying this barrier in the hashmap
38    */
39   struct GNUNET_HashCode key;
40
41   /**
42    * The controller handle given while initiliasing this barrier
43    */
44   struct GNUNET_TESTBED_Controller *c;
45   
46   /**
47    * The name of the barrier
48    */
49   char *name;
50
51   /**
52    * The continuation callback to call when we have a status update on this
53    */
54   GNUNET_TESTBED_barrier_status_cb cb;
55
56   /**
57    * the closure for the above callback
58    */
59   void *cls;
60  
61 };
62
63
64 /**
65  * handle for hashtable of barrier handles
66  */
67 static struct GNUNET_CONTAINER_MultiHashMap *barrier_map;
68
69
70 /**
71  * Remove a barrier and it was the last one in the barrier hash map, destroy the
72  * hash map
73  *
74  * @param barrier the barrier to remove
75  */
76 static void
77 barrier_remove (struct GNUNET_TESTBED_Barrier *barrier)
78 {
79   GNUNET_assert (NULL != barrier_map); /* No barriers present */
80   GNUNET_assert (GNUNET_OK ==
81                  GNUNET_CONTAINER_multihashmap_remove (barrier_map,
82                                                        &barrier->key,
83                                                        barrier));
84   GNUNET_free (barrier->name);
85   GNUNET_free (barrier);
86   if (0 == GNUNET_CONTAINER_multihashmap_size (barrier_map))
87   {
88     GNUNET_CONTAINER_multihashmap_destroy (barrier_map);
89     barrier_map = NULL;
90   }
91 }
92
93
94 /**
95  * Handler for GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS messages
96  *
97  * @param c the controller handle to determine the connection this message
98  *   belongs to
99  * @param msg the barrier status message
100  * @return GNUNET_OK to keep the connection active; GNUNET_SYSERR to tear it
101  *   down signalling an error
102  */
103 int
104 GNUNET_TESTBED_handle_barrier_status_ (struct GNUNET_TESTBED_Controller *c,
105                                        const struct GNUNET_TESTBED_BarrierStatusMsg
106                                        *msg)
107 {
108   struct GNUNET_TESTBED_Barrier *barrier;
109   char *emsg;
110   const char *name;
111   struct GNUNET_HashCode key;  
112   size_t emsg_len;
113   int status;
114   uint16_t msize;
115   uint16_t name_len;
116   
117   emsg = NULL;
118   barrier = NULL;
119   msize = ntohs (msg->header.size);  
120   name = msg->data;
121   name_len = ntohs (msg->name_len);
122   if (  (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len + 1 > msize)
123         || ('\0' != name[name_len])  )
124   {
125     GNUNET_break_op (0);
126     return GNUNET_SYSERR;
127   }
128   status = ntohs (msg->status);
129   if (BARRIER_STATUS_ERROR == status)
130   {
131     status = -1;
132     emsg_len = msize - (sizeof (struct GNUNET_TESTBED_BarrierStatusMsg) + name_len
133                         + 1);
134     if (0 == emsg_len)
135     {
136       GNUNET_break_op (0);
137       return GNUNET_SYSERR;
138     }
139     emsg_len++;
140     emsg = GNUNET_malloc (emsg_len);
141     emsg_len--;
142     emsg[emsg_len] = '\0';
143     (void) memcpy (emsg, msg->data + name_len + 1, emsg_len);
144   }
145   if (NULL == barrier_map)
146     goto cleanup;
147   GNUNET_CRYPTO_hash (name, name_len, &key);
148   barrier = GNUNET_CONTAINER_multihashmap_get (barrier_map, &key);
149   if (NULL == barrier)
150     goto cleanup;
151   GNUNET_assert (NULL != barrier->cb);
152   barrier->cb (barrier->cls, name, barrier, status, emsg);
153   if (BARRIER_STATUS_INITIALISED == status)
154     return GNUNET_OK;           /* just initialised; skip cleanup */
155
156  cleanup:
157   GNUNET_free_non_null (emsg);
158   if (NULL != barrier)
159     barrier_remove (barrier);
160   return GNUNET_OK;
161 }
162
163
164 /**
165  * Initialise a barrier and call the given callback when the required percentage
166  * of peers (quorum) reach the barrier OR upon error.
167  *
168  * @param controller the handle to the controller
169  * @param name identification name of the barrier
170  * @param quorum the percentage of peers that is required to reach the barrier.
171  *   Peers signal reaching a barrier by calling
172  *   GNUNET_TESTBED_barrier_reached().
173  * @param cb the callback to call when the barrier is reached or upon error.
174  *   Cannot be NULL.
175  * @param cls closure for the above callback
176  * @return barrier handle; NULL upon error
177  */
178 struct GNUNET_TESTBED_Barrier *
179 GNUNET_TESTBED_barrier_init (struct GNUNET_TESTBED_Controller *controller,
180                              const char *name,
181                              unsigned int quorum,
182                              GNUNET_TESTBED_barrier_status_cb cb, void *cls)
183 {
184   struct GNUNET_TESTBED_Barrier *barrier;
185   struct GNUNET_HashCode key;
186   size_t name_len;
187   
188   GNUNET_assert (quorum <= 100);
189   GNUNET_assert (NULL != cb);
190   name_len = strlen (name);
191   GNUNET_assert (0 < name_len);
192   GNUNET_CRYPTO_hash (name, name_len, &key);
193   if (NULL == barrier_map)
194     barrier_map = GNUNET_CONTAINER_multihashmap_create (3, GNUNET_YES);
195   if (GNUNET_YES ==
196       GNUNET_CONTAINER_multihashmap_contains (barrier_map, &key))
197   {
198     GNUNET_break (0);
199     return NULL;
200   }
201   barrier = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Barrier));
202   barrier->name = GNUNET_strdup (name);
203   barrier->cb = cb;
204   barrier->cls = cls;
205   (void) memcpy (&barrier->key, &key, sizeof (struct GNUNET_HashCode));
206   GNUNET_assert (GNUNET_OK ==
207                  GNUNET_CONTAINER_multihashmap_put (barrier_map, &barrier->key,
208                                                     barrier,
209                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
210   return barrier;
211 }
212
213
214 /**
215  * Cancel a barrier.
216  *
217  * @param barrier the barrier handle
218  */
219 void
220 GNUNET_TESTBED_barrier_cancel (struct GNUNET_TESTBED_Barrier *barrier)
221 {
222   barrier_remove (barrier);
223 }
224
225 /* end of testbed_api_barriers.c */