From: Eric Wong Date: 2012-10-27T14:12:49+09:00 Subject: [ruby-core:48452] Re: [ruby-trunk - Feature #7220] Separate IO#dup, StringIO#initialize_copy from dup(2) "brixen (Brian Ford)" wrote: > There are serious problems with aliasing the same fd as IO#dup does. > It is not consistent across platforms. At the very least, the > implementation leaves huge holes, like allowing one fd to be closed, > and that IO's #closed? will return true, but the aliasing IO's > #closed? will return false, and closing it will raise Errno::EBADF. > That behavior shouldn't be behind a common Ruby method or concept. On *nix, IO#dup creates a _new_ fd, but _not_ a new file handle (inside the kernel). The close() syscall decrements a refcount and only releases the file handle when there are no other references to it. I don't know non-*nix platforms, but assuming those platforms don't support fork(), either, a non-*nix IO#close/#dup which emulates POSIX semantics could probably work like this: FD_REFCOUNT = [] # fileno => refcount FD_REFCOUNT_LOCK = Mutex.new def close raise IOError if @closed @closed = true FD_REFCOUNT_LOCK.synchronize do FD_REFCOUNT[fileno] -= 1 refcount = FD_REFCOUNT[fileno] # call real close() only when refcount is zero sysclose(fileno) if refcount == 0 end end def dup raise IOError if @closed FD_REFCOUNT_LOCK.synchronize do FD_REFCOUNT[fileno] += 1 end super end This is basically what happens inside the kernel anyways (except the kernel is multi-process-aware). For platforms without fork(), you should be able to emulate POSIX dup()/close() semantics using the example above as a starting point. > Further, if StringIO is supposed to mimic all this, there are numerous > bugs because StringIO instances that are aliases via #dup do report > the same value for eg closed?. Yeah, StringIO looks buggy here (but I don't expect StringIO to ever perfectly match IO).