[m-rev.] for review: improve I/O implementation for erlang backend
Peter Ross
pro at missioncriticalit.com
Fri Jun 22 12:42:57 AEST 2007
On Thu, Jun 21, 2007 at 05:45:52PM +1000, Peter Wang wrote:
> Estimated hours taken: 6
> Branches: main
>
> Improve the Erlang I/O implementation.
>
> library/io.m:
> In the Erlang implementation, implement:
>
> - support for pushback and line number tracking in I/O streams
> - seeking on binary streams
> - io.write_byte, io.write_bytes
> - io.make_temp
> - io.get_exit_status, io.set_exit_status
>
> Export some functions written in foreign_procs, otherwise they couldn't
> be called if foreign_procs got inlined into other modules.
>
> Fix some places in which we didn't convert Erlang error return values
> to strings.
>
> library/erlang_builtin.m:
> Make the Erlang global server track the current exit status.
>
> compiler/elds_to_erlang.m:
> Make the Erlang main wrapper, return the last set exit status to the
> operating system.
>
> tests/hard_coded/Mmakefile:
> tests/hard_coded/seek_test.exp:
> tests/hard_coded/seek_test.m:
> Add simple test case for io.seek_binary_input.
>
> tests/hard_coded/remove_file.exp2:
> Add expected output for Erlang backend.
>
> tests/general/structure_reuse/Mmakefile:
> Don't test this directory in Erlang grades.
>
>
> Index: compiler/elds_to_erlang.m
> ===================================================================
> RCS file: /home/mercury/mercury1/repository/mercury/compiler/elds_to_erlang.m,v
> retrieving revision 1.20
> diff -u -r1.20 elds_to_erlang.m
> --- compiler/elds_to_erlang.m 19 Jun 2007 01:10:52 -0000 1.20
> +++ compiler/elds_to_erlang.m 21 Jun 2007 07:24:43 -0000
> @@ -289,20 +289,34 @@
> StackTrace = erlang:get_stacktrace(),
> mercury__exception:'ML_report_uncaught_exception'(Excp),
> mercury__maybe_dump_stacktrace(StackTrace),
> - mercury__shutdown(),
> - % init:stop is preferred to calling halt but there seems
> - % to be no way to choose the exit code otherwise.
> - halt(1)
> + mercury__shutdown(true)
> end,
> - mercury__shutdown().
> + mercury__shutdown(false).
>
> mercury__startup() ->
> mercury__erlang_builtin:'ML_start_global_server'(),
> mercury__io:'ML_io_init_state'().
>
> - mercury__shutdown() ->
> + mercury__shutdown(ForceBadExit) ->
> mercury__io:'ML_io_finalize_state'(),
> - mercury__erlang_builtin:'ML_stop_global_server'().
> + 'ML_erlang_global_server' ! {get_exit_status, self()},
> + receive
> + {get_exit_status_ack, ExitStatus0} ->
> + void
> + end,
> + if
> + ExitStatus0 =:= 0 andalso ForceBadExit ->
> + ExitStatus = 1;
> + true ->
> + ExitStatus = ExitStatus0
> + end,
> + mercury__erlang_builtin:'ML_stop_global_server'(),
> + % init:stop is preferred to calling halt but there seems
> + % to be no way to choose the exit code otherwise.
> + case ExitStatus of
> + 0 -> void;
> + _ -> halt(ExitStatus)
> + end.
>
> mercury__maybe_dump_stacktrace(StackTrace) ->
> case os:getenv(""MERCURY_SUPPRESS_STACK_TRACE"") of
> Index: library/erlang_builtin.m
> ===================================================================
> RCS file: /home/mercury/mercury1/repository/mercury/library/erlang_builtin.m,v
> retrieving revision 1.2
> diff -u -r1.2 erlang_builtin.m
> --- library/erlang_builtin.m 14 Jun 2007 01:50:29 -0000 1.2
> +++ library/erlang_builtin.m 21 Jun 2007 07:24:44 -0000
> @@ -45,6 +45,7 @@
> [will_not_call_mercury, promise_pure, thread_safe],
> "
> Pid = spawn(fun global_server_loop/0),
> + Pid ! {set_exit_status, 0},
> register('ML_erlang_global_server', Pid)
> ").
>
> @@ -101,6 +102,24 @@
> From ! {trace_evaluate_runtime_condition_ack, Ret},
> global_server_loop();
>
> + {init_std_streams, Streams} ->
> + put('ML_std_streams', Streams),
> + global_server_loop();
> +
> + {get_std_streams, From} ->
> + Streams = get('ML_std_streams'),
> + From ! {get_std_streams_ack, Streams},
> + global_server_loop();
> +
> + {get_exit_status, From} ->
> + ExitStatus = get('ML_exit_status'),
> + From ! {get_exit_status_ack, ExitStatus},
> + global_server_loop();
> +
> + {set_exit_status, ExitStatus} ->
> + put('ML_exit_status', ExitStatus),
> + global_server_loop();
> +
> {stop, From} ->
> From ! {stop_ack};
>
> Index: library/io.m
> ===================================================================
> RCS file: /home/mercury/mercury1/repository/mercury/library/io.m,v
> retrieving revision 1.392
> diff -u -r1.392 io.m
> --- library/io.m 7 Jun 2007 07:23:45 -0000 1.392
> +++ library/io.m 21 Jun 2007 07:24:44 -0000
> @@ -5074,6 +5092,41 @@
> io.set_globals(Globals, !IO),
> io.insert_std_stream_names(!IO).
>
> +:- pred init_std_streams(io::di, io::uo) is det.
> +
> +init_std_streams(!IO).
> +
> +:- pragma foreign_proc("Erlang",
> + init_std_streams(_IO0::di, _IO::uo),
> + [will_not_call_mercury, promise_pure],
> +"
> + F = (fun() -> mercury_file_server(group_leader(), 1, []) end),
> + StdinPid = spawn(F),
> + StdoutPid = spawn(F),
> + StderrPid = spawn(F),
> + StdinBinaryPid = spawn(F),
> + StdoutBinaryPid = spawn(F),
> +
> + Stdin = {'ML_stream', make_ref(), StdinPid},
> + Stdout = {'ML_stream', make_ref(), StdoutPid},
> + Stderr = {'ML_stream', make_ref(), StderrPid},
> + StdinBinary = {'ML_stream', make_ref(), StdinBinaryPid},
> + StdoutBinary = {'ML_stream', make_ref(), StdoutBinaryPid},
> +
> + % Initialise the process dictionary.
> + put('ML_stdin_stream', Stdin),
> + put('ML_stdout_stream', Stdout),
> + put('ML_stderr_stream', Stderr),
> + put('ML_stdin_binary_stream', StdinBinary),
> + put('ML_stdout_binary_stream', StdoutBinary),
> +
> + % Save the standard streams to the global server. When we spawn a new
> + % Mercury thread later we will need to look it up in order to initialise
> + % the new process's process dictionary.
> + StdStreams = {Stdin, Stdout, Stderr, StdinBinary, StdoutBinaryPid},
> + 'ML_erlang_global_server' ! {init_std_streams, StdStreams}
> +").
> +
> :- pred io.finalize_state(io::di, io::uo) is det.
>
> % For use by the Mercury runtime.
> @@ -6057,13 +6110,248 @@
> :- pragma foreign_code("Erlang", "
>
> -mercury_standard_io_stream(Id) ->
> - % See stdlib io.erl. We don't use the atom standard_io as it only
> - % works with io: functions and not file: functions.
> - IoDevice = group_leader(),
> - {'ML_stream', Id, IoDevice}.
> + % For each open file we have a process running in the background.
> + % This is necessary so we can layer pushback support and line number
> + % tracking on top of what the Erlang runtime provides.
> + %
> +mercury_file_server(IoDevice, LineNr0, PutBack0) ->
> + receive
> + {From, close} ->
> + From ! {self(), close_ack},
> + file:close(IoDevice)
> + % XXX check error?
> + ;
> + {From, read_char} ->
> + case PutBack0 of
> + [] ->
> + case file:read(IoDevice, 1) of
> + {ok, [Char]} ->
> + Ret = Char,
> + LineNr = LineNr0 + one_if_nl(Char);
> + EofOrError ->
> + Ret = EofOrError,
> + LineNr = LineNr0
> + end,
> + PutBack = PutBack0;
> + [Char | PutBack] ->
> + Ret = Char,
> + LineNr = LineNr0 + one_if_nl(Char)
> + end,
> + From ! {self(), read_char_ack, Ret},
> + mercury_file_server(IoDevice, LineNr, PutBack)
> + ;
> + {From, putback, Char} ->
> + From ! {self(), putback_ack},
> + PutBack = [Char | PutBack0],
> + LineNr = LineNr0 - one_if_nl(Char),
> + mercury_file_server(IoDevice, LineNr, PutBack)
> + ;
> + {From, write_char, Char} ->
> + From ! {self(), write_char_ack},
> + % XXX use file:write with raw streams
> +
> + io:put_chars(IoDevice, [Char]),
> + LineNr = LineNr0 + one_if_nl(Char),
> + mercury_file_server(IoDevice, LineNr, PutBack0)
I don't understand how this code handles the write failing (eg file
system full)
I believe we should throw an exception in that case.
> + ;
> + {From, write_string, Chars} ->
> + From ! {self(), write_string_ack},
> + % XXX use file:write with raw streams
> + io:put_chars(IoDevice, Chars),
> + LineNr = LineNr0 + count_nls(Chars, 0),
> + mercury_file_server(IoDevice, LineNr, PutBack0)
> + ;
> + {From, write_int, Val} ->
> + From ! {self(), write_int_ack},
> + % XXX use file:write with raw streams
> + io:format(IoDevice, ""~B"", [Val]),
> + mercury_file_server(IoDevice, LineNr0, PutBack0)
> + ;
> + {From, sync} ->
> + % XXX file:sync seems to hang if run on a pid, e.g. standard I/O
> + if
> + is_pid(IoDevice) ->
> + void;
> + true ->
> + file:sync(IoDevice)
> + end,
> + From ! {self(), sync_ack},
> + mercury_file_server(IoDevice, LineNr0, PutBack0)
> + ;
> + {From, get_line_number} ->
> + From ! {self(), get_line_number_ack, LineNr0},
> + mercury_file_server(IoDevice, LineNr0, PutBack0)
> + ;
> + {From, set_line_number, N} ->
> + From ! {self(), set_line_number_ack},
> + mercury_file_server(IoDevice, N, PutBack0)
> + ;
> + {From, seek, Loc} ->
> + SeekResult = file:position(IoDevice, Loc),
> + From ! {self(), seek_ack, SeekResult},
> + mercury_file_server(IoDevice, LineNr0, PutBack0)
> + ;
> + Other ->
> + io:format(""** io.m: unrecognised message ~p~n"", [Other]),
> + mercury_file_server(IoDevice, LineNr0, PutBack0)
> + end.
> +
> +mercury_getc(Stream) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), read_char},
> + receive
> + {Pid, read_char_ack, Ret} ->
> + case Ret of
> + C when is_integer(C) ->
> + C;
> + eof ->
> + -1;
> + {error, Reason} ->
> + put('MR_io_exception', Reason),
> + -2
> + end
> + end.
> +
> +mercury_putback(Stream, Character) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), putback, Character},
> + receive
> + {Pid, putback_ack} ->
> + void
> + end.
> +
Add an explaination of why you do the ack,
and you don't actually return any data.
> +mercury_write_string(Stream, Characters) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), write_string, Characters},
> + receive
> + {Pid, write_string_ack} ->
> + void
> + end.
> +
> +mercury_write_char(Stream, Character) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), write_char, Character},
> + receive
> + {Pid, write_char_ack} ->
> + void
> + end.
> +
> +mercury_write_int(Stream, Value) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), write_int, Value},
> + receive
> + {Pid, write_int_ack} ->
> + void
> + end.
> +
> +mercury_sync(Stream) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), sync},
> + receive
> + {Pid, sync_ack} ->
> + void
> + end.
> +
> +mercury_get_line_number(Stream) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), get_line_number},
> + receive
> + {Pid, get_line_number_ack, LineNum} ->
> + LineNum
> + end.
> +
> +mercury_set_line_number(Stream, LineNum) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), set_line_number, LineNum},
> + receive
> + {Pid, set_line_number_ack} ->
> + void
> + end.
> +
> +mercury_seek(Stream, Loc) ->
> + {'ML_stream', _Id, Pid} = Stream,
> + Pid ! {self(), seek, Loc},
> + receive
> + {Pid, seek_ack, Result} ->
> + case Result of
> + {ok, NewPosition} ->
> + NewPosition;
> + {error, Reason} ->
> + put('MR_io_exception', Reason),
> + -1
> + end
> + end.
>
> mercury_current_text_input() ->
> get('ML_io_current_text_input').
Otherwise it looks fine.
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list