git-reply_bogus
authorGNU Libc Maintainers <debian-glibc@lists.debian.org>
Sat, 27 Aug 2022 11:38:11 +0000 (12:38 +0100)
committerAurelien Jarno <aurel32@debian.org>
Sat, 27 Aug 2022 11:38:11 +0000 (12:38 +0100)
committed for 2.35

commit 8c86ba446367fd676457e51eb44d7af2e5d9a392
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
Date:   Sat Jan 22 00:12:05 2022 +0000

    htl: Fix cleaning the reply port

    If any RPC fails, the reply port will already be deallocated.
    __pthread_thread_terminate thus has to defer taking its name until the very last
    __thread_terminate_release which doesn't reply a message.  But then we
    have to read from the pthread structure.

    This introduces __pthread_dealloc_finish() which does the recording of
    the thread termination, so the slot can be reused really only just before
    the __thread_terminate_release call. Only the real thread can set it, so
    let's decouple this from the pthread_state by just removing the
    PTHREAD_TERMINATED state and add a terminated field.

Gbp-Pq: Topic hurd-i386
Gbp-Pq: Name git-reply_bogus.diff

htl/pt-alloc.c
htl/pt-create.c
htl/pt-dealloc.c
htl/pt-detach.c
htl/pt-internal.h
htl/pt-join.c
sysdeps/mach/htl/pt-thread-terminate.c

index acc67f27119b3a1940bb2995879d9ffb2a89373d..e562efa5f897835696532cd31a0a194dfab2d029 100644 (file)
@@ -64,6 +64,7 @@ initialize_pthread (struct __pthread *new)
 
   new->state_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
   new->state_cond = (pthread_cond_t) PTHREAD_COND_INITIALIZER;
+  new->terminated = FALSE;
 
   memset (&new->res_state, '\0', sizeof (new->res_state));
 
@@ -94,10 +95,10 @@ __pthread_alloc (struct __pthread **pthread)
     {
       /* There is no need to take NEW->STATE_LOCK: if NEW is on this
          list, then it is protected by __PTHREAD_FREE_THREADS_LOCK
-         except in __pthread_dealloc where after it is added to the
+         except in __pthread_dealloc_finish where after it is added to the
          list (with the lock held), it drops the lock and then sets
          NEW->STATE and immediately stops using NEW.  */
-      if (new->state == PTHREAD_TERMINATED)
+      if (new->terminated)
        {
          __pthread_dequeue (new);
          break;
index b9cf2e1d60d42e1134bda860e56ba70d838cce47..a243fe8cc4258cb49573c8981ee601353c9ba78a 100644 (file)
@@ -256,7 +256,10 @@ __pthread_create_internal (struct __pthread **thread,
 failed_starting:
   /* If joinable, a reference was added for the caller.  */
   if (pthread->state == PTHREAD_JOINABLE)
-    __pthread_dealloc (pthread);
+    {
+      __pthread_dealloc (pthread);
+      __pthread_dealloc_finish (pthread);
+    }
 
   __pthread_setid (pthread->thread, NULL);
   atomic_decrement (&__pthread_total);
@@ -278,6 +281,7 @@ failed_thread_alloc:
                              / __vm_page_size) * __vm_page_size + stacksize);
 failed_stack_alloc:
   __pthread_dealloc (pthread);
+  __pthread_dealloc_finish (pthread);
 failed:
   return err;
 }
index 1de6a9ad2e18e980ce5d4f454ea5cfd0af914b00..0ce0c67a935140543028efe82131530576ffe86d 100644 (file)
@@ -29,12 +29,10 @@ extern struct __pthread *__pthread_free_threads;
 extern pthread_mutex_t __pthread_free_threads_lock;
 
 
-/* Deallocate the thread structure for PTHREAD.  */
+/* Deallocate the content of the thread structure for PTHREAD.  */
 void
 __pthread_dealloc (struct __pthread *pthread)
 {
-  assert (pthread->state != PTHREAD_TERMINATED);
-
   if (!atomic_decrement_and_test (&pthread->nr_refs))
     return;
 
@@ -56,13 +54,18 @@ __pthread_dealloc (struct __pthread *pthread)
   __pthread_mutex_lock (&__pthread_free_threads_lock);
   __pthread_enqueue (&__pthread_free_threads, pthread);
   __pthread_mutex_unlock (&__pthread_free_threads_lock);
+}
 
-  /* Setting PTHREAD->STATE to PTHREAD_TERMINATED makes this TCB
+/* Confirm deallocation of the thread structure for PTHREAD.  */
+void
+__pthread_dealloc_finish (struct __pthread *pthread)
+{
+  /* Setting PTHREAD->TERMINATED makes this TCB
      available for reuse.  After that point, we can no longer assume
      that PTHREAD is valid.
 
      Note that it is safe to not lock this update to PTHREAD->STATE:
      the only way that it can now be accessed is in __pthread_alloc,
      which reads this variable.  */
-  pthread->state = PTHREAD_TERMINATED;
+  pthread->terminated = TRUE;
 }
index 3a4a1c786fd269cea97005704d66ea5ffdf34abd..135830dc2795aef4d61b369a335436688d8b9b4d 100644 (file)
@@ -62,12 +62,6 @@ __pthread_detach (pthread_t thread)
       __pthread_dealloc (pthread);
       break;
 
-    case PTHREAD_TERMINATED:
-      /* Pretend THREAD wasn't there in the first place.  */
-      __pthread_mutex_unlock (&pthread->state_lock);
-      err = ESRCH;
-      break;
-
     default:
       /* Thou shalt not detach non-joinable threads!  */
       __pthread_mutex_unlock (&pthread->state_lock);
index 34e6da338e50f64e61ad6d83a86817eff17916b6..d72e57f2a4673b66f6b525b92c78bb33e2636665 100644 (file)
@@ -48,8 +48,6 @@ enum pthread_state
   PTHREAD_DETACHED,
   /* A joinable thread exited and its return code is available.  */
   PTHREAD_EXITED,
-  /* The thread structure is unallocated and available for reuse.  */
-  PTHREAD_TERMINATED
 };
 
 #ifndef PTHREAD_KEY_MEMBERS
@@ -95,6 +93,8 @@ struct __pthread
   enum pthread_state state;
   pthread_mutex_t state_lock;  /* Locks the state.  */
   pthread_cond_t state_cond;   /* Signalled when the state changes.  */
+  bool terminated;             /* Whether the kernel thread is over
+                                  and we can reuse this structure.  */
 
   /* Resolver state.  */
   struct __res_state res_state;
@@ -218,12 +218,18 @@ extern int __pthread_create_internal (struct __pthread **__restrict pthread,
    kernel thread or a stack).  THREAD has one reference.  */
 extern int __pthread_alloc (struct __pthread **thread);
 
-/* Deallocate the thread structure.  This is the dual of
+/* Deallocate the content of the thread structure.  This is the dual of
    __pthread_alloc (N.B. it does not call __pthread_stack_dealloc nor
-   __pthread_thread_terminate).  THREAD loses one reference and is
-   released if the reference counter drops to 0.  */
+   __pthread_thread_terminate).  THREAD loses one reference, and if
+   if the reference counter drops to 0 this returns 1, and the caller has
+   to call __pthread_dealloc_finish when it is really finished with using
+   THREAD.  */
 extern void __pthread_dealloc (struct __pthread *thread);
 
+/* Confirm deallocating the thread structure.  Before calling this
+   the structure will not be reused yet.  */
+extern void __pthread_dealloc_finish (struct __pthread *pthread);
+
 
 /* Allocate a stack of size STACKSIZE.  The stack base shall be
    returned in *STACKADDR.  */
index b3bd0cd3f9ec3463b36546964bda9b9c038e9f9d..86b7d41fbbdc975dc0908be31080860618ebceb0 100644 (file)
@@ -75,12 +75,6 @@ __pthread_join_common (pthread_t thread, void **status, int try,
       __pthread_dealloc (pthread);
       break;
 
-    case PTHREAD_TERMINATED:
-      /* Pretend THREAD wasn't there in the first place.  */
-      __pthread_mutex_unlock (&pthread->state_lock);
-      err = ESRCH;
-      break;
-
     default:
       /* Thou shalt not join non-joinable threads!  */
       __pthread_mutex_unlock (&pthread->state_lock);
index fbc7eb6919fe9b6d31e55a84badec7084e8e60ae..7b1aabb9dfb5b2dc09c4000dd6e97079d6e6c912 100644 (file)
@@ -35,6 +35,7 @@ __pthread_thread_terminate (struct __pthread *thread)
   void *stackaddr;
   size_t stacksize;
   error_t err;
+  int self;
 
   kernel_thread = thread->kernel_thread;
 
@@ -52,25 +53,32 @@ __pthread_thread_terminate (struct __pthread *thread)
 
   wakeup_port = thread->wakeupmsg.msgh_remote_port;
 
-  /* Each thread has its own reply port, allocated from MiG stub code calling
-     __mig_get_reply_port.  Destroying it is a bit tricky because the calls
-     involved are also RPCs, causing the creation of a new reply port if
-     currently null. The __thread_terminate_release call is actually a one way
-     simple routine designed not to require a reply port.  */
   self_ktid = __mach_thread_self ();
-  reply_port = (self_ktid == kernel_thread)
-      ? __mig_get_reply_port () : MACH_PORT_NULL;
+  self = self_ktid == kernel_thread;
   __mach_port_deallocate (__mach_task_self (), self_ktid);
 
   /* The kernel thread won't be there any more.  */
   thread->kernel_thread = MACH_PORT_DEAD;
 
-  /* Finally done with the thread structure.  */
+  /* Release thread resources.  */
   __pthread_dealloc (thread);
 
-  /* The wake up port is now no longer needed.  */
+  /* The wake up port (needed for locks in __pthread_dealloc) is now no longer
+     needed.  */
   __mach_port_destroy (__mach_task_self (), wakeup_port);
 
+  /* Each thread has its own reply port, allocated from MiG stub code calling
+     __mig_get_reply_port.  Destroying it is a bit tricky because the calls
+     involved are also RPCs, causing the creation of a new reply port if
+     currently null. The __thread_terminate_release call is actually a one way
+     simple routine designed not to require a reply port.  */
+  reply_port = self ? __mig_get_reply_port () : MACH_PORT_NULL;
+  /* From here we shall not use a MIG reply port any more.  */
+
+  /* Finally done with the thread structure (we still needed it to access the
+     reply port).  */
+  __pthread_dealloc_finish (thread);
+
   /* Terminate and release all that's left.  */
   err = __thread_terminate_release (kernel_thread, mach_task_self (),
                                    kernel_thread, reply_port,