[HN Gopher] Don't Share Java FileChannels
       ___________________________________________________________________
        
       Don't Share Java FileChannels
        
       Author : pkolaczk
       Score  : 34 points
       Date   : 2023-03-12 20:39 UTC (2 hours ago)
        
 (HTM) web link (pkolaczk.github.io)
 (TXT) w3m dump (pkolaczk.github.io)
        
       | CodesInChaos wrote:
       | Would not using these interrupts be a realistic alternative?
        
         | pkolaczk wrote:
         | Actually this was the way we solved it. Although, that's still
         | quite risky and I'd not recommend it as a good workaround.
        
           | p4l4g4 wrote:
           | I would say you can get a long way with trying to prevent
           | your own code from emitting interrupts. But how do you stop
           | libraries or the JVM from emmiting them?
        
         | brazzy wrote:
         | The problem is that the interrupts may be used by library or
         | framework code that you're not aware of, in hard-to-reproduce
         | edge cases.
        
       | vbezhenar wrote:
       | They should have seen ClosedByInterruptException in the logs and
       | the whole mystery would be resolved much faster.
        
       | jesprenj wrote:
       | Don't write multithreaded software (:
        
         | yjftsjthsd-h wrote:
         | I mean, unironically if you can[0] that's probably not a bad
         | approach - just document (or programmatically ensure) that your
         | code isn't multi thread safe and call it a day.
         | 
         | [0] A major caveat, but often true for "boring" business logic
         | applications.
        
           | mcapodici wrote:
           | I have done barely any multithreaded coding, but if I had to
           | I would look at akka etc. first; i.e. use a framework!
        
             | marginalia_nu wrote:
             | Java's actually got pretty sane multithreading support.
             | Some classes are doing things that are a bit unintuitive
             | (like this one), but once you grok the Java Memory Model
             | it's actually very straightforward.
        
             | yjftsjthsd-h wrote:
             | Oh sure - there are lots of great tools to do it these
             | days. My point is mostly that in most cases you can just
             | skip it all together.
        
         | karmakaze wrote:
         | That's the XP way of not sharing FileChannels.
        
       | cutler wrote:
       | Solution: use Clojure's STM. If you listen to Rich Hickey's early
       | presentations following Clojure 1.0 he detailed how spending over
       | a decade trying to write concurrent code in Java with locks drove
       | him to search for something simpler.
        
         | MrBuddyCasino wrote:
         | STM is not that fast. The easier solution is to switch to
         | virtual threads and sidestep the issue.
        
       | Arnavion wrote:
       | So why _does_ Java close the channel when an  "interrupt"
       | happens? I looked at the git-blame of [1] to see if the commit
       | message would explain it, but it's just been there since the
       | first OpenJDK commit in 2007. The only other info I could find by
       | searching for that exception name is [2] but it doesn't elaborate
       | the "IO safety issues". And are these "interrupts" just the
       | underlying syscall returning EINTR or something Java-specific?
       | 
       | [1]:
       | https://github.com/openjdk/jdk/blame/c313e1ac7b3305b1c012755...
       | 
       | [2]: https://stackoverflow.com/questions/1161297/
       | 
       | ---
       | 
       | Edit: marginalia_nu's and cesarb's comments make sense. Searching
       | for "interruptiblechannel" yields:
       | 
       | https://www.taogenjia.com/2020/07/13/Java-understanding-nio/
       | 
       | >NIO's designers chose to shut down a channel when a blocked
       | thread is interrupted because they couldn't find a way to
       | reliably handle interrupted I/O operations in the same manner
       | across operating systems. The only way to guarantee deterministic
       | behavior was to shut down the channel.
       | 
       | Since the interrupts being talked about here are a Java concept,
       | Java would need to interrupt the syscall itself, and it makes
       | sense there's no way to do that equally on all OSes.
        
         | marginalia_nu wrote:
         | "AbstractInterruptibleChannel" seems to be doing this, and the
         | comments/javadocs offer some hint. As to why they're designed
         | this way, that's a good question.
         | 
         | https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/...
        
         | cesarb wrote:
         | > And are these "interrupts" just the underlying syscall
         | returning EINTR or something Java-specific?
         | 
         | It's something Java-specific:
         | https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.h...
        
       | Arnavion wrote:
       | And for those looking for the equivalent POSIX C API, it's
       | `pread` and `pwrite`. I've come across a lot of people (me
       | included) who resort to locking + seek because they don't know
       | these exist.
        
       | avg_dev wrote:
       | I enjoyed the article. Long time since I programmed in Java. I
       | recall being recommended a book called Java Concurrency in
       | Practice and it languishing on my shelf.
       | 
       | I had a few notes:
       | 
       | 1. It is really nice to have such thorough documentation. Even if
       | a programmer doesn't always have or make the time to read it.
       | 
       | 2. I _think_ I remember reading an interview with Peter Norvig in
       | Coders at Work where he talks about programmers never having the
       | time to fully grok their API docs (it may well have been a more
       | general statement about rushing to get stuff done).
       | 
       | Some time ago I personally learned a virtualization discovery API
       | whereby one could make some calls and learn, through some sort of
       | traversal, how a topology of VMs was laid out. My title at the
       | time was Intermediate Software Developer. I remember I was pretty
       | happy with my solution and shared during standup that it was
       | working well but was kind of slow, and that there was another,
       | more complicated and finicky type of traversal mentioned in the
       | docs, and that my reading of the docs was that learning and
       | coding this other method was necessary or helpful is some use
       | cases but that for our situation it would not make a difference
       | and would just add complexity. Well within a couple of weeks
       | another team member - Junior Software Developer - read the same
       | docs and tried out the more finicky version and wouldn't you
       | know, the discovery process suddenly became blazing fast.
        
       | spuz wrote:
       | Good insights. Would the solution therefore be to synchronize on
       | all the FileChannel methods (not just those you think you need to
       | synchronize on) or is there another way to get around the too
       | many open files error?
        
         | p4l4g4 wrote:
         | You can't just lock on the file operations, since this problem
         | comes from thread interruptions. No interrupt, no problem. So,
         | instead you need to make file operations and _any_ thread
         | interrupt mutual exclusive.
         | 
         | Finding and patching all possible locations which could
         | interrupt your threads doing file operations is probably a
         | foolish effort.
         | 
         | So, raising the limit, or load balancing (depending on the type
         | of application) is probably the best solution.
        
         | CodesInChaos wrote:
         | > is there another way to get around the too many open files
         | error?
         | 
         | Since this isn't an actual leak, raising the limit should be
         | fine. The default limit on Linux is 1024 due to some issues
         | with SELECT, but you can easily raise it to a much higher value
         | if you don't use that API.
        
           | mritun wrote:
           | Raising the FD limit is the right answer. 1024 is absurdly
           | low in modern context and is a carry over from the past that
           | needs to die.
        
             | [deleted]
        
         | pianoben wrote:
         | Cache the file contents, perhaps? Isolate actual file I/O to
         | dedicated threads and vend reads and writes from it? Buffer
         | writes in-memory, only flushing at some interval or when the
         | buffer fills up? Use a DB server and not raw files?
         | 
         | Lots of ways to skin this cat, but it really depends on what
         | the application is doing and why.
        
         | agilob wrote:
         | I don't know much about these file descriptors but in this case
         | I would protect it using ReentrantLock or Monitor from Guava
         | 
         | https://docs.oracle.com/javase/7/docs/api/java/util/concurre...
         | 
         | They are more powerful than synchronized and can produce log
         | messages when things go sideways with locking, dead locking or
         | external crashes.
        
         | stefan_ wrote:
         | You have now serialized all your request handlers. No, just
         | raise the fricking limit. It's a holdover.
        
       ___________________________________________________________________
       (page generated 2023-03-12 23:00 UTC)