diff --git a/Cargo.lock b/Cargo.lock index 14e414ff..e6fd0bd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -447,8 +447,8 @@ dependencies = [ [[package]] name = "diplomat" -version = "0.16.1" -source = "git+https://github.com/Devolutions/diplomat?tag=0.16.1-devo.2#002db80cd71327de9553ea01d4d54552d8df05e7" +version = "0.17.0" +source = "git+https://github.com/Devolutions/diplomat?tag=0.17.0-devo.1#b1d544ff8c035dea6db0d5f2a98f395b0fd3b293" dependencies = [ "diplomat_core", "proc-macro2", @@ -458,13 +458,13 @@ dependencies = [ [[package]] name = "diplomat-runtime" -version = "0.16.0" -source = "git+https://github.com/Devolutions/diplomat?tag=0.16.1-devo.2#002db80cd71327de9553ea01d4d54552d8df05e7" +version = "0.16.1" +source = "git+https://github.com/Devolutions/diplomat?tag=0.17.0-devo.1#b1d544ff8c035dea6db0d5f2a98f395b0fd3b293" [[package]] name = "diplomat_core" -version = "0.16.1" -source = "git+https://github.com/Devolutions/diplomat?tag=0.16.1-devo.2#002db80cd71327de9553ea01d4d54552d8df05e7" +version = "0.17.0" +source = "git+https://github.com/Devolutions/diplomat?tag=0.17.0-devo.1#b1d544ff8c035dea6db0d5f2a98f395b0fd3b293" dependencies = [ "proc-macro2", "quote", diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index 908b48ee..291bcfd8 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -25,8 +25,8 @@ rand = "0.10" rand_core = "0.10" # FFI -diplomat = { git = "https://github.com/Devolutions/diplomat", tag = "0.16.1-devo.2" } -diplomat-runtime = { git = "https://github.com/Devolutions/diplomat", tag = "0.16.1-devo.2" } +diplomat = { git = "https://github.com/Devolutions/diplomat", tag = "0.17.0-devo.1" } +diplomat-runtime = { git = "https://github.com/Devolutions/diplomat", tag = "0.17.0-devo.1" } time = "0.3" hex = "0.4" diff --git a/ffi/dotnet/Devolutions.Picky/Generated/BorrowLease.cs b/ffi/dotnet/Devolutions.Picky/Generated/BorrowLease.cs index 239dcb7f..9bdc1074 100644 --- a/ffi/dotnet/Devolutions.Picky/Generated/BorrowLease.cs +++ b/ffi/dotnet/Devolutions.Picky/Generated/BorrowLease.cs @@ -16,6 +16,8 @@ internal interface IBorrowLease : ILifetimeEdge internal interface IVersionedReference : ILifetimeEdge { + object? Source { get; } + IBorrowLease LeaseForOperation(); } @@ -39,6 +41,12 @@ internal BorrowLease( internal T* Ptr { get; } + internal RustHandle Owner => + Volatile.Read(ref _owner) ?? throw new ObjectDisposedException(nameof(BorrowLease)); + + internal static ILifetimeEdge VersionedEdge(RustHandle owner, MutationVersion version) => + new VersionedReference(owner, version); + public BorrowKind Kind => _kind; public ILifetimeEdge HoldForCall() @@ -108,6 +116,8 @@ internal VersionedReference(RustHandle owner, MutationVersion version) _version = version; } + public object? Source => Volatile.Read(ref _owner); + public IBorrowLease LeaseForOperation() { RustHandle? owner = Volatile.Read(ref _owner); diff --git a/ffi/dotnet/Devolutions.Picky/Generated/RustHandle.cs b/ffi/dotnet/Devolutions.Picky/Generated/RustHandle.cs index 5d73fb7b..92256cf4 100644 --- a/ffi/dotnet/Devolutions.Picky/Generated/RustHandle.cs +++ b/ffi/dotnet/Devolutions.Picky/Generated/RustHandle.cs @@ -22,6 +22,38 @@ internal static class LifetimeEdge moved?.TransferToPersistent(); return moved; } + + // A view dies on its own while the Rust value it points into lives on, so whatever it + // was asked to keep alive would be freed under that value. + internal static void CheckHolder(BorrowLease? holder) where T : unmanaged + { + if (holder is not null && !holder.Owner.IsOwned) + { + throw new InvalidOperationException( + "A borrowed view cannot keep another value alive; call this on the owning value."); + } + } + + // Rust may have kept `source` inside `holder`, which the return type doesn't show. It runs + // after the call, which still needs the lease, and cannot fail once Rust kept the pointer. + internal static void Retain( + BorrowLease? holder, + ref BorrowLease? source) + where THolder : unmanaged + where TSource : unmanaged + { + if (holder is null || source is null) + { + return; + } + + RustHandle sourceHandle = source.Owner; + holder.Owner.AddEdge( + source.Kind == BorrowKind.Exclusive + ? Move(ref source)! + : sourceHandle.CurrentVersionEdge(), + sourceHandle); + } } internal enum BorrowKind @@ -102,6 +134,32 @@ internal ILifetimeEdge[] HoldDependenciesForCall() } } + internal void Add(WrapperKind kind, ILifetimeEdge edge, object source) + { + ILifetimeEdge?[] edges = Volatile.Read(ref _edges); + foreach (ILifetimeEdge? existing in edges) + { + // Storing the same value again would otherwise grow this list on every call. + if (edge is IVersionedReference + && existing is IVersionedReference versioned + && ReferenceEquals(versioned.Source, source)) + { + edge.Release(); + return; + } + } + + if (edge is IBorrowLease lease && IsReadView(kind, lease)) + { + edge = lease.IntoVersionedEdge(); + } + + ILifetimeEdge?[] added = new ILifetimeEdge?[edges.Length + 1]; + Array.Copy(edges, added, edges.Length); + added[edges.Length] = edge; + Volatile.Write(ref _edges, added); + } + internal void Release() { ILifetimeEdge?[] edges = Interlocked.Exchange( @@ -178,6 +236,8 @@ internal void Enter(BorrowKind kind) } } + internal MutationVersion CurrentVersion() => _mutations.Read(); + internal MutationVersion Exit(BorrowKind kind) { if (kind == BorrowKind.Shared) @@ -433,6 +493,13 @@ internal DependencyOperationLease HoldForCall() "The source of this borrowed value was disposed."); } + internal bool IsOwned => _wrapperKind == WrapperKind.Owned; + + internal void AddEdge(ILifetimeEdge edge, object source) => _edges.Add(_wrapperKind, edge, source); + + internal ILifetimeEdge CurrentVersionEdge() => + BorrowLease.VersionedEdge(this, _borrows.CurrentVersion()); + internal void ExitBorrow(BorrowKind kind) => _borrows.Exit(kind); internal MutationVersion ExitBorrowKeepingReference(BorrowKind kind) => _borrows.Exit(kind); diff --git a/ffi/justfile b/ffi/justfile index 4dd1c1dc..3678f2e4 100755 --- a/ffi/justfile +++ b/ffi/justfile @@ -15,11 +15,11 @@ default: bindings # A tag on the Devolutions fork rather than a branch or a bare rev: a tag is an # immutable ref, so its commit survives the fork's branches being rebased. -# The crates.io 0.16.1 release predates the .NET lifetime rework these bindings +# The crates.io 0.17.0 release lacks the .NET stored-argument fix these bindings # are generated with; switch back to a crates.io version once upstream publishes # one that has it. diplomat_repo := "https://github.com/Devolutions/diplomat" -diplomat_tag := "0.16.1-devo.2" +diplomat_tag := "0.17.0-devo.1" # diplomat-tool uses `if let` guards (stable since Rust 1.95), which this repo's # pinned toolchain is too old for.