1
- import { hasCrypto , skip } from '../common/index.mjs' ;
2
- if ( ! hasCrypto ) {
3
- skip ( 'requires crypto' ) ;
4
- }
5
-
1
+ import { mustCall } from '../common/index.mjs' ;
6
2
import { Readable } from 'node:stream' ;
7
3
import { memoryUsage } from 'node:process' ;
8
4
import assert from 'node:assert' ;
9
5
import { setImmediate } from 'node:timers/promises' ;
10
- import { test } from 'node:test' ;
11
6
12
7
// Based on: https://github.com/nodejs/node/issues/46347#issuecomment-1413886707
13
8
// edit: make it cross-platform as /dev/urandom is not available on Windows
14
9
15
- test ( 'Stream Readable.toWeb() should not cause memory leak' , async function ( ) {
16
- const { randomBytes } = await import ( 'node:crypto' ) ;
17
-
18
- const MAX_MEM = 256 * 1024 * 1024 ; // 256 MiB
19
-
20
- function checkMemoryUsage ( ) {
21
- assert ( memoryUsage ( ) . arrayBuffers < MAX_MEM ) ;
22
- }
23
-
24
- let end = false ;
10
+ const MAX_MEM = 256 * 1024 * 1024 ; // 256 MiB
25
11
26
- const timeout = setTimeout ( ( ) => {
27
- end = true ;
28
- } , 5000 ) ;
29
-
30
- const randomNodeStream = new Readable ( {
31
- read ( size ) {
32
- if ( end ) {
33
- this . push ( null ) ;
34
- return ;
35
- }
12
+ function checkMemoryUsage ( ) {
13
+ assert ( memoryUsage ( ) . arrayBuffers < MAX_MEM ) ;
14
+ }
36
15
37
- randomBytes ( size , ( err , buf ) => {
38
- if ( err ) {
39
- this . destroy ( err ) ;
40
- return ;
41
- }
16
+ const MAX_BUFFERS = 1000 ;
17
+ let buffersCreated = 0 ;
42
18
43
- this . push ( buf ) ;
44
- } ) ;
19
+ const randomNodeStream = new Readable ( {
20
+ read ( size ) {
21
+ if ( buffersCreated >= MAX_BUFFERS ) {
22
+ this . push ( null ) ;
23
+ return ;
45
24
}
46
- } ) ;
47
25
48
- randomNodeStream . on ( 'error' , ( err ) => {
49
- clearTimeout ( timeout ) ;
50
- assert . fail ( err ) ;
51
- } ) ;
26
+ this . push ( Buffer . alloc ( size ) ) ;
27
+ buffersCreated ++ ;
28
+ }
29
+ } ) ;
52
30
53
- // Before doing anything, make sure memory usage is okay
54
- checkMemoryUsage ( ) ;
31
+ randomNodeStream . on ( 'error' , ( err ) => {
32
+ assert . fail ( err ) ;
33
+ } ) ;
55
34
56
- // Create stream and check memory usage remains okay
35
+ // Before doing anything, make sure memory usage is okay
36
+ checkMemoryUsage ( ) ;
57
37
58
- const randomWebStream = Readable . toWeb ( randomNodeStream ) ;
38
+ // Create stream and check memory usage remains okay
59
39
60
- checkMemoryUsage ( ) ;
40
+ const randomWebStream = Readable . toWeb ( randomNodeStream ) ;
61
41
62
- try {
42
+ checkMemoryUsage ( ) ;
43
+
44
+ let timeout ;
45
+ try {
46
+ // Wait two seconds before consuming the stream to see if memory usage increases
47
+ timeout = setTimeout ( mustCall ( async ( ) => {
48
+ // Did the stream leak memory?
49
+ checkMemoryUsage ( ) ;
63
50
// eslint-disable-next-line no-unused-vars
64
51
for await ( const _ of randomWebStream ) {
65
52
// Yield event loop to allow garbage collection
@@ -68,8 +55,10 @@ test('Stream Readable.toWeb() should not cause memory leak', async function() {
68
55
// check memory usage remains okay
69
56
checkMemoryUsage ( ) ;
70
57
}
71
- } catch ( err ) {
58
+ } ) , 2000 ) ;
59
+ } catch ( err ) {
60
+ if ( timeout ) {
72
61
clearTimeout ( timeout ) ;
73
- assert . fail ( err ) ;
74
62
}
75
- } ) ;
63
+ assert . fail ( err ) ;
64
+ }
0 commit comments