views:

61

answers:

3

Hi there,

I'm writing some Perl which takes TV shows recorded on Windows Media Center and moves/renames/deletes them depending on certain criteria.

Since the Perl runs fairly frequently, I'd like to cleanly determine whether or not the file is in use (in other words, the show is in the process of being recorded) so I can avoid doing anything with it.

My current method looks at the status of a file (using "stat") and compares it again after 5 seconds, like so:

sub file_in_use
{
  my $file = shift;

  my @before = stat($file);
  sleep 5;
  my @after = stat($file);

  return 0 if ($before ~~ $after);
  return 1;
 }

It seems to work, but I'm concious that there is probably a better and cleaner way to do this.

Can you please advise?

+1  A: 

If the file is being used full time and not released you cannot perform a move on the file as it will produce an error about file being used, if that is true you could use the follow approch.

#!/usr/bin/perl
use File::Copy;

move("test_file","my_new_location/test_file") or die "Error: $!";

I've made a simple file reader that will in theory lock the file as being used:

open (A,">>test_file");
while (1) {
    sleep 5;
}
close(A);

After that i tried running the move script and it returned me with a permission denied ...

I am not sure on how your video is being recorded/displayed so i can't really assure it will work for you.

For instance if the file is being written and closed right after there is a chance where it would move the file without complains ...

You can test it with this feeder example:

while (1) {
    open (A,">>test_file");
    print A "some data\n";
    close(A);
    sleep 5;
}
Prix
This works well too. The only problem is that if the file isn't locked then you need to rename it back again. Although I suspect renaming has very little effect on the file system it doesn't feel particularly "clean" - especially with all that renaming going on.
Richard
+1  A: 

The only improvement I would suggest is to stat all of your files at once, so you only need to sleep for 5 seconds one time instead of sleeping 5 seconds for every file:

my (%before, %after);
foreach my $file (@files_that_might_be_in_use) {
    $before{$file} = [ stat $file ];
}
sleep 5;
foreach my $file (@files_that_might_be_in_use) {
    $after{$file} = [ stat $file ];

    if (@{$before{$file}} ~~ @{$after{$file}}) {
        # file is not in use ... 
    } else {
        # file is in use ... 
    }
}
mobrule
+3  A: 

If the recording process locks the file, you could attempt to open it in read-write mode and see if it fails with ERROR_SHARING_VIOLATION as GetLastError (accessed via Perl's $^E special variable).

For example:

#! /usr/bin/perl

use warnings;
use strict;

sub usage { "Usage: $0 file ..\n" }

die usage unless @ARGV;

foreach my $path (@ARGV) {
  print "$path: ";

  if (open my $fh, "+<", $path) {
    print "available\n";
    close $fh;
  }
  else {
    print $^E == 0x20 ? "in use by another process\n" : "$!\n";
  }
}

Sample output with Dir100526Lt.pdf open by the Adobe reader:

C:\Users\Greg\Downloads>check-lock.pl Dir100526Lt.pdf setup.exe
Dir100526Lt.pdf: in use by another process
setup.exe: available

Be aware that any time you first test a condition and then later act based on the result of that test, you're creating a race condition. It seems that the worst this could bite you in your application is in the following unlucky sequence:

  1. test a video for availability as above
  2. answer: available!
  3. in the meantime, a recorder starts up and locks the video
  4. back in your program, you try to move the video, but it fails with a sharing violation
Greg Bacon
Perfect! Thank you very much. Good point about the race condition - as such I'm hoping that, once the recording has finished, there should be no reason for it to start it back up again.
Richard
cookie for you i like that special variable... just a quick question, if he knows the file he needs to check and do check it is availability at the time and lock it right away it would be like miliseconds until another application can get a hold of it so it would be like very very very hard to happen no ?
Prix
@Prix A millisecond is a long time to a computer. Unlikely means that it *will* happen sometimes, and these sorts of errors tend to be difficult to debug. Granted, in this case, it's probably not worth the fuss, but it's good for us to train ourselves to spot synchronization cracks. Rather than checking for availability and then locking, acquire the lock straight away. If someone else has it, the system will give it to you as soon as it's available. Otherwise, you know you have it, and it won't appear available to anyone else.
Greg Bacon