Skip to content
This repository was archived by the owner on Apr 12, 2018. It is now read-only.
This repository was 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

@ggreg

Description

@ggreg

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.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions