guile-devel
[Top][All Lists]
Advanced

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

Re: About removing the low level thread API


From: Mikael Djurfeldt
Subject: Re: About removing the low level thread API
Date: Thu, 21 Apr 2005 00:35:33 +0200

This is a copy of the file thread-interface.text in the workbook
module.  I will go to Graz tomorrow but if there are any follow-ups on
this post I will try to give some limited replies next week.
[Marius, I apologize for not answering another message from you--will
do that next week as well.]
---
Q1. How are we going to choose whether to use threads and which thread
    package to use?  Compile-time, link-time or run-time?

Q2. Should Guile have a "plug-in" thread interface a la glib or should
    we have multiple versions of libguile installed on the system?

Q3. Should Guile have a C level thread API?  (The present guile-devel
    news-thread arrived at the answer "no", but in my opinion the
    issue needs further thought.)

Q4. How should the C level API be implemented?  Scheme level
    implemented on top of C level or the reverse?

----------------------------------------------------------------------

Q1. How are we going to choose whether to use threads and which thread
    package to use?  Compile-time, link-time or run-time?

Currently, we make a compile-time choice of one of three thread
packages:

  I. null-threads (on systems which don't support threads or where the
     sysadmin don't want threads in Guile)

 II. coop-threads (a qthreads based cooperative thread package)

III. coop-pthreads (a pthreads based cooperative thread package)

Is it right to make this choice at libguile compile-time or should it
be at application link time or even run-time?  If run-time, should
individual Guile modules be able to independently choose which thread
library to use?

Our answer should be arrived at under consideration of our vision for
future development, which Marius and I both think is full concurrency
using pthreads.  We basically have the Stalinist view that it would be
good for the whole world to use the pthreads interface (arguments
similar to those for using libc: portability, the possibility to get
kernel-level optimizations etc).  So, it might be that we in the
future drop II and III in favour of (or keep II and III and add):

 IV. pthreads (a preemptive thread package)

Arguments for/against compile-time choice:

+ Simple.

+ Enables compile-time optimizations.  In a pthreads version, we could
  remove logic required for cooperative threading while cooperative
  thread libraries don't require mutecis for protection of common
  resources.

- The sysadmin and application developer might be different people.
  If the sysadmin has installed Guile without thread support, an
  application developer (or user for that matter!) will be in deep
  trouble if the application needs thread support.  This situation is
  unreasonable but could be resolved if we install multiple versions
  of libguile, one for each thread package.

Arguments for link-time choice:

(Link-time in this context means deciding which thread package to use
once, at initialization of libguile.)

+ Moves the choice of thread library to the application developer
  where it should be.

+ Reasonably simple.  Additional requirements are only:

   1. a plug-in interface (a struct with pointers to the thread
      library functions)

   2. glue code

Arguments against run-time choice:

- Unreasonably complex.

- Would require multiple types of mutecis and condition variables or
  overly complex united forms.  In this way the complexity intrudes
  also semantically.

I think both Marius and I agree that the only reasonable answers to Q1
are:

* link-time choice with one libguile

or

* compile-time choice with multiple libguiles (libguile,
  libguile-coop, libguile-copt etc)

This results in Q2:

----------------------------------------------------------------------

Q2. Should Guile have a "plug-in" thread interface a la glib or should
    we have multiple versions of libguile installed on the system?

An example of such a plug-in interface is included below.

Arguments for/against plug-in interface:

+ Gives true modularity by decoupling libguile from the thread
  library.

+ Gives the application developer the freedom to use other thread
  packages with Guile, including experimenting with new thread
  libraries.

  Apart from choosing between COOP threads, COOP-pthreads and
  pthreads, there might be other thread packages out there which the
  application developer might prefer.  For example, the Debian
  distribution provides State threads (libst1), LWP threads
  (liblwp2) and GNU Portable Threads (libpth; a cooperative thread
  library).

  The reason why this is a significant point is that it can be
  difficult to make different thread libraries play well together in
  the same application.  If Guile uses X-threads, the application
  usually runs into trouble if it uses Y-threads.

- (Probably insignificant) overhead due to one pointer indirection.

- Puts a restriction on the thread library capabilities that we can
  use.  (We can't continue to extend this API ad infinitum but must
  decide on a basic set of calls that we think most thread libraries
  can implement.  But we may also use the strategy of GLib: that some
  of the calls are optional and can be initialized to NULL.)

- Requires dynamic linking with an additional glue-code library if the
  thread package isn't directly written for Guile.  Such a glue-code
  library initializes the plug-in interface and provides possibly
  required extra support code.

Arguments for/against multiple versions of libguile:

+ Allows for compile-time optimizations (see Q1: compile-time choice).

- Risk for combinatorial explosion of libguile versions due to
  compilation options.

- *** Risk for combinatorial explosion of libguile support library
  versions.

  Note that we can't just draw the distinction between libguile on one
  hand which supports the application on the other hand.  We also have
  libraries like libguile-gtk which have the double role of being
  application of libguile and providing application framework for user
  applications.  While having multiple version of libguile may be
  reasonable, having, in addition, multiple versions of libguile-gtk
  et al is unreasonable.

  A partial solution is to introduce an abstraction which separates
  guile code + semi-guile code like libguile-gtk from the particular
  thread library being used so that, for example, a single
  libguile-gtk can support all versions of libguile.

  The Scheme-level thread interface provides such an abstraction so
  that Guile scheme modules are independent of the particular thread
  packages being used.  The subset of primitive functions in this
  interface can provide a C level abstraction for loadable C-level
  Guile extensions, but we could also provide a C level thread API.
  In fact, such a C level thread API has been in place since
  1998-01-30 (guile-1.2) but has just been deprecated.

  This brings us to Q3:

----------------------------------------------------------------------

Q3. Should Guile have a C level thread API?

While an application should be free to use whatever thread package it
wants by direct calls to its API, points discussed above shows that
Guile Scheme and C modules should use an abstraction in order be
independent of the thread package being used.  The C level could
either use the Scheme primitives, like this:

   SCM my_mutex = scm_make_mutex ();
   scm_lock_mutex (my_mutex);

However, for thread creation, we would still probably want to use
scm_spawn_thread, so this alterntive is rather a _partial_ C level
thread API.

In a full C level thread API, one would be able to:

   scm_t_mutex my_mutex;
   scm_mutex_init (&my_mutex);
   scm_mutex_lock (&my_mutex);

(Although, as Marius has pointed out, scm_t_mutex is a bit rigid,
since it requires knowledge of mutex size at compile-time.  In the
Glib thread interface and in the plug-in interface proposed below,
scm_mutex_init is replaced with a mutex constructor function.)

Arguments for/against C level thread API:

+ Easy programming in C.

  A major goal of Guile is to be an extension language for
  applications written in C.  Because of this, we try to provide a
  nice environment not only for Scheme but also for C programmers.

  If we do provide a C API, it should be similar to the pthread API.
  In this case, the C programmer will feel very familiar with the
  interface and it's easy to convert code written for the pthread
  API.

  If we don't provide a C API, the code needs to deal with GC issues.
  For example, it is fairly common to store mutecis in dynamic
  application datastructures.  If we go with Scheme API only, the
  application needs to make sure the mutex is GC protected.

+ Efficiency.

  In some cases, a thread package needs to be very efficient.  Thread
  package developers make great efforts to reduce overhead.  Even the
  need to switch from user space to system space is sometimes
  considered as causing significant overhead, so there are alternative
  thread libraries which run entirely in user space.

  For such applications where this matters, it seems wasteful to go by
  the indirections of the SCM data type and do unnecessary type checks
  when calling Scheme primitives from C.  Note that the C level
  interface is not only intended for the direct cooperation with
  Scheme code, in which overhead may be of less significance, but is
  also intended for time-critical C code in a loadable Guile extension
  module.

  (This point is only valid together with the Scheme on top of C
   alternative in Q4 below.)

+ Deprecation will break applications.

  The current C level thread API has been in place since 1998-01-30
  (guile-1.2) but has just been deprecated.

  Maybe only I have been using this API in several of my applications,
  but it doesn't seem nice towards application developers to remove
  such a useful feature when there is no simple replacement,
  especially when it's been in place for so long.

- Demands extra work for Guile developers to maintain it.

(Missing some - points?  They might rather belong to Q2 or Q4 and
 could be listen there.)

----------------------------------------------------------------------

Q4. How should the C level API be implemented?  Scheme level
    implemented on top of C level or reverse?

Scheme level on top of C level means that Scheme primitives call the C
level API for core thread functionality.

C level on top of Scheme level means that the Scheme primitives
implement the core thread library functionality directly and that the
C level interface is an adapter on top of this to facilitate coding
for C programmers.  (An example is provided by the currently
deprecated C level API for coop-pthreads.)

Just as regarding Q3, this may be a matter of coding philosophy:

Either one aims for a slim C level solution or one wants to take
advantage of the advanced facilities of a dynamically typed language
and is prepared to pay for it.

Arguments for Scheme level implemented on top of C level:

+ More "natural"

  Results in a natural layering of the code.  The reverse will, in
  fact, be C - Scheme - C.

Arguments for C level implemented on top of Scheme level:

+ Easier to use Scheme level facilities when implementing the thread
  packages.  For example, we might decide, at a later stage, to add
  some debugging facilities to the thread support.  We might prefer to
  write this in Scheme code.  With C implemented on top of Scheme
  level this seems easier than for the reverse.  Also, in implementing
  certain thread functionality, we might want to use Scheme facilities
  such as SCM lists.  If we have an SCM list in a mutex, how will it
  be GC protected if the mutex resides under the C API?

- We won't want to do that anyway in the envisioned pthreads
  implementation

  Since we're envisioning Guile using pthreads with full concurrency
  as a future standard thread package for Guile, it is likely that
  we won't need to implement much core thread functionality.  It's
  more likely that we just want to wrap pthreads as Scheme primitives
  which seems to harmonize better with the Scheme on top of C
  alternatives.

- Overhead

  Overhead problems will be even greater than those we'd have if we
  don't provide a C API at all.

----------------------------------------------------------------------

OK, was it difficult to figure out what I want to see in Guile? ;-)

If so, I want:

* A plug-in interface which decouples libguile from the thread
  library and allows the application developer to choose which thread
  library to use with Guile.  Preferably it should support both
  cooperative and preemptive scheduling.

* An efficient and pthreads-like C level API.  The plug-in interface
  could serve this role in addition to the decoupling role.

* Scheme primitives implemented using the low-level C API.

If you've read this far you have my respect!

Good luck with decisions and implementation,

Mikael

----------------------------------------------------------------------

Proposed plug-in interface structure:

struct scm_threads {
  /* Threads */
  scm_thread_t (*make_thread) (scm_threadattr_t *attr,
                               void * (*start_routine) (void *),
                               void *arg);
  void (*exit) (void *retval);
  int (*cancel) (scm_thread_t thread);
  int (*join) (scm_thread_t thread, void **retval);
  size_t (*free) (scm_thread_t thread); /* returns freed amount of memory */
  
  /* Cooperative threads */
  void (*yield) (void); /* optional */
  
  /* Mutices */
  scm_mutex_t * (*make_mutex) (const scm_mutexattr_t *mutexattr);
  int (*mutex_lock) (scm_mutex_t *mutex);
  int (*mutex_trylock) (scm_mutex_t *mutex);
  int (*mutex_unlock) (scm_mutex_t *mutex);
  size_t (*mutex_free) (scm_mutex_t *mutex); /* returns freed amount
of memory */
  
  /* Condition variables */
  scm_cond_t * (*make_cond) (scm_condattr_t *cond_attr);
  int (*cond_signal) (scm_cond_t *cond);
  int (*cond_broadcast) (scm_cond_t *cond);
  int (*cond_wait) (scm_cond_t *cond, scm_mutex_t *mutex);
  int (*cond_timedwait) (scm_cond_t *cond,
                         scm_mutex_t *mutex,
                         const struct timespec *abstime);
  size_t (*cond_free) (scm_cond_t *cond); /* returns freed amount of memory */
  
  /* Keys */
  int (*key_create) (scm_key_t *key, void (*destr_function) (void *));
  int (*key_delete) (scm_key_t key);
  int (*setspecific) (scm_key_t key, const void *pointer);
  void * (*getspecific) (scm_key_t key);

  /* Local data
     
     This is used by Guile to store the root structure.
     It could be a custom data field in the thread data structure
     or a dedicated key.
  */
  void (*set_data) (void *data);
  void * (*data) (void);
  
  /* GC */
  void (*mark_stacks) (void);
  
  /* Parameters */
  void (*set_stack_size) (size_t size);
};




reply via email to

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