From ab62603c5ac2ad6ff7c8354f96628903a26cba4e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 5 May 2020 00:38:21 -0600 Subject: [PATCH] add close/{1,2}, better EOF action handling in read_term --- src/prolog/clause_types.rs | 3 ++ src/prolog/lib/builtins.pl | 44 ++++++++++++++++++++++-------- src/prolog/machine/streams.rs | 31 +++++++++++++++++++-- src/prolog/machine/system_calls.rs | 28 +++++++++++++++++-- 4 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/prolog/clause_types.rs b/src/prolog/clause_types.rs index ce5d9e68..e6f577e7 100644 --- a/src/prolog/clause_types.rs +++ b/src/prolog/clause_types.rs @@ -165,6 +165,7 @@ pub enum SystemClauseType { CodesToNumber, CopyTermWithoutAttrVars, CheckCutPoint, + Close, CopyToLiftedHeap, CreatePartialString, CurrentHostname, @@ -310,6 +311,7 @@ impl SystemClauseType { &SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile) => { clause_name!("$use_qualified_module_from_file") } + &SystemClauseType::Close => clause_name!("$close"), &SystemClauseType::CopyToLiftedHeap => clause_name!("$copy_to_lh"), &SystemClauseType::DeleteAttribute => clause_name!("$del_attr_non_head"), &SystemClauseType::DeleteHeadAttribute => clause_name!("$del_attr_head"), @@ -462,6 +464,7 @@ impl SystemClauseType { ("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint), ("$compile_batch", 0) => Some(SystemClauseType::REPL(REPLCodePtr::CompileBatch)), ("$copy_to_lh", 2) => Some(SystemClauseType::CopyToLiftedHeap), + ("$close", 2) => Some(SystemClauseType::Close), ("$current_hostname", 1) => Some(SystemClauseType::CurrentHostname), ("$current_input", 1) => Some(SystemClauseType::CurrentInput), ("$current_output", 1) => Some(SystemClauseType::CurrentOutput), diff --git a/src/prolog/lib/builtins.pl b/src/prolog/lib/builtins.pl index 2ec5680e..f1f20f8c 100644 --- a/src/prolog/lib/builtins.pl +++ b/src/prolog/lib/builtins.pl @@ -43,18 +43,18 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :- (:)/7, (:)/8, (:)/9, (:)/10, (:)/11, (:)/12, abolish/1, asserta/1, assertz/1, atom_chars/2, atom_codes/2, atom_concat/3, atom_length/2, - bagof/3, catch/3, char_code/2, clause/2, - current_input/1, current_output/1, current_op/3, - current_predicate/1, current_prolog_flag/2, - expand_goal/2, expand_term/2, fail/0, false/0, - findall/3, findall/4, get_char/1, halt/0, - max_arity/1, number_chars/2, number_codes/2, - once/1, op/3, open/3, open/4, read_term/2, - read_term/3, repeat/0, retract/1, - set_prolog_flag/2, set_input/1, set_output/1, - setof/3, sub_atom/5, subsumes_term/2, - term_variables/2, throw/1, true/0, - unify_with_occurs_check/2, write/1, + bagof/3, catch/3, char_code/2, clause/2, close/1, + close/2, current_input/1, current_output/1, + current_op/3, current_predicate/1, + current_prolog_flag/2, expand_goal/2, + expand_term/2, fail/0, false/0, findall/3, + findall/4, get_char/1, halt/0, max_arity/1, + number_chars/2, number_codes/2, once/1, op/3, + open/3, open/4, read_term/2, read_term/3, + repeat/0, retract/1, set_prolog_flag/2, + set_input/1, set_output/1, setof/3, sub_atom/5, + subsumes_term/2, term_variables/2, throw/1, + true/0, unify_with_occurs_check/2, write/1, write_canonical/1, write_term/2, write_term/3, writeq/1]). @@ -1161,3 +1161,23 @@ open(SourceSink, Mode, Stream, StreamOptions) :- '$open'(SourceSink, Mode, Stream, Alias, EOFAction, Reposition, Type) ). + +parse_close_options(Options, OptionValues, Stub) :- + DefaultOptions = [force-false], + parse_options_list(Options, parse_close_options_, DefaultOptions, OptionValues, Stub). + +parse_close_options_(force(Force), force-Force) :- + ( nonvar(Force), lists:member(Force, [true, false]), ! + ; + throw(error(domain_error(close_option, force(Force)), _)) + ). +parse_close_options_(E, _) :- + throw(error(domain_error(close_option, E), _)). + + +close(Stream, CloseOptions) :- + parse_close_options(CloseOptions, [Force], close/2), + '$close'(Stream, CloseOptions). + +close(Stream) :- + close(Stream, []). diff --git a/src/prolog/machine/streams.rs b/src/prolog/machine/streams.rs index 1dbb7532..e4df4734 100644 --- a/src/prolog/machine/streams.rs +++ b/src/prolog/machine/streams.rs @@ -12,7 +12,7 @@ use std::fmt; use std::fs::File; use std::io::{stdout, Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; use std::hash::{Hash, Hasher}; -use std::net::TcpStream; +use std::net::{Shutdown, TcpStream}; use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -42,6 +42,18 @@ pub enum StreamInstance { TcpStream(TcpStream), } +impl Drop for StreamInstance { + fn drop(&mut self) { + match self { + StreamInstance::TcpStream(ref mut tcp_stream) => { + tcp_stream.shutdown(Shutdown::Both).unwrap(); + } + _ => { + } + } + } +} + impl fmt::Debug for StreamInstance { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { @@ -140,12 +152,21 @@ impl Default for StreamOptions { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Hash)] pub struct Stream { pub options: StreamOptions, stream_inst: WrappedStreamInstance, } +impl PartialEq for Stream { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.stream_inst == other.stream_inst + } +} + +impl Eq for Stream {} + impl From for Stream { fn from(tcp_stream: TcpStream) -> Self { tcp_stream.set_read_timeout(None).unwrap(); @@ -292,6 +313,12 @@ impl Stream { } } + #[inline] + pub(crate) + fn close(&mut self) { + *self.stream_inst.0.borrow_mut() = StreamInstance::Null; + } + #[inline] pub(crate) fn is_input_stream(&self) -> bool { diff --git a/src/prolog/machine/system_calls.rs b/src/prolog/machine/system_calls.rs index 83d4eb48..7a73a155 100644 --- a/src/prolog/machine/system_calls.rs +++ b/src/prolog/machine/system_calls.rs @@ -1743,6 +1743,26 @@ impl MachineState { } }; } + &SystemClauseType::Close => { + let mut stream = + self.get_stream_or_alias(self[temp_v!(1)], indices, "close", 2)?; + + if stream.is_output_stream() { + stream.flush().unwrap(); // 8.11.6.1b) + } + + if stream == *current_input_stream { + *current_input_stream = readline::input_stream(); + } else if stream == *current_output_stream { + *current_output_stream = Stream::stdout(); + } + + stream.close(); + + if let Some(alias) = stream.options.alias { + indices.stream_aliases.remove(&alias); + } + } &SystemClauseType::CopyToLiftedHeap => { match self.store(self.deref(self[temp_v!(1)])) { Addr::Usize(lh_offset) => { @@ -2298,6 +2318,10 @@ impl MachineState { stream.options = options; + if let Some(ref alias) = &stream.options.alias { + indices.stream_aliases.insert(alias.clone(), stream.clone()); + } + let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream)); let stream_var = self.store(self.deref(self[temp_v!(3)])); @@ -3464,12 +3488,12 @@ impl MachineState { match TcpStream::connect(socket_addr).map_err(|e| e.kind()) { Ok(tcp_stream) => { let mut stream = Stream::from(tcp_stream); + stream.options = options; - if let Some(ref alias) = &options.alias { + if let Some(ref alias) = &stream.options.alias { indices.stream_aliases.insert(alias.clone(), stream.clone()); } - stream.options = options; self.heap.to_unifiable(HeapCellValue::Stream(stream)) } Err(ErrorKind::PermissionDenied) => { -- 2.54.0