FIX: Resource cleanup on Python shutdown to avoid segfaults#255
Merged
bewithgaurav merged 17 commits intomainfrom Oct 1, 2025
Merged
FIX: Resource cleanup on Python shutdown to avoid segfaults#255bewithgaurav merged 17 commits intomainfrom
bewithgaurav merged 17 commits intomainfrom
Conversation
Contributor
There was a problem hiding this comment.
Pull Request Overview
This PR addresses critical stability issues by implementing proper Python shutdown detection and resource cleanup in the native extension code to prevent segmentation faults during interpreter shutdown after SQL errors.
- Added Python shutdown/finalization state checks in the LOG function and SqlHandle::free method to avoid unsafe API calls during cleanup
- Enhanced exception handling to silently ignore errors when Python is in an inconsistent state
- Added comprehensive tests to verify that SQL syntax errors don't cause segfaults during Python shutdown
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| mssql_python/pybind/ddbc_bindings.cpp | Enhanced LOG function and SqlHandle::free method with Python shutdown detection and safe cleanup logic |
| tests/test_005_connection_cursor_lifecycle.py | Added new test cases to verify SQL syntax errors don't cause segfaults during Python shutdown scenarios |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
… bewithgaurav/fix_segfault_cleanup_sql_failures
📊 Code Coverage Report
Diff CoverageDiff: main...HEAD, staged and unstaged changes
Summary
mssql_python/pybind/ddbc_bindings.cppLines 677-697 677 py::object sys_module = py::module_::import("sys");
678 if (!sys_module.is_none()) {
679 // Check if the attribute exists before accessing it (for Python version compatibility)
680 if (py::hasattr(sys_module, "_is_finalizing")) {
! 681 py::object finalizing_func = sys_module.attr("_is_finalizing");
! 682 if (!finalizing_func.is_none() && finalizing_func().cast<bool>()) {
! 683 return true; // Python is finalizing
! 684 }
! 685 }
686 }
687 return false;
688 } catch (...) {
! 689 std::cerr << "Error occurred while checking Python finalization state." << std::endl;
690 // Be conservative - don't assume shutdown on any exception
691 // Only return true if we're absolutely certain Python is shutting down
! 692 return false;
! 693 }
694 }
695
696 // TODO: Revisit GIL considerations if we're using python's logger
697 template <typename... Args>Lines 706-732 706
707 py::object logger = py::module_::import("mssql_python.logging_config").attr("get_logger")();
708 if (py::isinstance<py::none>(logger)) return;
709
! 710 try {
! 711 std::string ddbcFormatString = "[DDBC Bindings log] " + formatString;
! 712 if constexpr (sizeof...(args) == 0) {
! 713 logger.attr("debug")(py::str(ddbcFormatString));
! 714 } else {
! 715 py::str message = py::str(ddbcFormatString).format(std::forward<Args>(args)...);
! 716 logger.attr("debug")(message);
! 717 }
! 718 } catch (const std::exception& e) {
! 719 std::cerr << "Logging error: " << e.what() << std::endl;
720 }
! 721 } catch (const py::error_already_set& e) {
722 // Python is shutting down or in an inconsistent state, silently ignore
! 723 (void)e; // Suppress unused variable warning
! 724 return;
725 } catch (const std::exception& e) {
726 // Any other error, ignore to prevent crash during cleanup
! 727 (void)e; // Suppress unused variable warning
! 728 return;
729 }
730 }
731
732 // TODO: Add more nuanced exception classes📋 Files Needing Attention📉 Files with overall lowest coverage (click to expand)mssql_python.pybind.connection.connection.cpp: 67.6%
mssql_python.ddbc_bindings.py: 68.5%
mssql_python.pybind.ddbc_bindings.cpp: 69.3%
mssql_python.cursor.py: 79.6%
mssql_python.connection.py: 81.7%
mssql_python.helpers.py: 84.7%
mssql_python.auth.py: 85.3%
mssql_python.type.py: 86.8%
mssql_python.pooling.py: 88.8%
mssql_python.exceptions.py: 90.4%🔗 Quick Links
|
sumitmsft
reviewed
Sep 29, 2025
…s://github.com/microsoft/mssql-python into bewithgaurav/fix_segfault_cleanup_sql_failures
… bewithgaurav/fix_segfault_cleanup_sql_failures
gargsaumya
approved these changes
Oct 1, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work Item / Issue Reference
Summary
This pull request addresses a critical segfault issue that occurred during Python interpreter shutdown when using the
mssql_pythonpackage, especially in scenarios with unclosed cursors, syntax errors, or concurrent operations. The changes introduce robust detection of Python's shutdown state to prevent unsafe resource cleanup and improve test coverage for these edge cases.Key changes include:
Python Shutdown Safety & Resource Cleanup
is_python_finalizing()inddbc_bindings.cppto reliably detect when Python is shutting down or finalizing, using bothPy_IsInitialized()and thesys._is_finalizingattribute for compatibility across Python versions.LOGfunction to skip logging if Python is shutting down, and to handle exceptions gracefully to avoid crashes during interpreter cleanup. [1] [2]SqlHandle::free()method to prevent freeingSQL_HANDLE_STMThandles during shutdown, as their parent DBC handles may already be freed, avoiding double-free and segfaults. Handles are marked as freed instead.Test Coverage for Shutdown and Concurrency Edge Cases
test_005_connection_cursor_lifecycle.pyto ensure no segfaults occur during Python shutdown, including:These changes significantly improve the stability of the
mssql_pythonpackage in complex and error-prone scenarios, especially during interpreter shutdown and multithreaded usage.