Skip to content

Commit e41352b

Browse files
neoeinsteinEric Wong
authored andcommitted
Git.pm: Add faculties to allow temp files to be cached
This patch offers a generic interface to allow temp files to be cached while using an instance of the 'Git' package. If many temp files are created and destroyed during the execution of a program, this caching mechanism can help reduce the amount of files created and destroyed by the filesystem. The temp_acquire method provides a weak guarantee that a temp file will not be stolen by subsequent requests. If a file is locked when another acquire request is made, a simple error is thrown. Signed-off-by: Marcus Griep <marcus@griep.us> Acked-by: Eric Wong <normalperson@yhbt.net>
1 parent 04c6e9e commit e41352b

File tree

1 file changed

+123
-2
lines changed

1 file changed

+123
-2
lines changed

perl/Git.pm

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ require Exporter;
5757
command_output_pipe command_input_pipe command_close_pipe
5858
command_bidi_pipe command_close_bidi_pipe
5959
version exec_path hash_object git_cmd_try
60-
remote_refs);
60+
remote_refs
61+
temp_acquire temp_release temp_reset);
6162

6263

6364
=head1 DESCRIPTION
@@ -99,7 +100,9 @@ use Carp qw(carp croak); # but croak is bad - throw instead
99100
use Error qw(:try);
100101
use Cwd qw(abs_path);
101102
use IPC::Open2 qw(open2);
102-
103+
use File::Temp ();
104+
require File::Spec;
105+
use Fcntl qw(SEEK_SET SEEK_CUR);
103106
}
104107

105108

@@ -933,6 +936,124 @@ sub _close_cat_blob {
933936
delete @$self{@vars};
934937
}
935938

939+
940+
{ # %TEMP_* Lexical Context
941+
942+
my (%TEMP_LOCKS, %TEMP_FILES);
943+
944+
=item temp_acquire ( NAME )
945+
946+
Attempts to retreive the temporary file mapped to the string C<NAME>. If an
947+
associated temp file has not been created this session or was closed, it is
948+
created, cached, and set for autoflush and binmode.
949+
950+
Internally locks the file mapped to C<NAME>. This lock must be released with
951+
C<temp_release()> when the temp file is no longer needed. Subsequent attempts
952+
to retrieve temporary files mapped to the same C<NAME> while still locked will
953+
cause an error. This locking mechanism provides a weak guarantee and is not
954+
threadsafe. It does provide some error checking to help prevent temp file refs
955+
writing over one another.
956+
957+
In general, the L<File::Handle> returned should not be closed by consumers as
958+
it defeats the purpose of this caching mechanism. If you need to close the temp
959+
file handle, then you should use L<File::Temp> or another temp file faculty
960+
directly. If a handle is closed and then requested again, then a warning will
961+
issue.
962+
963+
=cut
964+
965+
sub temp_acquire {
966+
my ($self, $name) = _maybe_self(@_);
967+
968+
my $temp_fd = _temp_cache($name);
969+
970+
$TEMP_LOCKS{$temp_fd} = 1;
971+
$temp_fd;
972+
}
973+
974+
=item temp_release ( NAME )
975+
976+
=item temp_release ( FILEHANDLE )
977+
978+
Releases a lock acquired through C<temp_acquire()>. Can be called either with
979+
the C<NAME> mapping used when acquiring the temp file or with the C<FILEHANDLE>
980+
referencing a locked temp file.
981+
982+
Warns if an attempt is made to release a file that is not locked.
983+
984+
The temp file will be truncated before being released. This can help to reduce
985+
disk I/O where the system is smart enough to detect the truncation while data
986+
is in the output buffers. Beware that after the temp file is released and
987+
truncated, any operations on that file may fail miserably until it is
988+
re-acquired. All contents are lost between each release and acquire mapped to
989+
the same string.
990+
991+
=cut
992+
993+
sub temp_release {
994+
my ($self, $temp_fd, $trunc) = _maybe_self(@_);
995+
996+
if (ref($temp_fd) ne 'File::Temp') {
997+
$temp_fd = $TEMP_FILES{$temp_fd};
998+
}
999+
unless ($TEMP_LOCKS{$temp_fd}) {
1000+
carp "Attempt to release temp file '",
1001+
$temp_fd, "' that has not been locked";
1002+
}
1003+
temp_reset($temp_fd) if $trunc and $temp_fd->opened;
1004+
1005+
$TEMP_LOCKS{$temp_fd} = 0;
1006+
undef;
1007+
}
1008+
1009+
sub _temp_cache {
1010+
my ($name) = @_;
1011+
1012+
my $temp_fd = \$TEMP_FILES{$name};
1013+
if (defined $$temp_fd and $$temp_fd->opened) {
1014+
if ($TEMP_LOCKS{$$temp_fd}) {
1015+
throw Error::Simple("Temp file with moniker '",
1016+
$name, "' already in use");
1017+
}
1018+
} else {
1019+
if (defined $$temp_fd) {
1020+
# then we're here because of a closed handle.
1021+
carp "Temp file '", $name,
1022+
"' was closed. Opening replacement.";
1023+
}
1024+
$$temp_fd = File::Temp->new(
1025+
TEMPLATE => 'Git_XXXXXX',
1026+
DIR => File::Spec->tmpdir
1027+
) or throw Error::Simple("couldn't open new temp file");
1028+
$$temp_fd->autoflush;
1029+
binmode $$temp_fd;
1030+
}
1031+
$$temp_fd;
1032+
}
1033+
1034+
=item temp_reset ( FILEHANDLE )
1035+
1036+
Truncates and resets the position of the C<FILEHANDLE>.
1037+
1038+
=cut
1039+
1040+
sub temp_reset {
1041+
my ($self, $temp_fd) = _maybe_self(@_);
1042+
1043+
truncate $temp_fd, 0
1044+
or throw Error::Simple("couldn't truncate file");
1045+
sysseek($temp_fd, 0, SEEK_SET) and seek($temp_fd, 0, SEEK_SET)
1046+
or throw Error::Simple("couldn't seek to beginning of file");
1047+
sysseek($temp_fd, 0, SEEK_CUR) == 0 and tell($temp_fd) == 0
1048+
or throw Error::Simple("expected file position to be reset");
1049+
}
1050+
1051+
sub END {
1052+
unlink values %TEMP_FILES if %TEMP_FILES;
1053+
}
1054+
1055+
} # %TEMP_* Lexical Context
1056+
9361057
=back
9371058
9381059
=head1 ERROR HANDLING

0 commit comments

Comments
 (0)