qemu-coroutine.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. * QEMU coroutines
  3. *
  4. * Copyright IBM, Corp. 2011
  5. *
  6. * Authors:
  7. * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
  8. * Kevin Wolf <kwolf@redhat.com>
  9. *
  10. * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  11. * See the COPYING.LIB file in the top-level directory.
  12. *
  13. */
  14. #include "trace.h"
  15. #include "qemu-common.h"
  16. #include "qemu/thread.h"
  17. #include "qemu/atomic.h"
  18. #include "block/coroutine.h"
  19. #include "block/coroutine_int.h"
  20. enum {
  21. POOL_BATCH_SIZE = 64,
  22. };
  23. /** Free list to speed up creation */
  24. static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool);
  25. static unsigned int release_pool_size;
  26. static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool);
  27. static __thread unsigned int alloc_pool_size;
  28. static __thread Notifier coroutine_pool_cleanup_notifier;
  29. static void coroutine_pool_cleanup(Notifier *n, void *value)
  30. {
  31. Coroutine *co;
  32. Coroutine *tmp;
  33. QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) {
  34. QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
  35. qemu_coroutine_delete(co);
  36. }
  37. }
  38. Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
  39. {
  40. Coroutine *co = NULL;
  41. if (CONFIG_COROUTINE_POOL) {
  42. co = QSLIST_FIRST(&alloc_pool);
  43. if (!co) {
  44. if (release_pool_size > POOL_BATCH_SIZE) {
  45. /* Slow path; a good place to register the destructor, too. */
  46. if (!coroutine_pool_cleanup_notifier.notify) {
  47. coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup;
  48. qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier);
  49. }
  50. /* This is not exact; there could be a little skew between
  51. * release_pool_size and the actual size of release_pool. But
  52. * it is just a heuristic, it does not need to be perfect.
  53. */
  54. alloc_pool_size = atomic_xchg(&release_pool_size, 0);
  55. QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool);
  56. co = QSLIST_FIRST(&alloc_pool);
  57. }
  58. }
  59. if (co) {
  60. QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
  61. alloc_pool_size--;
  62. }
  63. }
  64. if (!co) {
  65. co = qemu_coroutine_new();
  66. }
  67. co->entry = entry;
  68. QTAILQ_INIT(&co->co_queue_wakeup);
  69. return co;
  70. }
  71. static void coroutine_delete(Coroutine *co)
  72. {
  73. co->caller = NULL;
  74. if (CONFIG_COROUTINE_POOL) {
  75. if (release_pool_size < POOL_BATCH_SIZE * 2) {
  76. QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next);
  77. atomic_inc(&release_pool_size);
  78. return;
  79. }
  80. if (alloc_pool_size < POOL_BATCH_SIZE) {
  81. QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next);
  82. alloc_pool_size++;
  83. return;
  84. }
  85. }
  86. qemu_coroutine_delete(co);
  87. }
  88. void qemu_coroutine_enter(Coroutine *co, void *opaque)
  89. {
  90. Coroutine *self = qemu_coroutine_self();
  91. CoroutineAction ret;
  92. trace_qemu_coroutine_enter(self, co, opaque);
  93. if (co->caller) {
  94. fprintf(stderr, "Co-routine re-entered recursively\n");
  95. abort();
  96. }
  97. co->caller = self;
  98. co->entry_arg = opaque;
  99. ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER);
  100. qemu_co_queue_run_restart(co);
  101. switch (ret) {
  102. case COROUTINE_YIELD:
  103. return;
  104. case COROUTINE_TERMINATE:
  105. trace_qemu_coroutine_terminate(co);
  106. coroutine_delete(co);
  107. return;
  108. default:
  109. abort();
  110. }
  111. }
  112. void coroutine_fn qemu_coroutine_yield(void)
  113. {
  114. Coroutine *self = qemu_coroutine_self();
  115. Coroutine *to = self->caller;
  116. trace_qemu_coroutine_yield(self, to);
  117. if (!to) {
  118. fprintf(stderr, "Co-routine is yielding to no one\n");
  119. abort();
  120. }
  121. self->caller = NULL;
  122. qemu_coroutine_switch(self, to, COROUTINE_YIELD);
  123. }