Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/SystemConfiguration/NetworkReachability.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,12 +523,18 @@ public StatusCode SetNotification (Notification? callback)
unsafe {
rv = SCNetworkReachabilitySetCallback (Handle, &Callback, &ctx) != 0;
}
if (!rv) {
gch.Free ();
return StatusCodeError.SCError ();
}
} else {
if (callback is null) {
this.notification = null;
unsafe {
rv = SCNetworkReachabilitySetCallback (Handle, null, null) != 0;
}
if (gch.IsAllocated)
gch.Free ();
if (!rv)
return StatusCodeError.SCError ();

Expand Down Expand Up @@ -678,5 +684,12 @@ public bool SetDispatchQueue (DispatchQueue? queue)
GC.KeepAlive (queue);
return result;
}

protected override void Dispose (bool disposing)
{
if (gch.IsAllocated)
gch.Free ();
base.Dispose (disposing);
}
}
}
1 change: 1 addition & 0 deletions tests/cecil-tests/Documentation.KnownFailures.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16350,6 +16350,7 @@ M:StoreKit.SKStoreProductViewController.LoadProduct(StoreKit.StoreProductParamet
M:StoreKit.SKStoreProductViewController.LoadProductAsync(Foundation.NSDictionary,StoreKit.SKAdImpression)
M:StoreKit.SKStoreProductViewController.LoadProductAsync(StoreKit.StoreProductParameters,StoreKit.SKAdImpression)
M:StoreKit.SKStoreProductViewController.remove_Finished(System.EventHandler)
M:SystemConfiguration.NetworkReachability.Dispose(System.Boolean)
M:ThreadNetwork.THClient.CheckPreferredNetworkAsync(Foundation.NSData)
M:ThreadNetwork.THClient.DeleteCredentialsForBorderAgentAsync(Foundation.NSData)
M:ThreadNetwork.THClient.IsPreferredNetworkAvailableAsync
Expand Down
111 changes: 111 additions & 0 deletions tests/monotouch-test/SystemConfiguration/NetworkReachabilityTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#endif
using SystemConfiguration;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace MonoTouchFixtures.SystemConfiguration {

Expand Down Expand Up @@ -142,5 +144,114 @@ public void Schedule ()
Assert.IsTrue (defaultRouteReachability.Schedule (CFRunLoop.Main, CFRunLoop.ModeDefault), "Schedule");
Assert.IsTrue (defaultRouteReachability.Unschedule (CFRunLoop.Main, CFRunLoop.ModeDefault), "Unschedule");
}

[Test]
public void SetNotification ()
{
var ip = new IPAddress (0);
using var reachability = new NetworkReachability (ip);

// Test setting a notification
var statusCode = reachability.SetNotification ((flags) => {
});
Assert.AreEqual (StatusCode.OK, statusCode, "SetNotification should succeed");

// Test clearing the notification (this should free the GCHandle)
statusCode = reachability.SetNotification (null);
Assert.AreEqual (StatusCode.OK, statusCode, "SetNotification(null) should succeed");

// Test setting notification again after clearing
statusCode = reachability.SetNotification ((flags) => {
});
Assert.AreEqual (StatusCode.OK, statusCode, "SetNotification should succeed again");

// Test that disposing also works (should free the GCHandle in Dispose)
}

[Test]
public void SetNotification_GCHandleFreed ()
{
// Create weak references to track GC collection
var weakRefs = new WeakReference [10];

// Create NetworkReachability instances on a background thread
var thread = new Thread (() => {
for (int i = 0; i < 10; i++) {
var ip = new IPAddress (0);
var reachability = new NetworkReachability (ip);

// Set a notification to allocate the GCHandle
reachability.SetNotification ((flags) => {
});

// Store weak reference to track if object is collected
weakRefs [i] = new WeakReference (reachability);

// Dispose to ensure GCHandle is freed
reachability.Dispose ();
}
});

thread.Start ();
thread.Join ();

// Force garbage collection
GC.Collect ();
GC.WaitForPendingFinalizers ();
GC.Collect ();

// Assert that at least one NetworkReachability instance has been collected
var collectedCount = 0;
for (int i = 0; i < weakRefs.Length; i++) {
if (!weakRefs [i].IsAlive) {
collectedCount++;
}
}

Assert.IsTrue (collectedCount > 0, $"Expected at least one NetworkReachability instance to be collected, but {collectedCount} were collected");
}

[Test]
public void SetNotification_GCHandleFreedWithNull ()
{
// Create weak references to track GC collection
var weakRefs = new WeakReference [10];

// Create NetworkReachability instances on a background thread
var thread = new Thread (() => {
for (int i = 0; i < 10; i++) {
var ip = new IPAddress (0);
var reachability = new NetworkReachability (ip);

// Set a notification to allocate the GCHandle
reachability.SetNotification ((flags) => {
});

// Clear notification to free the GCHandle
reachability.SetNotification (null);

// Store weak reference to track if object is collected
weakRefs [i] = new WeakReference (reachability);
}
});

thread.Start ();
thread.Join ();

// Force garbage collection
GC.Collect ();
GC.WaitForPendingFinalizers ();
GC.Collect ();

// Assert that at least one NetworkReachability instance has been collected
var collectedCount = 0;
for (int i = 0; i < weakRefs.Length; i++) {
if (!weakRefs [i].IsAlive) {
collectedCount++;
}
}

Assert.IsTrue (collectedCount > 0, $"Expected at least one NetworkReachability instance to be collected, but {collectedCount} were collected");
}
}
}
Loading