-
Notifications
You must be signed in to change notification settings - Fork 2
Phase 3: Adding WB to object.c #26
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
base: phase3
Are you sure you want to change the base?
Conversation
b47926d to
9ccdfaa
Compare
|
"How to handle this case" has two parts:
If the answer to 2. is Pyrona-awareness checking, then I believe we should discuss this further before I start making more changes. @mjp41 @xFrednet |
I assumed, that we would check if the type is pyrona aware.
I'm free after tomorrow's daily, so we could expand it to discuss this further |
9ccdfaa to
7e956e9
Compare
|
@xFrednet This PR adds a strawman implementation of the LRC dirty flag and a function |
7e956e9 to
ebb2b28
Compare
|
Small comment regarding the PR description:
#35 starts using the dirty flag to determine if we can close a region or not. This might cause some test failures once both are on the phase 3 branch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Three comments, the rest looks good to me. I believe this actually discovered a new case in the WB that I didn't consider previously. FrankenScript has a MoveReference function which I believe we'll also need.
Objects/typeobject.c
Outdated
| if (value == NULL || _Pyrona_AddReference(obj, value)) { | ||
| if (*dictptr) { | ||
| _Pyrona_RemoveReference(obj, *dictptr); | ||
| } | ||
| Py_XSETREF(*dictptr, Py_XNewRef(value)); | ||
| return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this can run into the problem described here: #35 (comment)
But the Py_REGIONCHANGEREFERENCE will sadly not be the solution here. Since we move a reference. I think we have to add another Py_REGIONMOVEREFERENCE util, which would handle cases where the owner of the same object changes.
I drafted up this implementation, but it doesn't handle cowns yet. I have to think a bit longer about this 🤔
int _Py_RegionMoveReference(PyObject* old_src, PyObject* src, PyObject* tgt) {
result = 0;
regionmetadata *parent = regionmetadata_get_parent(Py_REGION(tgt));
bool was_clean = regionmetadata_is_dirty(parent);
// Prevent the parent from closing by marking it as dirty
regionmetadata_mark_as_dirty(parent);
// First remove the reference to allow the move of bridges and cross
// region references. Any parent regions should remain open since they've
// been marked as dirty.
_Pyrona_RemoveReference(old_src, tgt);
// Attempt to move the reference
if (!_Pyrona_AddReference(src, tgt)) {
// Re-add the previous reference since the new owner is invalid
_Pyrona_AddReference(old_src, tgt);
result = -1
}
if (was_clean) {
regionmetadata_mark_as_not_dirty(parent);
}
// TODO, this doesn't handle cases where `old_src` is a cown...
return result;
}This can be optimized further. We can add a fast track, if the old and new src are from the same region and the target is not bridge object.
EDIT: I now found an easier way based on the changes in #34 we can simply inc the LRC do the move and then DEC the LRC again, all while keeping the region open do to the virtual LRC we added :D
These are the problems that make my brain happy to think about :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always thought of move between regions as "move to local" followed by "move from local". I think that might be what you end result might be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do wonder if we should build a Py_XSETREF_WITH_REGION operation, like I have added for Py_CLEAR in my PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do wonder if we should build a
Py_XSETREF_WITH_REGIONoperation, like I have added forPy_CLEARin my PR.
I like the idea, but feel like the error handling is too inconsistent, some require a jump, others just a simple return 0 or return -1.
I always thought of move between regions as "move to local" followed by "move from local". I think that might be what you end result might be?
Yes, implementing a change reference like that should work and keep the region open, as we expect.
Right. I think the PR description is >1 month old by now :) |
What does that to? I thought |
In FrankenScript it is just syntactic sugar, we can get away with it since every problematic add will stop execution and show the error. However, here we need to make sure that the objects are in a "decent" state when the exception is thrown. The problem with removing and then adding a reference is, that the removal of the reference could close the region, which could intern release a cown. @mjp41 last change in #34 gave me an idea. We can just increase the LRC during this move. That will prevent the region from closing and then we can do the "remove reference, add reference dance" without accidentally closing any regions. |
I believe we always add before move. Does that prevent the premature closing? |
Yes, it prevents the premature closing. But could result in errors for bridge objects. |
Meaning the error in #35 (comment) ? |
Yes! I saw your answer in that PR, but first wanted to think a bit more about it before answering. |
Great, thanks for clarifying! So we are ultimately dealing with a single case. Nice! |
| if (Py_CHECKWRITE(v)) { | ||
| if (!Py_IS_REGION_AWARE(tp)) { | ||
| _PyObject_mark_region_as_dirty(v); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we modify CHECKWRITE to do this as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah you mean Py-CHECKWRITE(obj) marks the region as dirty if !Py_IS_REGION_AWARE(obj->ob_type)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's excellent! And obvious in hindsight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we add that check to CHECKWRITE that will add that check to many more places that what I think we currently would need it. Maybe that's OK because branch prediction.
Objects/typeobject.c
Outdated
| if (value == NULL || _Pyrona_AddReference(obj, value)) { | ||
| if (*dictptr) { | ||
| _Pyrona_RemoveReference(obj, *dictptr); | ||
| } | ||
| Py_XSETREF(*dictptr, Py_XNewRef(value)); | ||
| return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always thought of move between regions as "move to local" followed by "move from local". I think that might be what you end result might be?
Objects/typeobject.c
Outdated
| if (value == NULL || _Pyrona_AddReference(obj, value)) { | ||
| if (*dictptr) { | ||
| _Pyrona_RemoveReference(obj, *dictptr); | ||
| } | ||
| Py_XSETREF(*dictptr, Py_XNewRef(value)); | ||
| return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do wonder if we should build a Py_XSETREF_WITH_REGION operation, like I have added for Py_CLEAR in my PR.
|
@mjp41 regarding move btw regions as reg1 --> local --> reg2 that seems logical to me. I'm not sure I understand your comment fully though. Are you suggesting what the REMOVE barrier should do? |
|
@mjp41 I don't follow the |
I was more reacting that I didn't think a "MOVE" between regions was needed, but it could be more optimal. |
So the code here: cpython/Include/cpython/object.h Lines 371 to 386 in 591bfc7
Does the update and the decref. I wonder if we should have a related macro that does the update, region ref remove, and the decref. |
5b6bc25 to
6d2e2ce
Compare
|
@mjp41 I've added the proposed SET_XREF_WITH_REGION now (and rebased on new Phase3). |
Co-authored-by: Fridtjof Stoldt <xFrednet@gmail.com>
Currently called from one place.
6d2e2ce to
610dcb0
Compare
xFrednet
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only skimmed over the changes again, but this looks good to me. It probably makes sense to merge this and just do any fixes in followup PRs
The first two cases are in
PyObject_SetAttr.I believe that
PyObject_SetAttrfalls out for free with the defaulttp_setattroimplementation which simply callsPyObjectGenericSetAttrwhich in turn always ends up indictobject.c-- unless we hit line 1567 which usestp_descr_set, overloading of__set__.However,
__set__descriptors' behaviour should fall out for free as they will eventually hit a "normal" store which should be trapped by the write barrier.The normal
tp_setattroimplementation is changed by__setattr__being overridden by user. We should expect things to fall out for free in this case since that code will eventually hit a "normal" store.Exceptions: unless the implementation of
__setattr__or__set__is written in C. This is handled using thePy_TPFLAGS_REGION_AWAREfrom PR #33. If this flag is not set, a dirty flag is set in the object.The third and last case is a call to
PyObject_GenericSetDictwhich is the setter foro.__dict__ = v. In this case, we need an add ref on the v and a remove ref on the old value ofo.__dict__. This has been implemented. However, I have so far been unable to trigger this code.o.__dict__ = vrunssubtype_setdictwhich I have extended in a similar fashion.