Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Proposal: Safe pointers#1043

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

Merged
filmor merged 9 commits intopythonnet:masterfromlosttech:features/SafePointers
Feb 26, 2020

Conversation

lostmsu
Copy link
Member

@lostmsulostmsu commentedFeb 13, 2020
edited
Loading

What does this implement/fix? Explain your changes.

With C# 7.3ref structs and C# 8.0using working on any method namedDispose it is possible to track new vs borrowed references at compile time.

This proposal introducesBorrowedReference andNewReference types, that areref-only structs, meaning you can't put them intoPyObject directly, so they can implement differentToPyObject methods, one doingIncRef, and the other one not (alternatively, we can havePyObject constructor overloads). Along with Roslyn-basedNonCopyableAnalyzer andFxCop afterdotnet/roslyn-analyzers#3305 is fixed, this can ensure, that we do reference counting correctly at compile time.

We can gradually change return and parameter types in PInvoke methods inRuntime to one ofBorrowedReference orNewReference to take advantage of this.

Downsides

Build system must support C# 8.0, which is currently not supported in all build configurations.
Without C# 8.0 one would have to disposeNewReference instances withtry ... finally ... block.

@lostmsulostmsu added the rfc labelFeb 13, 2020
@@ -292,7 +292,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
for (int i = 0; i < pynkwargs; ++i)
{
var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(keylist, i));
kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i);
kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i).DangerousGetAddress();
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

This generates a compiler warning, that uncovers a potentially dangerous pattern, wherekwargsDict may contain borrowed references.

@@ -139,12 +139,13 @@ public PyObject Values()
/// </remarks>
public PyObject Items()
{
IntPtr items = Runtime.PyDict_Items(obj);
if (items == IntPtr.Zero)
using var items = Runtime.PyDict_Items(this.obj);
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Afterdotnet/roslyn-analyzers#3305 is fixed we can configure build to fail here ifusing would be omitted

@lostmsulostmsu requested a review fromfilmorFebruary 14, 2020 00:05
@lostmsu
Copy link
MemberAuthor

@amos402 can you check this one out too?

@codecov-io
Copy link

codecov-io commentedFeb 14, 2020
edited
Loading

Codecov Report

Merging#1043 intomaster willincrease coverage by3.64%.
The diff coverage isn/a.

Impacted file tree graph

@@            Coverage Diff             @@##           master    #1043      +/-   ##==========================================+ Coverage   83.11%   86.75%   +3.64%==========================================  Files           1        1                Lines         302      302              ==========================================+ Hits          251      262      +11+ Misses         51       40      -11
FlagCoverage Δ
#setup_linux65.56% <ø> (ø)⬆️
#setup_windows71.52% <ø> (+6.62%)⬆️
Impacted FilesCoverage Δ
setup.py86.75% <0%> (+3.64%)⬆️

Continue to review full report at Codecov.

Legend -Click here to learn more
Δ = absolute <relative> (impact),ø = not affected,? = missing data
Powered byCodecov. Last updatef5548e3...6ae63cd. Read thecomment docs.

@amos402
Copy link
Member

You can also implement an implicit operator for PyObject make it easy for usage.
Reference struct also can provide more interfaces, like RefCnt, ToString, ect.
By the by,Py.GILState can be strcuct easily too.

@lostmsulostmsu mentioned this pull requestFeb 20, 2020
@lostmsu
Copy link
MemberAuthor

@filmor I'd love if we get this in and take as new standard for coding Python handle manipulations, as it would make otherwise hard-to-debug segmentation fault/access violation bugs easier to prevent/spot/debug.

@lostmsu
Copy link
MemberAuthor

@amos402 I can proceed to merge it, if you can sign-off a review, and are for this change in general.

I do not plan to extend the scope yet (like makingPyObject changes to take these, etc). We can do it in the future PRs.

@@ -83,6 +83,7 @@
</Compile>
<Compile Include="arrayobject.cs" />
<Compile Include="assemblymanager.cs" />
<Compile Include="BorrowedReference.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

Is it good for separate these three items to different files? They're small, related, and it make the project files be scattered.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

The might get more code later on. Generally, I prefer a type per file approach for anything except delegate types.

Copy link
Member

Choose a reason for hiding this comment

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

Or create a folder for them? Just just too far if they sorted by name I thought🤣

{
using System;
[NonCopyable]
ref struct NewReference
Copy link
Member

Choose a reason for hiding this comment

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

Is it necessary to limit it as a stack-allocated value? If make it to a normal struct and rename it toPyReference, it can be assigned to a collection and implement the IDisposable forusing () usage.
For better usage, aBorrowedReference may be able to promote to aPyReference for using the implict operate overloading.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

With[NoCopyable] you won't be able to put it into a collection anyway. And without[NoCopyable] it does not provide any safety guarantees.

Having it asref struct saves you from addingref everywhere you take it.

Copy link
Member

Choose a reason for hiding this comment

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

But....sometimes I just want to put them into collections...😂

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Then you can still doDangerousGetHandle or create aPyObject from them.

Copy link
Member

Choose a reason for hiding this comment

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

But this type also has a purpose for reminding others that it's a reference don't forget to decref it, isn't it?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Because it must be clear from code, that care is necessary around this scenario.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Also, as mentioned above,[NoCopyable] would already prevent you from putting an instance into a collection, even ifref were removed. And[NoCopyable] is basically the sole purpose of this change, as if you do

NewReferencerefA=GetNewReferenceSomehow();NewReferencerefB=refA;

Then you already miappropriated refcounts.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe more introduce a type namedPyHandle for that?

Copy link
Member

Choose a reason for hiding this comment

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

emmm, but that makeBorrowedReference embarrassed.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

@amos402 The idea withPyHandle can be reviewed separately. This PR is only for tracking new vs borrowed references as used in Python documentation for C API.

public IntPtr Pointer { get; set; }
public bool IsNull => this.Pointer == IntPtr.Zero;

public PyObject ToPyObject()
Copy link
Member

Choose a reason for hiding this comment

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

I prefer two methods:
ToPyObject: inref and create aPyObject, not assign thePointer to nullptr
AsPyObject: use this implementation

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Constructing a newPyObject from references should be done usingPyObject constructors (which can be added later). I agree though, thatToPyObject is not descriptive enough. MaybeMoveToPyObject orIntoPyObject?

}

[Obsolete("Use overloads, that take BorrowedReference or NewReference")]
public IntPtr DangerousGetAddress()
Copy link
Member

Choose a reason for hiding this comment

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

Use a property namedAddress can be more fit?
Why I have to be warned if I trying use the raw pointer, seems it doesn't make any sense. Or just don't expose the interface and useexplicit operator IntPtr instead.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

This is modeled afterSafeHandle class and itsDangerousGetHandle method.
The whole point of introducing these references is to get rid ofIntPtrs entirely. It should warn you against converting intoIntPtr. MaybeObsolete part is unnecessary asDangerous sounds like a warning enough. I will removeObsolete.

removed public property Pointer from NewReference and replaced with DangerousGetAddress
@lostmsu
Copy link
MemberAuthor

lostmsu commentedFeb 26, 2020
edited
Loading

@amos402@filmor I urge you to review this ASAP. As I am investigating segfaults in#1050 and#1060 , I'd love to have type checker on my side. Will definitely bring this up during the next call.

BTW, I don't think it is necessary to update branch with the latest master every time before merging, as long as there are no conflicts. In an unlikely case, when it breaks the build, the change can be reverted and the PR reopened.

@filmor
Copy link
Member

I like this a lot, I'm was just a bit reluctant about using C# 8 as it won't be officially supported on .NET Framework, but apparently you are not doing that yet.

@filmorfilmor merged commit770fc01 intopythonnet:masterFeb 26, 2020
@amos402amos402 mentioned this pull requestMar 10, 2020
4 tasks
AlexCatarino pushed a commit to QuantConnect/pythonnet that referenced this pull requestJun 29, 2020
* NewReference type and an example usage* BorrowedReference + example, that exposes dangerous pattern* make BorrowedReference readonly ref struct* BorrowedReference.Pointer is a private readonly field* renamed NewReference.ToPyObject to MoveToPyObject* removed public property Pointer from NewReference and replaced with DangerousGetAddress
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@amos402amos402amos402 left review comments

@filmorfilmorAwaiting requested review from filmor

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

4 participants
@lostmsu@codecov-io@amos402@filmor

[8]ページ先頭

©2009-2025 Movatter.jp