#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.
*/
* 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;
+}
/**
struct OperationQueue *queue;
queue = GNUNET_malloc (sizeof (struct OperationQueue));
- queue->max_active = max_active;
+ queue->active = max_active;
return queue;
}
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);
}
* 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
*/
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);
}
*/
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);
}
*
* @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 */