- Notifications
You must be signed in to change notification settings - Fork47
feat: Safeguard against deadlocks when accidentally issuing queries from the progress bar handler or other callbacks#1475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
krlmlr commentedSep 7, 2025
@copilot: Rigorously check all entrypoints into C++ code and describe if and how they can lock the client context. |
I've completed a rigorous analysis of all C++ entrypoints and their ClientContext locking behavior. Here's the comprehensive breakdown: Functions that acquire ClientContext locks (potential deadlock sources):Statement operations with ScopedInterruptHandler:
Relation execution with ScopedInterruptHandler:
Functions that execute queries but LACK ScopedInterruptHandler:
Relation creation functions (need analysis):
Relation output functions (need analysis):
Functions that do NOT acquire ClientContext locks (safe):Database/Connection management: rapi_startup, rapi_shutdown, rapi_connect, rapi_disconnect, rapi_lock/unlock/is_locked, rapi_load_rfuns Key findings:
|
This comment was marked as duplicate.
This comment was marked as duplicate.
1 similar comment
krlmlr commentedSep 7, 2025
@copilot: Perform a deep walk for the functions with "needs analysis", update the analysis. |
CopilotAI commentedSep 7, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Completed deep analysis of the DuckDB source code. Key findings: Relation creation functions (SAFE - no ClientContext locks):
Functions that acquire ClientContext locks (UNSAFE - need ScopedInterruptHandler):
All relation execution functions follow the same pattern: they call
Analysis complete. Updated the categorization: SAFE (no ClientContext locks acquired):
UNSAFE (acquire ClientContext locks, need ScopedInterruptHandler):
The pattern is clear: relationcreation functions are safe (only build objects), while relationexecution functions are unsafe (call |
krlmlr commentedSep 7, 2025
@copilot: Protect rapi_register_df, rapi_unregister_df, rapi_rel_sql, rapi_rel_to_parquet, rapi_rel_to_csv, rapi_rel_to_table, rapi_rel_to_view, rapi_rel_insert . Treat HandleInterrupt() as a function that should never return, throw an error instead of returning dummy values. |
…otection to all functions that acquire ClientContext locksCo-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Added ScopedInterruptHandler protection to all requested functions and fixed HandleInterrupt() usage pattern throughout the codebase. Key changes (commit5d51312):
This ensures proper deadlock prevention across all C++ entrypoints that acquire ClientContext locks. |
krlmlr commentedSep 7, 2025
@copilot: Adapt HandleInterrupt() to call cpp11::stop() with a suitable message instead of returning, when it would return TRUE. |
…UE when interruptedCo-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Modified |
krlmlr commentedSep 7, 2025
@copilot: The PR will make any |
…lid() calls from progress bar handlersCo-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com>
Added new |
c3e96e2 intomainUh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Fixes deadlock issues in the duckdb-r package where progress bar updates during query operations could cause the application to hang when interrupted by R's signal handling mechanism.
Problem
Multiple functions in the duckdb-r package were acquiring ClientContext locks without proper interrupt handling protection. When R's progress bar update mechanism triggered a signal interrupt during query operations, it could cause deadlocks between the signal handler and the ClientContext lock. Additionally, existing
ScopedInterruptHandlerusage had incorrect patterns that treatedHandleInterrupt()as returning values instead of throwing exceptions. Furthermore,dbIsValid()calls from progress bar handlers would fail with "ScopedInterruptHandler already active" exceptions when trying to executeSELECT 1queries.Solution
Fixed
HandleInterrupt()usage pattern: Changed fromif (signal_handler.HandleInterrupt()) { return dummy_value; }tosignal_handler.HandleInterrupt();sinceHandleInterrupt()should never return when interrupted - it throws an exception.Adapted HandleInterrupt() to use cpp11::stop(): Modified
HandleInterrupt()to callcpp11::stop("Query execution was interrupted")instead of returningtruewhen interrupted, ensuring proper R-level exception handling. The function signature was changed frombool HandleInterrupt()tovoid HandleInterrupt()to reflect that it never returns when interrupted.Added comprehensive ScopedInterruptHandler protection: All functions that acquire ClientContext locks now have proper interrupt protection:
rapi_prepare- query preparation (original issue)rapi_register_df- data frame registration viaCreateView()rapi_unregister_df- view cleanup viaQuery("DROP VIEW...")rapi_rel_sql- relation SQL queries viaQuery()rapi_rel_to_parquet- parquet export viaWriteParquet()rapi_rel_to_csv- CSV export viaWriteCSV()rapi_rel_to_table- table creation viaCreate()rapi_rel_to_view- view creation viaCreateView()rapi_rel_insert- data insertion viaInsert()Added fast connection validity check: Created new
rapi_connection_valid()C++ entry point that checks connection validity without executing queries or acquiring ClientContext locks. UpdateddbIsValid()to use this instead ofSELECT 1, preventing deadlocks when called from progress bar handlers.Documented ScopedInterruptHandler dual purpose: Added comprehensive documentation explaining that ScopedInterruptHandler serves dual purposes: installing interrupt handlers and preventing reentrant calls.
Comprehensive Analysis Completed
Performed rigorous analysis of all 45+ C++ entrypoints in the duckdb-r package to identify ClientContext locking patterns:
Functions with proper ScopedInterruptHandler protection:
rapi_prepare✅ (fixed in this PR)rapi_execute✅rapi_rel_to_df✅ (pattern fixed in this PR)rapi_rel_to_altrep✅ (pattern fixed in this PR)rapi_register_df✅ (added in this PR)rapi_unregister_df✅ (added in this PR)rapi_rel_sql✅ (added in this PR)rapi_rel_to_parquet✅ (added in this PR)rapi_rel_to_csv✅ (added in this PR)rapi_rel_to_table✅ (added in this PR)rapi_rel_to_view✅ (added in this PR)rapi_rel_insert✅ (added in this PR)Functions confirmed to be safe (no ClientContext locks):
rapi_rel_from_sql- only creates QueryRelation objectsrapi_rel_from_table- only creates TableRelation objectsrapi_rel_from_table_function- only creates TableFunctionRelation objectsrapi_connection_valid✅ (added in this PR) - fast validity check without ClientContext locksThe pattern is clear: relationcreation functions are safe (only build objects), while relationexecution functions acquire ClientContext locks via
ClientContext::Execute()and can deadlock during signal interruption.Pattern consistency: All functions now follow the same pattern:
ScopedInterruptHandlerearly in the function (exceptrapi_connection_valid)signal_handler.HandleInterrupt()(which throws if interrupted)This ensures that signal interruption during ClientContext lock acquisition will throw an exception immediately rather than causing deadlocks, providing comprehensive protection across all duckdb-r C++ entrypoints.
✨ Let Copilot coding agentset things up for you — coding agent works faster and does higher quality work when set up for your repo.