<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def run_processes(self, procdetails: List[ProcessDetails], subproc_run_timeout_sec: float = 1, stop_event_timeout_ms: int = 1000, kill_timeout_sec: float = 5) -> None: """ Run multiple child processes. Args: procdetails: list of :class:`ProcessDetails` objects (q.v.) subproc_run_timeout_sec: time (in seconds) to wait for each process when polling child processes to see how they're getting on (default ``1``) stop_event_timeout_ms: time to wait (in ms) while checking the Windows stop event for this service (default ``1000``) kill_timeout_sec: how long (in seconds) will we wait for the subprocesses to end peacefully, before we try to kill them? .. todo:: cardinal_pythonlib.winservice.WindowsService: NOT YET IMPLEMENTED: Windows service autorestart """


        # https://stackoverflow.com/questions/16333054
        def cleanup():
            self.debug("atexit function called: cleaning up")
            for pmgr_ in self.process_managers:
                pmgr_.stop()

        atexit.register(cleanup)

        # Set up process info
        self.process_managers = []  # type: List[ProcessManager]
        n = len(procdetails)
        for i, details in enumerate(procdetails):
            pmgr = ProcessManager(details, i + 1, n,
                                  kill_timeout_sec=kill_timeout_sec,
                                  debugging=self.debugging)
            self.process_managers.append(pmgr)

        # Start processes
        for pmgr in self.process_managers:
            pmgr.start()
        self.info("All started")

        # Run processes
        something_running = True
        stop_requested = False
        subproc_failed = False
        while something_running and not stop_requested and not subproc_failed:
            if (win32event.WaitForSingleObject(
                    self.h_stop_event,
                    stop_event_timeout_ms) == win32event.WAIT_OBJECT_0):
                stop_requested = True
                self.info("Stop requested; stopping")
            else:
                something_running = False
                for pmgr in self.process_managers:
                    if subproc_failed:
                        break
                    try:
                        retcode = pmgr.wait(timeout_s=subproc_run_timeout_sec)
                        if retcode != 0:
                            subproc_failed = True
                    except subprocess.TimeoutExpired:
                        something_running = True

        # Kill any outstanding processes
        #
        # (a) Slow way
        # for pmgr in self.process_managers:
        #     pmgr.stop()
        #
        # (b) Faster (slightly more parallel) way
        # for pmgr in self.process_managers:
        #     pmgr.terminate()
        # for pmgr in self.process_managers:
        #     pmgr.stop_having_terminated()
        #
        # ... No, it's bad if we leave things orphaned.
        # Let's go for slow, clean code.

        for pmgr in self.process_managers:
            pmgr.stop()
        self.info("All stopped")