@@ -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
99100use Error qw( :try) ;
100101use Cwd qw( abs_path) ;
101102use 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