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; }
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; }
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; }
/*! * \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; }
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; }
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; }
/*! * \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; }
/*! * \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; }
/*! * \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); } }
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); } }
/*! * \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; }
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; }
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; }
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; }
/*! * \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); }
/*! * \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); }
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); }
/*! * \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); }