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