* ports.c (scm_port_for_each): Make a snapshot of the port table
authorMarius Vollmer <mvo@zagadka.de>
Thu, 8 Feb 2001 18:44:34 +0000 (18:44 +0000)
committerMarius Vollmer <mvo@zagadka.de>
Thu, 8 Feb 2001 18:44:34 +0000 (18:44 +0000)
before iterating over it.  The table might change while the user
code is running.  With the snapshot, the user can depend on the
fact that each port that was open at teh start of the iteration is
encountered exactly once.  (ice-9 popen) depends on this.

libguile/ports.c

index a840ff5..391981b 100644 (file)
@@ -697,15 +697,32 @@ SCM_DEFINE (scm_port_for_each, "port-for-each", 1, 0, 0,
 #define FUNC_NAME s_scm_port_for_each
 {
   int i;
+  SCM ports;
+
   SCM_VALIDATE_PROC (1, proc);
 
   /* when pre-emptive multithreading is supported, access to the port
      table will need to be controlled by a mutex.  */
+
+  /* Even without pre-emptive multithreading, running arbitrary code
+     while scanning the port table is unsafe because the port table
+     can change arbitrarily (from a GC, for example).  So we build a
+     list in advance while blocking the GC. -mvo */
+
   SCM_DEFER_INTS;
+  scm_block_gc++;
+  ports = SCM_EOL;
   for (i = 0; i < scm_port_table_size; i++)
+    ports = scm_cons (scm_port_table[i]->port, ports);
+  scm_block_gc--;
+  SCM_ALLOW_INTS;
+
+  while (ports != SCM_EOL)
     {
-      scm_apply (proc, scm_cons (scm_port_table[i]->port, SCM_EOL), SCM_EOL);
+      scm_apply (proc, scm_cons (SCM_CAR (ports), SCM_EOL), SCM_EOL);
+      ports = SCM_CDR (ports);
     }
+
   return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME