Skip to content

Code Health | Make SqlDependencyProcessDispatcherStorage not unsafe #3208

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 24, 2025

Conversation

benrr101
Copy link
Contributor

@benrr101 benrr101 commented Mar 10, 2025

Description: Fairly small change, but worthy of its own PR. This PR changes the SqlDependencyProcessDispatcherStorage class to be safe (and not unsafe). The data for the property is now stored as an IntPtr which is the safe version of a void* that it was previously stored as. After changing that, we no longer need to use a fixed block to get an unsafe pointer to data.

The other improvement is that this PR removes the spin-lock and replaces it with a lock block.

Testing: Project still builds, and tests are still working.

@benrr101 benrr101 added the Code Health 💊 Issues/PRs that are targeted to source code quality improvements. label Mar 10, 2025
@benrr101 benrr101 requested a review from a team March 10, 2025 23:34
@cheenamalhotra cheenamalhotra requested a review from Copilot March 10, 2025 23:35
Copy link

@Copilot 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.

PR Overview

This PR aims to improve the safety of the SqlDependencyProcessDispatcherStorage class by replacing the unsafe pointer manipulation with a safe IntPtr-based approach and updating the locking mechanism. Key changes include:

  • Removing the unsafe keyword and converting s_data from void* to IntPtr.
  • Replacing the spin-lock based synchronization with a lock on a dedicated lock object.
  • Updating memory allocation and copying logic to use safe methods.

Reviewed Changes

File Description
src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SqlDependencyProcessDispatcherStorage.netfx.cs Updated to use IntPtr for safe pointer handling and replaced spin-lock with lock-based synchronization

Copilot reviewed 1 out of 1 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (1)

src/Microsoft.Data.SqlClient/src/Interop/Windows/Sni/SqlDependencyProcessDispatcherStorage.netfx.cs:40

  • NativeSetData allocates memory only when s_data is IntPtr.Zero. Consider documenting or handling the case when NativeSetData is called again with a data array of a different size to avoid potential memory inconsistencies.
if (s_data == IntPtr.Zero)

@Wraith2
Copy link
Contributor

Wraith2 commented Mar 11, 2025

The new version is still using pointers and not safehandles. The difference between void* and IntPtr seems academic. The code is as safe as it was before it simply lacks the unsafe keyword but that doesn't make it better (or worse) simply different. In a future version of the runtime the new version may still require an unsafe context.

If it isn't broken then I don't see this adding value.

@EgorBo
Copy link
Member

EgorBo commented Mar 11, 2025

While I agree that this PR in fact doesn't make code any safer, the hand-made SpinLock implementation is indeed better to be replaced by a lock IMO. The lock itself is optimized for cases without no contention (thin-lock) and it also does a little of spin-waiting under the hood first. The hand-made spin wait with Sleep(50) does look like it could cause performance/latency troubles.

@benrr101 benrr101 changed the title Code Health | Make SqlDependencyProcessDispatcherStorage safe! Code Health | Make SqlDependencyProcessDispatcherStorage not unsafe Mar 11, 2025
@benrr101
Copy link
Contributor Author

@Wraith2 I think the lock change stands on its own. Being able to remove unsafe keywords seems like an improvement to me, but maybe it's six of one, half dozen of the other.

The reason for not calling the lock change out in the first place was the code changes have been sitting around for a long time waiting for other PRs to go through. When I finally got to submitting it, I didn't really remember all the changes I made and only skimmed the diff.

@EgorBo
Copy link
Member

EgorBo commented Mar 11, 2025

Being able to remove unsafe keywords seems like an improvement to me,

Like it was mentioned before, the new code will most likely be annotated with unsafe anyway, see dotnet/designs#330


Trace.Assert(0 == s_size); // Size should still be zero at this point.
s_data = Marshal.AllocHGlobal(data.Length);
Trace.Assert(s_data != IntPtr.Zero);
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this Assert() is helpful. AllocHGlobal() either allocates memory successfully and returns an IntPtr to it, or it throws. Granted, the AllocHGlobal() docs don't explicitly say the returned IntPtr.Zero will never be true, but so what if it is? Will Copy() work if s_data.Zero is true? Those docs don't say either way, so I'm not sure we should be making any assumptions here. I think this Assert() gives a false sense of program state below (i.e. s_data.Zero == false). If it's important that we never call Copy() with a Zero IntPtr, then we need an if-statement.

This is just FYI - no need to make any changes - just wanted to point it out.

@paulmedynski paulmedynski self-assigned this Mar 21, 2025
Copy link
Contributor

@mdaigle mdaigle left a comment

Choose a reason for hiding this comment

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

Even if it's still technically unsafe, less pointer type conversions and better locking still feel worthwhile.

Copy link

codecov bot commented Mar 24, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 72.59%. Comparing base (1e59b88) to head (7bc41fe).
Report is 19 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3208      +/-   ##
==========================================
+ Coverage   72.58%   72.59%   +0.01%     
==========================================
  Files         289      289              
  Lines       59503    59503              
==========================================
+ Hits        43188    43197       +9     
+ Misses      16315    16306       -9     
Flag Coverage Δ
addons 92.58% <ø> (ø)
netcore 75.15% <ø> (+0.03%) ⬆️
netfx 71.19% <ø> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@benrr101 benrr101 merged commit ab504ef into main Mar 24, 2025
252 checks passed
@benrr101 benrr101 deleted the dev/russellben/cleanup/sni-sqldependencyshenanigans branch March 24, 2025 19:04
@cheenamalhotra cheenamalhotra added this to the 6.1-preview1 milestone Mar 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Code Health 💊 Issues/PRs that are targeted to source code quality improvements.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants