Skip to content

fix: prevent crash when force-closing during PDF download (#59)#61

Merged
happycola233 merged 1 commit intomainfrom
codex/fix-crash-when-exiting-pdf-download
Feb 5, 2026
Merged

fix: prevent crash when force-closing during PDF download (#59)#61
happycola233 merged 1 commit intomainfrom
codex/fix-crash-when-exiting-pdf-download

Conversation

@happycola233
Copy link
Owner

@happycola233 happycola233 commented Feb 5, 2026

Motivation

  • 用户在下载 PDF 时强行点叉或关闭窗口会导致程序崩溃,因为后台下载线程在窗口销毁后仍然尝试调用 Tk API(如 root.after)。(终止下载导致崩溃 #59)

Description

  • 将下载/工作线程设为守护线程(t.daemon = True)以避免后台线程阻止进程退出;修改点在 src/tchMaterial-parser.pyw 中的 thread_it
  • 增加全局标志 app_closing 并在 ui_call 中先检查该标志以避免在关闭过程中调度 UI 更新,同时对 root.after 抛错做安全吞掉处理。
  • 改造窗口关闭处理 on_closing,增加幂等性保护,先设置 app_closing = True,结束子进程并尝试 root.destroy(),替代直接调用 sys.exit(0),从而安全完成资源清理。
  • 修改影响的文件:src/tchMaterial-parser.pyw

Testing

  • 已执行 python -m py_compile src/tchMaterial-parser.pyw 校验语法,结果成功。

Codex Task

@happycola233 happycola233 changed the title fix: prevent crash when force-closing during PDF download fix: prevent crash when force-closing during PDF download (#59) Feb 5, 2026
@happycola233 happycola233 changed the title fix: prevent crash when force-closing during PDF download (#59) fix: prevent crash when force-closing during PDF download #59 Feb 5, 2026
@happycola233 happycola233 changed the title fix: prevent crash when force-closing during PDF download #59 fix: prevent crash when force-closing during PDF download (#59) Feb 5, 2026
@happycola233 happycola233 marked this pull request as ready for review February 5, 2026 08:50
@happycola233 happycola233 requested a review from Copilot February 5, 2026 08:50
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a crash that occurs when users force-close the application window during a PDF download. The issue was caused by background download threads attempting to call Tkinter UI update methods after the main window was destroyed.

Changes:

  • Enabled daemon threads to allow the process to exit cleanly even with running background threads
  • Added app_closing flag with guards in UI update calls to prevent post-destruction UI access
  • Replaced sys.exit(0) with root.destroy() for proper resource cleanup during window closure

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return

try:
root.after(0, lambda: (not app_closing) and func(*args, **kwargs))
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lambda function uses (not app_closing) and func(*args, **kwargs) which will return the result of func(*args, **kwargs) when app_closing is False, but will return False when app_closing is True. This is correct for preventing execution, but the lambda will still be queued even if app_closing becomes True between line 706 and line 710. Additionally, this creates a redundant check since line 706-707 already guards against app_closing. Consider simplifying to root.after(0, lambda: func(*args, **kwargs)) since the early return on line 706-707 already handles the app_closing check.

Suggested change
root.after(0, lambda: (not app_closing) and func(*args, **kwargs))
root.after(0, lambda: func(*args, **kwargs))

Copilot uses AI. Check for mistakes.

session = requests.Session() # 初始化请求
download_states = [] # 初始化下载状态
app_closing = False
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global flag app_closing is accessed and modified across multiple threads without synchronization primitives (e.g., threading.Lock). In Python, while simple boolean operations are generally atomic due to the GIL, there's no guarantee that all threads will immediately see the updated value due to potential memory caching. This could lead to race conditions where background threads continue to call ui_call even after app_closing is set to True. Consider using threading.Event instead, which provides proper memory barriers and thread synchronization. For example: app_closing = threading.Event(), then use app_closing.is_set() to check and app_closing.set() to signal closure.

Copilot uses AI. Check for mistakes.
Comment on lines +711 to +713
except Exception:
# 主窗口销毁后,root.after 会抛错,直接忽略即可
pass
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a bare except Exception: here silently swallows all exceptions, which could hide legitimate errors beyond just the expected case where root has been destroyed. While this may work for the intended use case, it makes debugging more difficult. Consider being more specific about what exception you expect (e.g., except tk.TclError: for Tkinter-related errors when the window is destroyed), or at minimum, log the exception for debugging purposes.

Copilot uses AI. Check for mistakes.
Comment on lines +811 to +812
except Exception:
pass
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a bare except Exception: here silently swallows all exceptions during window destruction. While this provides robustness, it could hide legitimate errors. Consider being more specific (e.g., except tk.TclError:) or at minimum, log the exception using print_error(e) to aid debugging, consistent with error handling elsewhere in the codebase (e.g., lines 28-29, 383).

Suggested change
except Exception:
pass
except Exception as e:
print_error(e)

Copilot uses AI. Check for mistakes.
@happycola233 happycola233 merged commit e802e70 into main Feb 5, 2026
7 checks passed
@happycola233 happycola233 deleted the codex/fix-crash-when-exiting-pdf-download branch February 5, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants