| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Asterisk -- An open source telephony toolkit. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-01-17 16:04:10 +00:00
										 |  |  |  * Copyright (C) 2012-2013, Digium, Inc. | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Mark Michelson <mmmichelson@digium.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See http://www.asterisk.org for more information about
 | 
					
						
							|  |  |  |  * the Asterisk project. Please do not directly contact | 
					
						
							|  |  |  |  * any of the maintainers of this project for assistance; | 
					
						
							|  |  |  |  * the project provides a web site, mailing lists and IRC | 
					
						
							|  |  |  |  * channels for your use. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software, distributed under the terms of | 
					
						
							|  |  |  |  * the GNU General Public License Version 2. See the LICENSE file | 
					
						
							|  |  |  |  * at the top of the source tree. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "asterisk/threadpool.h"
 | 
					
						
							|  |  |  | #include "asterisk/taskprocessor.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | #include "asterisk/astobj2.h"
 | 
					
						
							|  |  |  | #include "asterisk/utils.h"
 | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-17 16:04:10 +00:00
										 |  |  | /* Needs to stay prime if increased */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | #define THREAD_BUCKETS 89
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief An opaque threadpool structure | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * A threadpool is a collection of threads that execute | 
					
						
							|  |  |  |  * tasks from a common queue. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | struct ast_threadpool { | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! Threadpool listener */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	struct ast_threadpool_listener *listener; | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	/*!
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	 * \brief The container of active threads. | 
					
						
							|  |  |  | 	 * Active threads are those that are currently running tasks | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	struct ao2_container *active_threads; | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	/*!
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	 * \brief The container of idle threads. | 
					
						
							|  |  |  | 	 * Idle threads are those that are currenly waiting to run tasks | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	struct ao2_container *idle_threads; | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	/*!
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	 * \brief The container of zombie threads. | 
					
						
							|  |  |  | 	 * Zombie threads may be running tasks, but they are scheduled to die soon | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	struct ao2_container *zombie_threads; | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	/*!
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	 * \brief The main taskprocessor | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	 * | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	 * Tasks that are queued in this taskprocessor are | 
					
						
							|  |  |  | 	 * doled out to the worker threads. Worker threads that | 
					
						
							|  |  |  | 	 * execute tasks from the threadpool are executing tasks | 
					
						
							|  |  |  | 	 * in this taskprocessor. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * The threadpool itself is actually the private data for | 
					
						
							|  |  |  | 	 * this taskprocessor's listener. This way, as taskprocessor | 
					
						
							|  |  |  | 	 * changes occur, the threadpool can alert its listeners | 
					
						
							|  |  |  | 	 * appropriately. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	struct ast_taskprocessor *tps; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*!
 | 
					
						
							|  |  |  | 	 * \brief The control taskprocessor | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * This is a standard taskprocessor that uses the default | 
					
						
							|  |  |  | 	 * taskprocessor listener. In other words, all tasks queued to | 
					
						
							|  |  |  | 	 * this taskprocessor have a single thread that executes the | 
					
						
							|  |  |  | 	 * tasks. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * All tasks that modify the state of the threadpool and all tasks | 
					
						
							|  |  |  | 	 * that call out to threadpool listeners are pushed to this | 
					
						
							|  |  |  | 	 * taskprocessor. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * For instance, when the threadpool changes sizes, a task is put | 
					
						
							|  |  |  | 	 * into this taskprocessor to do so. When it comes time to tell the | 
					
						
							|  |  |  | 	 * threadpool listener that worker threads have changed state, | 
					
						
							|  |  |  | 	 * the task is placed in this taskprocessor. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * This is done for three main reasons | 
					
						
							|  |  |  | 	 * 1) It ensures that listeners are given an accurate portrayal | 
					
						
							|  |  |  | 	 * of the threadpool's current state. In other words, when a listener | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	 * gets told a count of active, idle and zombie threads, it does not | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	 * need to worry that internal state of the threadpool might be different | 
					
						
							|  |  |  | 	 * from what it has been told. | 
					
						
							|  |  |  | 	 * 2) It minimizes the locking required in both the threadpool and in | 
					
						
							|  |  |  | 	 * threadpool listener's callbacks. | 
					
						
							|  |  |  | 	 * 3) It ensures that listener callbacks are called in the same order | 
					
						
							|  |  |  | 	 * that the threadpool had its state change. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	struct ast_taskprocessor *control_tps; | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | 	/*! True if the threadpool is in the process of shutting down */ | 
					
						
							| 
									
										
										
										
											2012-12-10 04:08:29 +00:00
										 |  |  | 	int shutting_down; | 
					
						
							| 
									
										
										
										
											2012-12-10 06:13:09 +00:00
										 |  |  | 	/*! Threadpool-specific options */ | 
					
						
							|  |  |  | 	struct ast_threadpool_options options; | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 21:09:55 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief listener for a threadpool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The listener is notified of changes in a threadpool. It can | 
					
						
							|  |  |  |  * react by doing things like increasing the number of threads | 
					
						
							|  |  |  |  * in the pool | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct ast_threadpool_listener { | 
					
						
							|  |  |  | 	/*! Callbacks called by the threadpool */ | 
					
						
							|  |  |  | 	const struct ast_threadpool_listener_callbacks *callbacks; | 
					
						
							|  |  |  | 	/*! User data for the listener */ | 
					
						
							|  |  |  | 	void *user_data; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief states for worker threads | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | enum worker_state { | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! The worker is either active or idle */ | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 	ALIVE, | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	/*!
 | 
					
						
							|  |  |  | 	 * The worker has been asked to shut down but | 
					
						
							|  |  |  | 	 * may still be in the process of executing tasks. | 
					
						
							|  |  |  | 	 * This transition happens when the threadpool needs | 
					
						
							|  |  |  | 	 * to shrink and needs to kill active threads in order | 
					
						
							|  |  |  | 	 * to do so. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ZOMBIE, | 
					
						
							|  |  |  | 	/*!
 | 
					
						
							|  |  |  | 	 * The worker has been asked to shut down. Typically | 
					
						
							|  |  |  | 	 * only idle threads go to this state directly, but | 
					
						
							|  |  |  | 	 * active threads may go straight to this state when | 
					
						
							|  |  |  | 	 * the threadpool is shut down. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 	DEAD, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * A thread that executes threadpool tasks | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct worker_thread { | 
					
						
							|  |  |  | 	/*! A unique (within a run of Asterisk) ID for the thread. Used for hashing and searching */ | 
					
						
							|  |  |  | 	int id; | 
					
						
							|  |  |  | 	/*! Condition used in conjunction with state changes */ | 
					
						
							|  |  |  | 	ast_cond_t cond; | 
					
						
							|  |  |  | 	/*! Lock used alongside the condition for state changes */ | 
					
						
							|  |  |  | 	ast_mutex_t lock; | 
					
						
							|  |  |  | 	/*! The actual thread that is executing tasks */ | 
					
						
							|  |  |  | 	pthread_t thread; | 
					
						
							|  |  |  | 	/*! A pointer to the threadpool. Needed to be able to execute tasks */ | 
					
						
							|  |  |  | 	struct ast_threadpool *pool; | 
					
						
							|  |  |  | 	/*! The current state of the worker thread */ | 
					
						
							|  |  |  | 	enum worker_state state; | 
					
						
							|  |  |  | 	/*! A boolean used to determine if an idle thread should become active */ | 
					
						
							|  |  |  | 	int wake_up; | 
					
						
							|  |  |  | 	/*! Options for this threadpool */ | 
					
						
							|  |  |  | 	struct ast_threadpool_options options; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /* Worker thread forward declarations. See definitions for documentation */ | 
					
						
							|  |  |  | static int worker_thread_hash(const void *obj, int flags); | 
					
						
							|  |  |  | static int worker_thread_cmp(void *obj, void *arg, int flags); | 
					
						
							|  |  |  | static void worker_thread_destroy(void *obj); | 
					
						
							|  |  |  | static void worker_active(struct worker_thread *worker); | 
					
						
							|  |  |  | static void *worker_start(void *arg); | 
					
						
							|  |  |  | static struct worker_thread *worker_thread_alloc(struct ast_threadpool *pool); | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | static int worker_thread_start(struct worker_thread *worker); | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | static int worker_idle(struct worker_thread *worker); | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | static int worker_set_state(struct worker_thread *worker, enum worker_state state); | 
					
						
							| 
									
										
										
										
											2012-12-04 22:11:31 +00:00
										 |  |  | static void worker_shutdown(struct worker_thread *worker); | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Notify the threadpool listener that the state has changed. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This notifies the threadpool listener via its state_changed callback. | 
					
						
							|  |  |  |  * \param pool The threadpool whose state has changed | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | static void threadpool_send_state_changed(struct ast_threadpool *pool) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int active_size = ao2_container_count(pool->active_threads); | 
					
						
							|  |  |  | 	int idle_size = ao2_container_count(pool->idle_threads); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	if (pool->listener && pool->listener->callbacks->state_changed) { | 
					
						
							|  |  |  | 		pool->listener->callbacks->state_changed(pool, pool->listener, active_size, idle_size); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Struct used for queued operations involving worker state changes | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | struct thread_worker_pair { | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! Threadpool that contains the worker whose state has changed */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	struct ast_threadpool *pool; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! Worker whose state has changed */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	struct worker_thread *worker; | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Destructor for thread_worker_pair | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | static void thread_worker_pair_destructor(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_worker_pair *pair = obj; | 
					
						
							|  |  |  | 	ao2_ref(pair->worker, -1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Allocate and initialize a thread_worker_pair | 
					
						
							|  |  |  |  * \param pool Threadpool to assign to the thread_worker_pair | 
					
						
							|  |  |  |  * \param worker Worker thread to assign to the thread_worker_pair | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 17:24:11 +00:00
										 |  |  | static struct thread_worker_pair *thread_worker_pair_alloc(struct ast_threadpool *pool, | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 		struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_worker_pair *pair = ao2_alloc(sizeof(*pair), thread_worker_pair_destructor); | 
					
						
							|  |  |  | 	if (!pair) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	pair->pool = pool; | 
					
						
							|  |  |  | 	ao2_ref(worker, +1); | 
					
						
							|  |  |  | 	pair->worker = worker; | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	return pair; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Move a worker thread from the active container to the idle container. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function is called from the threadpool's control taskprocessor thread. | 
					
						
							|  |  |  |  * \param data A thread_worker_pair containing the threadpool and the worker to move. | 
					
						
							|  |  |  |  * \return 0 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | static int queued_active_thread_idle(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_worker_pair *pair = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_link(pair->pool->idle_threads, pair->worker); | 
					
						
							|  |  |  | 	ao2_unlink(pair->pool->active_threads, pair->worker); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	threadpool_send_state_changed(pair->pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(pair, -1); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Queue a task to move a thread from the active list to the idle list | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is called by a worker thread when it runs out of tasks to perform and | 
					
						
							|  |  |  |  * goes idle. | 
					
						
							|  |  |  |  * \param pool The threadpool to which the worker belongs | 
					
						
							|  |  |  |  * \param worker The worker thread that has gone idle | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | static void threadpool_active_thread_idle(struct ast_threadpool *pool, | 
					
						
							|  |  |  | 		struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	struct thread_worker_pair *pair; | 
					
						
							|  |  |  | 	SCOPED_AO2LOCK(lock, pool); | 
					
						
							|  |  |  | 	if (pool->shutting_down) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pair = thread_worker_pair_alloc(pool, worker); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	if (!pair) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	ast_taskprocessor_push(pool->control_tps, queued_active_thread_idle, pair); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Kill a zombie thread | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This runs from the threadpool's control taskprocessor thread. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param data A thread_worker_pair containing the threadpool and the zombie thread | 
					
						
							|  |  |  |  * \return 0 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int queued_zombie_thread_dead(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_worker_pair *pair = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_unlink(pair->pool->zombie_threads, pair->worker); | 
					
						
							|  |  |  | 	threadpool_send_state_changed(pair->pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(pair, -1); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Queue a task to kill a zombie thread | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is called by a worker thread when it acknowledges that it is time for | 
					
						
							|  |  |  |  * it to die. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void threadpool_zombie_thread_dead(struct ast_threadpool *pool, | 
					
						
							|  |  |  | 		struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	struct thread_worker_pair *pair; | 
					
						
							|  |  |  | 	SCOPED_AO2LOCK(lock, pool); | 
					
						
							|  |  |  | 	if (pool->shutting_down) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pair = thread_worker_pair_alloc(pool, worker); | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	if (!pair) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_taskprocessor_push(pool->control_tps, queued_zombie_thread_dead, pair); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 06:13:09 +00:00
										 |  |  | static int queued_idle_thread_dead(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_worker_pair *pair = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_unlink(pair->pool->idle_threads, pair->worker); | 
					
						
							|  |  |  | 	threadpool_send_state_changed(pair->pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_ref(pair, -1); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void threadpool_idle_thread_dead(struct ast_threadpool *pool, | 
					
						
							|  |  |  | 		struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_worker_pair *pair; | 
					
						
							|  |  |  | 	SCOPED_AO2LOCK(lock, pool); | 
					
						
							|  |  |  | 	if (pool->shutting_down) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pair = thread_worker_pair_alloc(pool, worker); | 
					
						
							|  |  |  | 	if (!pair) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_taskprocessor_push(pool->control_tps, queued_idle_thread_dead, pair); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Execute a task in the threadpool | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  |  * This is the function that worker threads call in order to execute tasks | 
					
						
							|  |  |  |  * in the threadpool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param pool The pool to which the tasks belong. | 
					
						
							|  |  |  |  * \retval 0 Either the pool has been shut down or there are no tasks. | 
					
						
							|  |  |  |  * \retval 1 There are still tasks remaining in the pool. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | static int threadpool_execute(struct ast_threadpool *pool) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	ao2_lock(pool); | 
					
						
							| 
									
										
										
										
											2012-12-10 04:08:29 +00:00
										 |  |  | 	if (!pool->shutting_down) { | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 		ao2_unlock(pool); | 
					
						
							| 
									
										
										
										
											2012-12-10 04:08:29 +00:00
										 |  |  | 		return ast_taskprocessor_execute(pool->tps); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	ao2_unlock(pool); | 
					
						
							| 
									
										
										
										
											2012-12-10 04:08:29 +00:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Destroy a threadpool's components. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is the destructor called automatically when the threadpool's | 
					
						
							|  |  |  |  * reference count reaches zero. This is not to be confused with | 
					
						
							|  |  |  |  * threadpool_destroy. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * By the time this actually gets called, most of the cleanup has already | 
					
						
							|  |  |  |  * been done in the pool. The only thing left to do is to release the | 
					
						
							|  |  |  |  * final reference to the threadpool listener. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param obj The pool to destroy | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void threadpool_destructor(void *obj) | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	struct ast_threadpool *pool = obj; | 
					
						
							| 
									
										
										
										
											2012-12-03 17:24:11 +00:00
										 |  |  | 	ao2_cleanup(pool->listener); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * \brief Allocate a threadpool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is implemented as a taskprocessor listener's alloc callback. This | 
					
						
							|  |  |  |  * is because the threadpool exists as the private data on a taskprocessor | 
					
						
							|  |  |  |  * listener. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  |  * \param name The name of the threadpool. | 
					
						
							|  |  |  |  * \param options The options the threadpool uses. | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  |  * \retval NULL Could not initialize threadpool properly | 
					
						
							|  |  |  |  * \retval non-NULL The newly-allocated threadpool | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-09-29 20:28:24 +00:00
										 |  |  | static struct ast_threadpool *threadpool_alloc(const char *name, const struct ast_threadpool_options *options) | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-09-29 20:28:24 +00:00
										 |  |  | 	RAII_VAR(struct ast_threadpool *, pool, NULL, ao2_cleanup); | 
					
						
							|  |  |  | 	struct ast_str *control_tps_name; | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-29 20:28:24 +00:00
										 |  |  | 	pool = ao2_alloc(sizeof(*pool), threadpool_destructor); | 
					
						
							|  |  |  | 	control_tps_name = ast_str_create(64); | 
					
						
							|  |  |  | 	if (!pool || !control_tps_name) { | 
					
						
							|  |  |  | 		ast_free(control_tps_name); | 
					
						
							| 
									
										
										
										
											2012-12-10 20:14:23 +00:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  | 	ast_str_set(&control_tps_name, 0, "%s-control", name); | 
					
						
							| 
									
										
										
										
											2012-12-10 20:14:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  | 	pool->control_tps = ast_taskprocessor_get(ast_str_buffer(control_tps_name), TPS_REF_DEFAULT); | 
					
						
							|  |  |  | 	ast_free(control_tps_name); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	if (!pool->control_tps) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pool->active_threads = ao2_container_alloc(THREAD_BUCKETS, worker_thread_hash, worker_thread_cmp); | 
					
						
							|  |  |  | 	if (!pool->active_threads) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pool->idle_threads = ao2_container_alloc(THREAD_BUCKETS, worker_thread_hash, worker_thread_cmp); | 
					
						
							|  |  |  | 	if (!pool->idle_threads) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	pool->zombie_threads = ao2_container_alloc(THREAD_BUCKETS, worker_thread_hash, worker_thread_cmp); | 
					
						
							|  |  |  | 	if (!pool->zombie_threads) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  | 	pool->options = *options; | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	ao2_ref(pool, +1); | 
					
						
							|  |  |  | 	return pool; | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-07 00:30:35 +00:00
										 |  |  | static int threadpool_tps_start(struct ast_taskprocessor_listener *listener) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief helper used for queued task when tasks are pushed | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | struct task_pushed_data { | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! Pool into which a task was pushed */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	struct ast_threadpool *pool; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! Indicator of whether the pool had no tasks prior to the new task being added */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	int was_empty; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Allocate and initialize a task_pushed_data | 
					
						
							|  |  |  |  * \param pool The threadpool to set in the task_pushed_data | 
					
						
							|  |  |  |  * \param was_empty The was_empty value to set in the task_pushed_data | 
					
						
							|  |  |  |  * \retval NULL Unable to allocate task_pushed_data | 
					
						
							|  |  |  |  * \retval non-NULL The newly-allocated task_pushed_data | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | static struct task_pushed_data *task_pushed_data_alloc(struct ast_threadpool *pool, | 
					
						
							|  |  |  | 		int was_empty) | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-04 21:21:15 +00:00
										 |  |  | 	struct task_pushed_data *tpd = ao2_alloc(sizeof(*tpd), NULL); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!tpd) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tpd->pool = pool; | 
					
						
							|  |  |  | 	tpd->was_empty = was_empty; | 
					
						
							|  |  |  | 	return tpd; | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Activate idle threads | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-12-11 16:34:00 +00:00
										 |  |  |  * This function always returns CMP_MATCH because all workers that this | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  |  * function acts on need to be seen as matches so they are unlinked from the | 
					
						
							|  |  |  |  * list of idle threads. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Called as an ao2_callback in the threadpool's control taskprocessor thread. | 
					
						
							|  |  |  |  * \param obj The worker to activate | 
					
						
							|  |  |  |  * \param arg The pool where the worker belongs | 
					
						
							|  |  |  |  * \retval CMP_MATCH | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-11 16:34:00 +00:00
										 |  |  | static int activate_thread(void *obj, void *arg, int flags) | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct worker_thread *worker = obj; | 
					
						
							|  |  |  | 	struct ast_threadpool *pool = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	if (!ao2_link(pool->active_threads, worker)) { | 
					
						
							|  |  |  | 		/* If we can't link the idle thread into the active container, then
 | 
					
						
							|  |  |  | 		 * we'll just leave the thread idle and not wake it up. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Failed to activate thread %d. Remaining idle\n", | 
					
						
							|  |  |  | 				worker->id); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (worker_set_state(worker, ALIVE)) { | 
					
						
							|  |  |  | 		ast_debug(1, "Failed to activate thread %d. It is dead\n", | 
					
						
							|  |  |  | 				worker->id); | 
					
						
							|  |  |  | 		/* The worker thread will no longer exist in the active threads or
 | 
					
						
							|  |  |  | 		 * idle threads container after this. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		ao2_unlink(pool->active_threads, worker); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	return CMP_MATCH; | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-11 16:53:16 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Add threads to the threadpool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function is called from the threadpool's control taskprocessor thread. | 
					
						
							|  |  |  |  * \param pool The pool that is expanding | 
					
						
							|  |  |  |  * \delta The number of threads to add to the pool | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void grow(struct ast_threadpool *pool, int delta) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-17 16:04:10 +00:00
										 |  |  | 	int current_size = ao2_container_count(pool->active_threads) + | 
					
						
							|  |  |  | 		ao2_container_count(pool->idle_threads); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pool->options.max_size && current_size + delta > pool->options.max_size) { | 
					
						
							|  |  |  | 		delta = pool->options.max_size - current_size; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	ast_debug(3, "Increasing threadpool %s's size by %d\n", | 
					
						
							|  |  |  | 			ast_taskprocessor_name(pool->tps), delta); | 
					
						
							| 
									
										
										
										
											2012-12-11 16:53:16 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < delta; ++i) { | 
					
						
							|  |  |  | 		struct worker_thread *worker = worker_thread_alloc(pool); | 
					
						
							|  |  |  | 		if (!worker) { | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 		if (ao2_link(pool->idle_threads, worker)) { | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 			if (worker_thread_start(worker)) { | 
					
						
							|  |  |  | 				ast_log(LOG_ERROR, "Unable to start worker thread %d. Destroying.\n", worker->id); | 
					
						
							|  |  |  | 				ao2_unlink(pool->active_threads, worker); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Failed to activate worker thread %d. Destroying.\n", worker->id); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-12-11 16:53:16 +00:00
										 |  |  | 		ao2_ref(worker, -1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-12-11 16:34:00 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							| 
									
										
										
										
											2012-12-11 04:23:31 +00:00
										 |  |  |  * \brief Queued task called when tasks are pushed into the threadpool | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This function first calls into the threadpool's listener to let it know | 
					
						
							|  |  |  |  * that a task has been pushed. It then wakes up all idle threads and moves | 
					
						
							|  |  |  |  * them into the active thread container. | 
					
						
							|  |  |  |  * \param data A task_pushed_data | 
					
						
							|  |  |  |  * \return 0 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-11 04:23:31 +00:00
										 |  |  | static int queued_task_pushed(void *data) | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	struct task_pushed_data *tpd = data; | 
					
						
							|  |  |  | 	struct ast_threadpool *pool = tpd->pool; | 
					
						
							|  |  |  | 	int was_empty = tpd->was_empty; | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 	unsigned int existing_active; | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	if (pool->listener && pool->listener->callbacks->task_pushed) { | 
					
						
							|  |  |  | 		pool->listener->callbacks->task_pushed(pool, pool->listener, was_empty); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	existing_active = ao2_container_count(pool->active_threads); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* The first pass transitions any existing idle threads to be active, and
 | 
					
						
							|  |  |  | 	 * will also remove any worker threads that have recently entered the dead | 
					
						
							|  |  |  | 	 * state. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA, | 
					
						
							|  |  |  | 			activate_thread, pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If no idle threads could be transitioned to active grow the pool as permitted. */ | 
					
						
							|  |  |  | 	if (ao2_container_count(pool->active_threads) == existing_active) { | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 		if (!pool->options.auto_increment) { | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 			ao2_ref(tpd, -1); | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 			return 0; | 
					
						
							| 
									
										
										
										
											2012-12-11 16:53:16 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 		grow(pool, pool->options.auto_increment); | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 		/* An optional second pass transitions any newly added threads. */ | 
					
						
							|  |  |  | 		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA, | 
					
						
							|  |  |  | 				activate_thread, pool); | 
					
						
							| 
									
										
										
										
											2012-12-11 16:53:16 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	threadpool_send_state_changed(pool); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	ao2_ref(tpd, -1); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Taskprocessor listener callback called when a task is added | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The threadpool uses this opportunity to queue a task on its control taskprocessor | 
					
						
							|  |  |  |  * in order to activate idle threads and notify the threadpool listener that the | 
					
						
							|  |  |  |  * task has been pushed. | 
					
						
							|  |  |  |  * \param listener The taskprocessor listener. The threadpool is the listener's private data | 
					
						
							|  |  |  |  * \param was_empty True if the taskprocessor was empty prior to the task being pushed | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | static void threadpool_tps_task_pushed(struct ast_taskprocessor_listener *listener, | 
					
						
							|  |  |  | 		int was_empty) | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-15 20:48:45 +00:00
										 |  |  | 	struct ast_threadpool *pool = ast_taskprocessor_listener_get_user_data(listener); | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	struct task_pushed_data *tpd; | 
					
						
							|  |  |  | 	SCOPED_AO2LOCK(lock, pool); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	if (pool->shutting_down) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tpd = task_pushed_data_alloc(pool, was_empty); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	if (!tpd) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-11 04:23:31 +00:00
										 |  |  | 	ast_taskprocessor_push(pool->control_tps, queued_task_pushed, tpd); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Queued task that handles the case where the threadpool's taskprocessor is emptied | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This simply lets the threadpool's listener know that the threadpool is devoid of tasks | 
					
						
							|  |  |  |  * \param data The pool that has become empty | 
					
						
							|  |  |  |  * \return 0 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-11 04:23:31 +00:00
										 |  |  | static int queued_emptied(void *data) | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ast_threadpool *pool = data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	/* We already checked for existence of this callback when this was queued */ | 
					
						
							| 
									
										
										
										
											2012-12-04 23:45:39 +00:00
										 |  |  | 	pool->listener->callbacks->emptied(pool, pool->listener); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Taskprocessor listener emptied callback | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The threadpool queues a task to let the threadpool listener know that | 
					
						
							|  |  |  |  * the threadpool no longer contains any tasks. | 
					
						
							|  |  |  |  * \param listener The taskprocessor listener. The threadpool is the listener's private data. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | static void threadpool_tps_emptied(struct ast_taskprocessor_listener *listener) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-15 20:48:45 +00:00
										 |  |  | 	struct ast_threadpool *pool = ast_taskprocessor_listener_get_user_data(listener); | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	SCOPED_AO2LOCK(lock, pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pool->shutting_down) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	if (pool->listener && pool->listener->callbacks->emptied) { | 
					
						
							|  |  |  | 		ast_taskprocessor_push(pool->control_tps, queued_emptied, pool); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Taskprocessor listener shutdown callback | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The threadpool will shut down and destroy all of its worker threads when | 
					
						
							|  |  |  |  * this is called back. By the time this gets called, the taskprocessor's | 
					
						
							|  |  |  |  * control taskprocessor has already been destroyed. Therefore there is no risk | 
					
						
							|  |  |  |  * in outright destroying the worker threads here. | 
					
						
							|  |  |  |  * \param listener The taskprocessor listener. The threadpool is the listener's private data. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | static void threadpool_tps_shutdown(struct ast_taskprocessor_listener *listener) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-15 20:48:45 +00:00
										 |  |  | 	struct ast_threadpool *pool = ast_taskprocessor_listener_get_user_data(listener); | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 20:15:00 +00:00
										 |  |  | 	if (pool->listener && pool->listener->callbacks->shutdown) { | 
					
						
							|  |  |  | 		pool->listener->callbacks->shutdown(pool->listener); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	ao2_cleanup(pool->active_threads); | 
					
						
							|  |  |  | 	ao2_cleanup(pool->idle_threads); | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	ao2_cleanup(pool->zombie_threads); | 
					
						
							| 
									
										
										
										
											2012-12-03 17:24:11 +00:00
										 |  |  | 	ao2_cleanup(pool); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Table of taskprocessor listener callbacks for threadpool's main taskprocessor | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | static struct ast_taskprocessor_listener_callbacks threadpool_tps_listener_callbacks = { | 
					
						
							| 
									
										
										
										
											2012-12-07 00:30:35 +00:00
										 |  |  | 	.start = threadpool_tps_start, | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 	.task_pushed = threadpool_tps_task_pushed, | 
					
						
							|  |  |  | 	.emptied = threadpool_tps_emptied, | 
					
						
							|  |  |  | 	.shutdown = threadpool_tps_shutdown, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief ao2 callback to kill a set number of threads. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Threads will be unlinked from the container as long as the | 
					
						
							|  |  |  |  * counter has not reached zero. The counter is decremented with | 
					
						
							|  |  |  |  * each thread that is removed. | 
					
						
							|  |  |  |  * \param obj The worker thread up for possible destruction | 
					
						
							|  |  |  |  * \param arg The counter | 
					
						
							|  |  |  |  * \param flags Unused | 
					
						
							|  |  |  |  * \retval CMP_MATCH The counter has not reached zero, so this flag should be removed. | 
					
						
							|  |  |  |  * \retval CMP_STOP The counter has reached zero so no more threads should be removed. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | static int kill_threads(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int *num_to_kill = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	if (*num_to_kill > 0) { | 
					
						
							|  |  |  | 		--(*num_to_kill); | 
					
						
							|  |  |  | 		return CMP_MATCH; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return CMP_STOP; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief ao2 callback to zombify a set number of threads. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Threads will be zombified as long as as the counter has not reached | 
					
						
							|  |  |  |  * zero. The counter is decremented with each thread that is zombified. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Zombifying a thread involves removing it from its current container, | 
					
						
							|  |  |  |  * adding it to the zombie container, and changing the state of the | 
					
						
							|  |  |  |  * worker to a zombie | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This callback is called from the threadpool control taskprocessor thread. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param obj The worker thread that may be zombified | 
					
						
							|  |  |  |  * \param arg The pool to which the worker belongs | 
					
						
							|  |  |  |  * \param data The counter | 
					
						
							|  |  |  |  * \param flags Unused | 
					
						
							|  |  |  |  * \retval CMP_MATCH The zombified thread should be removed from its current container | 
					
						
							|  |  |  |  * \retval CMP_STOP Stop attempting to zombify threads | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int zombify_threads(void *obj, void *arg, void *data, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct worker_thread *worker = obj; | 
					
						
							|  |  |  | 	struct ast_threadpool *pool = arg; | 
					
						
							|  |  |  | 	int *num_to_zombify = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((*num_to_zombify)-- > 0) { | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 		if (!ao2_link(pool->zombie_threads, worker)) { | 
					
						
							|  |  |  | 			ast_log(LOG_WARNING, "Failed to zombify active thread %d. Thread will remain active\n", worker->id); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 		worker_set_state(worker, ZOMBIE); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 		return CMP_MATCH; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return CMP_STOP; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Remove threads from the threadpool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The preference is to kill idle threads. However, if there are | 
					
						
							|  |  |  |  * more threads to remove than there are idle threads, then active | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  |  * threads will be zombified instead. | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * This function is called from the threadpool control taskprocessor thread. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param pool The threadpool to remove threads from | 
					
						
							|  |  |  |  * \param delta The number of threads to remove | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | static void shrink(struct ast_threadpool *pool, int delta) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	 * Preference is to kill idle threads, but | 
					
						
							|  |  |  | 	 * we'll move on to deactivating active threads | 
					
						
							|  |  |  | 	 * if we have to | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	int idle_threads = ao2_container_count(pool->idle_threads); | 
					
						
							|  |  |  | 	int idle_threads_to_kill = MIN(delta, idle_threads); | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	int active_threads_to_zombify = delta - idle_threads_to_kill; | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	ast_debug(3, "Destroying %d idle threads in threadpool %s\n", idle_threads_to_kill, | 
					
						
							|  |  |  | 			ast_taskprocessor_name(pool->tps)); | 
					
						
							| 
									
										
										
										
											2012-12-09 19:10:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 			kill_threads, &idle_threads_to_kill); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	ast_debug(3, "Destroying %d active threads in threadpool %s\n", active_threads_to_zombify, | 
					
						
							|  |  |  | 			ast_taskprocessor_name(pool->tps)); | 
					
						
							| 
									
										
										
										
											2012-12-09 19:10:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	ao2_callback_data(pool->active_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, | 
					
						
							|  |  |  | 			zombify_threads, pool, &active_threads_to_zombify); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Helper struct used for queued operations that change the size of the threadpool | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | struct set_size_data { | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! The pool whose size is to change */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	struct ast_threadpool *pool; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/*! The requested new size of the pool */ | 
					
						
							| 
									
										
										
										
											2012-12-03 17:24:11 +00:00
										 |  |  | 	unsigned int size; | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Allocate and initialize a set_size_data | 
					
						
							|  |  |  |  * \param pool The pool for the set_size_data | 
					
						
							|  |  |  |  * \param size The size to store in the set_size_data | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | static struct set_size_data *set_size_data_alloc(struct ast_threadpool *pool, | 
					
						
							| 
									
										
										
										
											2012-12-03 17:24:11 +00:00
										 |  |  | 		unsigned int size) | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-04 22:11:31 +00:00
										 |  |  | 	struct set_size_data *ssd = ao2_alloc(sizeof(*ssd), NULL); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	if (!ssd) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ssd->pool = pool; | 
					
						
							|  |  |  | 	ssd->size = size; | 
					
						
							|  |  |  | 	return ssd; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Change the size of the threadpool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This can either result in shrinking or growing the threadpool depending | 
					
						
							|  |  |  |  * on the new desired size and the current size. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function is run from the threadpool control taskprocessor thread | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param data A set_size_data used for determining how to act | 
					
						
							|  |  |  |  * \return 0 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | static int queued_set_size(void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-03-07 00:05:16 +00:00
										 |  |  | 	RAII_VAR(struct set_size_data *, ssd, data, ao2_cleanup); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	struct ast_threadpool *pool = ssd->pool; | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	unsigned int num_threads = ssd->size; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-17 16:04:10 +00:00
										 |  |  | 	/* We don't count zombie threads as being "live" when potentially resizing */ | 
					
						
							| 
									
										
										
										
											2012-12-03 17:24:11 +00:00
										 |  |  | 	unsigned int current_size = ao2_container_count(pool->active_threads) + | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 			ao2_container_count(pool->idle_threads); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	if (current_size == num_threads) { | 
					
						
							| 
									
										
										
										
											2013-03-07 21:55:28 +00:00
										 |  |  | 		ast_debug(3, "Not changing threadpool size since new size %u is the same as current %u\n", | 
					
						
							|  |  |  | 			  num_threads, current_size); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	if (current_size < num_threads) { | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, | 
					
						
							|  |  |  | 				activate_thread, pool); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* As the above may have altered the number of current threads update it */ | 
					
						
							|  |  |  | 		current_size = ao2_container_count(pool->active_threads) + | 
					
						
							|  |  |  | 				ao2_container_count(pool->idle_threads); | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 		grow(pool, num_threads - current_size); | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, | 
					
						
							|  |  |  | 				activate_thread, pool); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 		shrink(pool, current_size - num_threads); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	threadpool_send_state_changed(pool); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | void ast_threadpool_set_size(struct ast_threadpool *pool, unsigned int size) | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	struct set_size_data *ssd; | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	SCOPED_AO2LOCK(lock, pool); | 
					
						
							|  |  |  | 	if (pool->shutting_down) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	ssd = set_size_data_alloc(pool, size); | 
					
						
							| 
									
										
										
										
											2012-11-30 00:19:50 +00:00
										 |  |  | 	if (!ssd) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_taskprocessor_push(pool->control_tps, queued_set_size, ssd); | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 23:45:39 +00:00
										 |  |  | struct ast_threadpool_listener *ast_threadpool_listener_alloc( | 
					
						
							| 
									
										
										
										
											2013-01-15 19:36:33 +00:00
										 |  |  | 		const struct ast_threadpool_listener_callbacks *callbacks, void *user_data) | 
					
						
							| 
									
										
										
										
											2012-12-04 23:45:39 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-15 19:36:33 +00:00
										 |  |  | 	struct ast_threadpool_listener *listener = ao2_alloc(sizeof(*listener), NULL); | 
					
						
							| 
									
										
										
										
											2012-12-04 23:45:39 +00:00
										 |  |  | 	if (!listener) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	listener->callbacks = callbacks; | 
					
						
							| 
									
										
										
										
											2013-01-15 19:36:33 +00:00
										 |  |  | 	listener->user_data = user_data; | 
					
						
							| 
									
										
										
										
											2012-12-04 23:45:39 +00:00
										 |  |  | 	return listener; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 21:09:55 +00:00
										 |  |  | void *ast_threadpool_listener_get_user_data(const struct ast_threadpool_listener *listener) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return listener->user_data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 06:13:09 +00:00
										 |  |  | struct pool_options_pair { | 
					
						
							|  |  |  | 	struct ast_threadpool *pool; | 
					
						
							|  |  |  | 	struct ast_threadpool_options options; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 20:14:23 +00:00
										 |  |  | struct ast_threadpool *ast_threadpool_create(const char *name, | 
					
						
							|  |  |  | 		struct ast_threadpool_listener *listener, | 
					
						
							| 
									
										
										
										
											2013-01-15 19:44:25 +00:00
										 |  |  | 		const struct ast_threadpool_options *options) | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	struct ast_taskprocessor *tps; | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  | 	RAII_VAR(struct ast_taskprocessor_listener *, tps_listener, NULL, ao2_cleanup); | 
					
						
							| 
									
										
										
										
											2014-09-29 20:28:24 +00:00
										 |  |  | 	RAII_VAR(struct ast_threadpool *, pool, NULL, ao2_cleanup); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-29 20:28:24 +00:00
										 |  |  | 	pool = threadpool_alloc(name, options); | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  | 	if (!pool) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  | 	tps_listener = ast_taskprocessor_listener_alloc(&threadpool_tps_listener_callbacks, pool); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	if (!tps_listener) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-11 16:36:08 +00:00
										 |  |  | 	if (options->version != AST_THREADPOOL_OPTIONS_VERSION) { | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Incompatible version of threadpool options in use.\n"); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 20:14:23 +00:00
										 |  |  | 	tps = ast_taskprocessor_create_with_listener(name, tps_listener); | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 	if (!tps) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-06 20:25:17 +00:00
										 |  |  | 	pool->tps = tps; | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	if (listener) { | 
					
						
							|  |  |  | 		ao2_ref(listener, +1); | 
					
						
							|  |  |  | 		pool->listener = listener; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-01-15 19:44:25 +00:00
										 |  |  | 	ast_threadpool_set_size(pool, pool->options.initial_size); | 
					
						
							| 
									
										
										
										
											2013-01-15 18:40:36 +00:00
										 |  |  | 	ao2_ref(pool, +1); | 
					
						
							| 
									
										
										
										
											2012-11-29 18:54:51 +00:00
										 |  |  | 	return pool; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | int ast_threadpool_push(struct ast_threadpool *pool, int (*task)(void *data), void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	SCOPED_AO2LOCK(lock, pool); | 
					
						
							| 
									
										
										
										
											2012-12-10 04:08:29 +00:00
										 |  |  | 	if (!pool->shutting_down) { | 
					
						
							|  |  |  | 		return ast_taskprocessor_push(pool->tps, task, data); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-02-28 21:31:14 +00:00
										 |  |  | 	return -1; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | void ast_threadpool_shutdown(struct ast_threadpool *pool) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-17 16:04:10 +00:00
										 |  |  | 	if (!pool) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	/* Shut down the taskprocessors and everything else just
 | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	 * takes care of itself via the taskprocessor callbacks | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-12-10 05:25:38 +00:00
										 |  |  | 	ao2_lock(pool); | 
					
						
							|  |  |  | 	pool->shutting_down = 1; | 
					
						
							|  |  |  | 	ao2_unlock(pool); | 
					
						
							| 
									
										
										
										
											2012-12-03 16:59:26 +00:00
										 |  |  | 	ast_taskprocessor_unreference(pool->control_tps); | 
					
						
							|  |  |  | 	ast_taskprocessor_unreference(pool->tps); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * A monotonically increasing integer used for worker | 
					
						
							|  |  |  |  * thread identification. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int worker_id_counter; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int worker_thread_hash(const void *obj, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct worker_thread *worker = obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return worker->id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int worker_thread_cmp(void *obj, void *arg, int flags) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct worker_thread *worker1 = obj; | 
					
						
							|  |  |  | 	struct worker_thread *worker2 = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return worker1->id == worker2->id ? CMP_MATCH : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief shut a worker thread down | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Set the worker dead and then wait for its thread | 
					
						
							|  |  |  |  * to finish executing. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param worker The worker thread to shut down | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void worker_shutdown(struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	worker_set_state(worker, DEAD); | 
					
						
							|  |  |  | 	if (worker->thread != AST_PTHREADT_NULL) { | 
					
						
							|  |  |  | 		pthread_join(worker->thread, NULL); | 
					
						
							|  |  |  | 		worker->thread = AST_PTHREADT_NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Worker thread destructor | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Called automatically when refcount reaches 0. Shuts | 
					
						
							|  |  |  |  * down the worker thread and destroys its component | 
					
						
							|  |  |  |  * parts | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void worker_thread_destroy(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct worker_thread *worker = obj; | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | 	ast_debug(3, "Destroying worker thread %d\n", worker->id); | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	worker_shutdown(worker); | 
					
						
							|  |  |  | 	ast_mutex_destroy(&worker->lock); | 
					
						
							|  |  |  | 	ast_cond_destroy(&worker->cond); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief start point for worker threads | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Worker threads start in the active state but may | 
					
						
							|  |  |  |  * immediately go idle if there is no work to be | 
					
						
							|  |  |  |  * done | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param arg The worker thread | 
					
						
							|  |  |  |  * \retval NULL | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void *worker_start(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct worker_thread *worker = arg; | 
					
						
							| 
									
										
										
										
											2016-02-12 10:59:44 -05:00
										 |  |  | 	enum worker_state saved_state; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	if (worker->options.thread_start) { | 
					
						
							|  |  |  | 		worker->options.thread_start(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_lock(&worker->lock); | 
					
						
							|  |  |  | 	while (worker_idle(worker)) { | 
					
						
							|  |  |  | 		ast_mutex_unlock(&worker->lock); | 
					
						
							|  |  |  | 		worker_active(worker); | 
					
						
							|  |  |  | 		ast_mutex_lock(&worker->lock); | 
					
						
							|  |  |  | 		if (worker->state != ALIVE) { | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		threadpool_active_thread_idle(worker->pool, worker); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-12 10:59:44 -05:00
										 |  |  | 	saved_state = worker->state; | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 	ast_mutex_unlock(&worker->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Reaching this portion means the thread is
 | 
					
						
							|  |  |  | 	 * on death's door. It may have been killed while | 
					
						
							|  |  |  | 	 * it was idle, in which case it can just die | 
					
						
							|  |  |  | 	 * peacefully. If it's a zombie, though, then | 
					
						
							|  |  |  | 	 * it needs to let the pool know so | 
					
						
							|  |  |  | 	 * that the thread can be removed from the | 
					
						
							|  |  |  | 	 * list of zombie threads. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2016-02-12 10:59:44 -05:00
										 |  |  | 	if (saved_state == ZOMBIE) { | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 		threadpool_zombie_thread_dead(worker->pool, worker); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-25 18:25:31 +00:00
										 |  |  | 	if (worker->options.thread_end) { | 
					
						
							|  |  |  | 		worker->options.thread_end(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Allocate and initialize a new worker thread | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This will create, initialize, and start the thread. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param pool The threadpool to which the worker will be added | 
					
						
							|  |  |  |  * \retval NULL Failed to allocate or start the worker thread | 
					
						
							|  |  |  |  * \retval non-NULL The newly-created worker thread | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct worker_thread *worker_thread_alloc(struct ast_threadpool *pool) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct worker_thread *worker = ao2_alloc(sizeof(*worker), worker_thread_destroy); | 
					
						
							|  |  |  | 	if (!worker) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	worker->id = ast_atomic_fetchadd_int(&worker_id_counter, 1); | 
					
						
							|  |  |  | 	ast_mutex_init(&worker->lock); | 
					
						
							|  |  |  | 	ast_cond_init(&worker->cond, NULL); | 
					
						
							|  |  |  | 	worker->pool = pool; | 
					
						
							|  |  |  | 	worker->thread = AST_PTHREADT_NULL; | 
					
						
							|  |  |  | 	worker->state = ALIVE; | 
					
						
							| 
									
										
										
										
											2012-12-10 06:13:09 +00:00
										 |  |  | 	worker->options = pool->options; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	return worker; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-07 22:16:06 +00:00
										 |  |  | static int worker_thread_start(struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ast_pthread_create(&worker->thread, NULL, worker_start, worker); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Active loop for worker threads | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The worker will stay in this loop for its lifetime, | 
					
						
							|  |  |  |  * executing tasks as they become available. If there | 
					
						
							|  |  |  |  * are no tasks currently available, then the thread | 
					
						
							|  |  |  |  * will go idle. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param worker The worker thread executing tasks. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void worker_active(struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 	int alive; | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 	/* The following is equivalent to 
 | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * while (threadpool_execute(worker->pool)); | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * However, reviewers have suggested in the past | 
					
						
							|  |  |  | 	 * doing that can cause optimizers to (wrongly) | 
					
						
							|  |  |  | 	 * optimize the code away. | 
					
						
							| 
									
										
										
										
											2012-12-09 18:56:25 +00:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 	do { | 
					
						
							|  |  |  | 		alive = threadpool_execute(worker->pool); | 
					
						
							|  |  |  | 	} while (alive); | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Idle function for worker threads | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The worker waits here until it gets told by the threadpool | 
					
						
							|  |  |  |  * to wake up. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  |  * worker is locked before entering this function. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  |  * \param worker The idle worker | 
					
						
							|  |  |  |  * \retval 0 The thread is being woken up so that it can conclude. | 
					
						
							|  |  |  |  * \retval non-zero The thread is being woken up to do more work. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int worker_idle(struct worker_thread *worker) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-10 06:13:09 +00:00
										 |  |  | 	struct timeval start = ast_tvnow(); | 
					
						
							|  |  |  | 	struct timespec end = { | 
					
						
							|  |  |  | 		.tv_sec = start.tv_sec + worker->options.idle_timeout, | 
					
						
							|  |  |  | 		.tv_nsec = start.tv_usec * 1000, | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	while (!worker->wake_up) { | 
					
						
							| 
									
										
										
										
											2012-12-10 06:13:09 +00:00
										 |  |  | 		if (worker->options.idle_timeout <= 0) { | 
					
						
							| 
									
										
										
										
											2013-06-20 16:29:35 +00:00
										 |  |  | 			ast_cond_wait(&worker->cond, &worker->lock); | 
					
						
							|  |  |  | 		} else if (ast_cond_timedwait(&worker->cond, &worker->lock, &end) == ETIMEDOUT) { | 
					
						
							| 
									
										
										
										
											2012-12-10 06:13:09 +00:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!worker->wake_up) { | 
					
						
							|  |  |  | 		ast_debug(1, "Worker thread idle timeout reached. Dying.\n"); | 
					
						
							|  |  |  | 		threadpool_idle_thread_dead(worker->pool, worker); | 
					
						
							|  |  |  | 		worker->state = DEAD; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	worker->wake_up = 0; | 
					
						
							|  |  |  | 	return worker->state == ALIVE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \brief Change a worker's state | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The threadpool calls into this function in order to let a worker know | 
					
						
							|  |  |  |  * how it should proceed. | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  |  * | 
					
						
							|  |  |  |  * \retval -1 failure (state transition not permitted) | 
					
						
							|  |  |  |  * \retval 0 success | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | static int worker_set_state(struct worker_thread *worker, enum worker_state state) | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	SCOPED_MUTEX(lock, &worker->lock); | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (state) { | 
					
						
							|  |  |  | 	case ALIVE: | 
					
						
							|  |  |  | 		/* This can occur due to a race condition between being told to go active
 | 
					
						
							|  |  |  | 		 * and an idle timeout happening. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (worker->state == DEAD) { | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ast_assert(worker->state != ZOMBIE); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case DEAD: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case ZOMBIE: | 
					
						
							|  |  |  | 		ast_assert(worker->state != DEAD); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | 	worker->state = state; | 
					
						
							|  |  |  | 	worker->wake_up = 1; | 
					
						
							|  |  |  | 	ast_cond_signal(&worker->cond); | 
					
						
							| 
									
										
										
										
											2015-11-11 13:04:08 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2012-12-04 21:11:34 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | /*! Serializer group shutdown control object. */ | 
					
						
							|  |  |  | struct ast_serializer_shutdown_group { | 
					
						
							|  |  |  | 	/*! Shutdown thread waits on this conditional. */ | 
					
						
							|  |  |  | 	ast_cond_t cond; | 
					
						
							|  |  |  | 	/*! Count of serializers needing to shutdown. */ | 
					
						
							|  |  |  | 	int count; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void serializer_shutdown_group_dtor(void *vdoomed) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_serializer_shutdown_group *doomed = vdoomed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_cond_destroy(&doomed->cond); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ast_serializer_shutdown_group *ast_serializer_shutdown_group_alloc(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_serializer_shutdown_group *shutdown_group; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	shutdown_group = ao2_alloc(sizeof(*shutdown_group), serializer_shutdown_group_dtor); | 
					
						
							|  |  |  | 	if (!shutdown_group) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ast_cond_init(&shutdown_group->cond, NULL); | 
					
						
							|  |  |  | 	return shutdown_group; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ast_serializer_shutdown_group_join(struct ast_serializer_shutdown_group *shutdown_group, int timeout) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int remaining; | 
					
						
							|  |  |  | 	ast_mutex_t *lock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!shutdown_group) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	lock = ao2_object_get_lockaddr(shutdown_group); | 
					
						
							|  |  |  | 	ast_assert(lock != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ao2_lock(shutdown_group); | 
					
						
							|  |  |  | 	if (timeout) { | 
					
						
							|  |  |  | 		struct timeval start; | 
					
						
							|  |  |  | 		struct timespec end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		start = ast_tvnow(); | 
					
						
							|  |  |  | 		end.tv_sec = start.tv_sec + timeout; | 
					
						
							|  |  |  | 		end.tv_nsec = start.tv_usec * 1000; | 
					
						
							|  |  |  | 		while (shutdown_group->count) { | 
					
						
							|  |  |  | 			if (ast_cond_timedwait(&shutdown_group->cond, lock, &end)) { | 
					
						
							|  |  |  | 				/* Error or timed out waiting for the count to reach zero. */ | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		while (shutdown_group->count) { | 
					
						
							|  |  |  | 			if (ast_cond_wait(&shutdown_group->cond, lock)) { | 
					
						
							|  |  |  | 				/* Error */ | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	remaining = shutdown_group->count; | 
					
						
							|  |  |  | 	ao2_unlock(shutdown_group); | 
					
						
							|  |  |  | 	return remaining; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Increment the number of serializer members in the group. | 
					
						
							|  |  |  |  * \since 13.5.0 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param shutdown_group Group shutdown controller. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \return Nothing | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void serializer_shutdown_group_inc(struct ast_serializer_shutdown_group *shutdown_group) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ao2_lock(shutdown_group); | 
					
						
							|  |  |  | 	++shutdown_group->count; | 
					
						
							|  |  |  | 	ao2_unlock(shutdown_group); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*!
 | 
					
						
							|  |  |  |  * \internal | 
					
						
							|  |  |  |  * \brief Decrement the number of serializer members in the group. | 
					
						
							|  |  |  |  * \since 13.5.0 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param shutdown_group Group shutdown controller. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \return Nothing | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void serializer_shutdown_group_dec(struct ast_serializer_shutdown_group *shutdown_group) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ao2_lock(shutdown_group); | 
					
						
							|  |  |  | 	--shutdown_group->count; | 
					
						
							|  |  |  | 	if (!shutdown_group->count) { | 
					
						
							|  |  |  | 		ast_cond_signal(&shutdown_group->cond); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ao2_unlock(shutdown_group); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | struct serializer { | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 	/*! Threadpool the serializer will use to process the jobs. */ | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	struct ast_threadpool *pool; | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 	/*! Which group will wait for this serializer to shutdown. */ | 
					
						
							|  |  |  | 	struct ast_serializer_shutdown_group *shutdown_group; | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void serializer_dtor(void *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct serializer *ser = obj; | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	ao2_cleanup(ser->pool); | 
					
						
							|  |  |  | 	ser->pool = NULL; | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 	ao2_cleanup(ser->shutdown_group); | 
					
						
							|  |  |  | 	ser->shutdown_group = NULL; | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | static struct serializer *serializer_create(struct ast_threadpool *pool, | 
					
						
							|  |  |  | 	struct ast_serializer_shutdown_group *shutdown_group) | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-09-29 20:28:24 +00:00
										 |  |  | 	struct serializer *ser; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ser = ao2_alloc_options(sizeof(*ser), serializer_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK); | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	if (!ser) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ao2_ref(pool, +1); | 
					
						
							|  |  |  | 	ser->pool = pool; | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 	ser->shutdown_group = ao2_bump(shutdown_group); | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	return ser; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 15:37:33 -05:00
										 |  |  | AST_THREADSTORAGE_RAW(current_serializer); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | static int execute_tasks(void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | 	struct ast_taskprocessor *tps = data; | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 15:37:33 -05:00
										 |  |  | 	ast_threadstorage_set_ptr(¤t_serializer, tps); | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | 	while (ast_taskprocessor_execute(tps)) { | 
					
						
							|  |  |  | 		/* No-op */ | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-05 15:37:33 -05:00
										 |  |  | 	ast_threadstorage_set_ptr(¤t_serializer, NULL); | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | 	ast_taskprocessor_unreference(tps); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | static void serializer_task_pushed(struct ast_taskprocessor_listener *listener, int was_empty) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (was_empty) { | 
					
						
							|  |  |  | 		struct serializer *ser = ast_taskprocessor_listener_get_user_data(listener); | 
					
						
							|  |  |  | 		struct ast_taskprocessor *tps = ast_taskprocessor_listener_get_tps(listener); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-28 21:31:14 +00:00
										 |  |  | 		if (ast_threadpool_push(ser->pool, execute_tasks, tps)) { | 
					
						
							|  |  |  | 			ast_taskprocessor_unreference(tps); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int serializer_start(struct ast_taskprocessor_listener *listener) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | 	/* No-op */ | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void serializer_shutdown(struct ast_taskprocessor_listener *listener) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct serializer *ser = ast_taskprocessor_listener_get_user_data(listener); | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ser->shutdown_group) { | 
					
						
							|  |  |  | 		serializer_shutdown_group_dec(ser->shutdown_group); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	ao2_cleanup(ser); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct ast_taskprocessor_listener_callbacks serializer_tps_listener_callbacks = { | 
					
						
							| 
									
										
										
										
											2013-02-28 21:29:57 +00:00
										 |  |  | 	.task_pushed = serializer_task_pushed, | 
					
						
							|  |  |  | 	.start = serializer_start, | 
					
						
							|  |  |  | 	.shutdown = serializer_shutdown, | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-05 15:37:33 -05:00
										 |  |  | struct ast_taskprocessor *ast_threadpool_serializer_get_current(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ast_threadstorage_get_ptr(¤t_serializer); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | struct ast_taskprocessor *ast_threadpool_serializer_group(const char *name, | 
					
						
							|  |  |  | 	struct ast_threadpool *pool, struct ast_serializer_shutdown_group *shutdown_group) | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 	struct serializer *ser; | 
					
						
							|  |  |  | 	struct ast_taskprocessor_listener *listener; | 
					
						
							|  |  |  | 	struct ast_taskprocessor *tps; | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 	ser = serializer_create(pool, shutdown_group); | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	if (!ser) { | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listener = ast_taskprocessor_listener_alloc(&serializer_tps_listener_callbacks, ser); | 
					
						
							|  |  |  | 	if (!listener) { | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 		ao2_ref(ser, -1); | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tps = ast_taskprocessor_create_with_listener(name, listener); | 
					
						
							| 
									
										
										
										
											2016-07-13 12:30:58 -04:00
										 |  |  | 	if (!tps) { | 
					
						
							|  |  |  | 		/* ser ref transferred to listener but not cleaned without tps */ | 
					
						
							|  |  |  | 		ao2_ref(ser, -1); | 
					
						
							|  |  |  | 	} else if (shutdown_group) { | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 		serializer_shutdown_group_inc(shutdown_group); | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 	ao2_ref(listener, -1); | 
					
						
							| 
									
										
										
										
											2013-02-12 21:45:59 +00:00
										 |  |  | 	return tps; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-06-19 16:16:17 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct ast_taskprocessor *ast_threadpool_serializer(const char *name, struct ast_threadpool *pool) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ast_threadpool_serializer_group(name, pool, NULL); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-11-11 17:11:53 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | long ast_threadpool_queue_size(struct ast_threadpool *pool) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ast_taskprocessor_size(pool->tps); | 
					
						
							|  |  |  | } |