-
Notifications
You must be signed in to change notification settings - Fork 209
[Swift Bindings] Project value types with heap-allocated properties as C# classes #3038
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
[Swift Bindings] Project value types with heap-allocated properties as C# classes #3038
Conversation
|
||
protected virtual void Dispose(bool disposing) | ||
{ | ||
if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 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.
Does Dispose
really have to be thread-safe? It seems that if two threads are accessing the same instance at the same time, then they're already in trouble, since it seems unlikely both threads will only be calling Dispose
(and if any of those two thread are also calling instance members, then that's not thread-safe even if Dispose
by itself is).
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.
Calling instance methods doesn't change ref counts, but calling Dispose does -- that's the main difference. Also, Swift doesn't have mechanism to prevent from calling Destroy more than once.
It is true that we haven't run into a situation where that becomes a problem.
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.
Calling instance methods doesn't change ref counts, but calling Dispose does -- that's the main difference.
Right, but code like this isn't safe anyways:
// thread 1
Console.WriteLine (obj.ToString ());
obj.Dispose ();
// thread 2
obj.Dispose ();
Because if thread 2's Dispose
is executed when thread 1 is executing ToString
, a crash will occur. The fact that the Dispose
methods are thread-safe doesn't change this.
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.
Yes, you are right.
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.
What are the implications? I see these two:
- Perf implications
- Not a valid scenario
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 agree that calling Dispose twice should be handled correctly, but my point is that the extra work of making it thread-safe seems like it's not actually fixing any real-world scenarios.
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've reverted the thread-safety changes. Both points are valid -- the bindings should be thread-safe, and making only Dispose thread-safe does not guarantee overall thread-safety of the bindings.
I recommend that we revisit this issue when we encounter real-world scenarios.
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.
Related to #2975 .
we revisit this issue when we encounter real-world scenarios.
This is about how to use the bindings securely. You may want to think it through earlier rather than later. Once you encounter real-world scenarios related to security, it is typically too late.
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.
Thanks, lets add them here or in a follow-up.
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.
Follow-up #3052
Enum, | ||
Class, | ||
Protocol, | ||
Tuple |
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.
Do we need to place tuples in the type database? Arent they always constructed from other types?
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.
Void is the only case and it could be special-handled.
@@ -27,15 +28,20 @@ public static bool MethodRequiresSwiftSelf(MethodEnvironment env) | |||
return true; | |||
} | |||
|
|||
public static bool StructIsMarshalledAsCSStruct(StructDecl decl) | |||
public static bool IsTypeFrozen(TypeRecord typeRecord) |
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.
We might need to have a note somewhere that "Frozen" on the tooling side is not equal to "Frozen" on swift side (e.g. frozen struct containing a reference to a reference type).
Edit: I meant that the fact the a frozen type contains a reference type or a non-frozen struct does not make it "non-frozen" on Swift side. It is still "frozen" but has layout unknown at compile time. In the tooling I made a mistake of starting using "frozen" and "non-frozen" to mean projection as struct / class, which is loosly connected to the above.
AFAIK the rule is:
if the layout is known at compile time && no memory management is necessary: struct
Else: class
Since you are making the names more descriptive (which I really like!) I think it would be good to at least encode that information somewhere
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.
Thanks, added as a comment.
src/Swift.Bindings/src/Emitter/StringEmitter/Handler/MethodHandler.cs
Outdated
Show resolved
Hide resolved
…management-vtypes
Description
This PR updates marshalling to project Swift value types that require explicit memory handling as C# classes. Heap-allocated Swift types and value types with heap allocated properties are projected as C# classes. The projected C# classes implement
ValueWitnessTable->Destroy
in their finalizer to ensure proper memory deallocation.Changes
TypeRecord
withKind
andFlag
enums for marshallingStructIsMarshalledAsCSStruct
andArgumentIsMarshalledAsCSStruct
withIsFrozen
andIsHeapAllocated
MarshalToSwift
copy for frozen bound generics in P/Invoke callsstackalloc
for implicit memory management and perf improvementsDispose
handlingValidation
Integration tests and manual bindings tests are expanded to verify memory deallocation and parameter ref counting.
Out of Scope
inout
parameters are not yet supported, so memory handling is not implemented.UnmanagedCallersOnly
is not yet supported, so memory handling is not implementedContributes to #2851
Fixes #3035 #3035 #3036 #2985