diff options
author | Sree Harsha Totakura <totakura@in.tum.de> | 2013-04-09 17:18:16 +0000 |
---|---|---|
committer | Sree Harsha Totakura <totakura@in.tum.de> | 2013-04-09 17:18:16 +0000 |
commit | d65fc468de7a61f016e476cfe3fc471901f32e8a (patch) | |
tree | 47376ef0cdfc80af0d2af32e4ac388e511c4686d | |
parent | ae9a6cf3069a7ec482f2bb940595e24bdc092ca1 (diff) | |
download | gnunet-d65fc468de7a61f016e476cfe3fc471901f32e8a.tar.gz gnunet-d65fc468de7a61f016e476cfe3fc471901f32e8a.zip |
- support caching through inactive operations
-rw-r--r-- | src/testbed/test_testbed_api_operations.c | 61 | ||||
-rw-r--r-- | src/testbed/testbed_api_operations.c | 185 | ||||
-rw-r--r-- | src/testbed/testbed_api_operations.h | 24 |
3 files changed, 258 insertions, 12 deletions
diff --git a/src/testbed/test_testbed_api_operations.c b/src/testbed/test_testbed_api_operations.c index e4d3b72fa..af3e68c20 100644 --- a/src/testbed/test_testbed_api_operations.c +++ b/src/testbed/test_testbed_api_operations.c | |||
@@ -85,11 +85,19 @@ struct GNUNET_TESTBED_Operation *op6; | |||
85 | 85 | ||
86 | /** | 86 | /** |
87 | * This operation is started after op4 is released and should consume 1 resource | 87 | * This operation is started after op4 is released and should consume 1 resource |
88 | * on both queues q1 and q1. It should be started along with op5 and op6 | 88 | * on both queues q1 and q1. It should be started along with op5 and op6. It is |
89 | * then inactivated when op6 is released. op8's start should release this | ||
90 | * operation implicitly. | ||
89 | */ | 91 | */ |
90 | struct GNUNET_TESTBED_Operation *op7; | 92 | struct GNUNET_TESTBED_Operation *op7; |
91 | 93 | ||
92 | /** | 94 | /** |
95 | * This operation is started after op6 is finished in step task. It consumes 2 | ||
96 | * resources on both queues q1 and q1. | ||
97 | */ | ||
98 | struct GNUNET_TESTBED_Operation *op8; | ||
99 | |||
100 | /** | ||
93 | * The delay task identifier | 101 | * The delay task identifier |
94 | */ | 102 | */ |
95 | GNUNET_SCHEDULER_TaskIdentifier step_task; | 103 | GNUNET_SCHEDULER_TaskIdentifier step_task; |
@@ -167,9 +175,24 @@ enum Test | |||
167 | TEST_OP6_RELEASED, | 175 | TEST_OP6_RELEASED, |
168 | 176 | ||
169 | /** | 177 | /** |
178 | * op8 has began waiting | ||
179 | */ | ||
180 | TEST_OP8_WAITING, | ||
181 | |||
182 | /** | ||
170 | * op7 has released | 183 | * op7 has released |
171 | */ | 184 | */ |
172 | TEST_OP7_RELEASED | 185 | TEST_OP7_RELEASED, |
186 | |||
187 | /** | ||
188 | * op8 has started | ||
189 | */ | ||
190 | TEST_OP8_STARTED, | ||
191 | |||
192 | /** | ||
193 | * op8 has been released | ||
194 | */ | ||
195 | TEST_OP8_RELEASED | ||
173 | }; | 196 | }; |
174 | 197 | ||
175 | /** | 198 | /** |
@@ -238,6 +261,16 @@ step (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
238 | case TEST_OP4_STARTED: | 261 | case TEST_OP4_STARTED: |
239 | GNUNET_TESTBED_operation_release_ (op4); | 262 | GNUNET_TESTBED_operation_release_ (op4); |
240 | break; | 263 | break; |
264 | case TEST_OP6_RELEASED: | ||
265 | op8 = GNUNET_TESTBED_operation_create_ (&op8, &start_cb, &release_cb); | ||
266 | GNUNET_TESTBED_operation_queue_insert2_ (q1, op8, 2); | ||
267 | GNUNET_TESTBED_operation_queue_insert2_ (q2, op8, 2); | ||
268 | result = TEST_OP8_WAITING; | ||
269 | GNUNET_TESTBED_operation_begin_wait_ (op8); | ||
270 | break; | ||
271 | case TEST_OP8_STARTED: | ||
272 | GNUNET_TESTBED_operation_release_ (op8); | ||
273 | break; | ||
241 | default: | 274 | default: |
242 | GNUNET_assert (0); | 275 | GNUNET_assert (0); |
243 | } | 276 | } |
@@ -295,6 +328,11 @@ start_cb (void *cls) | |||
295 | } | 328 | } |
296 | } | 329 | } |
297 | break; | 330 | break; |
331 | case TEST_OP7_RELEASED: | ||
332 | GNUNET_assert (&op8 == cls); | ||
333 | result = TEST_OP8_STARTED; | ||
334 | step_task = GNUNET_SCHEDULER_add_now (&step, NULL); | ||
335 | break; | ||
298 | default: | 336 | default: |
299 | GNUNET_assert (0); | 337 | GNUNET_assert (0); |
300 | } | 338 | } |
@@ -356,11 +394,17 @@ release_cb (void *cls) | |||
356 | case TEST_OP5_RELEASED: | 394 | case TEST_OP5_RELEASED: |
357 | op6 = NULL; | 395 | op6 = NULL; |
358 | result = TEST_OP6_RELEASED; | 396 | result = TEST_OP6_RELEASED; |
359 | GNUNET_TESTBED_operation_release_ (op7); | 397 | GNUNET_TESTBED_operation_inactivate_ (op7); |
398 | step_task = GNUNET_SCHEDULER_add_now (&step, NULL); | ||
360 | break; | 399 | break; |
361 | case TEST_OP6_RELEASED: | 400 | case TEST_OP8_WAITING: |
362 | result = TEST_OP7_RELEASED; | 401 | GNUNET_assert (&op7 == cls); |
363 | op7 = NULL; | 402 | op7 = NULL; |
403 | result = TEST_OP7_RELEASED; | ||
404 | break; | ||
405 | case TEST_OP8_STARTED: | ||
406 | result = TEST_OP8_RELEASED; | ||
407 | op8 = NULL; | ||
364 | GNUNET_TESTBED_operation_queue_destroy_ (q1); | 408 | GNUNET_TESTBED_operation_queue_destroy_ (q1); |
365 | GNUNET_TESTBED_operation_queue_destroy_ (q2); | 409 | GNUNET_TESTBED_operation_queue_destroy_ (q2); |
366 | q1 = NULL; | 410 | q1 = NULL; |
@@ -417,11 +461,16 @@ main (int argc, char **argv) | |||
417 | GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, | 461 | GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, |
418 | "test_testbed_api_operations", "nohelp", options, | 462 | "test_testbed_api_operations", "nohelp", options, |
419 | &run, NULL); | 463 | &run, NULL); |
420 | if ((GNUNET_OK != ret) || (TEST_OP7_RELEASED != result)) | 464 | if ((GNUNET_OK != ret) || (TEST_OP8_RELEASED != result)) |
421 | return 1; | 465 | return 1; |
422 | op1 = NULL; | 466 | op1 = NULL; |
423 | op2 = NULL; | 467 | op2 = NULL; |
424 | op3 = NULL; | 468 | op3 = NULL; |
469 | op4 = NULL; | ||
470 | op5 = NULL; | ||
471 | op6 = NULL; | ||
472 | op7 = NULL; | ||
473 | op8 = NULL; | ||
425 | q1 = NULL; | 474 | q1 = NULL; |
426 | q2 = NULL; | 475 | q2 = NULL; |
427 | return 0; | 476 | return 0; |
diff --git a/src/testbed/testbed_api_operations.c b/src/testbed/testbed_api_operations.c index 9948ce357..fc5da29b5 100644 --- a/src/testbed/testbed_api_operations.c +++ b/src/testbed/testbed_api_operations.c | |||
@@ -94,6 +94,18 @@ struct OperationQueue | |||
94 | struct QueueEntry *aq_tail; | 94 | struct QueueEntry *aq_tail; |
95 | 95 | ||
96 | /** | 96 | /** |
97 | * DLL head for the inactive queue. Operations which are inactive and can be | ||
98 | * evicted if the queues it holds are maxed out and another operation begins | ||
99 | * to wait on them. | ||
100 | */ | ||
101 | struct QueueEntry *nq_head; | ||
102 | |||
103 | /** | ||
104 | * DLL tail for the inactive queue. | ||
105 | */ | ||
106 | struct QueueEntry *nq_tail; | ||
107 | |||
108 | /** | ||
97 | * Number of operations that are currently active in this queue. | 109 | * Number of operations that are currently active in this queue. |
98 | */ | 110 | */ |
99 | unsigned int active; | 111 | unsigned int active; |
@@ -129,7 +141,15 @@ enum OperationState | |||
129 | /** | 141 | /** |
130 | * The operation has started | 142 | * The operation has started |
131 | */ | 143 | */ |
132 | OP_STATE_STARTED | 144 | OP_STATE_STARTED, |
145 | |||
146 | /** | ||
147 | * The operation is inactive. It still holds resources on the operation | ||
148 | * queues. However, this operation will be evicted when another operation | ||
149 | * requires resources from the maxed out queues this operation is holding | ||
150 | * resources from. | ||
151 | */ | ||
152 | OP_STATE_INACTIVE | ||
133 | }; | 153 | }; |
134 | 154 | ||
135 | 155 | ||
@@ -248,6 +268,9 @@ remove_queue_entry (struct GNUNET_TESTBED_Operation *op, unsigned int index) | |||
248 | case OP_STATE_STARTED: | 268 | case OP_STATE_STARTED: |
249 | GNUNET_CONTAINER_DLL_remove (opq->aq_head, opq->aq_tail, entry); | 269 | GNUNET_CONTAINER_DLL_remove (opq->aq_head, opq->aq_tail, entry); |
250 | break; | 270 | break; |
271 | case OP_STATE_INACTIVE: | ||
272 | GNUNET_CONTAINER_DLL_remove (opq->nq_head, opq->nq_tail, entry); | ||
273 | break; | ||
251 | } | 274 | } |
252 | } | 275 | } |
253 | 276 | ||
@@ -294,6 +317,9 @@ change_state (struct GNUNET_TESTBED_Operation *op, enum OperationState state) | |||
294 | case OP_STATE_STARTED: | 317 | case OP_STATE_STARTED: |
295 | GNUNET_CONTAINER_DLL_insert_tail (opq->aq_head, opq->aq_tail, entry); | 318 | GNUNET_CONTAINER_DLL_insert_tail (opq->aq_head, opq->aq_tail, entry); |
296 | break; | 319 | break; |
320 | case OP_STATE_INACTIVE: | ||
321 | GNUNET_CONTAINER_DLL_insert_tail (opq->nq_head, opq->nq_tail, entry); | ||
322 | break; | ||
297 | } | 323 | } |
298 | } | 324 | } |
299 | op->state = state; | 325 | op->state = state; |
@@ -372,11 +398,96 @@ is_queue_empty (struct OperationQueue *opq) | |||
372 | { | 398 | { |
373 | if ( (NULL != opq->wq_head) | 399 | if ( (NULL != opq->wq_head) |
374 | || (NULL != opq->rq_head) | 400 | || (NULL != opq->rq_head) |
375 | || (NULL != opq->aq_head) ) | 401 | || (NULL != opq->aq_head) |
402 | || (NULL != opq->nq_head) ) | ||
376 | return GNUNET_NO; | 403 | return GNUNET_NO; |
377 | return GNUNET_YES; | 404 | return GNUNET_YES; |
378 | } | 405 | } |
379 | 406 | ||
407 | |||
408 | int | ||
409 | decide_capacity (struct OperationQueue *opq, | ||
410 | struct QueueEntry *entry, | ||
411 | struct GNUNET_TESTBED_Operation ***ops_, | ||
412 | unsigned int *n_ops_) | ||
413 | { | ||
414 | struct QueueEntry **evict_entries; | ||
415 | struct GNUNET_TESTBED_Operation **ops; | ||
416 | struct GNUNET_TESTBED_Operation *op; | ||
417 | unsigned int n_ops; | ||
418 | unsigned int n_evict_entries; | ||
419 | unsigned int need; | ||
420 | int deficit; | ||
421 | int rval; | ||
422 | |||
423 | GNUNET_assert (NULL != (op = entry->op)); | ||
424 | GNUNET_assert (0 < (need = entry->nres)); | ||
425 | GNUNET_assert (opq->active <= opq->max_active); | ||
426 | ops = NULL; | ||
427 | n_ops = 0; | ||
428 | evict_entries = NULL; | ||
429 | n_evict_entries = 0; | ||
430 | rval = GNUNET_OK; | ||
431 | if ((opq->active + need) <= opq->max_active) | ||
432 | goto ret; | ||
433 | deficit = need - (opq->max_active - opq->active); | ||
434 | for (entry = opq->nq_head; | ||
435 | (0 < deficit) && (NULL != entry); | ||
436 | entry = entry->next) | ||
437 | { | ||
438 | GNUNET_array_append (evict_entries, n_evict_entries, entry); | ||
439 | deficit -= entry->nres; | ||
440 | } | ||
441 | if (0 < deficit) | ||
442 | { | ||
443 | rval = GNUNET_NO; | ||
444 | goto ret; | ||
445 | } | ||
446 | for (n_ops = 0; n_ops < n_evict_entries;) | ||
447 | { | ||
448 | op = evict_entries[n_ops]->op; | ||
449 | GNUNET_array_append (ops, n_ops, op); /* increments n-ops */ | ||
450 | } | ||
451 | |||
452 | ret: | ||
453 | GNUNET_free_non_null (evict_entries); | ||
454 | if (NULL != ops_) *ops_ = ops; | ||
455 | if (NULL != n_ops_) *n_ops_ = n_ops; | ||
456 | return rval; | ||
457 | } | ||
458 | |||
459 | /* FIXME: improve.. */ | ||
460 | void | ||
461 | merge_ops (struct GNUNET_TESTBED_Operation ***old, | ||
462 | unsigned int *n_old, | ||
463 | struct GNUNET_TESTBED_Operation **new, | ||
464 | unsigned int n_new) | ||
465 | { | ||
466 | struct GNUNET_TESTBED_Operation **cur; | ||
467 | unsigned int i; | ||
468 | unsigned int j; | ||
469 | unsigned int n_cur; | ||
470 | |||
471 | GNUNET_assert (NULL != old); | ||
472 | n_cur = *n_old; | ||
473 | cur = *old; | ||
474 | for (i = 0; i < n_new; i++) | ||
475 | { | ||
476 | for (j = 0; j < *n_old; j++) | ||
477 | { | ||
478 | if (new[i] == cur[j]) | ||
479 | break; | ||
480 | } | ||
481 | if (j < *n_old) | ||
482 | continue; | ||
483 | GNUNET_array_append (cur, n_cur, new[j]); | ||
484 | } | ||
485 | *old = cur; | ||
486 | *n_old = n_cur; | ||
487 | } | ||
488 | |||
489 | |||
490 | |||
380 | /** | 491 | /** |
381 | * Checks for the readiness of an operation and schedules a operation start task | 492 | * Checks for the readiness of an operation and schedules a operation start task |
382 | * | 493 | * |
@@ -385,15 +496,40 @@ is_queue_empty (struct OperationQueue *opq) | |||
385 | static void | 496 | static void |
386 | check_readiness (struct GNUNET_TESTBED_Operation *op) | 497 | check_readiness (struct GNUNET_TESTBED_Operation *op) |
387 | { | 498 | { |
499 | struct GNUNET_TESTBED_Operation **evict_ops; | ||
500 | struct GNUNET_TESTBED_Operation **ops; | ||
501 | unsigned int n_ops; | ||
502 | unsigned int n_evict_ops; | ||
388 | unsigned int i; | 503 | unsigned int i; |
389 | 504 | ||
390 | GNUNET_assert (NULL == op->rq_entry); | 505 | GNUNET_assert (NULL == op->rq_entry); |
391 | GNUNET_assert (OP_STATE_WAITING == op->state); | 506 | GNUNET_assert (OP_STATE_WAITING == op->state); |
507 | evict_ops = NULL; | ||
508 | n_evict_ops = 0; | ||
392 | for (i = 0; i < op->nqueues; i++) | 509 | for (i = 0; i < op->nqueues; i++) |
393 | { | 510 | { |
394 | GNUNET_assert (0 < op->nres[i]); | 511 | ops = NULL; |
395 | if ((op->queues[i]->active + op->nres[i]) > op->queues[i]->max_active) | 512 | n_ops = 0; |
513 | if (GNUNET_NO == decide_capacity (op->queues[i], op->qentries[i], | ||
514 | &ops, &n_ops)) | ||
515 | { | ||
516 | GNUNET_free_non_null (evict_ops); | ||
396 | return; | 517 | return; |
518 | } | ||
519 | if (NULL == ops) | ||
520 | continue; | ||
521 | merge_ops (&evict_ops, &n_evict_ops, ops, n_ops); | ||
522 | GNUNET_free (ops); | ||
523 | } | ||
524 | if (NULL != evict_ops) | ||
525 | { | ||
526 | for (i = 0; i < n_evict_ops; i++) | ||
527 | GNUNET_TESTBED_operation_release_ (evict_ops[i]); | ||
528 | GNUNET_free (evict_ops); | ||
529 | evict_ops = NULL; | ||
530 | /* Evicting the operations should schedule this operation */ | ||
531 | GNUNET_assert (OP_STATE_READY == op->state); | ||
532 | return; | ||
397 | } | 533 | } |
398 | for (i = 0; i < op->nqueues; i++) | 534 | for (i = 0; i < op->nqueues; i++) |
399 | op->queues[i]->active += op->nres[i]; | 535 | op->queues[i]->active += op->nres[i]; |
@@ -594,6 +730,38 @@ GNUNET_TESTBED_operation_begin_wait_ (struct GNUNET_TESTBED_Operation *op) | |||
594 | 730 | ||
595 | 731 | ||
596 | /** | 732 | /** |
733 | * Marks an active operation as inactive - the operation will be kept in a | ||
734 | * ready-to-be-released state and continues to hold resources until another | ||
735 | * operation contents for them. | ||
736 | * | ||
737 | * @param op the operation to be marked as inactive. The operation start | ||
738 | * callback should have been called before for this operation to mark | ||
739 | * it as inactive. | ||
740 | */ | ||
741 | void | ||
742 | GNUNET_TESTBED_operation_inactivate_ (struct GNUNET_TESTBED_Operation *op) | ||
743 | { | ||
744 | GNUNET_assert (OP_STATE_STARTED == op->state); | ||
745 | change_state (op, OP_STATE_INACTIVE); | ||
746 | } | ||
747 | |||
748 | |||
749 | /** | ||
750 | * Marks and inactive operation as active. This fuction should be called to | ||
751 | * ensure that the oprelease callback will not be called until it is either | ||
752 | * marked as inactive or released. | ||
753 | * | ||
754 | * @param op the operation to be marked as active | ||
755 | */ | ||
756 | void | ||
757 | GNUNET_TESTBED_operation_activate_ (struct GNUNET_TESTBED_Operation *op) | ||
758 | { | ||
759 | GNUNET_assert (OP_STATE_INACTIVE == op->state); | ||
760 | change_state (op, OP_STATE_STARTED); | ||
761 | } | ||
762 | |||
763 | |||
764 | /** | ||
597 | * An operation is 'done' (was cancelled or finished); remove | 765 | * An operation is 'done' (was cancelled or finished); remove |
598 | * it from the queues and release associated resources. | 766 | * it from the queues and release associated resources. |
599 | * | 767 | * |
@@ -613,6 +781,8 @@ GNUNET_TESTBED_operation_release_ (struct GNUNET_TESTBED_Operation *op) | |||
613 | } | 781 | } |
614 | if (OP_STATE_READY == op->state) | 782 | if (OP_STATE_READY == op->state) |
615 | rq_remove (op); | 783 | rq_remove (op); |
784 | if (OP_STATE_INACTIVE == op->state) /* Activate the operation if inactive */ | ||
785 | GNUNET_TESTBED_operation_activate_ (op); | ||
616 | GNUNET_assert (NULL != op->queues); | 786 | GNUNET_assert (NULL != op->queues); |
617 | GNUNET_assert (NULL != op->qentries); | 787 | GNUNET_assert (NULL != op->qentries); |
618 | for (i = 0; i < op->nqueues; i++) | 788 | for (i = 0; i < op->nqueues; i++) |
@@ -621,9 +791,12 @@ GNUNET_TESTBED_operation_release_ (struct GNUNET_TESTBED_Operation *op) | |||
621 | remove_queue_entry (op, i); | 791 | remove_queue_entry (op, i); |
622 | opq = op->queues[i]; | 792 | opq = op->queues[i]; |
623 | switch (op->state) | 793 | switch (op->state) |
624 | { | 794 | { |
625 | case OP_STATE_INIT: | 795 | case OP_STATE_INIT: |
626 | case OP_STATE_WAITING: | 796 | case OP_STATE_INACTIVE: |
797 | GNUNET_assert (0); | ||
798 | break; | ||
799 | case OP_STATE_WAITING: | ||
627 | break; | 800 | break; |
628 | case OP_STATE_READY: | 801 | case OP_STATE_READY: |
629 | case OP_STATE_STARTED: | 802 | case OP_STATE_STARTED: |
diff --git a/src/testbed/testbed_api_operations.h b/src/testbed/testbed_api_operations.h index 346fe5b75..10a91fb21 100644 --- a/src/testbed/testbed_api_operations.h +++ b/src/testbed/testbed_api_operations.h | |||
@@ -175,5 +175,29 @@ void | |||
175 | GNUNET_TESTBED_operation_release_ (struct GNUNET_TESTBED_Operation *op); | 175 | GNUNET_TESTBED_operation_release_ (struct GNUNET_TESTBED_Operation *op); |
176 | 176 | ||
177 | 177 | ||
178 | /** | ||
179 | * Marks an active operation as inactive - the operation will be kept in a | ||
180 | * ready-to-be-released state and continues to hold resources until another | ||
181 | * operation contents for them. | ||
182 | * | ||
183 | * @param op the operation to be marked as inactive. The operation start | ||
184 | * callback should have been called before for this operation to mark | ||
185 | * it as inactive. | ||
186 | */ | ||
187 | void | ||
188 | GNUNET_TESTBED_operation_inactivate_ (struct GNUNET_TESTBED_Operation *op); | ||
189 | |||
190 | |||
191 | /** | ||
192 | * Marks and inactive operation as active. This fuction should be called to | ||
193 | * ensure that the oprelease callback will not be called until it is either | ||
194 | * marked as inactive or released. | ||
195 | * | ||
196 | * @param op the operation to be marked as active | ||
197 | */ | ||
198 | void | ||
199 | GNUNET_TESTBED_operation_activate_ (struct GNUNET_TESTBED_Operation *op); | ||
200 | |||
201 | |||
178 | #endif | 202 | #endif |
179 | /* end of testbed_api_operations.h */ | 203 | /* end of testbed_api_operations.h */ |