views:

121

answers:

3

I have the following code, which I'm trying to only allow a maximum of 5 children to run at a time, but I can't figure out how to decrement the child count when a child exits.

struct {
   char *s1;
   char *s2;
} s[] = {
  {"one", "oneB"},
  {"two", "twoB"},
  {"three", "thr4eeB"},
  {"asdf", "3th43reeB"},
  {"asdfasdf", "thr33eeB"},
  {"asdfasdfasdf", "thdfdreeB"},
  {"af3c3", "thrasdfeeB"},
  {"fec33", "threfdeB"},
  {NULL, NULL}
};

int main(int argc, char *argv[])
{
int i, im5, children = 0;
int pid = fork();
for (i = 0; s[i].s2; i++)
{
    im5 = 0;
    switch (pid)
    {
        case -1:
        {
            printf("Error\n");
            exit(255);
        }
       case 0:
       {
            printf("%s -> %s\n", s[i].s1, s[i].s2);
            if (i==5) im5 = 1;
            printf("%d\n", im5);
            sleep(i);
            exit(1);
        }
        default:
        {   // Here is where I need to sleep the parent until chilren < 5 
            // so where do i decrement children so that it gets modified in the parent process?
            while(children > 5)
                sleep(1);
            children++;
            pid = fork();
        }
    }
}
return 1;
}

Revised version seems to work based on comments

struct {
   char *s1;
   char *s2;
} s[] = {
  {"one", "oneB"},
  {"two", "twoB"},
  {"three", "thr4eeB"},
  {"asdf", "3th43reeB"},
  {"asdfasdf", "thr33eeB"},
  {"asdfasdfasdf", "thdfdreeB"},
  {"af3c3", "thrasdfeeB"},
  {"fec33", "threfdeB"},
  {NULL, NULL}
};

pthread_mutex_t children_count_lock;
int children = 0;

int main(int argc, char *argv[])
{
int i, im5;
int pid = fork();
for (i = 0; s[i].s2; i++)
{
    im5 = 0;
    switch (pid)
    {
        case -1:
        {
            printf("Error\n");
            exit(255);
        }
       case 0:
       {
            printf("%s -> %s\n", s[i].s1, s[i].s2);
            if (i==5) im5 = 1;
            printf("%d\n", im5);
            sleep(i);

            pthread_mutex_lock(&children_count_lock);
            children = children - 1;
            if (children < 0) children = 0;
            pthread_mutex_unlock(&children_count_lock);

            exit(1);
        }
        default:
        {   
            if (children > 4)
                wait();

            pthread_mutex_lock(&children_count_lock);
            children++;
            pthread_mutex_unlock(&children_count_lock);

            pid = fork();
        }
    }
}
return 1;
}
+2  A: 

The wait() family of functions will suspend the parent until a child process exits (do that instead of sleeping).


No, you don't need critical sections at all - the child and parent don't share memory. All your need is something like this in your default case:

    default:
    {   
        children++;  // Last fork() was successful

        while (children >= 5)
        {
            int status;
            // Wait for one child to exit
            if (wait(&status) == 0)
            {
                children--;
            }
        }

        pid = fork();
    }

(Forget what I said before about initialising children to 1, I didn't notice that children++ was supposed to be before the while loop).

caf
So i would just need to do if (children > 5) { wait(); children--; }?
@bstullkid -- multiprocessing is tricky -- wait will simply return if there is no child to wait for. If you have two children exit while you are waiting, you won't notice the second exit. I think you need to handle the decrement via shared memory on the child -- thus the need for the mutex.
tvanfosson
No, `wait()` won't miss children exiting. Child processes stick around in the Z (zombie) state until their parent `wait()`s for them, for exactly this reason.
caf
@caf -- hmmm. my memory must be going. Obviously, if wait doesn't miss an exiting child (and assuming you don't have any other signals that may cause it to return early), this is clearly the way to go.
tvanfosson
You could put your `wait` calls in a signal handler for `SIGCHLD` and keep your count in a `volatile sig_atomic_t` (it's an `int` that the platform can load or store in one instruction). You would then need something like a critical section (block SIGCHLD) to increment the count from the main code, but you could read it from main code and read/write it from the signal handler like a normal variable.
nategoose
@tvanfosson: You're thinking about a closely related issue - `SIGCHLD`, where a child can exit while you're handling the signal from a previous child exiting. `wait()` doesn't have that problem, though. You are right that `wait()` can be interrupted, so you need to test that it returns 0 - I've updated my answer.
caf
+2  A: 

Once you get your initial 5 children running, you probably want to use wait() to wait for a child process to finish, at which point you can start a new child.

David Gelhar
+1  A: 

Call wait() in the parent. This will block until one of the children exits.

Marcelo Cantos