-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
misleading or nonexistent errors in path resolving #348
Comments
Thanks for the report. I wasn't aware of this problem. I think the WebAssembly version can't reproduce this issue because it's single-threaded, and so only opens one file at a time. This also happens with my Linux VM when I only allocate one CPU to it. I can only reproduce this on the Linux VM when it has more than one CPU. After some investigation, I don't think I can make this work for extremely small ulimit values because the runtime may open certain files by itself outside of the control of esbuild (e.g. |
I think limiting esbuild to, say, a maximum of 64 open fds at any one time would be ideal - it's not a web server, after all. I'm not suggesting esbuild ought to work with fewer than 16 fds, rather it ought to report the nature of the file failures more accurately and should fail outright if the situation warrants it. |
Yes, I agree. That's why I left this open. I'll try to fix that too. I'd rather feed the error through to the log instead of causing a panic though. That will also give more context on the failure. |
Good stuff. The panic is also not library friendly. react-admin/examples/simple is the gift that keeps on giving. For the record this project has motivated me to learn Go. Half second build time for esbuild itself - that's crazy. A decade in JavaScript and NodeJS has warped my expectations of what a good language + platform should be. |
fwiw, 878781c did not work on my Mac with Just to convince myself I was using my build from latest master, I changed |
878781c is also non-deterministic. With |
That's strange. I can't reproduce that on my machine. I only get problems with What happens when you set the concurrency lower (the Is it possible that https://github.com/macports/macports-legacy-support and/or cgo on an unsupported platform is causing a lot of file handles to be used and not reclaimed? |
Anything is possible. I did not review the code. The only system call I needed was Are you certain there isn't a race condition in esbuild? |
I tried checking this by running the benchmark with dtrace after setting In my case it actually uses file descriptors 3 and 4 because it looks like Go's implementation of But even in this case, limiting |
Here's another data point - when If this patch is applied: --- a/internal/fs/fs.go
+++ b/internal/fs/fs.go
@@ -233 +233 @@ type realFS struct {
-var fileOpenLimit = make(chan bool, 32)
+var fileOpenLimit = make(chan bool, 1) and So there's quite a lot of variability even for a single-threaded wasm runtime. |
I can't get it to respect the DYLD_INSERT_LIBRARIES setting. I'll look at it again tomorrow. While I am the first to acknowledge that I'm running Go on an unsupported platform, it does work and did find a couple of genuine problems. Setting a file descriptor limit to 500+ is not a showstopper. But I wonder if other UNIX like platforms might have similar behavior. |
I think I've isolated the file descriptor leak on my mac to Although it appears that there's a fd leak in an error condition in macports-legacy-support/src/fdopendir.c it doesn't arise in typical use as verified by an fprintf to stderr. --- a/src/fdopendir.c
+++ b/src/fdopendir.c
@@ -58,6 +58,7 @@ DIR *fdopendir(int dirfd) {
dir = opendir (".");
if (best_fchdir(oldCWD) < 0) {
+ if (dir) PROTECT_ERRNO(closedir(dir));
if (oldCWD != -1) PROTECT_ERRNO(close(oldCWD));
return 0;
} The untested patch above would probably address that edge case in macports-legacy-support, but it's not the cause of exhausting the low
I'm not sure where |
I'm closing this issue because the main issues were addressed in the latest release. The native binary file leak seems to be specific to my machine. |
Today I learned that Go has finalizers. This could explain how the dup'd fd encapsulated in a dir and returned by |
I figured out the solution to the --- a/src/fdopendir.c
+++ b/src/fdopendir.c
@@ -30,6 +30,18 @@ int best_fchdir(int dirfd);
#define PROTECT_ERRNO(what) ({ int __err = (errno); what; errno = __err; })
+/*
+ https://www.freebsd.org/cgi/man.cgi?query=fdopendir&sektion=3
+
+ Upon successful return from fdopendir(), the file descriptor is under the
+ control of the system, and if any attempt is made to close the file
+ descriptor, or to modify the state of the associated description other
+ than by means of closedir(), readdir(), readdir_r(), or rewinddir(), the
+ behavior is undefined. Upon calling closedir() the file descriptor is
+ closed. The FD_CLOEXEC flag is set on the file descriptor by a
+ successful call to fdopendir().
+*/
+
DIR *fdopendir(int dirfd) {
DIR *dir;
struct stat st;
@@ -43,8 +55,12 @@ DIR *fdopendir(int dirfd) {
return 0;
}
- if (dirfd == AT_FDCWD)
- return opendir (".");
+ if (dirfd == AT_FDCWD) {
+ dir = opendir (".");
+ /* dirfd can be closed only upon success */
+ if (dir) PROTECT_ERRNO(close(dirfd));
+ return dir;
+ }
oldCWD = open(".", O_RDONLY);
if (oldCWD == -1)
@@ -58,6 +74,7 @@ DIR *fdopendir(int dirfd) {
dir = opendir (".");
if (best_fchdir(oldCWD) < 0) {
+ if (dir) PROTECT_ERRNO(closedir(dir));
if (oldCWD != -1) PROTECT_ERRNO(close(oldCWD));
return 0;
}
@@ -65,6 +82,9 @@ DIR *fdopendir(int dirfd) {
if (oldCWD != -1)
PROTECT_ERRNO(close(oldCWD));
+ /* dirfd can be closed only upon success */
+ if (dir && dirfd != -1) PROTECT_ERRNO(close(dirfd));
+
return dir;
} With this patch react-admin/examples/simple can be successfully bundled with native esbuild on my Mac with |
That's awesome! Congrats on figuring out the underlying issue. It sounds like that was pretty challenging. |
Thanks. I certainly learned a lot about Go as result. |
This comment has been minimized.
This comment has been minimized.
@airhorns In the absence of a reproducible test case, try the debugging techniques in the top post. Consider opening a new issue as few will follow a closed one. /unsubscribing |
I stumbled upon this bug while figuring out how to run the native version of
esbuild
on my machine.If your default
ulimit -n
max open file limit is low thenesbuild
error logging is misleading and not very helpful in determining the cause of the problem. In some cases it can also lead to incorrect bundling output without any error messages logged.Scenario 1: Bundling projects with a large number of source files
Attempt to bundle
react-admin/examples/simple
with the default max open file limit on my machine:Strangely,
esbuild-wasm
worked correctly with the sameulimit -n 256
.Scenario 2: Incorrect bundle output for
require()
statements in presence ofEMFILE
errors (too many open files)Given the following source file and a dozen
require
'd files withconsole.log()
side effects:It works as expected with a sufficiently high
ulimit -n
value:But if the
ulimit -n
value is too low, then the following incorrect output is produced without error or warning - notice the requires are not inlined into the bundle:It appears that
esbuild
's path resolving algorithm incorrectly treats all errors as if they wereENOENT
- no such file or directory.The following patch was useful in debugging the cause of the Scenario 1 failure and might be useful as a starting point for a fix:
Unfortunately, this patch did not log any errors for very low
ulimit -n
values - such as in Scenario 2.The text was updated successfully, but these errors were encountered: