When GNAT.Expect reaches the end of its input, it does not properly closes the input file descriptor, thus resulting in a memory leak. This was properly handles when calling Get_Process_Output, but not when writing the loop manually
--- -- The following program had errors reported by valgrind (fd leaks) with Ada.Directories; with GNAT.Expect; with GNAT.OS_Lib; procedure GNAT_Expect is File : constant String := "/tmp/data.expect"; Count : constant := 2048; Args : GNAT.OS_Lib.Argument_List (1 .. 4); Pd : GNAT.Expect.Process_Descriptor; Match : GNAT.Expect.Expect_Match; Cmd : constant String := "echo testdata > " & File; Res : Integer := 1; begin Args (1) := new String'("-o"); Args (2) := new String'("pipefail"); Args (3) := new String'("-c"); Args (4) := new String'(Cmd); for I in 1 .. Count loop GNAT.Expect.Non_Blocking_Spawn (Descriptor => Pd, Command => "/bin/bash", Args => Args, Buffer_Size => 0); begin GNAT.Expect.Expect (Descriptor => Pd, Result => Match, Regexp => "", Timeout => -1); exception when GNAT.Expect.Process_Died => GNAT.Expect.Close (Descriptor => Pd, Status => Res); end; end loop; end GNAT_Expect; Tested on x86_64-pc-linux-gnu, committed on trunk 2012-04-02 Emmanuel Briot <br...@adacore.com> * g-expect.adb (Expect_Internal): Fix leak of the input file descriptor.
Index: g-expect.adb =================================================================== --- g-expect.adb (revision 186067) +++ g-expect.adb (working copy) @@ -6,7 +6,7 @@ -- -- -- B o d y -- -- -- --- Copyright (C) 2000-2011, AdaCore -- +-- Copyright (C) 2000-2012, AdaCore -- -- -- -- GNAT is free software; you can redistribute it and/or modify it under -- -- terms of the GNU General Public License as published by the Free Soft- -- @@ -33,7 +33,7 @@ with System.OS_Constants; use System.OS_Constants; with Ada.Calendar; use Ada.Calendar; -with GNAT.IO; +with GNAT.IO; use GNAT.IO; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNAT.Regpat; use GNAT.Regpat; @@ -678,6 +678,7 @@ -- ??? Note that ddd tries again up to three times -- in that case. See LiterateA.C:174 + Close (Descriptors (D).Input_Fd); Descriptors (D).Input_Fd := Invalid_FD; Result := Expect_Process_Died; return; @@ -893,7 +894,8 @@ begin Non_Blocking_Spawn - (Process, Command, Arguments, Err_To_Out => Err_To_Out); + (Process, Command, Arguments, Err_To_Out => Err_To_Out, + Buffer_Size => 0); if Input'Length > 0 then Send (Process, Input); @@ -1055,17 +1057,18 @@ Command_With_Path : String_Access; begin - -- Create the rest of the pipes - - Set_Up_Communications - (Descriptor, Err_To_Out, Pipe1'Access, Pipe2'Access, Pipe3'Access); - Command_With_Path := Locate_Exec_On_Path (Command); if Command_With_Path = null then raise Invalid_Process; end if; + -- Create the rest of the pipes once we know we will be able to + -- execute the process. + + Set_Up_Communications + (Descriptor, Err_To_Out, Pipe1'Access, Pipe2'Access, Pipe3'Access); + -- Fork a new process Descriptor.Pid := Fork; @@ -1365,6 +1368,8 @@ end if; if Create_Pipe (Pipe2) /= 0 then + Close (Pipe1.Input); + Close (Pipe1.Output); return; end if; @@ -1389,7 +1394,7 @@ -- Create a separate pipe for standard error if Create_Pipe (Pipe3) /= 0 then - return; + Pipe3.all := Pipe2.all; end if; end if;