From 8ebd2410b0a9d2f4383f51ea2164adf5e50163c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20Grubbstr=C3=B6m=20=28Grubba=29?=
 <grubba@grubba.org>
Date: Thu, 25 Feb 2016 20:01:04 +0100
Subject: [PATCH] Runtime: Potential race condition fix.

Wait for the compiler to complete before complaining about
attempts to clone unfinished programs.

Thanks to Jeff Hungerford <hungerf3-roxen3@house.ofdoom.com>
for the report.

Potential fix for [Pike mailinglist 14495]/[LysLysKOM 21645192]
---
 src/object.c | 51 +++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 43 insertions(+), 8 deletions(-)

diff --git a/src/object.c b/src/object.c
index 98e9f6ce33..76d30b88a5 100644
--- a/src/object.c
+++ b/src/object.c
@@ -118,8 +118,24 @@ PMOD_EXPORT struct object *low_clone(struct program *p)
 {
   struct object *o;
 
-  if(!(p->flags & PROGRAM_PASS_1_DONE))
-    Pike_error("Attempting to clone an unfinished program\n");
+  if(!(p->flags & PROGRAM_PASS_1_DONE)) {
+    /* It might be compiling in a different thread.
+     * Wait for the compiler to finish.
+     * Note that this does not block if we are the compiler thread.
+     */
+    lock_pike_compiler();
+    /* We now have the compiler lock.
+     * Three posibilities:
+     *  * The compiler was busy compiling the program in
+     *    a different thread and has now finished.
+     *  * We are the thread that is compiling the program.
+     *  * The program is a remnant from a failed compilation.
+     */
+    unlock_pike_compiler();
+    if(!(p->flags & PROGRAM_PASS_1_DONE)) {
+      Pike_error("Attempting to clone an unfinished program\n");
+    }
+  }
 
 #ifdef PROFILING
   p->num_clones++;
@@ -344,12 +360,34 @@ PMOD_EXPORT struct object *debug_clone_object(struct program *p, int args)
 {
   ONERROR tmp;
   struct object *o;
+
+  /* NB: The following test is somewhat redundant, since it is
+   *     also performed by low_clone() below. The problem is
+   *     that the PROGRAM_NEEDS_PARENT check inbetween needs
+   *     this check to have been performed before.
+   */
+  if(!(p->flags & PROGRAM_PASS_1_DONE)) {
+    /* It might be compiling in a different thread.
+     * Wait for the compiler to finish.
+     * Note that this does not block if we are the compiler thread.
+     */
+    lock_pike_compiler();
+    /* We now have the compiler lock.
+     * Three posibilities:
+     *  * The compiler was busy compiling the program in
+     *    a different thread and has now finished.
+     *  * We are the thread that is compiling the program.
+     *  * The program is a remnant from a failed compilation.
+     */
+    unlock_pike_compiler();
+    if(!(p->flags & PROGRAM_PASS_1_DONE)) {
+      Pike_error("Attempting to clone an unfinished program\n");
+    }
+  }
+
   if(p->flags & PROGRAM_NEEDS_PARENT)
     Pike_error("Parent lost, cannot clone program.\n");
 
-  if(!(p->flags & PROGRAM_PASS_1_DONE))
-    Pike_error("Attempting to clone an unfinished program\n");
-
   o=low_clone(p);
   if (!args) {
     push_object(o);
@@ -400,9 +438,6 @@ PMOD_EXPORT struct object *parent_clone_object(struct program *p,
     debug_malloc_touch(o);
   }
 
-  if(!(p->flags & PROGRAM_PASS_1_DONE))
-    Pike_error("Attempting to clone an unfinished program\n");
-
   if(p->flags & PROGRAM_USES_PARENT)
   {
     add_ref( PARENT_INFO(o)->parent=parent );
-- 
GitLab