Commit c90fb2ea authored by Karl Heyes's avatar Karl Heyes

cleanup duplicate work, fix rare but potential deadlock, and

fix silly bug introduced ealrier

svn path=/trunk/icecast/; revision=5794
parent ef791453
......@@ -438,8 +438,6 @@ static void command_move_clients(client_t *client, source_t *source,
{
char *dest_source;
source_t *dest;
avl_node *client_node;
client_t *current;
xmlDocPtr doc;
xmlNodePtr node;
char buf[255];
......@@ -459,16 +457,23 @@ static void command_move_clients(client_t *client, source_t *source,
return;
}
dest = source_find_mount (dest_source);
if (dest == NULL)
{
client_send_400 (client, "No such destination");
return;
}
if (strcmp (dest->mount, source->mount) == 0)
{
client_send_400 (client, "supplied mountpoints are identical");
return;
}
dest = source_find_mount(dest_source);
if(dest == NULL) {
client_send_400(client, "No such source");
if (dest->running == 0)
{
client_send_400 (client, "Destination not running");
return;
}
......@@ -476,26 +481,11 @@ static void command_move_clients(client_t *client, source_t *source,
node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
xmlDocSetRootElement(doc, node);
avl_tree_wlock(source->client_tree);
client_node = avl_get_first(source->client_tree);
while(client_node) {
current = (client_t *)client_node->key;
avl_tree_wlock(dest->pending_tree);
avl_insert(dest->pending_tree, current);
avl_tree_unlock(dest->pending_tree);
client_node = avl_get_next(client_node);
source_move_clients (source, dest);
avl_delete(source->client_tree, current, source_remove_client);
source->listeners--;
}
avl_tree_unlock(source->client_tree);
memset(buf, '\000', sizeof(buf));
snprintf(buf, sizeof(buf)-1, "Clients moved from %s to %s", dest_source,
source->mount);
snprintf (buf, sizeof(buf), "Clients moved from %s to %s",
source->mount, dest_source);
xmlNewChild(node, NULL, "message", buf);
xmlNewChild(node, NULL, "return", "1");
......
......@@ -95,6 +95,7 @@ void connection_initialize(void)
thread_mutex_create(&_connection_mutex);
thread_mutex_create(&_queue_mutex);
thread_mutex_create(&move_clients_mutex);
thread_rwlock_create(&_source_shutdown_rwlock);
thread_cond_create(&_pool_cond);
thread_cond_create(&global.shutdown_cond);
......@@ -111,6 +112,7 @@ void connection_shutdown(void)
thread_rwlock_destroy(&_source_shutdown_rwlock);
thread_mutex_destroy(&_queue_mutex);
thread_mutex_destroy(&_connection_mutex);
thread_mutex_destroy(&move_clients_mutex);
_initialized = 0;
}
......
......@@ -56,6 +56,8 @@
#define MAX_FALLBACK_DEPTH 10
mutex_t move_clients_mutex;
/* avl tree helper */
static int _compare_clients(void *compare_arg, void *a, void *b);
static int _free_client(void *key);
......@@ -113,11 +115,6 @@ source_t *source_create(client_t *client, connection_t *con,
return src;
}
static int source_remove_source(void *key)
{
return 1;
}
/* Find a mount with this raw name - ignoring fallbacks. You should have the
* global source tree locked to call this.
*/
......@@ -250,7 +247,63 @@ client_t *source_find_client(source_t *source, int id)
avl_tree_unlock(source->client_tree);
return NULL;
}
void source_move_clients (source_t *source, source_t *dest)
{
client_t *client;
avl_node *node;
if (source->format->type != dest->format->type)
{
WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount);
return;
}
if (dest->running == 0)
{
WARN1 ("source %s not running, unable to move clients ", dest->mount);
return;
}
/* we don't want the two write locks to deadlock in here */
thread_mutex_lock (&move_clients_mutex);
/* we need to move the client and pending trees */
avl_tree_wlock (dest->pending_tree);
avl_tree_wlock (source->pending_tree);
while (1)
{
node = avl_get_first (source->pending_tree);
if (node == NULL)
break;
client = (client_t *)(node->key);
avl_delete (source->pending_tree, client, NULL);
/* TODO: reset client local format data? */
avl_insert (dest->pending_tree, (void *)client);
}
avl_tree_unlock (source->pending_tree);
avl_tree_wlock (source->client_tree);
while (1)
{
node = avl_get_first (source->client_tree);
if (node == NULL)
break;
client = (client_t *)(node->key);
avl_delete (source->client_tree, client, NULL);
/* TODO: reset client local format data? */
avl_insert (dest->pending_tree, (void *)client);
}
source->listeners = 0;
stats_event(source->mount, "listeners", "0");
avl_tree_unlock (source->client_tree);
avl_tree_unlock (dest->pending_tree);
thread_mutex_unlock (&move_clients_mutex);
}
void *source_main(void *arg)
{
......@@ -450,39 +503,11 @@ void *source_main(void *arg)
if(source->fallback_override && source->fallback_mount) {
avl_tree_rlock(global.source_tree);
fallback_source = source_find_mount(source->fallback_mount);
avl_tree_unlock(global.source_tree);
if(fallback_source) {
/* we need to move the client and pending trees */
avl_tree_wlock(fallback_source->pending_tree);
while (avl_get_first(fallback_source->pending_tree)) {
client_t *client = (client_t *)avl_get_first(
fallback_source->pending_tree)->key;
avl_delete(fallback_source->pending_tree, client,
source_remove_client);
/* TODO: reset client local format data? */
avl_tree_wlock(source->pending_tree);
avl_insert(source->pending_tree, (void *)client);
avl_tree_unlock(source->pending_tree);
}
avl_tree_unlock(fallback_source->pending_tree);
avl_tree_wlock(fallback_source->client_tree);
while (avl_get_first(fallback_source->client_tree)) {
client_t *client = (client_t *)avl_get_first(
fallback_source->client_tree)->key;
avl_delete(fallback_source->client_tree, client,
source_remove_client);
if (fallback_source)
source_move_clients (fallback_source, source);
/* TODO: reset client local format data? */
avl_tree_wlock(source->pending_tree);
avl_insert(source->pending_tree, (void *)client);
avl_tree_unlock(source->pending_tree);
}
avl_tree_unlock(fallback_source->client_tree);
}
avl_tree_unlock(global.source_tree);
}
while (global.running == ICE_RUNNING && source->running) {
......@@ -732,54 +757,18 @@ done:
}
#endif
avl_tree_rlock(global.source_tree);
fallback_source = source_find_mount(source->fallback_mount);
avl_tree_unlock(global.source_tree);
/* Now, we must remove this source from the source tree before
* removing the clients, otherwise new clients can sneak into the pending
* tree after we've cleared it
*/
avl_tree_wlock(global.source_tree);
avl_delete(global.source_tree, source, source_remove_source);
avl_tree_unlock(global.source_tree);
/* we need to empty the client and pending trees */
avl_tree_wlock(source->pending_tree);
while (avl_get_first(source->pending_tree)) {
client_t *client = (client_t *)avl_get_first(
source->pending_tree)->key;
if(fallback_source) {
avl_delete(source->pending_tree, client, source_remove_client);
/* TODO: reset client local format data? */
avl_tree_wlock(fallback_source->pending_tree);
avl_insert(fallback_source->pending_tree, (void *)client);
avl_tree_unlock(fallback_source->pending_tree);
}
else {
avl_delete(source->pending_tree, client, _free_client);
}
}
avl_tree_unlock(source->pending_tree);
avl_tree_wlock(source->client_tree);
while (avl_get_first(source->client_tree)) {
client_t *client = (client_t *)avl_get_first(source->client_tree)->key;
fallback_source = source_find_mount (source->fallback_mount);
avl_delete (global.source_tree, source, NULL);
if(fallback_source) {
avl_delete(source->client_tree, client, source_remove_client);
if (fallback_source != NULL)
source_move_clients (source, fallback_source);
/* TODO: reset client local format data? */
avl_tree_wlock(fallback_source->pending_tree);
avl_insert(fallback_source->pending_tree, (void *)client);
avl_tree_unlock(fallback_source->pending_tree);
}
else {
avl_delete(source->client_tree, client, _free_client);
}
}
avl_tree_unlock(source->client_tree);
avl_tree_unlock (global.source_tree);
/* delete this sources stats */
stats_event_dec(NULL, "sources");
......
......@@ -67,9 +67,12 @@ source_t *source_find_mount_raw(const char *mount);
client_t *source_find_client(source_t *source, int id);
int source_compare_sources(void *arg, void *a, void *b);
int source_free_source(void *key);
void source_move_clients (source_t *source, source_t *dest);
int source_remove_client(void *key);
void *source_main(void *arg);
extern mutex_t move_clients_mutex;
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment