| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU block layer thread pool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright IBM, Corp. 2008 | 
					
						
							|  |  |  |  * Copyright Red Hat, Inc. 2012 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: | 
					
						
							|  |  |  |  *  Anthony Liguori   <aliguori@us.ibm.com> | 
					
						
							|  |  |  |  *  Paolo Bonzini     <pbonzini@redhat.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This work is licensed under the terms of the GNU GPL, version 2.  See | 
					
						
							|  |  |  |  * the COPYING file in the top-level directory. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Contributions after 2012-01-13 are licensed under the terms of the | 
					
						
							|  |  |  |  * GNU GPL, version 2 or (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2016-01-29 17:50:05 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | #include "qemu-common.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-17 18:20:00 +01:00
										 |  |  | #include "qemu/queue.h"
 | 
					
						
							|  |  |  | #include "qemu/thread.h"
 | 
					
						
							| 
									
										
										
										
											2015-09-01 14:48:02 +01:00
										 |  |  | #include "qemu/coroutine.h"
 | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | #include "trace.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-17 18:19:44 +01:00
										 |  |  | #include "block/thread-pool.h"
 | 
					
						
							| 
									
										
										
										
											2013-08-21 16:02:47 +01:00
										 |  |  | #include "qemu/main-loop.h"
 | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  | static void do_spawn_thread(ThreadPool *pool); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | typedef struct ThreadPoolElement ThreadPoolElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum ThreadState { | 
					
						
							|  |  |  |     THREAD_QUEUED, | 
					
						
							|  |  |  |     THREAD_ACTIVE, | 
					
						
							|  |  |  |     THREAD_DONE, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ThreadPoolElement { | 
					
						
							| 
									
										
										
										
											2014-10-07 13:59:14 +02:00
										 |  |  |     BlockAIOCB common; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     ThreadPool *pool; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     ThreadPoolFunc *func; | 
					
						
							|  |  |  |     void *arg; | 
					
						
							| 
									
										
										
										
											2012-10-31 10:09:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Moving state out of THREAD_QUEUED is protected by lock.  After
 | 
					
						
							|  |  |  |      * that, only the worker thread can write to it.  Reads and writes | 
					
						
							|  |  |  |      * of state and ret are ordered with memory barriers. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     enum ThreadState state; | 
					
						
							|  |  |  |     int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Access to this list is protected by lock.  */ | 
					
						
							|  |  |  |     QTAILQ_ENTRY(ThreadPoolElement) reqs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Access to this list is protected by the global mutex.  */ | 
					
						
							|  |  |  |     QLIST_ENTRY(ThreadPoolElement) all; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  | struct ThreadPool { | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     AioContext *ctx; | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:25 +02:00
										 |  |  |     QEMUBH *completion_bh; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     QemuMutex lock; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     QemuCond worker_stopped; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     QemuSemaphore sem; | 
					
						
							|  |  |  |     int max_threads; | 
					
						
							|  |  |  |     QEMUBH *new_thread_bh; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* The following variables are only accessed from one AioContext. */ | 
					
						
							|  |  |  |     QLIST_HEAD(, ThreadPoolElement) head; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* The following variables are protected by lock.  */ | 
					
						
							|  |  |  |     QTAILQ_HEAD(, ThreadPoolElement) request_list; | 
					
						
							|  |  |  |     int cur_threads; | 
					
						
							|  |  |  |     int idle_threads; | 
					
						
							|  |  |  |     int new_threads;     /* backlog of threads we need to create */ | 
					
						
							|  |  |  |     int pending_threads; /* threads created but not running yet */ | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     bool stopping; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *worker_thread(void *opaque) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     ThreadPool *pool = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_mutex_lock(&pool->lock); | 
					
						
							|  |  |  |     pool->pending_threads--; | 
					
						
							|  |  |  |     do_spawn_thread(pool); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     while (!pool->stopping) { | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |         ThreadPoolElement *req; | 
					
						
							|  |  |  |         int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         do { | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |             pool->idle_threads++; | 
					
						
							|  |  |  |             qemu_mutex_unlock(&pool->lock); | 
					
						
							|  |  |  |             ret = qemu_sem_timedwait(&pool->sem, 10000); | 
					
						
							|  |  |  |             qemu_mutex_lock(&pool->lock); | 
					
						
							|  |  |  |             pool->idle_threads--; | 
					
						
							|  |  |  |         } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list)); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |         if (ret == -1 || pool->stopping) { | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |         req = QTAILQ_FIRST(&pool->request_list); | 
					
						
							|  |  |  |         QTAILQ_REMOVE(&pool->request_list, req, reqs); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |         req->state = THREAD_ACTIVE; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |         qemu_mutex_unlock(&pool->lock); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         ret = req->func(req->arg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         req->ret = ret; | 
					
						
							| 
									
										
										
										
											2012-10-31 10:09:11 +01:00
										 |  |  |         /* Write ret before state.  */ | 
					
						
							|  |  |  |         smp_wmb(); | 
					
						
							|  |  |  |         req->state = THREAD_DONE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |         qemu_mutex_lock(&pool->lock); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:25 +02:00
										 |  |  |         qemu_bh_schedule(pool->completion_bh); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     pool->cur_threads--; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     qemu_cond_signal(&pool->worker_stopped); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     qemu_mutex_unlock(&pool->lock); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  | static void do_spawn_thread(ThreadPool *pool) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     QemuThread t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Runs with lock taken.  */ | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     if (!pool->new_threads) { | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     pool->new_threads--; | 
					
						
							|  |  |  |     pool->pending_threads++; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-30 10:20:32 +00:00
										 |  |  |     qemu_thread_create(&t, "worker", worker_thread, pool, QEMU_THREAD_DETACHED); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void spawn_thread_bh_fn(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     ThreadPool *pool = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_mutex_lock(&pool->lock); | 
					
						
							|  |  |  |     do_spawn_thread(pool); | 
					
						
							|  |  |  |     qemu_mutex_unlock(&pool->lock); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  | static void spawn_thread(ThreadPool *pool) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     pool->cur_threads++; | 
					
						
							|  |  |  |     pool->new_threads++; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     /* If there are threads being created, they will spawn new workers, so
 | 
					
						
							|  |  |  |      * we don't spend time creating many threads in a loop holding a mutex or | 
					
						
							|  |  |  |      * starving the current vcpu. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * If there are no idle threads, ask the main thread to create one, so we | 
					
						
							|  |  |  |      * inherit the correct affinity instead of the vcpu affinity. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     if (!pool->pending_threads) { | 
					
						
							|  |  |  |         qemu_bh_schedule(pool->new_thread_bh); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:25 +02:00
										 |  |  | static void thread_pool_completion_bh(void *opaque) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:25 +02:00
										 |  |  |     ThreadPool *pool = opaque; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     ThreadPoolElement *elem, *next; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | restart: | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     QLIST_FOREACH_SAFE(elem, &pool->head, all, next) { | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:12 +08:00
										 |  |  |         if (elem->state != THREAD_DONE) { | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-04-02 17:39:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         trace_thread_pool_complete(pool, elem, elem->common.opaque, | 
					
						
							|  |  |  |                                    elem->ret); | 
					
						
							|  |  |  |         QLIST_REMOVE(elem, all); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (elem->common.cb) { | 
					
						
							| 
									
										
										
										
											2012-10-31 10:09:11 +01:00
										 |  |  |             /* Read state before ret.  */ | 
					
						
							|  |  |  |             smp_rmb(); | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:26 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             /* Schedule ourselves in case elem->common.cb() calls aio_poll() to
 | 
					
						
							|  |  |  |              * wait for another request that completed at the same time. | 
					
						
							|  |  |  |              */ | 
					
						
							|  |  |  |             qemu_bh_schedule(pool->completion_bh); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-31 10:09:11 +01:00
										 |  |  |             elem->common.cb(elem->common.opaque, elem->ret); | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:28 +08:00
										 |  |  |             qemu_aio_unref(elem); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |             goto restart; | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:28 +08:00
										 |  |  |             qemu_aio_unref(elem); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-07 13:59:14 +02:00
										 |  |  | static void thread_pool_cancel(BlockAIOCB *acb) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     ThreadPoolElement *elem = (ThreadPoolElement *)acb; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     ThreadPool *pool = elem->pool; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     trace_thread_pool_cancel(elem, elem->common.opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     qemu_mutex_lock(&pool->lock); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     if (elem->state == THREAD_QUEUED && | 
					
						
							|  |  |  |         /* No thread has yet started working on elem. we can try to "steal"
 | 
					
						
							|  |  |  |          * the item from the worker if we can get a signal from the | 
					
						
							|  |  |  |          * semaphore.  Because this is non-blocking, we can do it with | 
					
						
							|  |  |  |          * the lock taken and ensure that elem will remain THREAD_QUEUED. | 
					
						
							|  |  |  |          */ | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |         qemu_sem_timedwait(&pool->sem, 0) == 0) { | 
					
						
							|  |  |  |         QTAILQ_REMOVE(&pool->request_list, elem, reqs); | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:25 +02:00
										 |  |  |         qemu_bh_schedule(pool->completion_bh); | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         elem->state = THREAD_DONE; | 
					
						
							|  |  |  |         elem->ret = -ECANCELED; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     qemu_mutex_unlock(&pool->lock); | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:12 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-07 13:59:14 +02:00
										 |  |  | static AioContext *thread_pool_get_aio_context(BlockAIOCB *acb) | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:12 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     ThreadPoolElement *elem = (ThreadPoolElement *)acb; | 
					
						
							|  |  |  |     ThreadPool *pool = elem->pool; | 
					
						
							|  |  |  |     return pool->ctx; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-31 16:34:37 +01:00
										 |  |  | static const AIOCBInfo thread_pool_aiocb_info = { | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     .aiocb_size         = sizeof(ThreadPoolElement), | 
					
						
							| 
									
										
										
										
											2014-09-11 13:41:12 +08:00
										 |  |  |     .cancel_async       = thread_pool_cancel, | 
					
						
							|  |  |  |     .get_aio_context    = thread_pool_get_aio_context, | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-07 13:59:14 +02:00
										 |  |  | BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool, | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:49 +01:00
										 |  |  |         ThreadPoolFunc *func, void *arg, | 
					
						
							| 
									
										
										
										
											2014-10-07 13:59:15 +02:00
										 |  |  |         BlockCompletionFunc *cb, void *opaque) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     ThreadPoolElement *req; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-31 16:34:37 +01:00
										 |  |  |     req = qemu_aio_get(&thread_pool_aiocb_info, NULL, cb, opaque); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     req->func = func; | 
					
						
							|  |  |  |     req->arg = arg; | 
					
						
							|  |  |  |     req->state = THREAD_QUEUED; | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     req->pool = pool; | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     QLIST_INSERT_HEAD(&pool->head, req, all); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     trace_thread_pool_submit(pool, req, arg); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     qemu_mutex_lock(&pool->lock); | 
					
						
							|  |  |  |     if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { | 
					
						
							|  |  |  |         spawn_thread(pool); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     QTAILQ_INSERT_TAIL(&pool->request_list, req, reqs); | 
					
						
							|  |  |  |     qemu_mutex_unlock(&pool->lock); | 
					
						
							|  |  |  |     qemu_sem_post(&pool->sem); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     return &req->common; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct ThreadPoolCo { | 
					
						
							|  |  |  |     Coroutine *co; | 
					
						
							|  |  |  |     int ret; | 
					
						
							|  |  |  | } ThreadPoolCo; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void thread_pool_co_cb(void *opaque, int ret) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ThreadPoolCo *co = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     co->ret = ret; | 
					
						
							|  |  |  |     qemu_coroutine_enter(co->co, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:49 +01:00
										 |  |  | int coroutine_fn thread_pool_submit_co(ThreadPool *pool, ThreadPoolFunc *func, | 
					
						
							|  |  |  |                                        void *arg) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     ThreadPoolCo tpc = { .co = qemu_coroutine_self(), .ret = -EINPROGRESS }; | 
					
						
							|  |  |  |     assert(qemu_in_coroutine()); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:49 +01:00
										 |  |  |     thread_pool_submit_aio(pool, func, arg, thread_pool_co_cb, &tpc); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  |     qemu_coroutine_yield(); | 
					
						
							|  |  |  |     return tpc.ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:49 +01:00
										 |  |  | void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg) | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:49 +01:00
										 |  |  |     thread_pool_submit_aio(pool, func, arg, NULL, NULL); | 
					
						
							| 
									
										
										
										
											2012-02-23 13:23:34 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  | static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!ctx) { | 
					
						
							|  |  |  |         ctx = qemu_get_aio_context(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memset(pool, 0, sizeof(*pool)); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     pool->ctx = ctx; | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:25 +02:00
										 |  |  |     pool->completion_bh = aio_bh_new(ctx, thread_pool_completion_bh, pool); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     qemu_mutex_init(&pool->lock); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     qemu_cond_init(&pool->worker_stopped); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:45 +01:00
										 |  |  |     qemu_sem_init(&pool->sem, 0); | 
					
						
							|  |  |  |     pool->max_threads = 64; | 
					
						
							|  |  |  |     pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     QLIST_INIT(&pool->head); | 
					
						
							|  |  |  |     QTAILQ_INIT(&pool->request_list); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  | ThreadPool *thread_pool_new(AioContext *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ThreadPool *pool = g_new(ThreadPool, 1); | 
					
						
							|  |  |  |     thread_pool_init_one(pool, ctx); | 
					
						
							|  |  |  |     return pool; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void thread_pool_free(ThreadPool *pool) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!pool) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(QLIST_EMPTY(&pool->head)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_mutex_lock(&pool->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Stop new threads from spawning */ | 
					
						
							|  |  |  |     qemu_bh_delete(pool->new_thread_bh); | 
					
						
							|  |  |  |     pool->cur_threads -= pool->new_threads; | 
					
						
							|  |  |  |     pool->new_threads = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Wait for worker threads to terminate */ | 
					
						
							|  |  |  |     pool->stopping = true; | 
					
						
							|  |  |  |     while (pool->cur_threads > 0) { | 
					
						
							|  |  |  |         qemu_sem_post(&pool->sem); | 
					
						
							|  |  |  |         qemu_cond_wait(&pool->worker_stopped, &pool->lock); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_mutex_unlock(&pool->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-15 16:44:25 +02:00
										 |  |  |     qemu_bh_delete(pool->completion_bh); | 
					
						
							| 
									
										
										
										
											2013-03-07 13:41:46 +01:00
										 |  |  |     qemu_sem_destroy(&pool->sem); | 
					
						
							|  |  |  |     qemu_cond_destroy(&pool->worker_stopped); | 
					
						
							|  |  |  |     qemu_mutex_destroy(&pool->lock); | 
					
						
							|  |  |  |     g_free(pool); | 
					
						
							|  |  |  | } |