Subject: Re: contribution soapbox(was Re: G++ guru's please comment - Re: FW: pthread_create problem in Cygwin 1.1.8-2])
From: René Møller Fonseca <[email-masked]>
To: Robert Collins <[email-masked]>
Cc: [email-masked]
Date: Mon, 09 Apr 2001 17:24:13 +0200

Hi Robert,

The tricky part is to make pthread_once MT-safe without doing a busy
wait.

Your code is not MT-safe; more than one thread can enter init_routine.

I'm not familiar with the requirements of pthread_once but I'm expecting
the following to be true:

1. Only one thread may enter init_routine (otherwise the "once" suffix
doesn't make any sense).

2. if init_routine is currently being run by some thread then all other
threads must block until the function has completed.

Here is my implementation of pthread_once using busy waiting. If
init_routine takes a long time then busy waiting becomes very bad :-(

>>>>
typedef struct {unsigned int state; unsigned int lock;} pthread_once_t;

#define PTHREAD_ONCE_INIT {0, 0}

inline unsigned int pthread_once_try_lock(pthread_once_t* once_control)
{
  register unsigned int previous;
  asm volatile ("xchgl %0, %1" : "=&r" (previous), "=m"
(once_control->lock) : "0" (1));
  return !previous;
}

int __pthread_once(pthread_once_t* once_control, void (*init_routine)
(void)) {
  if (!once_control->state) { // should init_routine possible be called
by this thread
    while (!pthread_once_try_lock(once_control)); // wait for exclusive
lock
      // only one thread can be here at any time
      if (!once_control->state) { // should this thread run init_routine
        init_routine();
        once_control->state = 1; // make sure init_routine is not called
again
      }
    once_control->lock = 0; // release lock
  }
  return 0;
}
<<<<

Warning: I have not checked and optimized it thoroughly.

P.S. I have put the code in the attachment!

René

Robert Collins wrote:
> 
> Rene, this isn't pointed at you! I just reread my reply and I realised
> I'd missed an IMO important point.
> 
> ----- Original Message -----
> From: "René Møller Fonseca" <[email-masked]>
> 
> > Please correct me if I'm wrong.
> >
> > The problem, as I understand it, is that gcc is not built with thread
> > support which is required for exception handling to work in a
> > multithreaded environment. Unfortunately the thread support has not
> been
> > ported to the win32 api. I suspect functionality like "pthread_once"
> to
> > be required (tricky to implement).
> 
> Guys, Girls, secret service agents,
> 
> Contributing to cygwin is not hard. In fact it's dead easy. The biggest
> obstacle is perception. All you need are basic C++ (even straight C will
> do if you're a hacker or just willing to try new things) skills, and a
> small target.
> 
> (Non-coders, you are hereby off the coding hook, and onto the why don't
> you contribute your knowledge in the form of feedback on documentation -
> something every open source project can use more of).
> 
> I strongly suspect that most of you coders out there are guilty of the
> thought "Gee it would be nice if cygwin had feature foo, but I [wouldn't
> know where to start to fix it|I don't have the time to contribute] so
> I'll just spend hours now working around the lack of foo".
> 
> I am guilty of that thought. Then one day I got tired of the the fact
> that I couldn't built squid with thread support, so I ported the thread
> code to win32 threads. During that port I realised just how thin a layer
> cygwin is over win32 - it's hardly there at all. So I threw out the
> win32 threads code, and fixed up what was missing in Cygwin. That core
> fixup took me about twice as long as porting the original code, but I
> can reuse the fixedup cygwin code to port other applications with
> greater ease.
> 
> The follow-up to that spurt of coding was that I got tired of the
> lamentations about threads and cygwin and started fixing the entire
> thread support... In fact I've now been labelled the pthreads
> maintainer, which as far as I can tell means that you lot are now
> *allowed* to complain about pthreads!
> 
> I want to ask you all to do something for cygwin (and I have no right to
> ask this, but I'm going to anyway): The next time you work around a bug
> in cygwin when porting an application, put aside 2 hours (thats all
> you'll need for most things). Download the cygwin source via CVS. (The
> documentation is accurate). Build it. Add your feature, or a tweak to an
> existing feature, or even just the outline code to return ENOSYS - no
> supported for the relevant function call. Send that in with a Changelog.
> You've now become a contributor, and saved yourself sending in a patch
> to the application developers for cygwin compatability, all by changing
> cygwin. And in future, chances are someone else will add to what you've
> done, and finish off your partial code, or tweak it even more.
> 
> Rob.
> 
> P.S. To support my claim about perception being the issue, here is the
> code for the diffucult function pthread_once.
> 
> int
> __pthread_once (pthread_once_t * once_control, void (*init_routine)
> (void))
> {
>   if (*once_control!=PTHREAD_ONCE_INIT)
>     init_routine();
>   *once_control=!PTHREAD_ONCE_INIT;
>   return 0;
> }
> 
> And most of the cygwin internal functions are not much longer.
> 
> --
> Want to unsubscribe from this list?
> Check out: http://cygwin.com/ml/#unsubscribe-simple

#include <stdio.h>

typedef struct {unsigned int state; unsigned int lock;} pthread_once_t;

#define PTHREAD_ONCE_INIT {0, 0}

inline unsigned int pthread_once_try_lock(pthread_once_t* once_control) {
  register unsigned int previous;
  asm volatile ("xchgl %0, %1" : "=&r" (previous), "=m" (once_control->lock) : "0" (1));
  return !previous;
}

int __pthread_once(pthread_once_t* once_control, void (*init_routine) (void)) {
  if (!once_control->state) { // should init_routine possible be called by this thread

    while (!pthread_once_try_lock(once_control)); // wait for exclusive lock
      // only one thread can be here at any time
      if (!once_control->state) { // should this thread run init_routine
        printf("D: state=%d lock=%d\n", once_control->state, once_control->lock);
        init_routine();
        once_control->state = 1; // make sure init_routine is not called again
      }
    once_control->lock = 0; // release lock
  }
  return 0;
}

void myfunction() {
  printf("I'm only executed once\n");
}

int main() {
  pthread_once_t once= PTHREAD_ONCE_INIT;
  printf("A: state=%d lock=%d\n", once.state, once.lock);
  __pthread_once(&once, &myfunction);
  printf("B: state=%d lock=%d\n", once.state, once.lock);
  __pthread_once(&once, &myfunction);
  printf("C: state=%d lock=%d\n", once.state, once.lock);
  return 0;
}

--
Want to unsubscribe from this list?
Check out: http://cygwin.com/ml/#unsubscribe-simple