#!/usr/bin/perl
#
# Fsdb::Support::NamedTmpfile.pm
# Copyright (C) 2007 by John Heidemann <johnh@isi.edu>
# $Id: b84f24f02848d9777818453f2ced50674f8caa28 $
#
# This program is distributed under terms of the GNU general
# public license, version 2. See the file COPYING
# in $dblibdir for details.
#
package Fsdb::Support::NamedTmpfile;
=head1 NAME
Fsdb::Support::NamedTmpfile - dreate temporary files that can be opened
=head1 SYNOPSIS
use Fsdb::Support::NamedTmpfile;
$pathname = Fsdb::Support::NamedTmpfile::alloc($tmpdir);
=head1 FUNCTIONS
=head2 alloc
$pathname = Fsdb::Support::NamedTmpfile::alloc($tmpdir);
Create a unique filename for temporary data.
$TMPDIR is optional.
The file is automatically removed on program exit,
but the pathname exists for the duration of execution
(and can be opened).
Note that there is a potential race condition between when we pick the file
and when the caller opens it, when an external program could intercede.
The caller therefor should open files with exclusive access.
This routine is Perl thread-safe, and process fork safe.
(Files are only cleaned up by the process that creates them.)
While this routine is basically "new", we don't call it such
because we do not return an object.
=cut
@ISA = ();
($VERSION) = 1.0;
# use threads;
# use threads::shared;
use Carp;
use File::Temp qw(tempfile);
# my $named_tmpfiles_lock : shared;
# my %named_tmpfiles : shared;
my %named_tmpfiles;
my $tmpdir = undef;
my $template = undef;
sub alloc {
my($tmpdir) = @_;
if (!defined($tmpdir)) {
$tmpdir = (defined($ENV{'TMPDIR'}) ? $ENV{'TMPDIR'} : "/tmp") if (!defined($tmpdir));
};
if (!defined($template)) {
$template = sprintf("fsdb.%d.XXXXXX", $$);
};
croak "tmpdir $tmpdir is not a directory\n" if (! -d $tmpdir);
croak "tmpdir $tmpdir is not writable\n" if (! -w $tmpdir);
my($fh, $fn) = tempfile($template, SUFFIX => "~", DIR => $tmpdir);
close $fh;
{
# lock($named_tmpfiles_lock);
$named_tmpfiles{$fn} = $$;
}
return $fn;
}
=head2 cleanup_one
Fsdb::Support::NamedTmpfile::cleanup_one('some_filename');
cleanup one tmpfile, forgetting about it if necessary.
=cut
sub cleanup_one {
my($fn) = @_;
return if (!defined($fn));
# xxx: doesn't check for inclusion first
{
# lock($named_tmpfiles_lock);
unlink($fn) if ($named_tmpfiles{$fn} == $$ && -f $fn);
delete $named_tmpfiles{$fn};
}
}
=head2 cleanup_all
Fsdb::Support::NamedTmpfile::cleanup_all
Cleanup all tmpfiles
Not a method.
=cut
sub cleanup_all {
my(%named_tmpfiles_copy);
{
# lock($named_tmpfiles_lock);
%named_tmpfiles_copy = %named_tmpfiles;
%named_tmpfiles = ();
}
foreach my $fn (keys %named_tmpfiles_copy) {
unlink($fn) if ($named_tmpfiles_copy{$fn} == $$ && -f $fn);
};
}
sub END {
# graceful termination
cleanup_all;
}
1;