mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-09-22 23:08:08 +03:00
Preserve runtime ownership after start failure
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
@@ -19,6 +19,9 @@
|
||||
- Registries strongly own process-lifetime plugin instances, capabilities/factories, descriptors, and immutable metadata.
|
||||
They do not own factory-created widgets, active runtimes, replies, or controller state. Factories return a fresh object
|
||||
per request; invalid QObject results are explicitly destroyed.
|
||||
- Once a runtime factory returns a valid launch, startup transfers that exact runtime to the calling connection attempt
|
||||
even when `start()` raises, so the attempt can stop and dispose partial resources. A controlled failure returns `None`
|
||||
only when no valid runtime was acquired.
|
||||
- External entry points and plugin-returned data are a boundary even when plugins are trusted for execution. Validate
|
||||
types and required fields, isolate optional provider failure where the operation can continue, and keep the primary
|
||||
failure observable when it cannot.
|
||||
|
||||
@@ -1100,6 +1100,8 @@ class PluginRegistry:
|
||||
|
||||
def startCoreRuntime(self, config, routing, **kwargs):
|
||||
"""Create and start the core runtime selected for *config*."""
|
||||
launch = None
|
||||
|
||||
try:
|
||||
launch = self.createCoreRuntime(config, routing, **kwargs)
|
||||
|
||||
@@ -1116,7 +1118,10 @@ class PluginRegistry:
|
||||
|
||||
logger.error(f'core runtime start failed for {factoryId!r}: {ex}')
|
||||
|
||||
return None, False
|
||||
# Once construction succeeds, ownership must still cross to the
|
||||
# connection transaction even if start() raises. Returning None
|
||||
# here would abandon a partially acquired process/thread/runtime.
|
||||
return (launch.runtime if launch is not None else None), False
|
||||
|
||||
def prepareDownloadTest(self, config, port: int):
|
||||
"""Create a proxy-only test configuration through its runtime factory."""
|
||||
|
||||
@@ -539,6 +539,44 @@ class PluginFailureIsolationTest(unittest.TestCase):
|
||||
finally:
|
||||
registry.shutdown()
|
||||
|
||||
def testCoreRuntimeStartExceptionReturnsRuntimeForCallerRollback(self):
|
||||
"""Transfer a partially started runtime to the connection transaction."""
|
||||
|
||||
class RaisingStartRuntime(FixtureCoreRuntime):
|
||||
def start(self, configuration, *args, **kwargs):
|
||||
raise RuntimeError('start fixture')
|
||||
|
||||
runtime = RaisingStartRuntime()
|
||||
|
||||
class RaisingStartFactory(CoreRuntimeFactory):
|
||||
factoryId = 'raising-start-factory'
|
||||
configurationTypes = (FixtureConfiguration,)
|
||||
runtimeTypes = (RaisingStartRuntime,)
|
||||
|
||||
def create(self, request: CoreRuntimeRequest):
|
||||
return CoreRuntimeLaunch(runtime, request.configuration)
|
||||
|
||||
class RaisingStartPlugin(FuriousPlugin):
|
||||
metadata = PluginMetadata(
|
||||
'tests.raising-start-factory',
|
||||
'Raising start factory',
|
||||
)
|
||||
capabilities = (RaisingStartFactory(),)
|
||||
|
||||
registry = PluginRegistry()
|
||||
registry.register(RaisingStartPlugin())
|
||||
|
||||
try:
|
||||
returnedRuntime, success = registry.startCoreRuntime(
|
||||
FixtureConfiguration({'type': 'fixture'}),
|
||||
'direct',
|
||||
)
|
||||
|
||||
self.assertIs(returnedRuntime, runtime)
|
||||
self.assertFalse(success)
|
||||
finally:
|
||||
registry.shutdown()
|
||||
|
||||
def testShutdownExceptionDoesNotBlockOtherPlugins(self):
|
||||
"""Run every shutdown hook once even when one hook raises."""
|
||||
stopped = []
|
||||
|
||||
Reference in New Issue
Block a user