Skip to content
This repository has been archived by the owner on Apr 12, 2018. It is now read-only.

Process.on_sigchld do not work when Process.join was called #6

Open
ggreg opened this issue Mar 17, 2014 · 1 comment
Open

Process.on_sigchld do not work when Process.join was called #6

ggreg opened this issue Mar 17, 2014 · 1 comment
Assignees
Labels

Comments

@ggreg
Copy link
Contributor

ggreg commented Mar 17, 2014

There is a unexpected behavior when calling Process.join() at almost the same time the child process exits. I added the following test:

   def test_process_on_exit_is_called(self):                                                                    
          from multiprocessing import Semaphore                                                                    

          sem = Semaphore(1)                                                                                       
          process = Process(target=lambda *_: sem.acquire(),                                                       
                            on_exit=lambda *_: sem.release())                                                      
          process.start(wait=True)
          print('started')
          process.join()                                                                                           
          print('joined')
          self.assertEquals(sem.get_value(), 1)                                                                    
          print('done')

And added some debug strings in ProcessOpen.poll() and Process.on_sigchld():

class ProcessOpen...
   def poll(self, flag=os.WNOHANG):
          if self.returncode is None:
              while True:
                  try:
                      print('poll::waitpid')
                      pid, sts = os.waitpid(self.pid, flag)
                      print('poll::waitpid done')
                  except os.error as e:
                      if e.errno == errno.EINTR:
                          print('poll::waitpid interrupted')
                          continue
                      # Child process not yet created. See #1731717
                      # e.errno == errno.ECHILD == 10
                      # or, child has already exited.
                      print('poll::waitpid OSError {}'.format(e))
                      return None
                  else:
                      break
              if pid == self.pid:
                  if os.WIFSIGNALED(sts):
                      self.returncode = -os.WTERMSIG(sts)
                  else:
                      assert os.WIFEXITED(sts)
                      self.returncode = os.WEXITSTATUS(sts)
[...]

class Process...
    def on_sigchld(self, signum, sigframe):
          if self._child is not None and self._child.pid:
              print('on_sigchld')
              pid, status = os.waitpid(self._child.pid, os.WNOHANG)
              if pid == self._child.pid:
                  self._exitcode = os.WEXITSTATUS(status)

              print('on_exit')
              if self._on_exit:
                  self._on_exit(self)
              os.wait()
              print('clean')
              self.clean()

When I execute the test, I get:

======================================================================
FAIL: test_process_on_exit_is_called (test_process.TestProcess)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/greg/semio/src/projects/process-kit/tests/test_process.py", line 398, in test_process_on_exit_is_called
    self.assertEquals(sem.get_value(), 1)
AssertionError: 0 != 1
-------------------- >> begin captured stdout << ---------------------
started
poll::waitpid
on_sigchld
poll::waitpid OSError [Errno 10] No child processes
joined

--------------------- >> end captured stdout << ----------------------

It means that the parent process enters ProcessOpen.poll() and is interrupted by the SIGCHLD signal in os.waitpid(). The Process.on_sigchld() signal handler is executed. However when it evaluates os.waitpid(), it returns the execution back to the call of os.waitpid() inside ProcessOpen.poll(). Hence the remaining code of Process.on_sigchld() is never called.

@ggreg
Copy link
Contributor Author

ggreg commented Mar 17, 2014

  • Process.join() now disable the SIGCHLD handler.
  • Process.on_sigchld() calls Process.join().

I'm fixing 🔧 the unit tests (3 are failing). Pull request incoming!

ggreg pushed a commit that referenced this issue Mar 18, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
ggreg pushed a commit that referenced this issue Mar 19, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
ggreg pushed a commit that referenced this issue Mar 19, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
ggreg pushed a commit that referenced this issue Mar 21, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
ggreg pushed a commit that referenced this issue Mar 21, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
ggreg pushed a commit that referenced this issue Mar 21, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
ggreg pushed a commit that referenced this issue Mar 24, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
ggreg pushed a commit that referenced this issue Mar 24, 2014
Process.join() now disables Process.on_sigchld() by restablishing the
default handler SIG_DFL. Process.on_sigchld() calls Process.join() to
avoid calling os.waitpid() in two difference places because it could
interrupted and then resumed in the wrong place.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Development

No branches or pull requests

1 participant