@matheusbsbs / matheus-bagliano / commits / e41352b24e
Fork 0

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>

Marcus Griep committed Aug 12, 2008 at 12:00 UTC e41352b24e29eba43d00a3fd117befaef1d594bc
1 file changed +123 -2
perl/Git.pm
+123 -2
@@ -57,7 +57,8 @@ require Exporter;
57 command_output_pipe command_input_pipe command_close_pipe
58 command_bidi_pipe command_close_bidi_pipe
59 version exec_path hash_object git_cmd_try
60 - remote_refs);
60 + remote_refs
61 + temp_acquire temp_release temp_reset);
62
63
64 =head1 DESCRIPTION
@@ -99,7 +100,9 @@ use Carp qw(carp croak); # but croak is bad - throw instead
100 use Error qw(:try);
101 use Cwd qw(abs_path);
102 use IPC::Open2 qw(open2);
102 -
103 +use File::Temp ();
104 +require File::Spec;
105 +use Fcntl qw(SEEK_SET SEEK_CUR);
106 }
107
108
@@ -933,6 +936,124 @@ sub _close_cat_blob {
936 delete @$self{@vars};
937 }
938
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 +
1057 =back
1058
1059 =head1 ERROR HANDLING