static void _free_instances(instance_t *instance) { instance_t *next; next = NULL; do { config_free_instance(instance); next = instance->next; free(instance); instance = next; } while (next != NULL); }
void input_loop(void) { input_module_t *inmod=NULL; instance_t *instance, *prev, *next; queue_item *queued; int shutdown = 0; int current_module = 0; int valid_stream = 1; int inc_count; int not_waiting_for_critical; int foundmodule = 0; thread_cond_create(&ices_config->queue_cond); thread_cond_create(&ices_config->event_pending_cond); thread_mutex_create(&ices_config->refcount_lock); thread_mutex_create(&ices_config->flush_lock); memset (&control, 0, sizeof (control)); while(ices_config->playlist_module && modules[current_module].open) { if(!strcmp(ices_config->playlist_module, modules[current_module].name)) { foundmodule = 1; inmod = modules[current_module].open(ices_config->module_params); break; } current_module++; } if(!inmod) { if(foundmodule) LOG_ERROR1("Couldn't initialise input module \"%s\"", ices_config->playlist_module); else LOG_ERROR1("No input module named \"%s\" could be found", ices_config->playlist_module); return; } ices_config->inmod = inmod; /* ok, basic config stuff done. Now, we want to start all our listening * threads. */ instance = ices_config->instances; while(instance) { stream_description *arg = calloc(1, sizeof(stream_description)); arg->stream = instance; arg->input = inmod; /* if(instance->savefilename != NULL) thread_create("savefile", savefile_stream, arg, 1); */ thread_create("stream", ices_instance_stream, arg, 1); instance = instance->next; } /* treat as if a signal has arrived straight away */ signal_usr1_handler (0); /* now we go into the main loop * We shut down the main thread ONLY once all the instances * have killed themselves. */ while(!shutdown) { ref_buffer *chunk = calloc(1, sizeof(ref_buffer)); buffer_queue *current; int ret; instance = ices_config->instances; prev = NULL; while(instance) { /* if an instance has died, get rid of it ** this should be replaced with something that tries to ** restart the instance, probably. */ if (instance->died) { LOG_DEBUG0("An instance died, removing it"); next = instance->next; if (prev) prev->next = next; else ices_config->instances = next; /* Just in case, flush any existing buffers * Locks shouldn't be needed, but lets be SURE */ thread_mutex_lock(&ices_config->flush_lock); input_flush_queue(instance->queue, 0); thread_mutex_unlock(&ices_config->flush_lock); config_free_instance(instance); free(instance); instance = next; continue; } prev = instance; instance = instance->next; } instance = ices_config->instances; if(!instance) { shutdown = 1; free(chunk); continue; } if(ices_config->shutdown) /* We've been signalled to shut down, but */ { /* the instances haven't done so yet... */ timing_sleep(250); /* sleep for quarter of a second */ free(chunk); continue; } /* If this is the first time through, set initial time. This should * be done before the call to inmod->getdata() below, in order to * properly keep time if this input module blocks. */ if (control.starttime == 0) control.starttime = timing_get_time(); /* get a chunk of data from the input module */ ret = inmod->getdata(inmod->internal, chunk); /* input module signalled non-fatal error. Skip this chunk */ if(ret==0) { free(chunk); continue; } /* Input module signalled fatal error, shut down - nothing we can do * from here */ if(ret < 0) { ices_config->shutdown = 1; thread_cond_broadcast(&ices_config->queue_cond); free(chunk); continue; } if(chunk->critical) valid_stream = 1; if(ret < 0) { /* Tell the input module to go to the next track, hopefully allowing * resync. */ ices_config->inmod->handle_event(ices_config->inmod, EVENT_NEXTTRACK,NULL); valid_stream = 0; } inc_count = 0; not_waiting_for_critical = 0; if(valid_stream) { while(instance) { if(instance->wait_for_critical && !chunk->critical) { instance = instance->next; continue; } not_waiting_for_critical = 1; if(instance->skip) { instance = instance->next; continue; } queued = malloc(sizeof(queue_item)); queued->buf = chunk; current = instance->queue; inc_count++; thread_mutex_lock(¤t->lock); if(current->head == NULL) { current->head = current->tail = queued; current->head->next = current->tail->next = NULL; } else { current->tail->next = queued; queued->next = NULL; current->tail = queued; } current->length++; thread_mutex_unlock(¤t->lock); instance = instance->next; } } /* If everything is waiting for a critical buffer, force one * early. (This will take effect on the next pass through) */ if(valid_stream && !not_waiting_for_critical) { ices_config->inmod->handle_event(ices_config->inmod, EVENT_NEXTTRACK,NULL); instance = ices_config->instances; while(instance) { thread_mutex_lock(&ices_config->flush_lock); input_flush_queue(instance->queue, 0); instance->wait_for_critical = 0; thread_mutex_unlock(&ices_config->flush_lock); instance = instance->next; } } /* Make sure we don't end up with a 0-refcount buffer that * will never hit any of the free points. (this happens * if all threads are set to skip, for example). */ thread_mutex_lock(&ices_config->refcount_lock); chunk->count += inc_count; if(!chunk->count) { free(chunk->buf); free(chunk); } thread_mutex_unlock(&ices_config->refcount_lock); if(valid_stream) { /* wake up the instances */ thread_cond_broadcast(&ices_config->queue_cond); } } LOG_INFO0 ("All instances removed, shutting down..."); ices_config->shutdown = 1; thread_cond_broadcast(&ices_config->event_pending_cond); timing_sleep(250); /* sleep for quarter of a second */ thread_cond_destroy(&ices_config->queue_cond); thread_cond_destroy(&ices_config->event_pending_cond); thread_mutex_destroy(&ices_config->flush_lock); thread_mutex_destroy(&ices_config->refcount_lock); inmod->handle_event(inmod, EVENT_SHUTDOWN, NULL); return; }