You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
zlib.*Sync methods fire asynchorous callbacks #1668
I didn't say garbage collection was broken, I said your expectations of it were incorrect and that there needed to be documentation to explain that.
In this case, the objects are not collectable simply because they are still accessible by future code--the close emitting function. That can be fixed.
JavaScript in general is not a good language for sync looping though, as you need to trigger GC manually and hope that enough things are collectable to not rapidly run out of memory.
@targos commented 20 days ago
I was running a script to read sequentially a lot of gzipped files, and print on stdout the result of a computation for each file.
After about 16'000 files, the process just stopped and Killed was printed on the terminal.
@indutny On a side note, zlib.* methods (both sync and async) are collected only during full GC runs. Could something be done with that? Edit: It's Buffers fault: #1671.
@bnoordhuis But those events are not public in *Sync case and can't be used outside of the core code. I am not even sure if they are needed in the *Sync case.
@bnoordhuis just curious, is there ever a time where a synchronous task allows adding event listeners?
It happens in other places too (e.g. fs.createSyncReadStream(), process.stdout).
It pretty much always causes problems, and I think it's a broken pattern that we shouldn't use.
For the specific problem demonstrated with @ChALkeR's test code:
'use strict';
var zlib = require('zlib');
var data = 'abcdefghijklmnopqrstuvwxyz';
var gzipped = zlib.gzipSync(data);
var time = process.hrtime();
var count = 0, limit = 800000, block = 20000;
function call() {
var contents = zlib.gunzipSync(gzipped);
count++;
if (count % block === 0) {
var t = process.hrtime(time);
console.log(count + ': ' + block / (t[0] + t[1] / 1e9) + ' per second ' + JSON.stringify(process.memoryUsage()));
time = process.hrtime();
}
}
for (var i = 0; i < limit; i++) {
call();
}
This appears to be fixed by just getting rid of the mechanism to emit close events altogether on the internal-ish Zlib object. It is not used internally and it is not documented. Still, that would probably be a semver-major move and perhaps an undesirable one? #5708
Another option is to add an options object as a parameter to (again, internal-ish) Zlib.prototype.close() and set an option indicating that the thing is invoked by a *Sync() function (and thus no event should be queued). Effective but perhaps too inelegant? #5707
Activity
ChALkeR commentedon May 10, 2015
@trevnorris
ChALkeR commentedon May 10, 2015
@Qard It's not the garbage collector that is broken as you supposed in #1665 (comment), it's
zlib.*Syncmethods not working as expected.ChALkeR commentedon May 10, 2015
That's since 07d3b21, I suppose.
I might be wrong, I haven't tested that commit.
@isaacs
Qard commentedon May 10, 2015
I didn't say garbage collection was broken, I said your expectations of it were incorrect and that there needed to be documentation to explain that.
In this case, the objects are not collectable simply because they are still accessible by future code--the close emitting function. That can be fixed.
JavaScript in general is not a good language for sync looping though, as you need to trigger GC manually and hope that enough things are collectable to not rapidly run out of memory.
trevnorris commentedon May 10, 2015
@ChALkeR Can you give me a code example where that manifests itself as a problem?
ChALkeR commentedon May 10, 2015
@trevnorris I initially posted that to the issue: #1665 (comment) has the code sample, #1479 is the original report.
trevnorris commentedon May 10, 2015
@ChALkeR Thanks. IMO it's a bug.
ChALkeR commentedon May 11, 2015
@indutny On a side note,
zlib.*methods (both sync and async) are collected only during full GC runs. Could something be done with that?Edit: It's
Buffers fault: #1671.ChALkeR commentedon May 17, 2015
Is that the case for
*Syncmethods? Will it be safe not to defer the event for*Syncmethods?bnoordhuis commentedon May 17, 2015
Yes. Events always need to be asynchronous. The problem in a nutshell:
In the first example, if
'complete'is synchronous, you don't have time to attach an event listener.In the second example, if the callback is synchronous, you get infinite recursion.
ChALkeR commentedon May 17, 2015
@bnoordhuis But those events are not public in
*Synccase and can't be used outside of the core code. I am not even sure if they are needed in the*Synccase.trevnorris commentedon May 18, 2015
@bnoordhuis just curious, is there ever a time where a synchronous task allows adding event listeners?
piscisaureus commentedon May 18, 2015
It happens in other places too (e.g.
fs.createSyncReadStream(),process.stdout).It pretty much always causes problems, and I think it's a broken pattern that we shouldn't use.
trevnorris commentedon May 18, 2015
@piscisaureus Sorry, I should have clarified the question. Is there ever a time where the public API allows attaching events to a synchronous task?
2 remaining items
Trott commentedon Mar 15, 2016
For the specific problem demonstrated with @ChALkeR's test code:
This appears to be fixed by just getting rid of the mechanism to emit
closeevents altogether on the internal-ishZlibobject. It is not used internally and it is not documented. Still, that would probably be a semver-major move and perhaps an undesirable one? #5708Another option is to add an
optionsobject as a parameter to (again, internal-ish)Zlib.prototype.close()and set an option indicating that the thing is invoked by a*Sync()function (and thus no event should be queued). Effective but perhaps too inelegant? #5707Trott commentedon Mar 17, 2016
I believe #5707 is a fix for this. PTAL