disable auto retry
[oweals/gnunet.git] / src / testbed / testbed_api_operations.c
index c98998bbffee4fa5cd5302c9ec4653a5680177fd..b897cb453ddb480889e5154b08c5bdd6529711e2 100644 (file)
 #include "testbed_api_operations.h"
 
 
+/**
+ * An entry in the operation queue
+ */
+struct QueueEntry
+{
+  /**
+   * The next DLL pointer
+   */
+  struct QueueEntry *next;
+
+  /**
+   * The prev DLL pointer
+   */
+  struct QueueEntry *prev;
+
+  /**
+   * The operation this entry holds
+   */
+  struct GNUNET_TESTBED_Operation *op;
+};
+
+
+/**
+ * Queue of operations where we can only support a certain
+ * number of concurrent operations of a particular type.
+ */
+struct OperationQueue
+{
+ /**
+   * The head of the operation queue
+   */
+  struct QueueEntry *head;
+
+  /**
+   * The tail of the operation queue
+   */
+  struct QueueEntry *tail;
+
+  /**
+   * Number of operations that can be concurrently
+   * active in this queue.
+   */
+  unsigned int active;
+};
+
+
+/**
+ * Operation state
+ */
+enum OperationState
+{
+    /**
+     * The operation is currently waiting for resources
+     */
+  OP_STATE_WAITING,
+
+    /**
+     * The operation has started
+     */
+  OP_STATE_STARTED
+};
+
+
 /**
  * Opaque handle to an abstract operation to be executed by the testing framework.
  */
@@ -42,33 +105,101 @@ struct GNUNET_TESTBED_Operation
    * not have been started yet).
    */
   OperationRelease release;
-                                
+
   /**
    * Closure for callbacks.
    */
   void *cb_cls;
 
-  // FIXME!
+  /**
+   * Array of operation queues this Operation belongs to.
+   */
+  struct OperationQueue **queues;
+
+  /**
+   * The id of the task which calls OperationStart for this operation
+   */
+  GNUNET_SCHEDULER_TaskIdentifier start_task_id;
+
+  /**
+   * Number of queues in the operation queues array
+   */
+  unsigned int nqueues;
+
+  /**
+   * The state of the operation
+   */
+  enum OperationState state;
 
 };
 
 
 /**
- * Queue of operations where we can only support a certain
- * number of concurrent operations of a particular type.
+ * Task for calling OperationStart on operation
+ *
+ * @param cls the Operation
+ * @param tc the TaskContext from scheduler
  */
-struct OperationQueue
+static void
+call_start (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
+  struct GNUNET_TESTBED_Operation *op = cls;
+
+  op->start_task_id = GNUNET_SCHEDULER_NO_TASK;
+  op->state = OP_STATE_STARTED;
+  if (NULL != op->start)
+  {
+    op->start (op->cb_cls);
+  }
+}
 
-  /**
-   * Maximum number of operationst that can be concurrently
-   * active in this queue.
-   */
-  unsigned int max_active;
 
-  // FIXME!
+/**
+ * Checks for the readiness of an operation and schedules a operation start task
+ *
+ * @param op the operation
+ */
+static void
+check_readiness (struct GNUNET_TESTBED_Operation *op)
+{
+  unsigned int i;
+
+  if (GNUNET_SCHEDULER_NO_TASK != op->start_task_id)
+    return;
+  for (i = 0; i < op->nqueues; i++)
+  {
+    if (0 == op->queues[i]->active)
+      return;
+  }
+  for (i = 0; i < op->nqueues; i++)
+  {
+    op->queues[i]->active--;
+  }
+  op->start_task_id = GNUNET_SCHEDULER_add_now (&call_start, op);
+}
 
-};
+
+/**
+ * Create an 'operation' to be performed.
+ *
+ * @param cls closure for the callbacks
+ * @param start function to call to start the operation
+ * @param release function to call to close down the operation
+ * @return handle to the operation
+ */
+struct GNUNET_TESTBED_Operation *
+GNUNET_TESTBED_operation_create_ (void *cls, OperationStart start,
+                                  OperationRelease release)
+{
+  struct GNUNET_TESTBED_Operation *op;
+
+  op = GNUNET_malloc (sizeof (struct GNUNET_TESTBED_Operation));
+  op->start = start;
+  op->release = release;
+  op->cb_cls = cls;
+  op->start_task_id = GNUNET_SCHEDULER_NO_TASK;
+  return op;
+}
 
 
 /**
@@ -84,7 +215,7 @@ GNUNET_TESTBED_operation_queue_create_ (unsigned int max_active)
   struct OperationQueue *queue;
 
   queue = GNUNET_malloc (sizeof (struct OperationQueue));
-  queue->max_active = max_active;
+  queue->active = max_active;
   return queue;
 }
 
@@ -98,7 +229,8 @@ GNUNET_TESTBED_operation_queue_create_ (unsigned int max_active)
 void
 GNUNET_TESTBED_operation_queue_destroy_ (struct OperationQueue *queue)
 {
-  GNUNET_break (0);
+  GNUNET_break (NULL == queue->head);
+  GNUNET_break (NULL == queue->tail);
   GNUNET_free (queue);
 }
 
@@ -107,9 +239,9 @@ GNUNET_TESTBED_operation_queue_destroy_ (struct OperationQueue *queue)
  * Add an operation to a queue.  An operation can be in multiple
  * queues at once.  Once all queues permit the operation to become
  * active, the operation will be activated.  The actual activation
- * will occur in a separate task (thus allowing multiple queue 
+ * will occur in a separate task (thus allowing multiple queue
  * insertions to be made without having the first one instantly
- * trigger the operation if the first queue has sufficient 
+ * trigger the operation if the first queue has sufficient
  * resources).
  *
  * @param queue queue to add the operation to
@@ -117,9 +249,20 @@ GNUNET_TESTBED_operation_queue_destroy_ (struct OperationQueue *queue)
  */
 void
 GNUNET_TESTBED_operation_queue_insert_ (struct OperationQueue *queue,
-                                       struct GNUNET_TESTBED_Operation *operation)
+                                        struct GNUNET_TESTBED_Operation
+                                        *operation)
 {
-  GNUNET_break (0);
+  struct QueueEntry *entry;
+
+  entry = GNUNET_malloc (sizeof (struct QueueEntry));
+  entry->op = operation;
+  GNUNET_CONTAINER_DLL_insert_tail (queue->head, queue->tail, entry);
+  operation->queues =
+      GNUNET_realloc (operation->queues,
+                      sizeof (struct OperationQueue *) *
+                      (++operation->nqueues));
+  operation->queues[operation->nqueues - 1] = queue;
+  check_readiness (operation);
 }
 
 
@@ -134,9 +277,27 @@ GNUNET_TESTBED_operation_queue_insert_ (struct OperationQueue *queue,
  */
 void
 GNUNET_TESTBED_operation_queue_remove_ (struct OperationQueue *queue,
-                                       struct GNUNET_TESTBED_Operation *operation)
+                                        struct GNUNET_TESTBED_Operation
+                                        *operation)
 {
-  GNUNET_break (0);
+  struct QueueEntry *entry;
+  struct QueueEntry *entry2;
+
+  for (entry = queue->head; NULL != entry; entry = entry->next)
+    if (entry->op == operation)
+      break;
+  GNUNET_assert (NULL != entry);
+  if (OP_STATE_STARTED == operation->state)
+    queue->active++;
+  entry2 = entry->next;
+  GNUNET_CONTAINER_DLL_remove (queue->head, queue->tail, entry);
+  GNUNET_free (entry);
+  for (; NULL != entry2; entry2 = entry2->next)
+    if (OP_STATE_STARTED != entry2->op->state)
+      break;
+  if (NULL == entry2)
+    return;
+  check_readiness (entry2->op);
 }
 
 
@@ -146,49 +307,23 @@ GNUNET_TESTBED_operation_queue_remove_ (struct OperationQueue *queue,
  *
  * @param operation operation that finished
  */
-static void
-operation_release (struct GNUNET_TESTBED_Operation *operation)
-{
-  // call operation->release, remove from queues
-  GNUNET_break (0);
-}
-
-
-/**
- * Cancel a pending operation.  Releases all resources
- * of the operation and will ensure that no event
- * is generated for the operation.  Does NOT guarantee
- * that the operation will be fully undone (or that
- * nothing ever happened).  
- * 
- * @param operation operation to cancel
- */
-void
-GNUNET_TESTBED_operation_cancel (struct GNUNET_TESTBED_Operation *operation)
-{
-  // test that operation had not yet generated an event
-  GNUNET_break (0);
-  operation_release (operation);
-}
-
-
-/**
- * Signal that the information from an operation has been fully
- * processed.  This function MUST be called for each event
- * of type 'operation_finished' to fully remove the operation
- * from the operation queue.  After calling this function, the
- * 'op_result' becomes invalid (!).
- * 
- * @param operation operation to signal completion for
- */
 void
-GNUNET_TESTBED_operation_done (struct GNUNET_TESTBED_Operation *operation)
+GNUNET_TESTBED_operation_release_ (struct GNUNET_TESTBED_Operation *operation)
 {
-  // test that operation was started and had generated an event
-  GNUNET_break (0);
-  operation_release (operation);
+  unsigned int i;
+
+  if (GNUNET_SCHEDULER_NO_TASK != operation->start_task_id)
+  {
+    GNUNET_SCHEDULER_cancel (operation->start_task_id);
+    operation->start_task_id = GNUNET_SCHEDULER_NO_TASK;
+  }
+  for (i = 0; i < operation->nqueues; i++)
+    GNUNET_TESTBED_operation_queue_remove_ (operation->queues[i], operation);
+  GNUNET_free (operation->queues);
+  if (NULL != operation->release)
+    operation->release (operation->cb_cls);
+  GNUNET_free (operation);
 }
 
 
-
 /* end of testbed_api_operations.c */