Example #1
0
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;
}
Example #2
0
/* 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);
}
Example #3
0
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(&current->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(&current->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;
}
Example #4
0
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;
}
Example #5
0
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);
}
Example #6
0
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;
}
Example #7
0
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;
}
Example #8
0
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;
}