Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  threads.c   Sprache: C

 

/*****************************************************************************
 * threads.c: LibVLC generic thread support
 *****************************************************************************
 * Copyright (C) 2009-2016 Rémi Denis-Courmont
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdatomic.h>

#include <vlc_common.h>
#include <vlc_threads.h>
#include <vlc_atomic.h>
#include "../libvlc.h"
#include "threads.h"

/* <stdatomic.h> types cannot be used in the C++ view of <vlc_threads.h> */
struct vlc_suuint { union { unsigned int value; }; };

static_assert (sizeof (atomic_uint) <= sizeof (struct vlc_suuint),
               "Size mismatch");
static_assert (alignof (atomic_uint) <= alignof (struct vlc_suuint),
               "Alignment mismatch");

/*** Global locks ***/

void vlc_global_mutex (unsigned n, bool acquire)
{
    static vlc_mutex_t locks[] = {
        VLC_STATIC_MUTEX,
        VLC_STATIC_MUTEX,
        VLC_STATIC_MUTEX,
        VLC_STATIC_MUTEX,
#ifdef _WIN32
        VLC_STATIC_MUTEX, // For MTA holder
#endif
    };
    static_assert (VLC_MAX_MUTEX == ARRAY_SIZE(locks),
                   "Wrong number of global mutexes");
    assert (n < ARRAY_SIZE(locks));

    vlc_mutex_t *lock = locks + n;
    if (acquire)
        vlc_mutex_lock (lock);
    else
        vlc_mutex_unlock (lock);
}

static void vlc_mutex_init_common(vlc_mutex_t *mtx, bool recursive)
{
    atomic_init(&mtx->value, 0);
    atomic_init(&mtx->recursion, recursive ? 1 : 0);
    atomic_init(&mtx->owner, 0);
}

void vlc_mutex_init(vlc_mutex_t *mtx)
{
    vlc_mutex_init_common(mtx, false);
}

void vlc_mutex_init_recursive(vlc_mutex_t *mtx)
{
    vlc_mutex_init_common(mtx, true);
}

bool vlc_mutex_held(const vlc_mutex_t *mtx)
{
    /* This comparison is thread-safe:
     * Even though other threads may modify the owner field at any time,
     * they will never make it compare equal to the calling thread.
     */

    return vlc_thread_id() == atomic_load_explicit(&mtx->owner,
                                                   memory_order_relaxed);
}

void vlc_mutex_lock(vlc_mutex_t *mtx)
{
    /* This is the Drepper (non-recursive) mutex algorithm
     * from his "Futexes are tricky" paper. The mutex value can be:
     * - 0: the mutex is free
     * - 1: the mutex is locked and uncontended
     * - 2: the mutex is contended (i.e., unlock needs to wake up a waiter)
     */

    if (vlc_mutex_trylock(mtx) == 0)
        return;

    int canc = vlc_savecancel(); /* locking is never a cancellation point */

    while (atomic_exchange_explicit(&mtx->value, 2, memory_order_acquire))
        vlc_atomic_wait(&mtx->value, 2);

    vlc_restorecancel(canc);
    atomic_store_explicit(&mtx->owner, vlc_thread_id(), memory_order_relaxed);
}

int vlc_mutex_trylock(vlc_mutex_t *mtx)
{
    /* Check the recursion counter:
     * - 0: mutex is not recursive.
     * - 1: mutex is recursive but free or locked non-recursively.
     * - n > 1: mutex is recursive and locked n time(s).
     */

    unsigned recursion = atomic_load_explicit(&mtx->recursion,
                                              memory_order_relaxed);
    if (unlikely(recursion)) {
        if (vlc_mutex_held(mtx)) {
            /* This thread already owns the mutex, locks recursively.
             * Other threads shall not have modified the recursion or owner fields.
             */

            atomic_store_explicit(&mtx->recursion, recursion + 1,
                                  memory_order_relaxed);
            return 0;
        }
    } else
        assert(!vlc_mutex_held(mtx));

    unsigned value = 0;

    if (atomic_compare_exchange_strong_explicit(&mtx->value, &value, 1,
                                                memory_order_acquire,
                                                memory_order_relaxed)) {
        atomic_store_explicit(&mtx->owner, vlc_thread_id(),
                              memory_order_relaxed);
        return 0;
    }

    return EBUSY;
}

void vlc_mutex_unlock(vlc_mutex_t *mtx)
{
    assert(vlc_mutex_held(mtx));

    unsigned recursion = atomic_load_explicit(&mtx->recursion,
                                              memory_order_relaxed);
    if (unlikely(recursion > 1)) {
        /* Non-last recursive unlocking. */
        atomic_store_explicit(&mtx->recursion, recursion - 1,
                              memory_order_relaxed);
        return;
    }

    atomic_store_explicit(&mtx->owner, 0, memory_order_relaxed);

    switch (atomic_exchange_explicit(&mtx->value, 0, memory_order_release)) {
        case 2:
            vlc_atomic_notify_one(&mtx->value);
        case 1:
            break;
        default:
            vlc_assert_unreachable();
    }
}

void vlc_cond_init(vlc_cond_t *cond)
{
    cond->head = NULL;
    vlc_mutex_init(&cond->lock);
}

struct vlc_cond_waiter {
    struct vlc_cond_waiter **pprev, *next;
    atomic_uint value;
};

static void vlc_cond_signal_waiter(struct vlc_cond_waiter *waiter)
{
    waiter->pprev = &waiter->next;
    waiter->next = NULL;
    atomic_fetch_add_explicit(&waiter->value, 1, memory_order_relaxed);
    vlc_atomic_notify_one(&waiter->value);
}

void vlc_cond_signal(vlc_cond_t *cond)
{
    struct vlc_cond_waiter *waiter;

    /* Some call sites signal their condition variable without holding the
     * corresponding lock. Thus an extra lock is needed here to ensure the
     * consistency of the linked list and the lifetime of its elements.
     * If all call sites locked cleanly, the inner lock would be unnecessary.
     */

    vlc_mutex_lock(&cond->lock);
    waiter = cond->head;

    if (waiter != NULL) {
        struct vlc_cond_waiter *next = waiter->next;
        struct vlc_cond_waiter **pprev = waiter->pprev;

        *pprev = next;

        if (next != NULL)
            next->pprev = pprev;

        vlc_cond_signal_waiter(waiter);
    }

    vlc_mutex_unlock(&cond->lock);
}

void vlc_cond_broadcast(vlc_cond_t *cond)
{
    struct vlc_cond_waiter *waiter;

    vlc_mutex_lock(&cond->lock);
    waiter = cond->head;
    cond->head = NULL;

    /* Keep the lock here so that waiters don't go out of scope */
    while (waiter != NULL) {
        struct vlc_cond_waiter *next = waiter->next;

        vlc_cond_signal_waiter(waiter);
        waiter = next;
    }

    vlc_mutex_unlock(&cond->lock);
}

static void vlc_cond_wait_prepare(struct vlc_cond_waiter *waiter,
                                  vlc_cond_t *cond, vlc_mutex_t *mutex)
{
    struct vlc_cond_waiter *next;

    waiter->pprev = &cond->head;
    atomic_init(&waiter->value, 0);

    vlc_mutex_lock(&cond->lock);
    next = cond->head;
    cond->head = waiter;
    waiter->next = next;

    if (next != NULL)
        next->pprev = &waiter->next;

    vlc_mutex_unlock(&cond->lock);
    vlc_mutex_unlock(mutex);
}

static void vlc_cond_wait_finish(struct vlc_cond_waiter *waiter,
                                 vlc_cond_t *cond, vlc_mutex_t *mutex)
{
    struct vlc_cond_waiter *next;

    /* If this waiter is still on the linked list, remove it before it goes
     * out of scope. Otherwise, this is a no-op.
     */

    vlc_mutex_lock(&cond->lock);
    next = waiter->next;
    *(waiter->pprev) = next;

    if (next != NULL)
        next->pprev = waiter->pprev;

    vlc_mutex_unlock(&cond->lock);

    /* Lock the caller's mutex as required by condition variable semantics. */
    vlc_mutex_lock(mutex);
}

void vlc_cond_wait(vlc_cond_t *cond, vlc_mutex_t *mutex)
{
    struct vlc_cond_waiter waiter;

    // wait on a multiply locked mutex not supported
    assert(atomic_load_explicit(&mutex->recursion, memory_order_relaxed) <= 1);

    vlc_cond_wait_prepare(&waiter, cond, mutex);
    vlc_atomic_wait(&waiter.value, 0);
    vlc_cond_wait_finish(&waiter, cond, mutex);
}

int vlc_cond_timedwait(vlc_cond_t *cond, vlc_mutex_t *mutex,
                       vlc_tick_t deadline)
{
    struct vlc_cond_waiter waiter;
    int ret;

    // wait on a multiply locked mutex not supported
    assert(atomic_load_explicit(&mutex->recursion, memory_order_relaxed) <= 1);

    vlc_cond_wait_prepare(&waiter, cond, mutex);
    ret = vlc_atomic_timedwait(&waiter.value, 0, deadline);
    vlc_cond_wait_finish(&waiter, cond, mutex);

    return ret;
}

/*** Generic semaphores ***/

void vlc_sem_init (vlc_sem_t *sem, unsigned value)
{
    atomic_init(&sem->value, value);
}

int vlc_sem_post (vlc_sem_t *sem)
{
    unsigned exp = atomic_load_explicit(&sem->value, memory_order_relaxed);

    do
    {
        if (unlikely(exp == UINT_MAX))
           return EOVERFLOW;
    } while (!atomic_compare_exchange_weak_explicit(&sem->value, &exp, exp + 1,
                                                    memory_order_release,
                                                    memory_order_relaxed));

    vlc_atomic_notify_one(&sem->value);
    return 0;
}

void vlc_sem_wait (vlc_sem_t *sem)
{
    unsigned exp = 1;

    while (!atomic_compare_exchange_weak_explicit(&sem->value, &exp, exp - 1,
                                                  memory_order_acquire,
                                                  memory_order_relaxed))
    {
        if (likely(exp == 0))
        {
            vlc_atomic_wait(&sem->value, 0);
            exp = 1;
        }
    }
}

int vlc_sem_timedwait(vlc_sem_t *sem, vlc_tick_t deadline)
{
    unsigned exp = 1;

    while (!atomic_compare_exchange_weak_explicit(&sem->value, &exp, exp - 1,
                                                  memory_order_acquire,
                                                  memory_order_relaxed))
    {
        if (likely(exp == 0))
        {
            int ret = vlc_atomic_timedwait(&sem->value, 0, deadline);
            if (ret)
                return ret;

            exp = 1;
        }
    }

    return 0;
}

int vlc_sem_trywait(vlc_sem_t *sem)
{
    unsigned exp = atomic_load_explicit(&sem->value, memory_order_relaxed);

    do
        if (exp == 0)
            return EAGAIN;
    while (!atomic_compare_exchange_weak_explicit(&sem->value, &exp, exp - 1,
                                                  memory_order_acquire,
                                                  memory_order_relaxed));

    return 0;
}

enum { VLC_LATCH_READY, VLC_LATCH_CONTEND, VLC_LATCH_PENDING };

void vlc_latch_init(vlc_latch_t *latch, size_t value)
{
    atomic_init(&latch->ready, value ? VLC_LATCH_PENDING : VLC_LATCH_READY);
    atomic_init(&latch->value, value);
}

static bool vlc_latch_count_down_ready(vlc_latch_t *latch, size_t n)
{
    size_t value;

    value = atomic_fetch_sub_explicit(&latch->value, n, memory_order_acq_rel);
    assert(value >= n);

    if (value != n)
        return false;

    if (atomic_exchange_explicit(&latch->ready, VLC_LATCH_READY,
                                 memory_order_release) == VLC_LATCH_CONTEND)
        vlc_atomic_notify_all(&latch->ready);

    return true;
}

void vlc_latch_count_down(vlc_latch_t *latch, size_t n)
{
    (void) vlc_latch_count_down_ready(latch, n);
}

void vlc_latch_count_down_and_wait(vlc_latch_t *latch, size_t n)
{
    if (!vlc_latch_count_down_ready(latch, n))
        vlc_latch_wait(latch);
}

bool vlc_latch_is_ready(const vlc_latch_t *latch)
{
    return atomic_load_explicit(&latch->ready,
                                memory_order_acquire) == VLC_LATCH_READY;
}

void vlc_latch_wait(vlc_latch_t *latch)
{
    unsigned int expected = VLC_LATCH_PENDING;

    if (!atomic_compare_exchange_strong_explicit(&latch->ready, &expected,
                                                 VLC_LATCH_CONTEND,
                                                 memory_order_acquire,
                                                 memory_order_acquire)) {
        if (expected == VLC_LATCH_READY)
            return;

        assert(expected == VLC_LATCH_CONTEND);
    }

    do
        vlc_atomic_wait(&latch->ready, VLC_LATCH_CONTEND);
    while (!vlc_latch_is_ready(latch));
}

void vlc_queuedmutex_init(vlc_queuedmutex_t *m)
{
    atomic_init(&m->head, 0);
    atomic_init(&m->tail, 0);
    atomic_init(&m->owner, 0);
}

bool vlc_queuedmutex_held(vlc_queuedmutex_t *m)
{
    return (vlc_thread_id() == atomic_load_explicit(&m->owner, memory_order_relaxed));
}

void vlc_queuedmutex_lock(vlc_queuedmutex_t *m)
{
    uint_fast32_t ticket = atomic_fetch_add_explicit(&m->tail, 1,
                                                     memory_order_relaxed);
    uint_fast32_t head;

    while ((head = atomic_load_explicit(&m->head,
                                        memory_order_acquire)) != ticket)
        vlc_atomic_wait(&m->head, head);

    atomic_store_explicit(&m->owner, vlc_thread_id(), memory_order_relaxed);
}

void vlc_queuedmutex_unlock(vlc_queuedmutex_t *m)
{
    atomic_store_explicit(&m->owner, 0, memory_order_relaxed);
    atomic_fetch_add_explicit(&m->head, 1, memory_order_release);
    vlc_atomic_notify_all(&m->head);
}

enum { VLC_ONCE_UNDONE, VLC_ONCE_DOING, VLC_ONCE_CONTEND, VLC_ONCE_DONE };

static_assert (VLC_ONCE_DONE == 3, "Check vlc_once in header file");

bool (vlc_once_begin)(vlc_once_t *restrict once)
{
    unsigned int value = VLC_ONCE_UNDONE;

    if (atomic_compare_exchange_strong_explicit(&once->value, &value,
                                                VLC_ONCE_DOING,
                                                memory_order_acquire,
                                                memory_order_acquire))
        return false;

    assert(value >= VLC_ONCE_DOING);

    if (unlikely(value == VLC_ONCE_DOING)
     && atomic_compare_exchange_strong_explicit(&once->value, &value,
                                                VLC_ONCE_CONTEND,
                                                memory_order_acquire,
                                                memory_order_acquire))
        value = VLC_ONCE_CONTEND;

    assert(value >= VLC_ONCE_CONTEND);

    while (unlikely(value != VLC_ONCE_DONE)) {
        vlc_atomic_wait(&once->value, VLC_ONCE_CONTEND);
        value = atomic_load_explicit(&once->value, memory_order_acquire);
    }

    return true;
}

void vlc_once_complete(vlc_once_t *restrict once)
{
    switch (atomic_exchange_explicit(&once->value, VLC_ONCE_DONE,
                                     memory_order_release)) {
        case VLC_ONCE_DOING: /* No waiters, nothing (else) to do */
            break;
        case VLC_ONCE_CONTEND: /* Notify waiters */
            vlc_atomic_notify_all(&once->value);
            break;
        default:
            vlc_assert_unreachable();
    }
}

Messung
CHZG
849510093

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104842
#Domains=499162