Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@monojenkins
Copy link
Contributor

Using the CoreCLR version of this line fixed the problem and my investigations
also revealed why.

The SafePipeHandle contains a reference to both the Socket as well as the
SafeSocketHandle, taking ownership of them. If there are no additional
references to either of them, so that both are collected in the same GC pass,
their finalizer may be invoked in any order.

If the Socket gets finalized first, it's SafeSocketHandle will have a _mstate
of 8 (two references) at the beginning of DangerousReleaseInternal() - which
will set it to 6 (one reference plus disposed flag).

Then when the SafePipeHandle gets collected, it will call DangerousRelease ()
on it's SafeSocketHandle. As mentioned above, it's _mstate will be 6.
Now we own the handle, so we'll decrease it's reference count by one and set the
Closed flag - the new _mstate should thus be 3.

However, using

new_state = (old_state & RefCount_Mask) - RefCount_One;

would set it to 1 - thus effectively clearing the `Disposed' flag. And since it's
reference count is now also zero, the next finalize pass will then throw.

The CoreCLR version of this class uses

new_state = old_state - RefCount_One;

which fixes this.

Backport of #16244.

/cc @steveisok @baulig

…#16034.

Using the CoreCLR version of this line fixed the problem and my investigations
also revealed why.

The `SafePipeHandle` contains a reference to both the Socket as well as the
`SafeSocketHandle`, taking ownership of them.  If there are no additional
references to either of them, so that both are collected in the same GC pass,
their finalizer may be invoked in any order.

If the Socket gets finalized first, it's `SafeSocketHandle` will have a `_mstate`
of 8 (two references) at the beginning of `DangerousReleaseInternal()` - which
will set it to 6 (one reference plus disposed flag).

Then when the `SafePipeHandle` gets collected, it will call `DangerousRelease ()`
on it's `SafeSocketHandle`.  As mentioned above, it's `_mstate` will be 6.
Now we own the handle, so we'll decrease it's reference count by one and set the
`Closed` flag - the new `_mstate` should thus be 3.

However, using

    new_state = (old_state & RefCount_Mask) - RefCount_One;

would set it to 1 - thus effectively clearing the `Disposed' flag.  And since it's
reference count is now also zero, the next finalize pass will then throw.

The CoreCLR version of this class uses

    new_state = old_state - RefCount_One;

which fixes this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants