Skip to content

Commit b70a610

Browse files
makonigwynne
andauthored
Refactor encoder property to weak reference in KeyedContainerImpl and UnkeyedContainerImpl to prevent retain cycles (#239)
* Refactor encoder property to weak reference in KeyedContainerImpl and UnkeyedContainerImpl to prevent retain cycles * Add memory growth tests for Leaf rendering * Revert "Refactor encoder property to weak reference in KeyedContainerImpl and UnkeyedContainerImpl to prevent retain cycles" This reverts commit 9a79408. * Update Tests/LeafTests/LeafMemoryGrowthTests.swift Co-authored-by: Gwynne Raskind <gwynne@darkrainfall.org> --------- Co-authored-by: Gwynne Raskind <gwynne@darkrainfall.org>
1 parent d469584 commit b70a610

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

Sources/Leaf/LeafEncoder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ extension LeafEncoder {
151151
}
152152

153153
private final class KeyedContainerImpl<Key>: KeyedEncodingContainerProtocol, LeafEncodingResolvable where Key: CodingKey {
154-
private let encoder: EncoderImpl
154+
private unowned let encoder: EncoderImpl
155155
private var data: [String: any LeafEncodingResolvable] = [:]
156156
private var nestedEncoderCaptures: [AnyObject] = []
157157

@@ -230,7 +230,7 @@ extension LeafEncoder {
230230
}
231231

232232
private final class UnkeyedContainerImpl: UnkeyedEncodingContainer, LeafEncodingResolvable {
233-
private let encoder: EncoderImpl
233+
private unowned let encoder: EncoderImpl
234234
private var data: [any LeafEncodingResolvable] = []
235235
private var nestedEncoderCaptures: [AnyObject] = []
236236

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import XCTest
2+
import Leaf
3+
import LeafKit
4+
import XCTVapor
5+
6+
/*
7+
to profile memory growth, use an sh script like this:
8+
```bash
9+
#!/bin/zsh
10+
swift test --filter LeafMemoryGrowthTests &
11+
sleep 5
12+
PID=$(ps aux | grep '[l]eafPackageTests' | awk '{print $2}' | head -n1)
13+
echo "leafPackageTests PID: $PID"
14+
leaks $PID
15+
```
16+
*/
17+
final class LeafMemoryGrowthTests: XCTestCase {
18+
// func testRepeatedRenderMemoryGrowth() async throws {
19+
// sleep(3) // Keep process alive for leaks profiling
20+
// var test = TestFiles()
21+
// test.files["/foo.leaf"] = "Hello #(name)!"
22+
//
23+
// try await withApp { app in
24+
// app.views.use(.leaf)
25+
// app.leaf.sources = .singleSource(test)
26+
// struct Foo: Encodable { var name: String }
27+
// // Render with context 1000 times
28+
// for _ in 0..<1000 {
29+
// _ = try await app.leaf.renderer.render(path: "foo", context: Foo(name: "World")).get()
30+
// }
31+
// }
32+
// sleep(10) // Keep process alive for leaks profiling
33+
// }
34+
//
35+
// func testRepeatedRenderNoContextMemoryGrowth() async throws {
36+
// sleep(3) // Keep process alive for leaks profiling
37+
// var test = TestFiles()
38+
// test.files["/foo.leaf"] = "Hello!"
39+
//
40+
// try await withApp { app in
41+
// app.views.use(.leaf)
42+
// app.leaf.sources = .singleSource(test)
43+
// // Render without context 1000 times (pass nil context)
44+
// for _ in 0..<1000 {
45+
// _ = try await app.leaf.renderer.render(path: "foo", context: Optional<Bool>.none).get()
46+
// }
47+
// }
48+
// sleep(10) // Keep process alive for leaks profiling
49+
// }
50+
}

0 commit comments

Comments
 (0)