static uint64_t worker_check_time_ms (worker_t *worker) { uint64_t tm = timing_get_time(); if (tm - worker->time_ms > 1000 && worker->time_ms) WARN2 ("worker %p has been stuck for %" PRIu64 " ms", worker, (tm - worker->time_ms)); return tm; }
/* This is identical to shout_sync(), really. */ void input_sleep(void) { int64_t sleep; /* no need to sleep if we haven't sent data */ if (control.senttime == 0) return; sleep = ((double)control.senttime / 1000) - (timing_get_time() - control.starttime); /* trap for long sleeps, typically indicating a clock change. it's not */ /* perfect though, as low bitrate/low samplerate vorbis can trigger this */ if(sleep > 8000) { LOG_WARN1("Extended sleep requested (%ld ms), sleeping for 5 seconds", sleep); timing_sleep(5000); } else if(sleep > 0) timing_sleep((uint64_t)sleep); }
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; }
void *worker (void *arg) { worker_t *worker = arg; long prev_count = -1; client_t **prevp = &worker->clients; worker->running = 1; worker->wakeup_ms = (int64_t)0; worker->time_ms = timing_get_time(); while (1) { client_t *client = *prevp; uint64_t sched_ms = worker->time_ms + 2; while (client) { if (client->worker != worker) abort(); /* process client details but skip those that are not ready yet */ if (client->flags & CLIENT_ACTIVE) { int ret = 0; client_t *nx = client->next_on_worker; if (worker->running == 0 || client->schedule_ms <= sched_ms) { ret = client->ops->process (client); if (ret < 0) { client->worker = NULL; if (client->ops->release) client->ops->release (client); } if (ret) { worker->count--; if (nx == NULL) /* is this the last client */ worker->last_p = prevp; client = *prevp = nx; continue; } } if ((client->flags & CLIENT_ACTIVE) && client->schedule_ms < worker->wakeup_ms) worker->wakeup_ms = client->schedule_ms; } prevp = &client->next_on_worker; client = *prevp; } if (prev_count != worker->count) { DEBUG2 ("%p now has %d clients", worker, worker->count); prev_count = worker->count; } if (worker->running == 0) { if (global.running == ICE_RUNNING) break; if (worker->count == 0 && worker->pending_count == 0) break; } prevp = worker_wait (worker); } worker_relocate_clients (worker); INFO0 ("shutting down"); return NULL; }
void client_destroy(client_t *client) { if (client == NULL) return; if (client->worker) { WARN0 ("client still on worker thread"); return; } /* release the buffer now, as the buffer could be on the source queue * and may of disappeared after auth completes */ if (client->refbuf) { refbuf_release (client->refbuf); client->refbuf = NULL; } if (client->flags & CLIENT_AUTHENTICATED) DEBUG1 ("client still in auth \"%s\"", httpp_getvar (client->parser, HTTPP_VAR_URI)); /* write log entry if ip is set (some things don't set it, like outgoing * slave requests */ if (client->respcode > 0 && client->parser) logging_access(client); if (client->flags & CLIENT_IP_BAN_LIFT) { INFO1 ("lifting IP ban on client at %s", client->connection.ip); connection_release_banned_ip (client->connection.ip); client->flags &= ~CLIENT_IP_BAN_LIFT; } if (client->parser) httpp_destroy (client->parser); /* we need to free client specific format data (if any) */ if (client->free_client_data) client->free_client_data (client); free(client->username); free(client->password); client->username = NULL; client->password = NULL; client->parser = NULL; client->respcode = 0; client->free_client_data = NULL; global_lock (); if (global.running != ICE_RUNNING || client->connection.error || (client->flags & CLIENT_KEEPALIVE) == 0 || client_connected (client) == 0) { global.clients--; stats_event_args (NULL, "clients", "%d", global.clients); config_clear_listener (client->server_conn); global_unlock (); connection_close (&client->connection); free(client); return; } global_unlock (); DEBUG0 ("keepalive detected, placing back onto worker"); client->counter = client->schedule_ms = timing_get_time(); client->connection.con_time = client->schedule_ms/1000; client->connection.discon.time = client->connection.con_time + 7; client->ops = &http_request_ops; client->flags = CLIENT_ACTIVE; client->shared_data = NULL; client->refbuf = NULL; client->pos = 0; client->intro_offset = client->connection.sent_bytes = 0; client_add_worker (client); }
void *worker (void *arg) { worker_t *worker = arg; long prev_count = -1; client_t **prevp = &worker->clients; uint64_t c = 0; worker->running = 1; worker->wakeup_ms = (int64_t)0; worker->time_ms = timing_get_time(); while (1) { client_t *client = *prevp; uint64_t sched_ms = worker->time_ms + 12; c = 0; while (client) { if (client->worker != worker) abort(); /* process client details but skip those that are not ready yet */ if (client->flags & CLIENT_ACTIVE) { int ret = 0; client_t *nx = client->next_on_worker; int process = (worker->running == 0 || client->schedule_ms <= sched_ms) ? 1 : 0; if (process) { if (c > 300 && c & 1) // only process alternate clients after so many process = 0; } else if (client->wakeup && *client->wakeup) { if (c & 1) process = 1; // enable this one to pass through else client->schedule_ms = worker->time_ms; } if (process) { c++; if ((c & 31) == 0) { // update these after so many to keep in sync worker->time_ms = timing_get_time(); worker->current_time.tv_sec = (time_t)(worker->time_ms/1000); } ret = client->ops->process (client); if (ret < 0) { client->worker = NULL; if (client->ops->release) client->ops->release (client); } if (ret) { worker->count--; if (nx == NULL) /* is this the last client */ worker->last_p = prevp; client = *prevp = nx; continue; } } if ((client->flags & CLIENT_ACTIVE) && client->schedule_ms < worker->wakeup_ms) worker->wakeup_ms = client->schedule_ms; } prevp = &client->next_on_worker; client = *prevp; } if (prev_count != worker->count) { DEBUG2 ("%p now has %d clients", worker, worker->count); prev_count = worker->count; } if (worker->running == 0) { if (global.running == ICE_RUNNING) break; if (worker->count == 0 && worker->pending_count == 0) break; } prevp = worker_wait (worker); } worker_relocate_clients (worker); INFO0 ("shutting down"); return NULL; }
static void *start_relay_stream (void *arg) { client_t *client = arg; relay_server *relay; source_t *src; int failed = 1, sources; global_lock(); sources = ++global.sources; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); /* set the start time, because we want to decrease the sources on all failures */ client->connection.con_time = time (NULL); do { ice_config_t *config = config_get_config(); mount_proxy *mountinfo; relay = client->shared_data; src = relay->source; thread_rwlock_wlock (&src->lock); src->flags |= SOURCE_PAUSE_LISTENERS; if (sources > config->source_limit) { config_release_config(); WARN1 ("starting relayed mountpoint \"%s\" requires a higher sources limit", relay->localmount); break; } config_release_config(); INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount); if (open_relay (relay) < 0) break; if (connection_complete_source (src) < 0) { WARN1 ("Failed to complete initialisation on %s", relay->localmount); break; } stats_event_inc (NULL, "source_relay_connections"); source_init (src); config = config_get_config(); mountinfo = config_find_mount (config, src->mount); source_update_settings (config, src, mountinfo); INFO1 ("source %s is ready to start", src->mount); config_release_config(); failed = 0; } while (0); client->ops = &relay_client_ops; client->schedule_ms = timing_get_time(); if (failed) { /* failed to start any connection, better clean up and reset */ if (relay->on_demand == 0) { yp_remove (relay->localmount); src->yp_public = -1; } relay->in_use = NULL; INFO2 ("listener count remaining on %s is %d", src->mount, src->listeners); src->flags &= ~(SOURCE_PAUSE_LISTENERS|SOURCE_RUNNING); } thread_rwlock_unlock (&src->lock); thread_spin_lock (&relay_start_lock); relays_connecting--; thread_spin_unlock (&relay_start_lock); client->flags |= CLIENT_ACTIVE; worker_wakeup (client->worker); return NULL; }
void *worker (void *arg) { worker_t *worker = arg; long prev_count = -1; client_t **prevp = &worker->clients; uint64_t c = 0; worker->running = 1; worker->wakeup_ms = (int64_t)0; worker->time_ms = timing_get_time(); while (1) { client_t *client = *prevp; uint64_t sched_ms = worker->time_ms + 12; c = 0; while (client) { if (client->worker != worker) abort(); /* process client details but skip those that are not ready yet */ if (client->flags & CLIENT_ACTIVE) { int ret = 0; client_t *nx = client->next_on_worker; int process = 1; if (worker->running) // force all active clients to run on worker shutdown { if (client->schedule_ms <= sched_ms) { if (c > 9000 && client->wakeup == NULL) process = 0; } else if (client->wakeup == NULL || *client->wakeup == 0) { process = 0; } } if (process) { if ((c & 511) == 0) { // update these periodically to keep in sync worker->time_ms = worker_check_time_ms (worker); worker->current_time.tv_sec = (time_t)(worker->time_ms/1000); } c++; ret = client->ops->process (client); if (ret < 0) { client->worker = NULL; if (client->ops->release) client->ops->release (client); } if (ret) { worker->count--; if (nx == NULL) /* is this the last client */ worker->last_p = prevp; client = *prevp = nx; continue; } } if ((client->flags & CLIENT_ACTIVE) && client->schedule_ms < worker->wakeup_ms) worker->wakeup_ms = client->schedule_ms; } prevp = &client->next_on_worker; client = *prevp; } if (prev_count != worker->count) { DEBUG2 ("%p now has %d clients", worker, worker->count); prev_count = worker->count; } if (worker->running == 0) { if (global.running == ICE_RUNNING) break; if (worker->count == 0 && worker->pending_count == 0) break; } prevp = worker_wait (worker); } worker_relocate_clients (worker); INFO0 ("shutting down"); return NULL; }