diff --git a/CHANGELOG.md b/CHANGELOG.md index 0681d7a..4ffbc7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### UNRELEASED + +- **IMPORTANT**: Fix short-lived callback lifetimes (#79) + ### v0.8.2+v0.25.0 - Update C# callback syntax to work on iOS (#84) diff --git a/bindgen/templates/CallbackInterfaceRuntime.cs b/bindgen/templates/CallbackInterfaceRuntime.cs index 382c0c8..1ce6791 100644 --- a/bindgen/templates/CallbackInterfaceRuntime.cs +++ b/bindgen/templates/CallbackInterfaceRuntime.cs @@ -9,30 +9,25 @@ static class UniffiCallbackResponseCode { } class ConcurrentHandleMap where T: notnull { - Dictionary leftMap = new Dictionary(); - Dictionary rightMap = new Dictionary(); + Dictionary map = new Dictionary(); Object lock_ = new Object(); ulong currentHandle = 0; public ulong Insert(T obj) { lock (lock_) { - ulong existingHandle = 0; - if (rightMap.TryGetValue(obj, out existingHandle)) { - return existingHandle; - } currentHandle += 1; - leftMap[currentHandle] = obj; - rightMap[obj] = currentHandle; + map[currentHandle] = obj; return currentHandle; } } public bool TryGet(ulong handle, out T result) { - // Possible null reference assignment - #pragma warning disable 8601 - return leftMap.TryGetValue(handle, out result); - #pragma warning restore 8601 + lock (lock_) { + #pragma warning disable 8601 // Possible null reference assignment + return map.TryGetValue(handle, out result); + #pragma warning restore 8601 + } } public bool Remove(ulong handle) { @@ -43,10 +38,9 @@ public bool Remove(ulong handle, out T result) { lock (lock_) { // Possible null reference assignment #pragma warning disable 8601 - if (leftMap.TryGetValue(handle, out result)) { + if (map.TryGetValue(handle, out result)) { #pragma warning restore 8601 - leftMap.Remove(handle); - rightMap.Remove(result); + map.Remove(handle); return true; } else { return false; diff --git a/dotnet-tests/UniffiCS.BindingTests/TestCallbacksFixture.cs b/dotnet-tests/UniffiCS.BindingTests/TestCallbacksFixture.cs index 2ae7d9c..17d2755 100644 --- a/dotnet-tests/UniffiCS.BindingTests/TestCallbacksFixture.cs +++ b/dotnet-tests/UniffiCS.BindingTests/TestCallbacksFixture.cs @@ -189,4 +189,20 @@ public void VoidCallbackExceptions() ); } } + + [Fact] + public void ShortLivedCallbackDoesNotInvalidateLongerLivedCallback() + { + var stringifier = new CsharpStringifier(); + using (var rustStringifier1 = new RustStringifier(stringifier)) + { + using (var rustStringifier2 = new RustStringifier(stringifier)) + { + Assert.Equal("C#: 123", rustStringifier2.FromSimpleType(123)); + } + // `stringifier` must remain valid after `rustStringifier2` drops the reference + + Assert.Equal("C#: 123", rustStringifier1.FromSimpleType(123)); + } + } } diff --git a/dotnet-tests/UniffiCS.BindingTests/TestNullToEmptyString.cs b/dotnet-tests/UniffiCS.BindingTests/TestNullToEmptyString.cs index e62a69d..06c172e 100644 --- a/dotnet-tests/UniffiCS.BindingTests/TestNullToEmptyString.cs +++ b/dotnet-tests/UniffiCS.BindingTests/TestNullToEmptyString.cs @@ -12,6 +12,8 @@ public class TestNullToEmptyString public void NullToEmptyStringWorks() { Assert.Equal("hello", LibGreeter.HelloWorld("hello")); + #pragma warning disable 8625 // Cannot convert null literal to non-nullable reference type Assert.Equal("", LibGreeter.HelloWorld(null)); + #pragma warning restore 8625 } } diff --git a/generate_bindings.sh b/generate_bindings.sh index 46b6ebd..cc54e86 100755 --- a/generate_bindings.sh +++ b/generate_bindings.sh @@ -6,4 +6,4 @@ GEN_DIR="dotnet-tests/UniffiCS/gen" rm -rf "$GEN_DIR" mkdir -p "$GEN_DIR" -target/debug/uniffi-bindgen-cs target/debug/libuniffi_fixtures.so --library --out-dir="$GEN_DIR" +target/debug/uniffi-bindgen-cs target/debug/libuniffi_fixtures.so --library --out-dir="$GEN_DIR" --no-format