public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
  • * Re: [PATCH v2] Cygwin: pthread: Fix a race issue introduced by the commit 2c5433e5da82
           [not found] <20240530050538.53724-1-takashi.yano@nifty.ne.jp>
           [not found] ` <0126a98a-3bdc-4303-a0d2-09f4c7009392@gmail.com>
    @ 2024-05-30 10:14 ` Bruno Haible
           [not found] ` <20240530205012.2aff4d507acac144e50df2a4@nifty.ne.jp>
      2 siblings, 0 replies; 13+ messages in thread
    From: Bruno Haible @ 2024-05-30 10:14 UTC (permalink / raw)
      To: cygwin, Takashi Yano
    
    Takashi Yano wrote in cygwin-patches:
    >  int
    >  pthread::once (pthread_once_t *once_control, void (*init_routine) (void))
    >  {
    > -  // already done ?
    > -  if (once_control->state)
    > +  /* Sign bit of once_control->state is used as done flag */
    > +  if (once_control->state & INT_MIN)
    >      return 0;
    >  
    > +  /* The type of &once_control->state is int *, which is compatible with
    > +     LONG * (the type of the first argument of InterlockedIncrement()). */
    > +  InterlockedIncrement (&once_control->state);
    >    pthread_mutex_lock (&once_control->mutex);
    > -  /* Here we must set a cancellation handler to unlock the mutex if needed */
    > -  /* but a cancellation handler is not the right thing. We need this in the thread
    > -   *cleanup routine. Assumption: a thread can only be in one pthread_once routine
    > -   *at a time. Stote a mutex_t *in the pthread_structure. if that's non null unlock
    > -   *on pthread_exit ();
    > -   */
    
    Sorry, in a unified diff form this is unreadable. One needs to look at the
    entire function. A context diff would have been better. So:
    
    int
    pthread::once (pthread_once_t *once_control, void (*init_routine) (void))
    {
      /* Sign bit of once_control->state is used as done flag */
      if (once_control->state & INT_MIN)
        return 0;
    
      /* The type of &once_control->state is int *, which is compatible with
         LONG * (the type of the first argument of InterlockedIncrement()). */
      InterlockedIncrement (&once_control->state);
      pthread_mutex_lock (&once_control->mutex);
      if (!(once_control->state & INT_MIN))
        {
          init_routine ();
          once_control->state |= INT_MIN;
        }
      pthread_mutex_unlock (&once_control->mutex);
      if (InterlockedDecrement (&once_control->state) == INT_MIN)
        pthread_mutex_destroy (&once_control->mutex);
      return 0;
    }
    
    1) The overall logic seems right (using bit 31 of the state word as a
    'done' bit), and the last thread that used the mutex destroys it.
    
    2) However, the 'state' field is not volatile, and therefore the memory
    model does not guarantee that an assignment
    
         once_control->state |= INT_MIN;
    
    done in one thread has an effect on the values seen by other threads
    that do
    
         if (once_control->state & INT_MIN)
    
    As long as there is no synchronization between the two CPUs that execute
    the two threads, one CPU may indefinitely see the old value of
    once_control->state, while the other CPU's new value is not being
    "broadcasted" to all CPUs.
    
    3) Also, as noted by Noel Grandin, there is a race: If one thread does
    
         once_control->state |= INT_MIN;
    
    while another thread does
    
         InterlockedIncrement (&once_control->state);
    or
         InterlockedDecrement (&once_control->state)
    
    the result can be that the increment or decrement is nullified. If it's
    an increment that gets nullified, the pthread_mutex_destroy occurs too
    early, with fatal consequences. If it's a decrement that get nullified,
    the pthread_mutex_destroy never happens, and there is therefore a resource
    leak.
    
    4) Does the test program that I posted in
    <https://cygwin.com/pipermail/cygwin/2024-May/255987.html> now pass?
    Notes:
      - You should set
          #define ENABLE_DEBUGGING 0
        because with the debugging output, it nearly always succeeds.
      - You should run it 10 times in a row, not just once. It may well
        succeed 9 out of 10 times and fail 1 out of 10 times.
    
    Bruno
    
    
    
    
    ^ permalink raw reply	[flat|nested] 13+ messages in thread
  • [parent not found: <20240530205012.2aff4d507acac144e50df2a4@nifty.ne.jp>]

  • end of thread, other threads:[~2024-06-02 14:27 UTC | newest]
    
    Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
    -- links below jump to the message on this page --
         [not found] <20240530050538.53724-1-takashi.yano@nifty.ne.jp>
         [not found] ` <0126a98a-3bdc-4303-a0d2-09f4c7009392@gmail.com>
    2024-05-30  8:47   ` [PATCH v2] Cygwin: pthread: Fix a race issue introduced by the commit 2c5433e5da82 Bruno Haible
    2024-05-30  8:59     ` Noel Grandin
    2024-05-30  9:15       ` Bruno Haible
    2024-05-30 10:28         ` Noel Grandin
    2024-05-30 13:53           ` SRW locks Bruno Haible
    2024-05-30 10:14 ` [PATCH v2] Cygwin: pthread: Fix a race issue introduced by the commit 2c5433e5da82 Bruno Haible
         [not found] ` <20240530205012.2aff4d507acac144e50df2a4@nifty.ne.jp>
         [not found]   ` <20240530205918.7c730117b567bb3bec3a0c3f@nifty.ne.jp>
    2024-05-30 13:38     ` Bruno Haible
    2024-05-31 14:01     ` Bruno Haible
    2024-06-01 14:18       ` Takashi Yano
    2024-06-01 16:08         ` Ken Brown
    2024-06-01 21:11           ` Takashi Yano
    2024-06-02 13:14         ` Bruno Haible
    2024-06-02 14:27           ` Takashi Yano
    

    This is a public inbox, see mirroring instructions
    for how to clone and mirror all data and code used for this inbox;
    as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).