Worker to be run in a child process. The worker stops when the poison pill "STOP" is reached.
(fn, work_queue, done_queue,
process_context_fn=None, process_context_args=None)
| 61 | |
| 62 | |
| 63 | def Worker(fn, work_queue, done_queue, |
| 64 | process_context_fn=None, process_context_args=None): |
| 65 | """Worker to be run in a child process. |
| 66 | The worker stops when the poison pill "STOP" is reached. |
| 67 | """ |
| 68 | # Install a default signal handler for SIGTERM that stops the processing |
| 69 | # loop below on the next occasion. The job function "fn" is supposed to |
| 70 | # register their own handler to avoid blocking, but still chain to this |
| 71 | # handler on SIGTERM to terminate the loop quickly. |
| 72 | stop = [False] |
| 73 | def handler(signum, frame): |
| 74 | stop[0] = True |
| 75 | signal.signal(signal.SIGTERM, handler) |
| 76 | |
| 77 | try: |
| 78 | kwargs = {} |
| 79 | if process_context_fn and process_context_args is not None: |
| 80 | kwargs.update(process_context=process_context_fn(*process_context_args)) |
| 81 | for args in iter(work_queue.get, "STOP"): |
| 82 | if stop[0]: |
| 83 | # SIGINT, SIGTERM or internal hard timeout caught outside the execution |
| 84 | # of "fn". |
| 85 | break |
| 86 | try: |
| 87 | done_queue.put(NormalResult(fn(*args, **kwargs))) |
| 88 | except AbortException: |
| 89 | # SIGINT, SIGTERM or internal hard timeout caught during execution of |
| 90 | # "fn". |
| 91 | break |
| 92 | except Exception as e: |
| 93 | logging.exception('Unhandled error during worker execution.') |
| 94 | done_queue.put(ExceptionResult(e)) |
| 95 | except KeyboardInterrupt: |
| 96 | assert False, 'Unreachable' |
| 97 | |
| 98 | |
| 99 | @contextmanager |
no test coverage detected