openFile gives "file is locked" error on Linux when creating a non-existing file
Viktor Dukhovni
ietf-dane at dukhovni.org
Tue Oct 8 10:27:04 UTC 2024
On Tue, Oct 08, 2024 at 01:15:40PM +0530, Harendra Kumar wrote:
> On Tue, 8 Oct 2024 at 11:50, Viktor Dukhovni <ietf-dane at dukhovni.org> wrote:
>
> > What sort of filesystem is "/tmp/fsevent_dir-.../watch-root" located in?
>
> This happens on github Linux CI. Not sure which filesystem they are
> using. Earlier I was wondering if something funny is happening in case
> they are using NFS. But NFS usually causes issues due to caching of
> directory entries if we are doing cross-node operations, here we are
> on a single node and operations are not running in parallel (or that's
> what I believe). I will remove the hspec layer from the tests to make
> sure that the code is simpler and our understanding is correct.
>
> I will also run the tests on circle-ci to check if the problem occurs
> there. I have never seen this problem in testing this on a Linux
> machine on AWS even if I ran the tests for days in a loop.
Looking more closely at the GHC code, we see that there's an internal
(RTS not OS level) exclusive lock on the (device, inode) pair as part of
opening a Unix file for writes, or shared lock for reads.
rts/FileLock.c:
int
lockFile(StgWord64 id, StgWord64 dev, StgWord64 ino, int for_writing)
{
Lock key, *lock;
ACQUIRE_LOCK(&file_lock_mutex);
key.device = dev;
key.inode = ino;
lock = lookupHashTable_(obj_hash, (StgWord)&key, hashLock, cmpLocks);
if (lock == NULL)
{
lock = stgMallocBytes(sizeof(Lock), "lockFile");
lock->device = dev;
lock->inode = ino;
lock->readers = for_writing ? -1 : 1;
insertHashTable_(obj_hash, (StgWord)lock, (void *)lock, hashLock);
insertHashTable(key_hash, id, lock);
RELEASE_LOCK(&file_lock_mutex);
return 0;
}
else
{
// single-writer/multi-reader locking:
if (for_writing || lock->readers < 0) {
RELEASE_LOCK(&file_lock_mutex);
return -1;
}
insertHashTable(key_hash, id, lock);
lock->readers++;
RELEASE_LOCK(&file_lock_mutex);
return 0;
}
}
This is obtained in "libraries/base/GHC/IO/FD.hs", via:
mkFD fd iomode mb_stat is_socket is_nonblock = do
...
case fd_type of
Directory ->
ioException (IOError Nothing InappropriateType "openFile"
"is a directory" Nothing Nothing)
-- regular files need to be locked
RegularFile -> do
-- On Windows we need an additional call to get a unique device id
-- and inode, since fstat just returns 0 for both.
-- See also Note [RTS File locking]
(unique_dev, unique_ino) <- getUniqueFileInfo fd dev ino
r <- lockFile (fromIntegral fd) unique_dev unique_ino
(fromBool write)
when (r == -1) $
ioException (IOError Nothing ResourceBusy "openFile"
"file is locked" Nothing Nothing)
...
This suggests that when the file in question is opened there's already a
read lock in for the same dev/ino. Perhaps the Github filesystem fails
to ensure uniqueness of dev+ino of open files (perhaps when open files
are already unlinked)?
--
Viktor.
More information about the ghc-devs
mailing list