    async def poll(self):
        """Poll the spawned process to see if it is still running.

        If the process is still running, we return None. If it is not running,
        we return the exit code of the process if we have access to it, or 0 otherwise.
        """
        # if we started the process, poll with Popen
        if self.proc is not None:
            status = self.proc.poll()
            if status is not None:
                # handle SIGCHILD to avoid zombie processes
                # and also close stdout/stderr file descriptors
                with self.proc:
                    # clear state if the process is done
                    self.clear_state()
            return status

        # if we resumed from stored state,
        # we don't have the Popen handle anymore, so rely on self.pid
        if not self.pid:
            # no pid, not running
            self.clear_state()
            return 0

        # We use psutil.pid_exists on windows
        if os.name == 'nt':
            alive = psutil.pid_exists(self.pid)
        else:
            alive = await self._signal(0)
        if not alive:
            self.clear_state()
            return 0
        else:
            return None
Poll the spawned process to see if it is still running.

If the process is still running, we return None. If it is not running,
we return the exit code of the process if we have access to it, or 0 otherwise.
jupyterhub/spawner.py