(
self,
queue: str,
tasks: List[Task],
log: BoundLogger,
locks: Collection[Lock],
queue_lock: Optional[Semaphore],
)
| 184 | """ |
| 185 | |
| 186 | def execute( |
| 187 | self, |
| 188 | queue: str, |
| 189 | tasks: List[Task], |
| 190 | log: BoundLogger, |
| 191 | locks: Collection[Lock], |
| 192 | queue_lock: Optional[Semaphore], |
| 193 | ) -> bool: |
| 194 | task_func = tasks[0].func |
| 195 | serialized_task_func = tasks[0].serialized_func |
| 196 | |
| 197 | all_task_ids = {task.id for task in tasks} |
| 198 | with g_fork_lock: |
| 199 | child_pid = os.fork() |
| 200 | |
| 201 | if child_pid == 0: |
| 202 | # Child process |
| 203 | log = log.bind(child_pid=os.getpid()) |
| 204 | assert isinstance(log, BoundLogger) |
| 205 | |
| 206 | # Disconnect the Redis connection inherited from the main process. |
| 207 | # Note that this doesn't disconnect the socket in the main process. |
| 208 | self.connection.connection_pool.disconnect() |
| 209 | |
| 210 | random.seed() |
| 211 | |
| 212 | # Ignore Ctrl+C in the child so we don't abort the job -- the main |
| 213 | # process already takes care of a graceful shutdown. |
| 214 | signal.signal(signal.SIGINT, signal.SIG_IGN) |
| 215 | |
| 216 | # Run the tasks. |
| 217 | success = self.execute_tasks(tasks, log) |
| 218 | |
| 219 | # Wait for any threads that might be running in the child, just |
| 220 | # like sys.exit() would. Note we don't call sys.exit() directly |
| 221 | # because it would perform additional cleanup (e.g. calling atexit |
| 222 | # handlers twice). See also: https://bugs.python.org/issue18966 |
| 223 | threading._shutdown() # type: ignore[attr-defined] |
| 224 | |
| 225 | os._exit(int(not success)) |
| 226 | else: |
| 227 | # Main process |
| 228 | log = log.bind(child_pid=child_pid) |
| 229 | assert isinstance(log, BoundLogger) |
| 230 | for task in tasks: |
| 231 | log.info( |
| 232 | "processing", |
| 233 | func=serialized_task_func, |
| 234 | task_id=task.id, |
| 235 | params={"args": task.args, "kwargs": task.kwargs}, |
| 236 | ) |
| 237 | |
| 238 | # Attach a signal handler to SIGCHLD (sent when the child process |
| 239 | # exits) so we can capture it. |
| 240 | signal.signal(signal.SIGCHLD, sigchld_handler) |
| 241 | |
| 242 | # Since newer Python versions retry interrupted system calls we can't |
| 243 | # rely on the fact that select() is interrupted with EINTR. Instead, |
nothing calls this directly
no test coverage detected