aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md4
-rw-r--r--src/stdlib/paths.c36
2 files changed, 35 insertions, 5 deletions
diff --git a/CHANGES.md b/CHANGES.md
index c717a95e..bfac654b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -33,6 +33,10 @@
- `Text.from_utf32()`/`Text.utf32()`
- Fixed bugs:
- `Int.parse()` had a memory bug.
+ - Breaking out of a `for line in file.by_line()!` loop would leak file handle
+ resources, which could lead to exhausting the number of open file handles.
+ When that happens, the standard library now forces a GC collection to clean
+ up resources, which can result in file handles being freed up.
## v0.3
diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c
index 385f3bdf..cd7a9c6d 100644
--- a/src/stdlib/paths.c
+++ b/src/stdlib/paths.c
@@ -280,7 +280,15 @@ static void _write(Path_t path, List_t bytes, int mode, int permissions) {
path = Path$expand_home(path);
const char *path_str = Path$as_c_string(path);
int fd = open(path_str, mode, permissions);
- if (fd == -1) fail("Could not write to file: ", path_str, "\n", strerror(errno));
+ if (fd == -1) {
+ if (errno == EMFILE || errno == ENFILE) {
+ // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will
+ // be closed by GC finalizers.
+ GC_gcollect();
+ fd = open(path_str, mode, permissions);
+ if (fd == -1) fail("Could not write to file: ", path_str, "\n", strerror(errno));
+ }
+ }
if (bytes.stride != 1) List$compact(&bytes, 1);
ssize_t written = write(fd, bytes.data, (size_t)bytes.length);
@@ -313,8 +321,17 @@ void Path$append_bytes(Path_t path, List_t bytes, int permissions) {
public
OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) {
path = Path$expand_home(path);
- int fd = open(Path$as_c_string(path), O_RDONLY);
- if (fd == -1) return NONE_LIST;
+ const char *path_str = Path$as_c_string(path);
+ int fd = open(path_str, O_RDONLY);
+ if (fd == -1) {
+ if (errno == EMFILE || errno == ENFILE) {
+ // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will
+ // be closed by GC finalizers.
+ GC_gcollect();
+ fd = open(path_str, O_RDONLY);
+ if (fd == -1) return NONE_LIST;
+ }
+ }
struct stat sb;
if (fstat(fd, &sb) != 0) return NONE_LIST;
@@ -660,8 +677,17 @@ public
OptionalClosure_t Path$by_line(Path_t path) {
path = Path$expand_home(path);
- FILE *f = fopen(Path$as_c_string(path), "r");
- if (f == NULL) return NONE_CLOSURE;
+ const char *path_str = Path$as_c_string(path);
+ FILE *f = fopen(path_str, "r");
+ if (f == NULL) {
+ if (errno == EMFILE || errno == ENFILE) {
+ // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will
+ // be closed by GC finalizers.
+ GC_gcollect();
+ f = fopen(path_str, "r");
+ if (f == NULL) return NONE_CLOSURE;
+ }
+ }
FILE **wrapper = GC_MALLOC(sizeof(FILE *));
*wrapper = f;