+static void grow(struct ast_threadpool *pool, int delta)
+{
+ int i;
+ for (i = 0; i < delta; ++i) {
+ struct worker_thread *worker = worker_thread_alloc(pool);
+ if (!worker) {
+ /* XXX Abandon */
+ return;
+ }
+ ao2_link(pool->active_threads, worker);
+ }
+}
+
+static int kill_threads(void *obj, void *arg, int flags)
+{
+ int *num_to_kill = arg;
+
+ if ((*num_to_kill)-- > 0) {
+ return CMP_MATCH;
+ } else {
+ return CMP_STOP;
+ }
+}
+
+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) {
+ ao2_link(pool->zombie_threads, worker);
+ return CMP_MATCH;
+ } else {
+ return CMP_STOP;
+ }
+}
+
+static void shrink(struct ast_threadpool *pool, int delta)
+{
+ /*
+ * Preference is to kill idle threads, but
+ * we'll move on to deactivating active threads
+ * if we have to
+ */
+ int idle_threads = ao2_container_count(pool->idle_threads);
+ int idle_threads_to_kill = MIN(delta, idle_threads);
+ int active_threads_to_zombify = delta - idle_threads_to_kill;
+ int i = 0;
+
+ ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | OBJ_NOLOCK,
+ kill_threads, &idle_threads_to_kill);
+
+ ao2_callback_data(pool->active_threads, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | OBJ_NOLOCK,
+ zombify_threads, pool, &active_threads_to_zombify);
+}
+
+struct set_size_data {
+ struct threadpool *pool;
+ int size;
+};
+
+void set_size_data_destroy(void *obj)
+{
+ struct set_size_data *ssd = obj;
+ ao2_ref(ssd->pool, -1);
+}
+
+static int queued_set_size(void *data)
+{
+ struct set_size_data *ssd = data;
+ struct ast_threadpool *pool = ssd->pool;
+ int num_threads = ssd->size;
+
+ /* We don't count zombie threads as being "live when potentially resizing */
+ int current_size = ao2_container_count(pool->active_threads) +
+ ao2_container_count(pool->idle_threads);
+
+ if (current_size = num_threads) {
+ return 0;
+ }
+
+ if (current_size < num_threads) {
+ grow(pool, num_threads - current_size);
+ } else {
+ shrink(pool, current_size - num_threads);
+ }
+
+ threadpool_send_state_changed(pool);
+ ao2_ref(set_size_data, -1);
+}
+