/*! * \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; }
/*! * \brief Retrieve a ast_sip_contact_status object from sorcery creating * one if not found. */ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact) { struct ast_sip_contact_status *status; SCOPED_MUTEX(lock, &creation_lock); status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (status) { return status; } status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (!status) { ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n", contact->aor, contact->uri); return NULL; } ast_string_field_set(status, uri, contact->uri); status->rtt_start = ast_tv(0, 0); status->rtt = 0; if (ast_sorcery_create(ast_sip_get_sorcery(), status)) { ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n", contact->uri); ao2_ref(status, -1); return NULL; } ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, "+1", 1.0, ast_sip_get_contact_status_label(status->status)); return status; }
/*! * \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; }
int ast_sem_getvalue(struct ast_sem *sem, int *sval) { SCOPED_MUTEX(lock, &sem->mutex); ast_assert(sem->count >= 0); *sval = sem->count; return 0; }
/*! * \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; }
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 int shutdown_waitfor_completion(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_complete) { if (ast_cond_timedwait(&shutdown_data->out, &shutdown_data->lock, &end) == ETIMEDOUT) { break; } } return shutdown_data->task_complete; } static int shutdown_has_completed(struct shutdown_data *shutdown_data) { SCOPED_MUTEX(lock, &shutdown_data->lock); return shutdown_data->task_complete; }
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); }
char *ast_crypt(const char *key, const char *salt) { const char *crypted; SCOPED_MUTEX(lock, &crypt_mutex); crypted = crypt(key, salt); /* Crypt may return success even if it doesn't recognize the salt. But * in those cases it always mangles the salt in some way. */ if (!crypted || !ast_begins_with(crypted, salt)) { return NULL; } return ast_strdup(crypted); }
/*! * \brief Retrieve a ast_sip_contact_status object from sorcery creating * one if not found. */ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact) { struct ast_sip_contact_status *status; SCOPED_MUTEX(lock, &creation_lock); status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (status) { return status; } status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (!status) { ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n", contact->aor, contact->uri); return NULL; } ast_string_field_set(status, uri, contact->uri); status->rtt_start = ast_tv(0, 0); status->rtt = 0; if (ast_sorcery_create(ast_sip_get_sorcery(), status)) { ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n", contact->uri); ao2_ref(status, -1); return NULL; } /* The permanent contact added after asterisk start should be qualified. */ if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED) && ast_tvzero(contact->expiration_time)) { /* * The FULLY_BOOTED to filter out contacts that already existed when asterisk started. * The zero expiration_time to select only permanent contacts. */ ao2_ref((struct ast_sip_contact *) contact, +1); if (ast_sip_push_task(NULL, qualify_and_schedule_aor_contact, (struct ast_sip_contact *) contact)) { ao2_ref((struct ast_sip_contact *) contact, -1); } } ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, "+1", 1.0, ast_sip_get_contact_status_label(status->status)); return status; }
int ast_sem_wait(struct ast_sem *sem) { SCOPED_MUTEX(lock, &sem->mutex); ast_assert(sem->count >= 0); /* Wait for a non-zero count */ ++sem->waiters; while (sem->count == 0) { ast_cond_wait(&sem->cond, &sem->mutex); } --sem->waiters; /* Take it! */ --sem->count; return 0; }
/*! * \brief Wait for a task to execute. */ static int task_wait(struct task_data *task_data) { struct timeval start = ast_tvnow(); struct timespec end; SCOPED_MUTEX(lock, &task_data->lock); end.tv_sec = start.tv_sec + 30; end.tv_nsec = start.tv_usec * 1000; while (!task_data->task_complete) { int res; res = ast_cond_timedwait(&task_data->cond, &task_data->lock, &end); if (res == ETIMEDOUT) { return -1; } } return 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; }
int ast_ari_add_handler(struct stasis_rest_handlers *handler) { RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup); size_t old_size, new_size; SCOPED_MUTEX(lock, &root_handler_lock); old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler); new_size = old_size + sizeof(handler); new_handler = ao2_alloc(new_size, NULL); if (!new_handler) { return -1; } memcpy(new_handler, root_handler, old_size); new_handler->children[new_handler->num_children++] = handler; ao2_cleanup(root_handler); ao2_ref(new_handler, +1); root_handler = new_handler; ast_module_ref(ast_module_info->self); return 0; }
static void stasis_message_sink_dtor(void *obj) { struct stasis_message_sink *sink = obj; { SCOPED_MUTEX(lock, &sink->lock); while (!sink->is_done) { /* Normally waiting forever is bad, but if we're not * done, we're not done. */ ast_cond_wait(&sink->cond, &sink->lock); } } ast_mutex_destroy(&sink->lock); ast_cond_destroy(&sink->cond); while (sink->num_messages > 0) { ao2_cleanup(sink->messages[--sink->num_messages]); } ast_free(sink->messages); sink->messages = NULL; sink->max_messages = 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); }
int ast_sem_wait(struct ast_sem *sem) { int res; SCOPED_MUTEX(lock, &sem->mutex); ast_assert(sem->count >= 0); /* Wait for a non-zero count */ ++sem->waiters; while (sem->count == 0) { res = ast_cond_wait(&sem->cond, &sem->mutex); /* Give up on error */ if (res != 0) { --sem->waiters; return res; } } --sem->waiters; /* Take it! */ --sem->count; return 0; }
int ast_crypt_validate(const char *key, const char *expected) { SCOPED_MUTEX(lock, &crypt_mutex); return strcmp(expected, crypt(key, expected)) == 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); }
static struct stasis_rest_handlers *get_root_handler(void) { SCOPED_MUTEX(lock, &root_handler_lock); ao2_ref(root_handler, +1); return root_handler; }