static void consumer_exec(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
    struct consumer *consumer = data;
    struct stasis_cache_update *cache_update = stasis_message_data(message);
    struct ast_device_state_message *device_state;

    if (!cache_update->new_snapshot) {
        return;
    }

    device_state = stasis_message_data(cache_update->new_snapshot);

    if (strcmp(device_state->device, UNIT_TEST_DEVICE_IDENTIFIER)) {
        /* not a device state we're interested in */
        return;
    }

    {
        SCOPED_AO2LOCK(lock, consumer);

        ++consumer->event_count;
        if (device_state->eid) {
            consumer->state = device_state->state;
            if (consumer->sig_on_non_aggregate_state) {
                consumer->sig_on_non_aggregate_state = 0;
                consumer->already_out = 1;
                ast_cond_signal(&consumer->out);
            }
        } else {
            consumer->aggregate_state = device_state->state;
            consumer->already_out = 1;
            ast_cond_signal(&consumer->out);
        }
    }
}
static int shutdown_task_exec(void *data)
{
	struct shutdown_data *shutdown_data = data;
	SCOPED_MUTEX(lock, &shutdown_data->lock);
	shutdown_data->task_started = 1;
	ast_cond_signal(&shutdown_data->out);
	while (!shutdown_data->task_stop_waiting) {
		ast_cond_wait(&shutdown_data->in, &shutdown_data->lock);
	}
	shutdown_data->task_complete = 1;
	ast_cond_signal(&shutdown_data->out);
	return 0;
}
Exemple #3
0
static struct stasis_app_command *exec_command_on_condition(
    struct stasis_app_control *control, stasis_app_command_cb command_fn,
    void *data, app_command_can_exec_cb can_exec_fn)
{
    int retval;
    struct stasis_app_command *command;

    command_fn = command_fn ? : noop_cb;

    command = command_create(command_fn, data);
    if (!command) {
        return NULL;
    }

    ao2_lock(control->command_queue);
    if (can_exec_fn && (retval = can_exec_fn(control))) {
        ao2_unlock(control->command_queue);
        command_complete(command, retval);
        return command;
    }

    ao2_link_flags(control->command_queue, command, OBJ_NOLOCK);
    ast_cond_signal(&control->wait_cond);
    ao2_unlock(control->command_queue);

    return command;
}
Exemple #4
0
static int user_event_hook_cb(int category, const char *event, char *body)
{
	char *parse;
	char *kvp;

	if (strcmp(event, "UserEvent")) {
		return -1;
	}

	parse = ast_strdupa(body);
	while ((kvp = strsep(&parse, "\r\n"))) {
		char *key, *value;

		kvp = ast_trim_blanks(kvp);
		if (ast_strlen_zero(kvp)) {
			continue;
		}
		key = strsep(&kvp, ":");
		value = ast_skip_blanks(kvp);
		verify_user_event_fields(received_user_events, key, value);
	}

	received_user_events++;

	ast_mutex_lock(&user_event_lock);
	if (received_user_events == expected_user_events) {
		ast_cond_signal(&user_event_cond);
	}
	ast_mutex_unlock(&user_event_lock);

	return 0;
}
static void *pthread_timer_open(void)
{
	struct pthread_timer *timer;

	if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
		errno = ENOMEM;
		return NULL;
	}

	timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
	timer->state = TIMER_STATE_IDLE;

	if (ast_pipe_nonblock(timer->pipe)) {
		ao2_ref(timer, -1);
		return NULL;
	}

	ao2_lock(pthread_timers);
	if (!ao2_container_count(pthread_timers)) {
		ast_mutex_lock(&timing_thread.lock);
		ast_cond_signal(&timing_thread.cond);
		ast_mutex_unlock(&timing_thread.lock);
	}
	ao2_link_flags(pthread_timers, timer, OBJ_NOLOCK);
	ao2_unlock(pthread_timers);

	return timer;
}
Exemple #6
0
/*!
 * \brief Idle function for worker threads
 *
 * The worker waits here until it gets told by the threadpool
 * to wake up.
 *
 * worker is locked before entering this function.
 *
 * \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)
{
	struct timeval start = ast_tvnow();
	struct timespec end = {
		.tv_sec = start.tv_sec + worker->options.idle_timeout,
		.tv_nsec = start.tv_usec * 1000,
	};
	while (!worker->wake_up) {
		if (worker->options.idle_timeout <= 0) {
			ast_cond_wait(&worker->cond, &worker->lock);
		} else if (ast_cond_timedwait(&worker->cond, &worker->lock, &end) == ETIMEDOUT) {
			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;
	}
	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.
 *
 * \retval -1 failure (state transition not permitted)
 * \retval 0 success
 */
static int worker_set_state(struct worker_thread *worker, enum worker_state state)
{
	SCOPED_MUTEX(lock, &worker->lock);

	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;
	}

	worker->state = state;
	worker->wake_up = 1;
	ast_cond_signal(&worker->cond);

	return 0;
}
static int pthread_timer_open(void)
{
	struct pthread_timer *timer;
	int fd;

	if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
		errno = ENOMEM;
		return -1;
	}

	timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
	timer->state = TIMER_STATE_IDLE;

	if (pipe(timer->pipe)) {
		ao2_ref(timer, -1);
		return -1;
	}

	ao2_lock(pthread_timers);
	if (!ao2_container_count(pthread_timers)) {
		ast_mutex_lock(&timing_thread.lock);
		ast_cond_signal(&timing_thread.cond);
		ast_mutex_unlock(&timing_thread.lock);
	}
	ao2_link(pthread_timers, timer);
	ao2_unlock(pthread_timers);

	fd = timer->pipe[PIPE_READ];

	ao2_ref(timer, -1);

	return fd;
}
Exemple #8
0
static void *pthread_timer_open(void)
{
	struct pthread_timer *timer;
	int i;

	if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
		errno = ENOMEM;
		return NULL;
	}

	timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
	timer->state = TIMER_STATE_IDLE;

	if (pipe(timer->pipe)) {
		ao2_ref(timer, -1);
		return NULL;
	}

	for (i = 0; i < ARRAY_LEN(timer->pipe); ++i) {
		int flags = fcntl(timer->pipe[i], F_GETFL);
		flags |= O_NONBLOCK;
		fcntl(timer->pipe[i], F_SETFL, flags);
	}
	
	ao2_lock(pthread_timers);
	if (!ao2_container_count(pthread_timers)) {
		ast_mutex_lock(&timing_thread.lock);
		ast_cond_signal(&timing_thread.cond);
		ast_mutex_unlock(&timing_thread.lock);
	}
	ao2_link(pthread_timers, timer);
	ao2_unlock(pthread_timers);

	return timer;
}
/*!
 * \brief a queued task to be used in the taskprocessor load test
 *
 * The task increments the number of tasks executed and puts the passed-in
 * data into the next slot in the array of random data.
 */
static int load_task(void *data)
{
	int *randdata = data;
	SCOPED_MUTEX(lock, &load_task_results.lock);
	load_task_results.task_rand[load_task_results.tasks_completed++] = *randdata;
	ast_cond_signal(&load_task_results.cond);
	return 0;
}
Exemple #10
0
static int test_msg_handle_msg_cb(struct ast_msg *msg)
{
	ast_mutex_lock(&handler_lock);
	handler_received_message = 1;
	ast_cond_signal(&handler_cond);
	ast_mutex_unlock(&handler_lock);

	return 0;
}
Exemple #11
0
/*!
 * \brief Mock resolver's cancel method
 *
 * This signals the resolution thread not to return any DNS results.
 *
 * \param query DNS query to cancel
 * \return 0
 */
static int test_cancel(struct ast_dns_query *query)
{
	ast_mutex_lock(&test_resolver_data.lock);
	test_resolver_data.canceled = 1;
	ast_cond_signal(&test_resolver_data.cancel_cond);
	ast_mutex_unlock(&test_resolver_data.lock);

	return 0;
}
Exemple #12
0
/*!
 * \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);
}
static void mixmonitor_ds_destroy(void *data)
{
	struct mixmonitor_ds *mixmonitor_ds = data;

	ast_mutex_lock(&mixmonitor_ds->lock);
	mixmonitor_ds->audiohook = NULL;
	mixmonitor_ds->destruction_ok = 1;
	ast_cond_signal(&mixmonitor_ds->destruction_condition);
	ast_mutex_unlock(&mixmonitor_ds->lock);
}
static void async_callback(const struct ast_dns_query *query)
{
	struct recurring_data *rdata = ast_dns_query_get_data(query);

	ast_assert(rdata != NULL);

	ast_mutex_lock(&rdata->lock);
	rdata->query_complete = 1;
	ast_cond_signal(&rdata->cond);
	ast_mutex_unlock(&rdata->lock);
}
/*!
 * \brief Thread that performs asynchronous resolution.
 *
 * This thread uses the query's user data to determine how to
 * perform the resolution. The query may either be canceled or
 * it may be completed with records.
 *
 * \param dns_query The ast_dns_query that is being performed
 * \return NULL
 */
static void *resolution_thread(void *dns_query)
{
	struct ast_dns_query *query = dns_query;

	static const char *ADDR1 = "127.0.0.1";
	static const size_t ADDR1_BUFSIZE = sizeof(struct in_addr);
	char addr1_buf[ADDR1_BUFSIZE];

	static const char *ADDR2 = "192.168.0.1";
	static const size_t ADDR2_BUFSIZE = sizeof(struct in_addr);
	char addr2_buf[ADDR2_BUFSIZE];

	struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query);
	struct recurring_data *rdata = recurring->user_data;

	ast_assert(rdata != NULL);

	/* Canceling is an interesting dance. This thread needs to signal that it is
	 * ready to be canceled. Then it needs to wait until the query is actually canceled.
	 */
	if (rdata->cancel_expected) {
		ast_mutex_lock(&rdata->lock);
		rdata->cancel_ready = 1;
		ast_cond_signal(&rdata->cond);

		while (!rdata->canceled) {
			ast_cond_wait(&rdata->cond, &rdata->lock);
		}
		ast_mutex_unlock(&rdata->lock);

		ast_dns_resolver_completed(query);
		ao2_ref(query, -1);

		return NULL;
	}

	/* When the query isn't canceled, we set the TTL of the results based on what
	 * we've been told to set it to
	 */
	ast_dns_resolver_set_result(query, 0, 0, ns_r_noerror, "asterisk.org", DNS_ANSWER, DNS_ANSWER_SIZE);

	inet_pton(AF_INET, ADDR1, addr1_buf);
	ast_dns_resolver_add_record(query, ns_t_a, ns_c_in, rdata->ttl1, addr1_buf, ADDR1_BUFSIZE);

	inet_pton(AF_INET, ADDR2, addr2_buf);
	ast_dns_resolver_add_record(query, ns_t_a, ns_c_in, rdata->ttl2, addr2_buf, ADDR2_BUFSIZE);

	++rdata->complete_resolutions;

	ast_dns_resolver_completed(query);

	ao2_ref(query, -1);
	return NULL;
}
/*!
 * \brief Queued task for baseline test.
 *
 * The task simply sets a boolean to indicate the
 * task has been run and then signals a condition
 * saying it's complete
 */
static int task(void *data)
{
	struct task_data *task_data = data;

	SCOPED_MUTEX(lock, &task_data->lock);
	if (task_data->wait_time > 0) {
		usleep(task_data->wait_time * 1000);
	}
	task_data->task_complete = 1;
	ast_cond_signal(&task_data->cond);
	return 0;
}
/*!
 * \brief Resolver's cancel() method
 *
 * \param query The query to cancel
 * \return 0
 */
static int recurring_cancel(struct ast_dns_query *query)
{
	struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query);
	struct recurring_data *rdata = recurring->user_data;

	ast_mutex_lock(&rdata->lock);
	rdata->canceled = 1;
	ast_cond_signal(&rdata->cond);
	ast_mutex_unlock(&rdata->lock);

	return 0;
}
Exemple #18
0
/*!
 * \brief Invoke the subscription's callback.
 * \param sub Subscription to invoke.
 * \param topic Topic message was published to.
 * \param message Message to send.
 */
static void subscription_invoke(struct stasis_subscription *sub,
				  struct stasis_message *message)
{
	/* Notify that the final message has been received */
	if (stasis_subscription_final_message(sub, message)) {
		SCOPED_AO2LOCK(lock, sub);

		sub->final_message_rxed = 1;
		ast_cond_signal(&sub->join_cond);
	}

	/* Since sub is mostly immutable, no need to lock sub */
	sub->callback(sub->data, sub, message);

	/* Notify that the final message has been processed */
	if (stasis_subscription_final_message(sub, message)) {
		SCOPED_AO2LOCK(lock, sub);

		sub->final_message_processed = 1;
		ast_cond_signal(&sub->join_cond);
	}
}
Exemple #19
0
static void guarantee_handler(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	/* Wait for our particular message */
	if (data == message) {
		struct caching_guarantee *guarantee;
		ast_assert(cache_guarantee_type() == stasis_message_type(message));
		guarantee = stasis_message_data(message);

		ast_mutex_lock(&guarantee->lock);
		guarantee->done = 1;
		ast_cond_signal(&guarantee->cond);
		ast_mutex_unlock(&guarantee->lock);
	}
}
Exemple #20
0
/*!
 * \internal \brief Dispatch a message to a subscriber synchronously
 * \param local \ref ast_taskprocessor_local object
 * \return 0
 */
static int dispatch_exec_sync(struct ast_taskprocessor_local *local)
{
	struct stasis_subscription *sub = local->local_data;
	struct sync_task_data *std = local->data;
	struct stasis_message *message = std->task_data;

	subscription_invoke(sub, message);
	ao2_cleanup(message);

	ast_mutex_lock(&std->lock);
	std->complete = 1;
	ast_cond_signal(&std->cond);
	ast_mutex_unlock(&std->lock);

	return 0;
}
Exemple #21
0
static int unload_module(void)
{
	int res;

	ast_mutex_lock(&timing_thread.lock);
	timing_thread.stop = 1;
	ast_cond_signal(&timing_thread.cond);
	ast_mutex_unlock(&timing_thread.lock);
	pthread_join(timing_thread.thread, NULL);

	if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
		ao2_ref(pthread_timers, -1);
		pthread_timers = NULL;
	}

	return res;
}
static int task_1(void *data)
{
	struct test_data *test = data;

	test->done = 0;
	test->task_start = ast_tvnow();
	test->tid = pthread_self();
	test->is_servant = ast_sip_thread_is_servant();
	usleep(M2U(test->sleep));
	test->task_end = ast_tvnow();

	ast_mutex_lock(&test->lock);
	test->done = 1;
	ast_mutex_unlock(&test->lock);
	ast_cond_signal(&test->cond);

	return test->interval;
}
Exemple #23
0
static struct stasis_app_command *exec_command(
	struct stasis_app_control *control, stasis_app_command_cb command_fn,
	void *data)
{
	RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);

	command_fn = command_fn ? : noop_cb;

	command = command_create(command_fn, data);
	if (!command) {
		return NULL;
	}

	ao2_lock(control->command_queue);
	ao2_link_flags(control->command_queue, command, OBJ_NOLOCK);
	ast_cond_signal(&control->wait_cond);
	ao2_unlock(control->command_queue);

	ao2_ref(command, +1);
	return command;
}
Exemple #24
0
int ast_sem_post(struct ast_sem *sem)
{
	SCOPED_MUTEX(lock, &sem->mutex);

	ast_assert(sem->count >= 0);

	if (sem->count == AST_SEM_VALUE_MAX) {
		errno = EOVERFLOW;
		return -1;
	}

	/* Give it up! */
	++sem->count;

	/* Release a waiter, if needed */
	if (sem->waiters) {
		ast_cond_signal(&sem->cond);
	}

	return 0;
}
Exemple #25
0
/*!
 * \brief Idle function for worker threads
 *
 * The worker waits here until it gets told by the threadpool
 * to wake up.
 *
 * worker is locked before entering this function.
 *
 * \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)
{
	struct timeval start = ast_tvnow();
	struct timespec end = {
		.tv_sec = start.tv_sec + worker->options.idle_timeout,
		.tv_nsec = start.tv_usec * 1000,
	};
	while (!worker->wake_up) {
		if (worker->options.idle_timeout <= 0) {
			ast_cond_wait(&worker->cond, &worker->lock);
		} else if (ast_cond_timedwait(&worker->cond, &worker->lock, &end) == ETIMEDOUT) {
			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;
	}
	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.
 */
static void worker_set_state(struct worker_thread *worker, enum worker_state state)
{
	SCOPED_MUTEX(lock, &worker->lock);
	worker->state = state;
	worker->wake_up = 1;
	ast_cond_signal(&worker->cond);
}
static int shutdown_waitfor_start(struct shutdown_data *shutdown_data)
{
	struct timeval start = ast_tvnow();
	struct timespec end = {
		.tv_sec = start.tv_sec + 5,
		.tv_nsec = start.tv_usec * 1000
	};
	SCOPED_MUTEX(lock, &shutdown_data->lock);

	while (!shutdown_data->task_started) {
		if (ast_cond_timedwait(&shutdown_data->out, &shutdown_data->lock, &end) == ETIMEDOUT) {
			break;
		}
	}

	return shutdown_data->task_started;
}

static void shutdown_poke(struct shutdown_data *shutdown_data)
{
	SCOPED_MUTEX(lock, &shutdown_data->lock);
	shutdown_data->task_stop_waiting = 1;
	ast_cond_signal(&shutdown_data->in);
}
Exemple #27
0
/*!
 * \internal
 * \brief Signal the astdb sync thread to do its thing.
 *
 * \note dblock is assumed to be held when calling this function.
 */
static void db_sync(void)
{
	dosync = 1;
	ast_cond_signal(&dbcond);
}
Exemple #28
0
static struct timespec make_deadline(int timeout_millis)
{
	struct timeval start = ast_tvnow();
	struct timeval delta = {
		.tv_sec = timeout_millis / 1000,
		.tv_usec = (timeout_millis % 1000) * 1000,
	};
	struct timeval deadline_tv = ast_tvadd(start, delta);
	struct timespec deadline = {
		.tv_sec = deadline_tv.tv_sec,
		.tv_nsec = 1000 * deadline_tv.tv_usec,
	};

	return deadline;
}

struct stasis_message_sink *stasis_message_sink_create(void)
{
	RAII_VAR(struct stasis_message_sink *, sink, NULL, ao2_cleanup);

	sink = ao2_alloc(sizeof(*sink), stasis_message_sink_dtor);
	if (!sink) {
		return NULL;
	}
	ast_mutex_init(&sink->lock);
	ast_cond_init(&sink->cond, NULL);
	sink->max_messages = 4;
	sink->messages =
		ast_malloc(sizeof(*sink->messages) * sink->max_messages);
	if (!sink->messages) {
		return NULL;
	}
	ao2_ref(sink, +1);
	return sink;
}

/*!
 * \brief Implementation of the stasis_message_sink_cb() callback.
 *
 * Why the roundabout way of exposing this via stasis_message_sink_cb()? Well,
 * it has to do with how we load modules.
 *
 * Modules have their own metadata compiled into them in the module info block
 * at the end of the file.  This includes dependency information in the
 * \c nonoptreq field.
 *
 * Asterisk loads the module, inspects the field, then loads any needed
 * dependencies. This works because Asterisk passes \c RTLD_LAZY to the initial
 * dlopen(), which defers binding function references until they are called.
 *
 * But when you take the address of a function, that function needs to be
 * available at load time. So if some module used the address of
 * message_sink_cb() directly, and \c res_stasis_test.so wasn't loaded yet, then
 * that module would fail to load.
 *
 * The stasis_message_sink_cb() function gives us a layer of indirection so that
 * the initial lazy binding will still work as expected.
 */
static void message_sink_cb(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	struct stasis_message_sink *sink = data;

	SCOPED_MUTEX(lock, &sink->lock);

	if (stasis_subscription_final_message(sub, message)) {
		sink->is_done = 1;
		ast_cond_signal(&sink->cond);
		return;
	}

	if (stasis_subscription_change_type() == stasis_message_type(message)) {
		/* Ignore subscription changes */
		return;
	}

	if (sink->num_messages == sink->max_messages) {
		size_t new_max_messages = sink->max_messages * 2;
		struct stasis_message **new_messages = ast_realloc(
			sink->messages,
			sizeof(*new_messages) * new_max_messages);
		if (!new_messages) {
			return;
		}
		sink->max_messages = new_max_messages;
		sink->messages = new_messages;
	}

	ao2_ref(message, +1);
	sink->messages[sink->num_messages++] = message;
	ast_cond_signal(&sink->cond);
}

stasis_subscription_cb stasis_message_sink_cb(void)
{
	return message_sink_cb;
}


int stasis_message_sink_wait_for_count(struct stasis_message_sink *sink,
	int num_messages, int timeout_millis)
{
	struct timespec deadline = make_deadline(timeout_millis);

	SCOPED_MUTEX(lock, &sink->lock);
	while (sink->num_messages < num_messages) {
		int r = ast_cond_timedwait(&sink->cond, &sink->lock, &deadline);

		if (r == ETIMEDOUT) {
			break;
		}
		if (r != 0) {
			ast_log(LOG_ERROR, "Unexpected condition error: %s\n",
				strerror(r));
			break;
		}
	}
	return sink->num_messages;
}

int stasis_message_sink_should_stay(struct stasis_message_sink *sink,
	int num_messages, int timeout_millis)
{
	struct timespec deadline = make_deadline(timeout_millis);

	SCOPED_MUTEX(lock, &sink->lock);
	while (sink->num_messages == num_messages) {
		int r = ast_cond_timedwait(&sink->cond, &sink->lock, &deadline);

		if (r == ETIMEDOUT) {
			break;
		}
		if (r != 0) {
			ast_log(LOG_ERROR, "Unexpected condition error: %s\n",
				strerror(r));
			break;
		}
	}
	return sink->num_messages;
}

int stasis_message_sink_wait_for(struct stasis_message_sink *sink, int start,
	stasis_wait_cb cmp_cb, const void *data, int timeout_millis)
{
	struct timespec deadline = make_deadline(timeout_millis);

	SCOPED_MUTEX(lock, &sink->lock);

	/* wait for the start */
	while (sink->num_messages < start + 1) {
		int r = ast_cond_timedwait(&sink->cond, &sink->lock, &deadline);

		if (r == ETIMEDOUT) {
			/* Timed out waiting for the start */
			return -1;
		}
		if (r != 0) {
			ast_log(LOG_ERROR, "Unexpected condition error: %s\n",
				strerror(r));
			return -2;
		}
	}


	while (!cmp_cb(sink->messages[start], data)) {
		++start;

		while (sink->num_messages < start + 1) {
			int r = ast_cond_timedwait(&sink->cond,
				&sink->lock, &deadline);

			if (r == ETIMEDOUT) {
				return -1;
			}
			if (r != 0) {
				ast_log(LOG_ERROR,
					"Unexpected condition error: %s\n",
					strerror(r));
				return -2;
			}
		}
	}

	return start;
}

struct stasis_message *stasis_test_message_create(void)
{
	RAII_VAR(void *, data, NULL, ao2_cleanup);

	if (!stasis_test_message_type()) {
		return NULL;
	}

	/* We just need the unique pointer; don't care what's in it */
	data = ao2_alloc(1, NULL);
	if (!data) {
		return NULL;
	}

	return stasis_message_create(stasis_test_message_type(), data);
}
Exemple #29
0
/*!
 * \internal
 * \brief Signal the astdb sync thread to do its thing.
 *
 * \note dblock is assumed to be held when calling this function.
 */
static void db_sync(void)
{
	ast_cond_signal(&dbcond);
}