mirror of
https://github.com/janet-lang/janet
synced 2026-03-03 06:19:48 +00:00
Dup io file descriptors when marshalling closable files.
For unclosable files, no need to dup, but for closable files we can get a resource leak. Valgrind and similar tools won't catch this but IO will unexpectedly start going to the wrong descriptor if a file was transferred to a new thread, closed, and then a new file was created.
This commit is contained in:
@@ -4014,7 +4014,7 @@
|
||||
"handler not supported for :datagram servers")
|
||||
(def s (net/listen host port type no-reuse))
|
||||
(if handler
|
||||
(ev/go (fn [] (net/accept-loop s handler))))
|
||||
(ev/go (fn :net/server-handler [] (net/accept-loop s handler))))
|
||||
s))
|
||||
|
||||
###
|
||||
@@ -4670,8 +4670,7 @@
|
||||
|
||||
(defn- run-main
|
||||
[env subargs arg]
|
||||
(when-let [entry (in env 'main)
|
||||
main (or (get entry :value) (in (get entry :ref) 0))]
|
||||
(when-let [main (module/value env 'main)]
|
||||
(def guard (if (get env :debug) :ydt :y))
|
||||
(defn wrap-main [&]
|
||||
(main ;subargs))
|
||||
|
||||
@@ -109,10 +109,11 @@ static int32_t checkflags(const uint8_t *str) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void *makef(FILE *f, int32_t flags) {
|
||||
static void *makef(FILE *f, int32_t flags, size_t bufsize) {
|
||||
JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
|
||||
iof->file = f;
|
||||
iof->flags = flags;
|
||||
iof->vbufsize = bufsize;
|
||||
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
||||
/* While we would like fopen to set cloexec by default (like O_CLOEXEC) with the e flag, that is
|
||||
* not standard. */
|
||||
@@ -164,6 +165,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
flags = JANET_FILE_READ;
|
||||
}
|
||||
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
||||
size_t bufsize = BUFSIZ;
|
||||
if (f != NULL) {
|
||||
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
||||
struct stat st;
|
||||
@@ -173,7 +175,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
janet_panicf("cannot open directory: %s", fname);
|
||||
}
|
||||
#endif
|
||||
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||
bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||
if (bufsize != BUFSIZ) {
|
||||
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
|
||||
if (result) {
|
||||
@@ -181,7 +183,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
}
|
||||
}
|
||||
}
|
||||
return f ? janet_makefile(f, flags)
|
||||
return f ? janet_wrap_abstract(makef(f, flags, bufsize))
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
@@ -410,12 +412,23 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx) {
|
||||
JanetFile *iof = (JanetFile *)p;
|
||||
if (ctx->flags & JANET_MARSHAL_UNSAFE) {
|
||||
janet_marshal_abstract(ctx, p);
|
||||
int fno = -1;
|
||||
#ifdef JANET_WINDOWS
|
||||
janet_marshal_int(ctx, _fileno(iof->file));
|
||||
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
|
||||
fno = _fileno(iof->file);
|
||||
} else {
|
||||
fno = _dup(_fileno(iof->file));
|
||||
}
|
||||
#else
|
||||
janet_marshal_int(ctx, fileno(iof->file));
|
||||
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
|
||||
fno = fileno(iof->file);
|
||||
} else {
|
||||
fno = dup(fileno(iof->file));
|
||||
}
|
||||
#endif
|
||||
janet_marshal_int(ctx, fno);
|
||||
janet_marshal_int(ctx, iof->flags);
|
||||
janet_marshal_size(ctx, iof->vbufsize);
|
||||
} else {
|
||||
janet_panic("cannot marshal file in safe mode");
|
||||
}
|
||||
@@ -444,6 +457,11 @@ static void *io_file_unmarshal(JanetMarshalContext *ctx) {
|
||||
} else {
|
||||
iof->flags = flags;
|
||||
}
|
||||
iof->vbufsize = janet_unmarshal_size(ctx);
|
||||
if (iof->vbufsize != BUFSIZ) {
|
||||
int result = setvbuf(iof->file, NULL, iof->vbufsize ? _IOFBF : _IONBF, iof->vbufsize);
|
||||
janet_assert(!result, "unmarshal setvbuf");
|
||||
}
|
||||
return iof;
|
||||
} else {
|
||||
janet_panic("cannot unmarshal file in safe mode");
|
||||
@@ -785,11 +803,11 @@ FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags) {
|
||||
}
|
||||
|
||||
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
|
||||
return makef(f, flags);
|
||||
return makef(f, flags, BUFSIZ);
|
||||
}
|
||||
|
||||
Janet janet_makefile(FILE *f, int32_t flags) {
|
||||
return janet_wrap_abstract(makef(f, flags));
|
||||
return janet_wrap_abstract(makef(f, flags, BUFSIZ));
|
||||
}
|
||||
|
||||
JanetAbstract janet_checkfile(Janet j) {
|
||||
|
||||
@@ -1281,6 +1281,7 @@ typedef struct JanetFile JanetFile;
|
||||
struct JanetFile {
|
||||
FILE *file;
|
||||
int32_t flags;
|
||||
size_t vbufsize;
|
||||
};
|
||||
|
||||
/* For janet_try and janet_restore */
|
||||
|
||||
@@ -84,4 +84,21 @@
|
||||
(assert-error "cannot schedule non-new fiber"
|
||||
(ev/go f))
|
||||
|
||||
# IO file copying
|
||||
(os/mkdir "tmp")
|
||||
(def f-original (file/open "tmp/out.txt" :wb))
|
||||
(xprin f-original "hello\n")
|
||||
(file/flush f-original)
|
||||
(ev/do-thread
|
||||
# Closes a COPY of the original file, otherwise we get a user-after-close file descriptor
|
||||
(:close f-original))
|
||||
(def g-original (file/open "tmp/out2.txt" :wb))
|
||||
(xprin g-original "world1\n")
|
||||
(xprin f-original "world2\n")
|
||||
(:close f-original)
|
||||
(xprin g-original "abc\n")
|
||||
(:close g-original)
|
||||
(assert (deep= @"hello\nworld2\n" (slurp "tmp/out.txt")) "file threading 1")
|
||||
(assert (deep= @"world1\nabc\n" (slurp "tmp/out2.txt")) "file threading 2")
|
||||
|
||||
(end-suite)
|
||||
|
||||
Reference in New Issue
Block a user