8000 delete target object from event handler collections when it has no mo… · pythonnet/pythonnet@5a28fd4 · GitHub
[go: up one dir, main page]

Skip to content
< 8000 /div>

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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 67 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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)
0