Esempio n. 1
0
struct stasis_subscription *internal_stasis_subscribe(
	struct stasis_topic *topic,
	stasis_subscription_cb callback,
	void *data,
	int needs_mailbox,
	int use_thread_pool)
{
	RAII_VAR(struct stasis_subscription *, sub, NULL, ao2_cleanup);

	if (!topic) {
		return NULL;
	}

	/* The ao2 lock is used for join_cond. */
	sub = ao2_t_alloc(sizeof(*sub), subscription_dtor, stasis_topic_name(topic));
	if (!sub) {
		return NULL;
	}
	ast_uuid_generate_str(sub->uniqueid, sizeof(sub->uniqueid));

	if (needs_mailbox) {
		char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];

		/* Create name with seq number appended. */
		ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "sub%c:%s",
			use_thread_pool ? 'p' : 'm',
			stasis_topic_name(topic));

		/*
		 * With a small number of subscribers, a thread-per-sub is
		 * acceptable. For a large number of subscribers, a thread
		 * pool should be used.
		 */
		if (use_thread_pool) {
			sub->mailbox = ast_threadpool_serializer(tps_name, pool);
		} else {
			sub->mailbox = ast_taskprocessor_get(tps_name, TPS_REF_DEFAULT);
		}
		if (!sub->mailbox) {
			return NULL;
		}
		ast_taskprocessor_set_local(sub->mailbox, sub);
		/* Taskprocessor has a reference */
		ao2_ref(sub, +1);
	}

	ao2_ref(topic, +1);
	sub->topic = topic;
	sub->callback = callback;
	sub->data = data;
	ast_cond_init(&sub->join_cond, NULL);

	if (topic_add_subscription(topic, sub) != 0) {
		return NULL;
	}
	send_subscription_subscribe(topic, sub);

	ao2_ref(sub, +1);
	return sub;
}
static struct stasis_message_router *stasis_message_router_create_internal(
	struct stasis_topic *topic, int use_thread_pool)
{
	int res;
	RAII_VAR(struct stasis_message_router *, router, NULL, ao2_cleanup);

	router = ao2_t_alloc(sizeof(*router), router_dtor, stasis_topic_name(topic));
	if (!router) {
		return NULL;
	}

	res = 0;
	res |= AST_VECTOR_INIT(&router->routes, 0);
	res |= AST_VECTOR_INIT(&router->cache_routes, 0);
	if (res) {
		return NULL;
	}

	if (use_thread_pool) {
		router->subscription = stasis_subscribe_pool(topic, router_dispatch, router);
	} else {
		router->subscription = stasis_subscribe(topic, router_dispatch, router);
	}
	if (!router->subscription) {
		return NULL;
	}

	ao2_ref(router, +1);
	return router;
}
Esempio n. 3
0
static void caching_topic_exec(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	RAII_VAR(struct stasis_caching_topic *, caching_topic_needs_unref, NULL, ao2_cleanup);
	struct stasis_caching_topic *caching_topic = data;
	const char *id = NULL;

	ast_assert(caching_topic != NULL);
	ast_assert(caching_topic->topic != NULL);
	ast_assert(caching_topic->cache != NULL);
	ast_assert(caching_topic->cache->id_fn != NULL);

	if (stasis_subscription_final_message(sub, message)) {
		caching_topic_needs_unref = caching_topic;
	}

	/* Handle cache clear event */
	if (stasis_cache_clear_type() == stasis_message_type(message)) {
		RAII_VAR(struct stasis_message *, old_snapshot, NULL, ao2_cleanup);
		RAII_VAR(struct stasis_message *, update, NULL, ao2_cleanup);
		struct stasis_message *clear_msg = stasis_message_data(message);
		const char *clear_id = caching_topic->cache->id_fn(clear_msg);
		struct stasis_message_type *clear_type = stasis_message_type(clear_msg);

		ast_assert(clear_type != NULL);

		if (clear_id) {
			old_snapshot = cache_put(caching_topic->cache, clear_type, clear_id, NULL);
			if (old_snapshot) {
				update = update_create(old_snapshot, NULL);
				stasis_publish(caching_topic->topic, update);
				return;
			}

			ast_log(LOG_ERROR,
				"Attempting to remove an item from the %s cache that isn't there: %s %s\n",
				stasis_topic_name(caching_topic->topic), stasis_message_type_name(clear_type), clear_id);
			return;
		}
	}

	id = caching_topic->cache->id_fn(message);
	if (id == NULL) {
		/* Object isn't cached; discard */
	} else {
		/* Update the cache */
		RAII_VAR(struct stasis_message *, old_snapshot, NULL, ao2_cleanup);
		RAII_VAR(struct stasis_message *, update, NULL, ao2_cleanup);

		old_snapshot = cache_put(caching_topic->cache, stasis_message_type(message), id, message);

		update = update_create(old_snapshot, message);
		if (update == NULL) {
			return;
		}

		stasis_publish(caching_topic->topic, update);
	}
}
Esempio n. 4
0
static int topic_pool_entry_cmp(void *obj, void *arg, int flags)
{
	const struct topic_pool_entry *object_left = obj;
	const struct topic_pool_entry *object_right = arg;
	const char *right_key = arg;
	int cmp;

	switch (flags & OBJ_SEARCH_MASK) {
	case OBJ_SEARCH_OBJECT:
		right_key = stasis_topic_name(object_right->topic);
		/* Fall through */
	case OBJ_SEARCH_KEY:
		cmp = strcasecmp(stasis_topic_name(object_left->topic), right_key);
		break;
	case OBJ_SEARCH_PARTIAL_KEY:
		/* Not supported by container */
		ast_assert(0);
		cmp = -1;
		break;
	default:
		/*
		 * What arg points to is specific to this traversal callback
		 * and has no special meaning to astobj2.
		 */
		cmp = 0;
		break;
	}
	if (cmp) {
		return 0;
	}
	/*
	 * At this point the traversal callback is identical to a sorted
	 * container.
	 */
	return CMP_MATCH;
}
Esempio n. 5
0
static int topic_pool_entry_hash(const void *obj, const int flags)
{
	const struct topic_pool_entry *object;
	const char *key;

	switch (flags & OBJ_SEARCH_MASK) {
	case OBJ_SEARCH_KEY:
		key = obj;
		break;
	case OBJ_SEARCH_OBJECT:
		object = obj;
		key = stasis_topic_name(object->topic);
		break;
	default:
		/* Hash can only work on something with a full key. */
		ast_assert(0);
		return 0;
	}
	return ast_str_case_hash(key);
}
Esempio n. 6
0
struct stasis_caching_topic *stasis_caching_topic_create(struct stasis_topic *original_topic, struct stasis_cache *cache)
{
	RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup);
	struct stasis_subscription *sub;
	RAII_VAR(char *, new_name, NULL, ast_free);
	int ret;

	ret = ast_asprintf(&new_name, "%s-cached", stasis_topic_name(original_topic));
	if (ret < 0) {
		return NULL;
	}

	caching_topic = ao2_alloc_options(sizeof(*caching_topic),
		stasis_caching_topic_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
	if (caching_topic == NULL) {
		return NULL;
	}

	caching_topic->topic = stasis_topic_create(new_name);
	if (caching_topic->topic == NULL) {
		return NULL;
	}

	ao2_ref(cache, +1);
	caching_topic->cache = cache;

	sub = internal_stasis_subscribe(original_topic, caching_topic_exec, caching_topic, 0);
	if (sub == NULL) {
		return NULL;
	}

	ao2_ref(original_topic, +1);
	caching_topic->original_topic = original_topic;

	/* This is for the reference contained in the subscription above */
	ao2_ref(caching_topic, +1);
	caching_topic->sub = sub;

	/* The subscription holds the reference, so no additional ref bump. */
	return caching_topic;
}