Skip to content

Commit b488cb8

Browse files
committed
Runtime: fix memory leak wrt channels
1 parent 950b56a commit b488cb8

File tree

5 files changed

+65
-29
lines changed

5 files changed

+65
-29
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
## Bug fixes
1010
* Fix small bug in global data flow analysis (#1768)
11+
* Runtime: no longer leak channels
1112

1213
# 5.9.1 (02-12-2024) - Lille
1314

dune-project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
(depends
2121
(ocaml (and (>= 4.08) (< 5.4)))
2222
(num :with-test)
23-
(ppx_expect (and (>= v0.14.2) :with-test))
23+
(ppx_expect (and (>= v0.16.1) :with-test))
2424
(ppxlib (>= 0.15.0))
2525
(re :with-test)
2626
(cmdliner (>= 1.1.0))

js_of_ocaml-compiler.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ depends: [
1515
"dune" {>= "3.17"}
1616
"ocaml" {>= "4.08" & < "5.4"}
1717
"num" {with-test}
18-
"ppx_expect" {>= "v0.14.2" & with-test}
18+
"ppx_expect" {>= "v0.16.1" & with-test}
1919
"ppxlib" {>= "0.15.0"}
2020
"re" {with-test}
2121
"cmdliner" {>= "1.1.0"}

runtime/js/io.js

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,17 @@ var caml_sys_fds = new Array(3);
2525
//Provides: caml_sys_close
2626
//Requires: caml_sys_fds
2727
function caml_sys_close(fd) {
28-
var file = caml_sys_fds[fd];
29-
if (file) file.close();
28+
var x = caml_sys_fds[fd];
29+
if (x && x.file) x.file.close();
3030
delete caml_sys_fds[fd];
3131
return 0;
3232
}
3333

34+
//Provides: MlChanid
35+
function MlChanid(id) {
36+
this.id = id;
37+
}
38+
3439
//Provides: caml_sys_open
3540
//Requires: caml_raise_sys_error
3641
//Requires: MlFakeFd_out
@@ -39,11 +44,16 @@ function caml_sys_close(fd) {
3944
//Requires: fs_node_supported
4045
//Requires: caml_sys_fds
4146
//Requires: caml_sys_open_for_node
47+
//Requires: MlChanid
4248
function caml_sys_open_internal(file, idx) {
49+
var chanid;
4350
if (idx === undefined) {
4451
idx = caml_sys_fds.length;
45-
}
46-
caml_sys_fds[idx] = file;
52+
chanid = new MlChanid(idx);
53+
} else if (caml_sys_fds[idx]) {
54+
chanid = caml_sys_fds[idx].chanid;
55+
} else chanid = new MlChanid(idx);
56+
caml_sys_fds[idx] = { file: file, chanid: chanid };
4757
return idx | 0;
4858
}
4959
function caml_sys_open(name, flags, _perms) {
@@ -125,41 +135,59 @@ function caml_ml_set_channel_name(chanid, name) {
125135
}
126136

127137
//Provides: caml_ml_channels
128-
var caml_ml_channels = new Array();
138+
//Requires: MlChanid
139+
function caml_ml_channels_state() {
140+
this.map = new globalThis.WeakMap();
141+
this.opened = new globalThis.Set();
142+
}
143+
caml_ml_channels_state.prototype.close = function (chanid) {
144+
this.opened.delete(chanid);
145+
};
146+
caml_ml_channels_state.prototype.get = function (chanid) {
147+
return this.map.get(chanid);
148+
};
149+
caml_ml_channels_state.prototype.set = function (chanid, val) {
150+
if (val.opened) this.opened.add(chanid);
151+
return this.map.set(chanid, val);
152+
};
153+
caml_ml_channels_state.prototype.all = function () {
154+
return this.opened.values();
155+
};
156+
157+
var caml_ml_channels = new caml_ml_channels_state();
158+
159+
//Provides: caml_ml_channel_get
160+
//Requires: caml_ml_channels
161+
function caml_ml_channel_get(id) {
162+
return caml_ml_channels.get(id);
163+
}
129164

130165
//Provides: caml_ml_channel_redirect
131166
//Requires: caml_ml_channel_get, caml_ml_channels
132167
function caml_ml_channel_redirect(captured, into) {
133168
var to_restore = caml_ml_channel_get(captured);
134169
var new_ = caml_ml_channel_get(into);
135-
caml_ml_channels[captured] = new_; // XXX
170+
caml_ml_channels.set(captured, new_);
136171
return to_restore;
137172
}
138173

139174
//Provides: caml_ml_channel_restore
140175
//Requires: caml_ml_channels
141176
function caml_ml_channel_restore(captured, to_restore) {
142-
caml_ml_channels[captured] = to_restore; // XXX
177+
caml_ml_channels.set(captured, to_restore);
143178
return 0;
144179
}
145180

146-
//Provides: caml_ml_channel_get
147-
//Requires: caml_ml_channels
148-
function caml_ml_channel_get(id) {
149-
return caml_ml_channels[id]; // XXX
150-
}
151-
152181
//Provides: caml_ml_out_channels_list
153182
//Requires: caml_ml_channels
183+
//Requires: caml_ml_channel_get
184+
//Requires: caml_sys_fds
154185
function caml_ml_out_channels_list() {
155186
var l = 0;
156-
for (var c = 0; c < caml_ml_channels.length; c++) {
157-
if (
158-
caml_ml_channels[c] &&
159-
caml_ml_channels[c].opened &&
160-
caml_ml_channels[c].out
161-
)
162-
l = [0, caml_ml_channels[c].fd, l];
187+
var keys = caml_ml_channels.all();
188+
for (var k of keys) {
189+
var chan = caml_ml_channel_get(k);
190+
if (chan.opened && chan.out) l = [0, k, l];
163191
}
164192
return l;
165193
}
@@ -169,7 +197,9 @@ function caml_ml_out_channels_list() {
169197
//Requires: caml_raise_sys_error
170198
//Requires: caml_sys_open
171199
function caml_ml_open_descriptor_out(fd) {
172-
var file = caml_sys_fds[fd];
200+
var s = caml_sys_fds[fd],
201+
file = s.file,
202+
chanid = s.chanid;
173203
if (file.flags.rdonly) caml_raise_sys_error("fd " + fd + " is readonly");
174204
var buffered = file.flags.buffered !== undefined ? file.flags.buffered : 1;
175205
var channel = {
@@ -182,16 +212,18 @@ function caml_ml_open_descriptor_out(fd) {
182212
buffer: new Uint8Array(65536),
183213
buffered: buffered,
184214
};
185-
caml_ml_channels[channel.fd] = channel;
186-
return channel.fd;
215+
caml_ml_channels.set(chanid, channel);
216+
return chanid;
187217
}
188218

189219
//Provides: caml_ml_open_descriptor_in
190220
//Requires: caml_ml_channels, caml_sys_fds
191221
//Requires: caml_raise_sys_error
192222
//Requires: caml_sys_open
193223
function caml_ml_open_descriptor_in(fd) {
194-
var file = caml_sys_fds[fd];
224+
var s = caml_sys_fds[fd],
225+
file = s.file,
226+
chanid = s.chanid;
195227
if (file.flags.wronly) caml_raise_sys_error("fd " + fd + " is writeonly");
196228
var refill = null;
197229
var channel = {
@@ -205,8 +237,8 @@ function caml_ml_open_descriptor_in(fd) {
205237
buffer: new Uint8Array(65536),
206238
refill: refill,
207239
};
208-
caml_ml_channels[channel.fd] = channel;
209-
return channel.fd;
240+
caml_ml_channels.set(chanid, channel);
241+
return chanid;
210242
}
211243

212244
//Provides: caml_ml_open_descriptor_in_with_flags
@@ -253,10 +285,12 @@ function caml_ml_is_binary_mode(chanid) {
253285
//Provides: caml_ml_close_channel
254286
//Requires: caml_ml_flush, caml_ml_channel_get
255287
//Requires: caml_sys_close
288+
//Requires: caml_ml_channels
256289
function caml_ml_close_channel(chanid) {
257290
var chan = caml_ml_channel_get(chanid);
258291
if (chan.opened) {
259292
chan.opened = false;
293+
caml_ml_channels.close(chanid);
260294
caml_sys_close(chan.fd);
261295
chan.fd = -1;
262296
chan.buffer = new Uint8Array(0);

runtime/js/parsing.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var caml_parser_trace = 0;
2424
//Requires: caml_lex_array, caml_parser_trace,caml_jsstring_of_string
2525
//Requires: caml_ml_output, caml_ml_string_length, caml_string_of_jsbytes
2626
//Requires: caml_jsbytes_of_string, MlBytes
27+
//Requires: caml_sys_fds
2728
function caml_parse_engine(tables, env, cmd, arg) {
2829
var ERRCODE = 256;
2930

@@ -82,7 +83,7 @@ function caml_parse_engine(tables, env, cmd, arg) {
8283

8384
function log(x) {
8485
var s = caml_string_of_jsbytes(x + "\n");
85-
caml_ml_output(2, s, 0, caml_ml_string_length(s));
86+
caml_ml_output(caml_sys_fds[2].chanid, s, 0, caml_ml_string_length(s));
8687
}
8788

8889
function token_name(names, number) {

0 commit comments

Comments
 (0)