6 ms·
> It should block if and only if there is actual io in flight which could produce a failure return that an application needs. Blocking simply means that the sp
by CyberRabbi 5y ago
> It should block if and only if there is actual io in flight which could produce a failure return that an application needs.
Blocking simply means that the specification does not guarantee an upper bound on the completion time. There is no other meaningful definition. POSIX is not an RTOS therefore nearly all system calls block. The alternative is that the specification guarantees an upper bound on completion time. In that case what is an acceptable upper bound for close() to complete in? 1ms? 10ms? 100ms? Any answer diminishes the versatility of the POSIX VFS.
> Syscalls should be fast unless there is a very good reason not to be.
I think this is an instance of confusing what should be with what is. We’ve been through this before with O_PONIES. The reality is that system calls aren’t “fast” and they can’t portably or dynamically be guaranteed to be fast. So far the only exception to this is gettimeofday() and friends.
Robust systems aren’t built on undocumented assumptions. Again, POSIX is not an RTOS. Anything you build that assumes a deterministic upper bound to a blocking system call execution time will inevitably break, evidenced by OP.
- virtue3 5y agoVery similar to people using node.getenv in hot sections of code and the resulting not understanding what's happening. https://github.com/nodejs/node/issues/3104 https://github.com/nodejs/node/issues/3104 When you call out to the sys or libc things are going to happen and you should try and be aware of what those are.
- fao_ 5y agoSorry... what? Why the hell was an application using env() to carry application state?! The environment list is created at init, it's literally placed right behind the C argument list as an array -- AUXV if you want to go read the ABI Specification for it. Therefore, anything you grab using getenv() can be considered to be static (Barring use of setenv), so the proper and correct thing to do is shove the things you need into a variable at init. Unless you yourself are editing it, but you should still use a variable because variables are typed and getenv is not (Thinking along the lines of storing port information, or whatever, where you need to parse it into a string to get it into the environment, and then need to parse it out of a string). For things like $HOME, those only ever change once, and you should really have a list of those that you check, because you will want to check XDG_HOME_DIR, and a few other areas. So you will want those in a list anyway, might as well do it at creation time when the data is fresh. Anything you set with setenv() only alters the your environment state, and that will carry down to newly created children at creation time. So the only reason I can think of why anyone would do this would be to communicate data to child processes. Except there are so, so many better and non-stringly typed ways to do this, including global variables. Child processes inherit copies(?) of their parent's state, you can just use that, so there is literally, NO reason ever to do this.
- thelopa 5y ago… unless you intend to exec after forking
- fao_ 5y agoSure, but just use execvp and it's a damn sight safer because you know exactly the state of your child's environment state. You can see this in the CERT C coding guidelines: https://wiki.sei.cmu.edu/confluence/display/c/ENV03-C.+Sanitize+the+environment+when+invoking+external+programs https://wiki.sei.cmu.edu/confluence/display/c/ENV03-C.+Sanit... also ENV02-C comes into effect, as well, if your program is invoked with SOME_INTERNAL_VARIABLE=1 PORT=2000 ./prog then you try to invoke your child with: setenv("SOME_INTERNAL_VARIABLE", "2", 1); (fork blah blah)
- loeg 5y ago> Blocking simply means that the specification does not guarantee an upper bound on the completion time. I don't think that's a commonly-accepted (or useful) definition of "blocking." By that definition, getpid(2) is blocking. > I think this is an instance of confusing what should be with what is. Who is doing the confusing? I said "should be." Are you saying they're fast now but should be slow? Why? > The reality is that system calls aren’t “fast” and they can’t portably or dynamically be guaranteed to be fast. This isn't a portable program; it's a Linux program. The problem isn't that close can't be portably guaranteed to complete in some time bound; it's that Linux is adding what is essentially an extra usleep(100000), with very high probability, for the devfs synthetic filesystem in Linux. This is entirely an own-goal; Linux has historically explicitly aimed to complete system calls quickly, when that does not break other functionality. It is a bug that can be fixed, e.g., with the proposed patch(es). POSIX does not mandate that close blocks on anything other than removing the index from the fd table -- it's even allowed to leave associated IO in-flight and silently ignore errors. It makes little sense for a synthetic filesystem without real IO to block close so grossly.
- CyberRabbi 5y ago> I don't think that's a commonly-accepted (or useful) definition of "blocking." By that definition, getpid(2) is blocking. When it comes to expecting a specific duration, getpid() is blocking. If you run getpid() in a tight loop and then have performance issues you can’t reasonably blame the system. > This isn't a portable program; it's a Linux program But the interface is a portable interface > POSIX does not mandate that close blocks on anything other than removing the index from the fd table And what if the fd-table is a very large hash table with high collision rate? How do you then specify how quickly close() should complete? 1ms/open fd? 10ms/open fd? Etc. It should be clear that the problem here is that the author of the code had a faulty understanding of the system in which their code runs. Today the issue was close() just happened to be too “slow.” If the amount of input devices were higher, let’s say 2x more, then the same issue would have manifested even if close() were 2x “faster.” No matter how fast you make close() there is a situation in which this issue would manifest itself. I.e. the application has a design flaw.
- 5y ago
- evouga 5y ago> The reality is that system calls aren’t “fast” and they can’t portably or dynamically be guaranteed to be fast. Perhaps, but the reality is also that the vast majority of games and other interactive applications routinely make blocking system calls in a tight main loop and expect these calls to take an unspecified but reasonable amount of time. “It’s a blocking syscall so if it takes 1s to close a file, that’s technically not a bug” is correct, but is any player of “Papers, Please” going to be sympathetic to that explanation? Probably not; they’ll think “Linux is slow,” “Linux is buggy,” “why can’t Linux run basic applications correctly that I have no problem running on Windows or OS X?,” etc. “Syscalls should be fast unless there is a very good reason not to be” strikes me as a wise operating principle, which weights usability and usefulness of the operating system alongside being technically correct.
- CyberRabbi 5y ago> “It’s a blocking syscall so if it takes 1s to close a file, that’s technically not a bug” is correct, but is any player of “Papers, Please” going to be sympathetic to that explanation? Probably not; they’ll think “Linux is slow,” “Linux is buggy,” “why can’t Linux run basic applications correctly that I have no problem running on Windows or OS X?,” etc. I don’t agree with this logic. Windows and macOS system calls also block. The issue of people considering Linux to be slow is not relevant to the fact that its systems calls block. The poorer quality of Linux games, and commercial Linux software in general, is more likely due to smaller market size / profit opportunity and the consequential lack of effort / investment into the Linux desktop/gaming ecosystem. Now if your argument is we should work around buggy applications and distribute hacked patches when the developers have abandoned them for the sake of improving user experience. I agree with that. > “Syscalls should be fast unless there is a very good reason not to be” strikes me as a wise operating principle, which weights usability and usefulness of the operating system alongside being technically correct. Linux already operates by this principle. We are examining a situation where best effort was not good enough to hide poor application design.
- Dylan16807 5y ago> Linux already operates by this principle. We are examining a situation where best effort was not good enough to hide poor application design. Linux has this principle as a goal, but it's probably not checked often. I would say this code fails the principle, independent of particular application problems.
- arghwhat 5y agoThe machine is not running POSIX, it's running Linux which is POSIX-ey, and an RTOS does not guarantee that system calls do not block. The insistence on only referring to POSIX was what caused the O_PONIES debate in the first place. If one assumes that "there is no upper bound on the completion time", then that also means assuming that a poll/read/write will never return within the lifetime of the machine as it could block for that long (maybe you're using this computer: https://www.youtube.com/watch?v=nm0POwEtiqE https://www.youtube.com/watch?v=nm0POwEtiqE), and so it is impossible to implement a functioning, responsive application, much less a game. In the real-world you need to make slightly more reasonable assumptions. And, again, when interacting with device files you must refer to the kernel documentation rather than POSIX, as POSIX does not describe how these files work in any meaningful way or form.
- CyberRabbi 5y ago> poll/read/write The “non-blocking” nature of those calls were invented for network servers, not for video games. Not only is jitter tolerable there but high latency is allowed from the lowest layers of the stack. It’s not uncommon to simply get no response from a network request. A video game should never ever do arbitrary system calls on its main drawing thread unless those system calls are specifically intended for that use case. Jitter is not tolerable in this use case since the timing requirements are so strict. The code must product a frame every 16.6ms, no exceptions. The interface must never become unresponsive. > RTOS does not guarantee that system calls do not block RTOSes do indeed provide upper bounds for all calls. > And, again, when interacting with device files you must refer to the kernel documentation rather than POSIX Yes that would be a relevant point if it were the case that the kernel documentation for these devices specified that close() should complete within some time bound.