bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

gnulib vs. glib threads


From: Bruno Haible
Subject: gnulib vs. glib threads
Date: Sun, 12 Oct 2008 13:49:02 +0200
User-agent: KMail/1.5.4

Here's a comparison between the threads implementations of gnulib
(lib/glthread/*) and GNOME glib (glib-2.12.4/gthread/gthread*),
especially the POSIX and Win32 implementation.

General differences:
  - glib is better documented.
  - The glib implementation is separated into a platform-independent
    part and a platform-dependent part. The two are connected by a vtable
    of pointers to the platform-dependent functions. Whereas in gnulib
    the dispatching is done through #ifdefs.
  - Feature comparison:
      - lock/mutex: each has 3 separate data types for simple mutex,
        read-write-lock, and recursive mutex. glib also has 'trylock' method.
        gnulib has only one method for unlocking a read-write-lock, simpler to
        use than glib's different rw_lock_reader_unlock, rw_lock_writer_unlock
        methods.
      - once-only: same features.
      - thread-local storage: glib has two TLS facilities: g_private_* and
        g_static_private_*. In the latter, the thread-local value setter
        supports an optional destroy function for the old value.
      - thread: glib also has priority control, iteration over all threads,
        the distinction between joinable and detached threads.
        Whereas gnulib has signal mask control on POSIX systems.
      - condition variable: same features.

Differences in the implementations for POSIX systems:

  - simple lock: glib and gnulib both use a pthread_mutex_t initialized with
    default attributes. glib introduces an extra indirection, for static
    initializers, whereas gnulib relies on the existence of
    PTHREAD_MUTEX_INITIALIZER.

  - read-write lock: glib implements its read-write lock as a structure
    containing 1 simple lock, 2 condition variables, and a couple of scalar
    fields. On platforms without pthread_rwlock_t gnulib does the same; on
    platforms with pthread_rwlock_t gnulib uses this type directly
    (optionally with extra fields for protecting the initialization when
    PTHREAD_RWLOCK_INITIALIZER does not exist).

  - recursive lock: glib implements its recursive lock as a structure
    containing a simple lock, an owner field, and a depth field. On platforms
    without recursive pthread_mutex_t (only old Solaris) gnulib does the same;
    on platforms with recursive pthread_mutex_t gnulib uses this type directly
    (optionally with extra fields for protecting the initialization when
    PTHREAD_RECURSIVE_MUTEX_INITIALIZER[_NP] does not exist).
    glib does not check against wraparound of the depth field; gnulib does.

  - once-only:
    glib implements its once-only control using a global mutex and a global
    condition variable. This is inefficient: It prevents two different
    once-only controls to be executed simultaneously in different threads.
    Whereas gnulib directly uses the pthread_once_t from the system.

  - thread-local storage: glib's g_static_private_* TLS consumes a single
    TLS key; it stores a pointer to a thread info structure in its value cell;
    this structure contains a pointer to an array of values; it manages a
    free-list of indices itself.
    glib's g_private_* TLS uses the system's TLS directly.
    gnulib uses the system's TLS directly.

  - thread: glib's thread data type is a pointer to a structure that wraps
    a pthread_t. This pointer is stored in TLS.
    glib's list of threads is managed as a linear list that causes scalability
    problems (due to linear traversal) during thread exit and join.
    gnulib uses a pthread_t directly, but assumes that pthread_equal is
    equivalent to ==.

  - condition variable: glib and gnulib both use the pthread_cond_t directly.


Differences in the implementation for Win32:

  - simple lock: glib and gnulib essentially use a CRITICAL_SECTION. So that
    it can be used in static initializers, glib introduces an extra
    indirection, whereas gnulib adds a spinlock.

  - read-write lock: glib implements its read-write lock as a structure
    containing 1 simple lock, 2 condition variables, and a couple of scalar
    fields. gnulib does it similarly, with self-made waitqueues instead of
    condition variables.

  - recursive lock: glib implements its recursive lock as a structure
    containing a simple lock, an owner field, and a depth field. gnulib
    does the same.
    glib does not check against wraparound of the depth field; gnulib does.

  - once-only:
    glib implements its once-only control using a global mutex and a global
    condition variable. This is inefficient: It prevents two different
    once-only controls to be executed simultaneously in different threads.
    Whereas gnulib directly uses essentially only a CRITICAL_SECTION from
    the system.

  - thread-local storage: glib's g_static_private_* TLS consumes a single
    TLS key; it stores a pointer to a thread info structure in its value cell;
    this structure contains a pointer to an array of values; it manages a
    free-list of indices itself.
    glib's g_private_* TLS consumes a single TLS key; it stores an array with
    room for 100 pointers in its value cell.
    gnulib uses the system's TLS directly.

  - thread: glib's and gnulib's thread data type is a pointer to a structure
    that wraps a HANDLE to the thread. This pointer is stored in TLS.
    glib's list of threads is managed as a linear list that causes scalability
    problems (due to linear traversal) during thread exit and join.
    At thread creation, glib forces a context switch from the child thread
    to the parent thread and back to the child thread, in order to avoid a
    race condition (namely, what happens if the child thread calls 
thread_self(),
    and gets joined by a third thread, before the parent thread had a chance
    to store the thread handle in the structure). Additionally, glib serializes
    thread creations, so that thread creation becomes a scalability bottleneck.
    gnulib avoids the race condition without forcing a context switch.

  - condition variable: glib's condition variable type consists of an array
    of event objects, one for each waiting thread, and a lock protecting these.
    In the timedwait operation, integer overflow can happen in the timeout
    conversion. There is also a scalability problem: The signaling operation,
    signaling a single thread, is an O(n) operation, where n is the number of
    waiting threads.
    gnulib's condition variable type consists of a single event object, a few
    counters, and a lock protecting these. The scalability problems are even
    worse: The signaling operation actually wakes up all waiting threads,
    and all but one will go waiting again.

Bruno





reply via email to

[Prev in Thread] Current Thread [Next in Thread]