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

Replace magic offsets with proper offset calculation#1441

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
lostmsu merged 1 commit intopythonnet:masterfromlosttech:PR/NoMagic
Apr 24, 2021

Conversation

lostmsu
Copy link
Member

@lostmsulostmsu commentedApr 12, 2021
edited
Loading

All Python types created to represent CLR concepts derive fromCLR MetaType (as before), which now has two new fields:

  • tp_clr_inst_offset, which is similar totp_dictoffset in that it tells where to findGCHandle in instances of this type (e.g. where to findGCHandle pointing toSystem.Uri in corresponding Python object)
  • tp_clr_inst, which holds an optional instance ofManagedType, that implements the behavior of the type itself (e.g.GCHandle pointing toClassObject(type = System.Uri))

So the layout of all Python types created by Python.NET is

PyTypetype;ninttp_clr_inst_offset;GCHandletp_clr_inst;// points, for example, to an instance of `ClassObject`

When a Python type, that reflects CLR type is created (such as<class 'System.Uri'>), the layout of itsinstances will be:

BaseTypeFieldsbase;/*if not already in base*/IntPtrdict;/*if not already in base*/IntPtrweaklist;/*if not already in base*/GCHandlegcHandle;// gcHandle points to `CLRObject` instance, which in turn, for example, points to the actual instance of `System.Uri`

The offset to GC handle is recorded in the Python type'stp_clr_inst_offset, and can be found asPyObject_Type(inst).tp_clr_inst_offset. Or, preferably, accessor functions inManagedType should be used, e.g.ManagedType.SetGCHandle(pyObj, gcHandle).

Still usesTypeOffset untilPyType use is widespread.

Related

This should (at least partially) supersede#1267

Notes

Also removed some macro-like method copy-pastes from CPython and bits of dead code.

Should not affect observed behavior.

@lostmsulostmsuforce-pushed thePR/NoMagic branch 2 times, most recently fromf788b6a tobc3ba6aCompareApril 12, 2021 22:46
@lostmsulostmsu marked this pull request as ready for reviewApril 12, 2021 22:51
@lostmsulostmsu requested a review fromfilmorApril 12, 2021 22:57
@lostmsu
Copy link
MemberAuthor

@amos402 can you take a look?

Copy link
Contributor

@BadSingletonBadSingleton left a comment

Choose a reason for hiding this comment

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

Mostly comments about error handling. I never quite understood the memory layout that was used before this PR, the new one makes sense, but I am not sure I grasp all the implications.

@@ -78,6 +68,9 @@ internal static IntPtr GetInstHandle(object ob)
return co.pyHandle;
}

internal static NewReference GetReference(object ob)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this throw if ob is null? (maybe debug only)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

It does raise aNullReferenceException just down the line. I think it is fine not to check here.

BadSingleton reacted with thumbs up emoji
}

void SetupGc ()
void SetupGc (bool force)
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the code in the function, I'm not sure "force" is the right word? Maybe this method needs to be split in two? (or inlined code since it's used in two places)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Good point. I replaced this with a debug only check before the call.

BadSingleton reacted with thumbs up emoji
@@ -90,8 +70,7 @@ internal static unsafe void Initialize()
root = new CLRModule();

// create a python module with the same methods as the clr module-like object
InitializeModuleDef();
py_clr_module = Runtime.PyModule_Create2(module_def, 3);
py_clr_module = Runtime.PyModule_New("clr").DangerousMoveToPointer();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not entirely sure of the internal differences withPyModule_Create2 but the documentation states that the caller is responsible to set a__file__ attribute. Maybe not necessary though.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

__file__is optional. I don't think we have a file name to provide here.

BadSingleton reacted with thumbs up emoji
@@ -165,9 +163,17 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
TypeManager.CopySlot(base_type, type, TypeOffset.tp_clear);


int clrInstOffset = Marshal.ReadInt32(base_type, Offsets.tp_clr_inst_offset);
Marshal.WriteInt32(type, Offsets.tp_clr_inst_offset, clrInstOffset);

// for now, move up hidden handle...
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think this comment should say something like "copy the instance GC from the base to the derived class"

((GCHandle)gc).Free();
GetGCHandle(new BorrowedReference(tp)).Free();
#if DEBUG
SetGCHandle(new BorrowedReference(tp), Runtime.CLRMetaType, default);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why set it to a reasonable value in debug? I'd be more inclined to make it crash and/or throw an exception.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

It is a legitimate question, and since took me a while to understand why I put it here and then forgot.

What it does in Debug build is that instead of CLR crash (typicallyExecutionEngineException) you might get when accessing badGCHandle it raises a debuggable exception, that you might notice when running tests in debug build.

Copy link
Contributor

Choose a reason for hiding this comment

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

To never forget again, a comment would be good.

if (clrType == typeof(Exception))
{
base_ = Exceptions.Exception;
}
else if (clrType.BaseType != null)
{
ClassBase bc = ClassManager.GetClass(clrType.BaseType);
base_ = bc.pyHandle;
if (bc.ObjectReference != null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this assert in debug? Or even throw an error in non-debug builds?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Now, this is for the rare scenario with circular initialization order. We have a test covering it, that prompted me to add theif in the first place.

Copy link
Contributor

Choose a reason for hiding this comment

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

Then a comment would be good.

self.FreeGCHandle();
Runtime.PyObject_GC_UnTrack(ob);
Runtime.PyObject_GC_Del(ob);
self?.FreeGCHandle();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this expected?/Should this be an error in debug?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I've seen a scenario or two when this happens aftertp_clear is called. Same as below.

BadSingleton reacted with thumbs up emoji
{
ClearObjectDict(ob);
}
self.tpHandle = IntPtr.Zero;
if (self is not null)self.tpHandle = 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.

same as above

@lostmsulostmsuforce-pushed thePR/NoMagic branch 2 times, most recently from936bff4 toe50e1d2CompareApril 22, 2021 05:23
@lostmsu
Copy link
MemberAuthor

@BadSingleton addressed your comments.

Copy link
Contributor

@BadSingletonBadSingleton left a comment

Choose a reason for hiding this comment

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

Looks good to me, again, I would like another reviewer to also take a look.

if (clrType == typeof(Exception))
{
base_ = Exceptions.Exception;
}
else if (clrType.BaseType != null)
{
ClassBase bc = ClassManager.GetClass(clrType.BaseType);
base_ = bc.pyHandle;
if (bc.ObjectReference != null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Then a comment would be good.

((GCHandle)gc).Free();
GetGCHandle(new BorrowedReference(tp)).Free();
#if DEBUG
SetGCHandle(new BorrowedReference(tp), Runtime.CLRMetaType, default);
Copy link
Contributor

Choose a reason for hiding this comment

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

To never forget again, a comment would be good.

removed some macro-like method copy-pastes from CPython and bits of dead codeAll Python types created to represent CLR concepts derive from `CLR MetaType` (as before), which now has two new fields:- `tp_clr_inst_offset`, which is similar to `tp_dictoffset` in that it tells where to find `GCHandle` in instances of this type (e.g. where to find `GCHandle` pointing to `System.Uri` in corresponding Python object)- `tp_clr_inst`, which holds an optional instance of `ManagedType`, that implements the behavior of the type itself (e.g. `GCHandle` pointing to `ClassObject(type = System.Uri)`)So the layout of all Python types created by Python.NET is  PyType type;  nint tp_clr_inst_offset;  GCHandel tp_clr_inst; (points, for example, to an instance of `ClassObject`)When a Python type, that reflects CLR type is created, the layout of instances will be:  BaseTypeFields base;  optional (if not in base): IntPtr dict;  optional (if not in base): IntPtr weaklist;  GCHandle gcHandle; (points to `CLRObject` instance, which in turn, for example, points to the actual instance of `System.Uri`)The offset to GC handle is recorded in the Python type's `tp_clr_inst_offset`, and can be found as `PyObject_Type(inst).tp_clr_inst_offset`. Or, preferably, accessor functions in `ManagedType` should be used.
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@BadSingletonBadSingletonBadSingleton approved these changes

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

2 participants
@lostmsu@BadSingleton

[8]ページ先頭

©2009-2025 Movatter.jp