static void
as_uv_wakeup(uv_async_t* wakeup)
{
	// Read command pointers from queue.
	as_event_loop* event_loop = wakeup->data;
	as_uv_command cmd;
	
	pthread_mutex_lock(&event_loop->lock);
	
	while (as_queue_pop(&event_loop->queue, &cmd)) {
		switch (cmd.type) {
			case AS_UV_PROCESS_COMMAND:
				as_event_command_execute_in_loop(cmd.ptr);
				break;
				
			case AS_UV_CLOSE_CONNECTION:
				uv_close((uv_handle_t*)cmd.ptr, as_uv_connection_closed);
				break;
				
			case AS_UV_EXIT_LOOP:
				as_event_close_loop(event_loop);
				return;
		}
	}
	pthread_mutex_unlock(&event_loop->lock);
}
static bool
as_uv_queue_close_connections(as_node* node, as_queue* conn_queue, as_queue* cmd_queue)
{
	as_uv_command qcmd;
	qcmd.type = AS_UV_CLOSE_CONNECTION;
	
	as_event_connection* conn;
	
	// Queue connection commands to event loops.
	while (as_queue_pop(conn_queue, &conn)) {
		qcmd.ptr = conn;
		
		if (! as_queue_push(cmd_queue, &qcmd)) {
			as_log_error("Failed to queue connection close");
			return false;
		}
		
		// In this case, connection counts are decremented before the connection is closed.
		// This is done because the node will be invalid when the deferred connection close occurs.
		// Since node destroy always waits till there are no node references, all transactions that
		// referenced this node should be completed by the time this code is executed.
		as_event_decr_connection(node->cluster, conn_queue);
		ck_pr_dec_32(&node->cluster->async_conn_pool);
	}
	return true;
}
static void
as_ev_close_connections(as_node* node, as_queue* conn_queue)
{
	as_event_connection* conn;
	
	// Queue connection commands to event loops.
	while (as_queue_pop(conn_queue, &conn)) {
		close(conn->fd);
		cf_free(conn);
		as_event_decr_connection(node->cluster, conn_queue);
		ck_pr_dec_32(&node->cluster->async_conn_pool);
	}
	as_queue_destroy(conn_queue);
}
as_connection_status
as_event_get_connection(as_event_command* cmd)
{
    as_queue* queue = &cmd->node->async_conn_qs[cmd->event_loop->index];
    as_async_connection* conn;

    // Find connection.
    while (as_queue_pop(queue, &conn)) {
        ck_pr_dec_32(&cmd->cluster->async_conn_pool);

        // Verify that socket is active and receive buffer is empty.
        int len = as_event_validate_connection(&conn->base);

        if (len == 0) {
            conn->cmd = cmd;
            cmd->conn = (as_event_connection*)conn;
            return AS_CONNECTION_FROM_POOL;
        }

        as_log_debug("Invalid async socket from pool: %d", len);
        as_event_release_connection(cmd->cluster, &conn->base, queue);
    }

    // Create connection structure only when node connection count within queue limit.
    if (as_queue_incr_total(queue)) {
        ck_pr_inc_32(&cmd->cluster->async_conn_count);
        conn = cf_malloc(sizeof(as_async_connection));
        conn->base.pipeline = false;
        conn->cmd = cmd;
        cmd->conn = &conn->base;
        return AS_CONNECTION_NEW;
    }
    else {
        as_error err;
        as_error_update(&err, AEROSPIKE_ERR_NO_MORE_CONNECTIONS,
                        "Max node/event loop %s async connections would be exceeded: %u",
                        cmd->node->name, queue->capacity);
        as_event_stop_timer(cmd);
        as_event_error_callback(cmd, &err);
        return AS_CONNECTION_TOO_MANY;
    }
}
static void
as_ev_wakeup(struct ev_loop* loop, ev_async* wakeup, int revents)
{
	// Read command pointers from queue.
	as_event_loop* event_loop = wakeup->data;
	void* cmd;
	
	pthread_mutex_lock(&event_loop->lock);
	
	while (as_queue_pop(&event_loop->queue, &cmd)) {
		if (cmd) {
			// Process new command.
			as_event_command_execute_in_loop(cmd);
		}
		else {
			// Received stop signal.
			as_event_close_loop(event_loop);
			return;
		}
	}
	pthread_mutex_unlock(&event_loop->lock);
}