Using forx in parallel mode with a large number of arguments can lead to
all instances not being able to finish their jobs due to resource
constrains. With an option to specify a max amount of parallel instances
this can be handled properly.

Signed-off-by: Sertonix <serto...@posteo.net>

---
 doc/forx.html       |  5 ++++-
 src/execline/forx.c | 34 ++++++++++++++++++++++++----------
 2 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/doc/forx.html b/doc/forx.html
index 8f1d558..0dc43c2 100644
--- a/doc/forx.html
+++ b/doc/forx.html
@@ -29,7 +29,7 @@
 </p>
 
 <pre>
-     forx [ -E | -e ] [ -p ] [ -o <em>okcodes</em> | -x <em>breakcodes</em> ] 
<em>variable</em> { <em>args...</em> } <em>loop...</em>
+     forx [ -E | -e ] [ -p | -P <em>maxpar</em> ] [ -o <em>okcodes</em> | -x 
<em>breakcodes</em> ] <em>variable</em> { <em>args...</em> } <em>loop...</em>
 </pre>
 
 <ul>
@@ -63,6 +63,9 @@ will exit 0 if all of the exit codes are in the values listed 
in the <em>okcodes
 list, else it will exit 1. If the <tt>-x</tt> option has been given,
 <tt>forx</tt> will exit 0 if none of the exit codes are in the values
 listed in the <em>breakcodes</em> list, else it will exit 1. </li>
+ <li> <tt>-P</tt>&nbsp;: like <tt>-p</tt> but run up to <em>maxpar</em> 
instances
+in parallel. 0 means unlimited. When <em>maxpar</em> is not 0 <em>forx</em>
+may not spawn all instances. </li>
  <li> <tt>-e</tt>&nbsp;: no autoimport. This is the default. </li>
  <li> <tt>-E</tt>&nbsp;: autoimport. Instead of spawning
 <em>loop...</em>, spawn <tt>importas -ui <em>variable</em> <em>variable</em>
diff --git a/src/execline/forx.c b/src/execline/forx.c
index 1695c00..3f7b9c2 100644
--- a/src/execline/forx.c
+++ b/src/execline/forx.c
@@ -23,10 +23,10 @@ static int fx_isok (unsigned short const *tab, unsigned int 
n, int code)
   return i < n ;
 }
 
-static int waitn_code (unsigned short const *tab, unsigned int nbc, pid_t 
*pids, unsigned int n, int not)
+static int waitn_code (unsigned short const *tab, unsigned int nbc, pid_t 
*pids, unsigned int n, int not, int once)
 {
   int ok = 1 ;
-  while (n)
+  do
   {
     int wstat ;
     unsigned int i = 0 ;
@@ -39,6 +39,7 @@ static int waitn_code (unsigned short const *tab, unsigned 
int nbc, pid_t *pids,
       pids[i] = pids[--n] ;
     }
   }
+  while (n && !once) ;
   return ok ;
 }
 
@@ -47,18 +48,19 @@ int main (int argc, char const **argv)
   char const *var ;
   unsigned short okcodes[256] ;
   size_t nbc = 0 ;
-  int flagpar = 0, not = 1, doimport = 0 ;
-  unsigned int argc1 ;
+  int not = 1, doimport = 0 ;
+  unsigned int argc1, maxpar = 1 ;
   PROG = "forx" ;
   {
     subgetopt l = SUBGETOPT_ZERO ;
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "po:x:Ee", &l) ;
+      int opt = subgetopt_r(argc, argv, "pP:o:x:Ee", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
-        case 'p' : flagpar = 1 ; break ;
+        case 'p' : maxpar = 0 ; break ;
+        case 'P' : if (!uint0_scan(l.arg, &maxpar)) dieusage() ; break ;
         case 'o' :
           not = 0 ;
           if (!ushort_scanlist(okcodes, 256, l.arg, &nbc)) dieusage() ;
@@ -81,14 +83,26 @@ int main (int argc, char const **argv)
   argc1 = el_semicolon(argv) ;
   if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
   if (!argc1 || (argc1 + 1 == argc)) return 0 ;
+  if (!maxpar || maxpar > argc1) maxpar = argc1;
 
   {
-    pid_t pids[flagpar ? argc1 : 1] ;
+    pid_t pids[maxpar] ;
+    int r = 0 ;
     for (unsigned int i = 0 ; i < argc1 ; i++)
     {
       pid_t pid = el_modif_and_spawn(argv + argc1 + 1, var, argv[i], doimport) 
;
       if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1+1]) ;
-      if (flagpar) pids[i] = pid ;
+      if (maxpar != 1) {
+        if (i < maxpar-1)
+          pids[i] = pid ;
+        else
+        {
+          r = waitn_code(okcodes, nbc, pids, maxpar, not, 1) ;
+          if (r < 0) strerr_diefu1sys(111, "waitn") ;
+          else if (!r) break ;
+          pids[maxpar-1] = pid ;
+        }
+      }
       else
       {
         int wstat ;
@@ -99,9 +113,9 @@ int main (int argc, char const **argv)
       }
     }
 
-    if (flagpar)
+    if (maxpar != 1)
     {
-      int r = waitn_code(okcodes, nbc, pids, argc1, not) ;
+      r = waitn_code(okcodes, nbc, pids, maxpar, not, 0) || r ;
       if (r < 0) strerr_diefu1sys(111, "waitn") ;
       else if (!r) return 1 ;
     }
-- 
2.45.2

Reply via email to