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

Skip to content

Commit 5a28fd4

Browse files
committed
delete target object from event handler collections when it has no more event handlers
fixes #1972
1 parent 6838ee1 commit 5a28fd4

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1313

1414
### Fixed
1515

16+
- Fixed objects leaking when Python attached event handlers to them even if they were later removed
17+
1618

1719
## [3.0.0](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.0) - 2022-09-29
1820

src/embed_tests/Events.cs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Threading;
4+
5+
using NUnit.Framework;
6+
7+
using Python.Runtime;
8+
9+
namespace Python.EmbeddingTest;
10+
11+
public class Events
12+
{
13+
[OneTimeSetUp]
14+
public void SetUp()
15+
{
16+
PythonEngine.Initialize();
17+
}
18+
19+
[OneTimeTearDown]
20+
public void Dispose()
21+
{
22+
PythonEngine.Shutdown();
23+
}
24+
25+
[Test]
26+
public void UsingDoesNotLeak()
27+
{
28+
using var scope = Py.CreateScope();
29+
scope.Exec(@"
30+
import gc
31+
32+
from Python.EmbeddingTest import ClassWithEventHandler
33+
34+
def event_handler():
35+
pass
36+
37+
for _ in range(2000):
38+
example = ClassWithEventHandler()
39+
example.LeakEvent += event_handler
40+
example.LeakEvent -= event_handler
41+
del example
42+
43+
gc.collect()
44+
");
45+
Runtime.Runtime.TryCollectingGarbage(10);
46+
Assert.AreEqual(0, ClassWithEventHandler.alive);
47+
}
48+
}
49+
50+
public class ClassWithEventHandler
51+
{
52+
internal static int alive;
53+
54+
public event EventHandler LeakEvent;
55+
private Array arr; // dummy array to exacerbate memory leak
56+
57+
public ClassWithEventHandler()
58+
{
59+
Interlocked.Increment(ref alive);
60+
this.arr = new int[800];
61+
}
62+
63+
~ClassWithEventHandler()
64+
{
65+
Interlocked.Decrement(ref alive);
66+
}
67+
}

src/runtime/Util/EventHandlerCollection.cs

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ internal bool RemoveEventHandler(BorrowedReference target, BorrowedReference han
9999
continue;
100100
}
101101
list.RemoveAt(i);
102+
if (list.Count == 0)
103+
{
104+
Remove(key);
105+
}
102106
return true;
103107
}
104108

0 commit comments

Comments
 (0)