Sewi has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I need to check if a file exists but never ever block on this check.
I tried -e first and then...

sysopen my $fh, $file, O_RDONLY|O_NDELAY; close $fh;
(NDELAY is the same as NONBLOCK)
...but it didn't work.

Test scenario:
Ubuntu computer with a sshfs-mounted path where the network cable was disconnected after the path had been mounted.

A external system call is no options as this must be OS independent (at least for Win, Linux, Mac).

alarm() doesn't work - the signal either doesn't fire or it's ignored.

Thanks

Replies are listed 'Best First'.
Re: -e blocking due to unaccessable network fs
by BrowserUk (Patriarch) on Jun 23, 2010 at 12:43 UTC
    alarm() doesn't work - the signal either doesn't fire or it's ignored.

    Have you tried setting PERL_SIGNALS=unsafe?

    See also Perl::Unsafe::Signals

Re: -e blocking due to unaccessable network fs
by almut (Canon) on Jun 23, 2010 at 13:25 UTC

    My speculation would be that the problem lies at a deeper level, which can be summarized as follows:

    • Blocking mode refers to waiting until a file descriptor becomes readable/writable, but regular files are defined to always be readable/writable (granted appropriate permissions) — POSIX spec, IIRC.  In other words, non-blocking I/O with regular files doesn't really make sense (it only makes sense for sockets, pipes, fifos, etc.)
    • Something is made to appear as a regular file via the sshfs mount which in reality involves a potentially failing network connection. And (presumably) the non-blocking flag either simply isn't handled properly at the lower levels of the sshfs implementation (where it could possibly be determined if the operation would block), or it's already being ignored earlier on due to having been applied to a regular file.

    In short, if an alarm doesn't even work with PERL_SIGNALS=unsafe (which would be the case, if the system call is in a non-interruptable state), I think all you can do is to create a second process/thread which is responsible for killing the other process in case it blocks for longer than a specified period.

    The problem with this approach is, though, that if the watching process is the child, it would kill the main program in case of timeout (usually undesired). Or, the other way round, if the child is doing the potentially blocking operation, you wouldn't easily have access to the handle from the main program in case the open succeeds...

    Once you've solved this issue, you can start thinking about portability.

Re: -e blocking due to unaccessable network fs
by Khen1950fx (Canon) on Jun 23, 2010 at 12:12 UTC
    Since what you are doing must be OS-independent, then I wouldn't be using O_NDELAY. For example, my system doesn't have it. Stick with O_NONBLOCK. See perlopentut. Using perlopentut as an example, I would do something like this:
    sysopen(FH, $path_to_file, O_RDONLY | O_NONBLOCK);
      Thanks.

      I changed it, but as both constants resolve to 2048 (Linux), there should be no difference.

Re: -e blocking due to unaccessable network fs
by Corion (Patriarch) on Jun 23, 2010 at 13:31 UTC

    I'm pretty sure that it doesn't work on Windows, but IO::AIO could have code to do nonblocking file-related system calls. It might even have a dummy implementation for systems where the nonblocking variants don't exist.