+
+/**
+ * Barrier wait handle
+ */
+struct GNUNET_TESTBED_BarrierWaitHandle
+{
+ /**
+ * The name of the barrier
+ */
+ char *name;
+
+ /**
+ * Then configuration used for the client connection
+ */
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * The client connection
+ */
+ struct GNUNET_CLIENT_Connection *conn;
+
+ /**
+ * Transmit handle
+ */
+ struct GNUNET_CLIENT_TransmitHandle *tx;
+
+ /**
+ * The message to transmit with tx
+ */
+ struct GNUNET_MessageHeader *msg;
+
+ /**
+ * The barrier wait callback
+ */
+ GNUNET_TESTBED_barrier_wait_cb cb;
+
+ /**
+ * The closure for the above callback
+ */
+ void *cls;
+};
+
+
+/**
+ * Function to destroy barrier wait handle
+ *
+ * @param h the handle to destroy
+ */
+static void
+destroy_handle (struct GNUNET_TESTBED_BarrierWaitHandle *h)
+{
+ GNUNET_free (h->name);
+ if (NULL != h->tx)
+ GNUNET_CLIENT_notify_transmit_ready_cancel (h->tx);
+ if (NULL != h->conn)
+ GNUNET_CLIENT_disconnect (h->conn);
+ if (NULL != h->msg)
+ GNUNET_free (h->msg);
+ GNUNET_CONFIGURATION_destroy (h->cfg);
+ GNUNET_free (h);
+}
+
+
+/**
+ * Type of a function to call when we receive a message
+ * from the service.
+ *
+ * @param cls closure
+ * @param msg message received, NULL on timeout or fatal error
+ */
+static void
+receive_handler (void *cls, const struct GNUNET_MessageHeader *message)
+{
+ struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
+ const struct GNUNET_TESTBED_BarrierStatusMsg *msg;
+ uint16_t msize;
+
+ if (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_STATUS != ntohs (message->type))
+ {
+ GNUNET_break_op (0);
+ goto fail;
+ }
+ msize = ntohs (message->size);
+ if (msize <= sizeof (struct GNUNET_TESTBED_BarrierStatusMsg))
+ {
+ GNUNET_break_op (0);
+ goto fail;
+ }
+ msg = (const struct GNUNET_TESTBED_BarrierStatusMsg *) message;
+ switch (ntohs (msg->status))
+ {
+ case BARRIER_STATUS_ERROR:
+ goto fail;
+ case BARRIER_STATUS_INITIALISED:
+ GNUNET_break (0); /* FIXME */
+ goto destroy;
+ case BARRIER_STATUS_CROSSED:
+ h->cb (h->cls, h->name, GNUNET_OK);
+ goto destroy;
+ }
+
+ fail:
+ h->cb (h->cls, h->name, GNUNET_SYSERR);
+
+ destroy:
+ destroy_handle (h);
+}
+
+
+/**
+ * Function called to notify a client about the connection
+ * begin ready to queue more data. "buf" will be
+ * NULL and "size" zero if the connection was closed for
+ * writing in the meantime.
+ *
+ * @param cls closure
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
+ */
+static size_t
+transmit_notify (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_TESTBED_BarrierWaitHandle *h = cls;
+ uint16_t msize;
+
+ h->tx = NULL;
+ if ((0 == size) || (NULL == buf))
+ {
+ destroy_handle (h);
+ return 0;
+ }
+ msize = htons (h->msg->size);
+ GNUNET_assert (msize <= size);
+ (void) memcpy (buf, h->msg, msize);
+ GNUNET_free (h->msg);
+ h->msg = NULL;
+ GNUNET_CLIENT_receive (h->conn, &receive_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
+ return msize;
+}
+
+
+/**
+ * Wait for a barrier to be crossed. This function should be called by the
+ * peers which have been started by the testbed. If the peer is not started by
+ * testbed this function may return error
+ *
+ * @param name the name of the barrier
+ * @param cb the barrier wait callback
+ * @param cls the closure for the above callback
+ * @return barrier wait handle which can be used to cancel the waiting at
+ * anytime before the callback is called. NULL upon error.
+ */
+struct GNUNET_TESTBED_BarrierWaitHandle *
+GNUNET_TESTBED_barrier_wait (const char *name,
+ GNUNET_TESTBED_barrier_wait_cb cb,
+ void *cls)
+{
+ struct GNUNET_TESTBED_BarrierWait *msg;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct GNUNET_TESTBED_BarrierWaitHandle *h;
+ char *cfg_filename;
+ size_t name_len;
+ uint16_t msize;
+
+ GNUNET_assert (NULL != cb);
+ GNUNET_assert (NULL != name);
+ cfg_filename = getenv (ENV_TESTBED_CONFIG);
+ if (NULL == cfg_filename)
+ return NULL;
+ cfg = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_filename));
+ {
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return NULL;
+ }
+ h = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_BarrierWaitHandle));
+ h->name = GNUNET_strdup (name);
+ h->cfg = cfg;
+ h->conn = GNUNET_CLIENT_connect ("testbed-barrier", h->cfg);
+ h->cb = cb;
+ h->cls = cls;
+ if (NULL == h->conn)
+ {
+ destroy_handle (h);
+ return NULL;
+ }
+ name_len = strlen (name);
+ msize = sizeof (struct GNUNET_TESTBED_BarrierWait) + name_len;
+ msg = GNUNET_malloc (msize);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_BARRIER_WAIT);
+ msg->header.size = htons (msize);
+ (void) memcpy (msg->name, name, name_len);
+ h->msg = &msg->header;
+ h->tx =
+ GNUNET_CLIENT_notify_transmit_ready (h->conn, msize,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_NO,
+ &transmit_notify,
+ h);
+ return h;
+}
+
+
+/**
+ * Cancel a barrier wait handle
+ *
+ * @param h the barrier wait handle
+ */
+void
+GNUNET_TESTBED_barrier_wait_cancel (struct GNUNET_TESTBED_BarrierWaitHandle *h)
+{
+ destroy_handle (h);
+}
+