|
34 | 34 | ALWAYS_LOADED_MODULES = [] |
35 | 35 | # Extensions that will always be loaded if installed. They don't expose commands but hook into CLI core. |
36 | 36 | ALWAYS_LOADED_EXTENSIONS = ['azext_ai_examples', 'azext_next'] |
| 37 | +MODULE_LOAD_TIMEOUT_SECONDS = 30 |
| 38 | +MAX_WORKER_THREAD_COUNT = 4 |
37 | 39 |
|
38 | 40 |
|
39 | 41 | def _configure_knack(): |
@@ -548,18 +550,18 @@ def _load_modules(self, args, command_modules): |
548 | 550 | import concurrent.futures |
549 | 551 |
|
550 | 552 | results = [] |
551 | | - with ThreadPoolExecutor(max_workers=4) as executor: |
| 553 | + with ThreadPoolExecutor(max_workers=MAX_WORKER_THREAD_COUNT) as executor: |
552 | 554 | future_to_module = {executor.submit(self._load_single_module, mod, args): mod |
553 | 555 | for mod in command_modules if mod not in BLOCKED_MODS} |
554 | 556 |
|
555 | | - for future in concurrent.futures.as_completed(future_to_module, timeout=60): |
| 557 | + for future in future_to_module: |
556 | 558 | try: |
557 | | - # @NOTE: Timeout to counteract deadlocks, but how to test? |
558 | | - result = future.result(timeout=30) |
| 559 | + result = future.result(timeout=MODULE_LOAD_TIMEOUT_SECONDS) |
559 | 560 | results.append(result) |
560 | 561 | except concurrent.futures.TimeoutError: |
561 | 562 | mod = future_to_module[future] |
562 | | - logger.warning("Module '%s' load timeout", mod) |
| 563 | + logger.warning("Module '%s' load timeout after %s seconds", mod, MODULE_LOAD_TIMEOUT_SECONDS) |
| 564 | + future.cancel() |
563 | 565 | results.append(ModuleLoadResult(mod, {}, {}, 0, |
564 | 566 | Exception(f"Module '{mod}' load timeout"))) |
565 | 567 | except Exception as ex: |
|
0 commit comments