From 8aff4aa8274d0c137c11254eb75197932b8eb953 Mon Sep 17 00:00:00 2001 From: Andrew Pullin Date: Thu, 20 Nov 2025 07:58:39 -0800 Subject: [PATCH] Fix Neutron backend API compatibility and multiprocessing fallback (#15885) Summary: This fixes two issues preventing the Neutron backend from building in sandcastle/build environments: 1. **NeutronAtenPassManager API compatibility**: The `NeutronAtenPassManager` class was updated to require a `neutron_target_spec` parameter, but the call site wasn't updated. This caused a `TypeError` at runtime. Fixed by passing the already-available `neutron_target_spec` to the pass manager constructor. 2. **Multiprocessing fallback**: The Neutron converter uses multiprocessing for isolation when converting models, but this fails in restricted build environments (sandcastle) with an `EOFError` when trying to create a multiprocessing Manager. Added a try/except block to catch `EOFError` and `OSError` and fall back to direct execution of the converter when multiprocessing is unavailable. This maintains isolation in normal environments while allowing builds to succeed in restricted ones. Both issues were pre-existing on trunk and not caused by recent changes. Differential Revision: D87252895 --- .../nxp/backend/neutron_converter_manager.py | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/backends/nxp/backend/neutron_converter_manager.py b/backends/nxp/backend/neutron_converter_manager.py index 7124929411e..a53a773f2ce 100644 --- a/backends/nxp/backend/neutron_converter_manager.py +++ b/backends/nxp/backend/neutron_converter_manager.py @@ -78,23 +78,35 @@ def convert(self, tflite_model: bytes, target: str) -> bytes: cctx.compilationOpts.minNumOpsPerGraph = 1 cctx.compilationOpts.excludeGraphPasses = "MergeTranspose" - logger = multiprocessing.log_to_stderr() - logger.setLevel(logging.WARNING) - queue = multiprocessing.Manager().Queue() + # Try to use multiprocessing for isolation, but fall back to direct execution + # if the environment doesn't support it (e.g., in sandcastle/build environments) + try: + logger = multiprocessing.log_to_stderr() + logger.setLevel(logging.WARNING) + queue = multiprocessing.Manager().Queue() + + process = multiprocessing.Process( + target=convert_unsafe, + args=(self.neutron_converter, tflite_model, cctx, queue), + ) + process.start() + process.join() # waits until the subprocess is complete - process = multiprocessing.Process( - target=convert_unsafe, - args=(self.neutron_converter, tflite_model, cctx, queue), - ) - process.start() - process.join() # waits until the subprocess is complete + if queue.empty(): # signals the unsafe task did not run till the end + raise RuntimeError( + f"Neutron converter module terminated unexpectedly with exit code {process.exitcode}" + ) - if queue.empty(): # signals the unsafe task did not run till the end - raise RuntimeError( - f"Neutron converter module terminated unexpectedly with exit code {process.exitcode}" + model_converted = queue.get() + process.close() + except (EOFError, OSError) as e: + # Multiprocessing failed (likely due to environment restrictions) + # Fall back to direct execution + logging.warning( + f"Multiprocessing not available ({e}), running neutron converter directly" + ) + model_converted = self.neutron_converter.convertModel( + list(tflite_model), cctx ) - model_converted = queue.get() - - process.close() return bytes(model_converted)