socket.sa
Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
---------------------------> Sather 1.1 source file <--------------------------
-- socket.sa
--
-- David Bailey
-- dbailey@icsi.berkeley.edu
--
-- January 1993 Sather 0.2 version
-- April 1995 Sather 1.0 version
-- December 1995 Sather 1.0.9 pSather version
-- June 1996 Sather 1.1 version unifying serial and parallel using "PP"
-- and introducing exceptions.
--
-- Bidirectional communication of strings between processes (intra-
-- or inter-machine) using Unix sockets. Blocking reads, potentially
-- blocking writes.
--
-- Classes: SOCKET, SOCKET_EXCEPTION, C_SOCKET
-- Required file: socket_support.c
--
-- Tested on (p)Sather 1.1 under SunOS 4 and 5. No guarantees beyond that.
-- NO INTERNAL ERROR MESSAGES SHOULD APPEAR IN USE;
-- PLEASE REPORT ANY OCCURRENCES.
--
-- Bug: In rare circumstances, killing a process that is initiating a
-- Unix socket may leave behind a file which will have to be manually
-- removed before another socket can be initiated with the same name.
-- Such files reside in the directory indicated in `unix_socket_directory'.
--
-- Idea: Add error codes to the interface.
-- Idea: Non-blocking socket creation as suggested by Cliff Draper.
--
--!!! THIS FILE HAS BEEN CREATED FROM socket.sa, DO NOT EDIT IT !!!
class SOCKET
class SOCKET is
-- Bidirectional communication of strings between processes (intra-
-- or inter-machine) using sockets. Blocking reads, potentially
-- blocking writes.
-- Four versions of create, distinguishing: (1) Unix-domain (intra-machine)
-- vs. Internet-domain (inter-machine); and (2) initiating vs. connecting.
-- "Dead" sockets may be returned if the passed name or port is
-- already in use (initiating side), or if no matching initiator was
-- found within `num_connect_attempts' seconds (connecting side), if
-- an invalid `hostname' is given (connecting side), or in anomalous
-- circumstances (in which case a message appears on stderr).
create_initiating_unix(name:STR):SAME pre void(self) is
-- Initate a SOCKET named `name', then block until another
-- process does a 'create_connecting_unix' with the same name,
-- on the same machine. `name' must be a legal Unix filename.
-- Upon error, return a dead socket.
res::=new;
if ~reasonable_filename(name) then
res.is_dead_var:=true; return res
end;
res.id:=C_SOCKET::
make_initiating_socket_unix(unix_socket_directory+name);
if res.id<0 then res.is_dead_var:=true end;
C_SOCKET::ignore_broken_pipe_signals;
return res
end;
create_connecting_unix(name:STR):SAME pre void(self) is
-- Connect to an existing Unix-domain SOCKET named `name'.
-- Will try once per second up to 10 seconds.
-- `name' must be a legal Unix filename. Upon error, return a
-- dead socket.
res::=new;
if ~reasonable_filename(name) then
res.is_dead_var:=true; return res
end;
res.id:=
C_SOCKET::make_connecting_socket_unix(unix_socket_directory+name);
if res.id<0 then res.is_dead_var:=true end;
C_SOCKET::ignore_broken_pipe_signals;
return res
end;
create_initiating_inet(port:INT):SAME pre void(self) is
-- Initate an Internet SOCKET on port `port', then block until
-- another process does a 'create_connecting_inet' to this port.
-- `port' must be >= `min_port_num'. Upon error, return a dead socket.
res::=new;
if port<min_port_num then res.is_dead_var:=true; return res end;
res.id:=C_SOCKET::make_initiating_socket_inet(port);
if res.id<0 then res.is_dead_var:=true end;
C_SOCKET::ignore_broken_pipe_signals; -- need even in inet case
return res
end;
create_connecting_inet(host:STR,port:INT):SAME pre void(self) is
-- Connect to an existing Internet SOCKET on port `port' of
-- machine `host'. Will try once per second up to 10 seconds.
-- `port' must be >= `min_port_num'. Upon error, return a dead socket.
res::=new;
if port<min_port_num then res.is_dead_var:=true; return res end;
res.id:=C_SOCKET::make_connecting_socket_inet(host,port);
if res.id<0 then res.is_dead_var:=true end;
C_SOCKET::ignore_broken_pipe_signals; -- need even in inet case
return res
end;
close is
-- Close the socket and mark it dead. Hopefully, by using this,
-- programs using Internet sockets can avoid the "lingering
-- socket" phenomenon...
C_SOCKET::close_socket(id);
is_dead_var:=true
end;
use_exceptions(b:BOOL) is
-- Turn on or off the raising of a SOCKET_EXCEPTION when either
-- (1) the socket dies due to external factors (usually
-- termination of the peer socket's process) in the course of
-- any operation other than an explicit `is_dead' check; or
-- (2) a caller attempts to read or write using an already closed
-- or dead socket. Note that in any case, `is_dead' will be set
-- when a socket dies or is closed. If exceptions are turned on
-- when the socket is already dead, an exception is raised.
use_exceptions_var:=b;
if use_exceptions_var and is_dead_var then
raise #SOCKET_EXCEPTION("Socket dead upon turning on exceptions")
end
end;
is_using_exceptions:BOOL is
-- Report whether this socket uses exceptions as described above.
return use_exceptions_var
end;
is_dead:BOOL is
-- Indicates whether the socket is alive and capable of
-- performing a `get_str' or `plus'.
-- NOTE: Not sure if is_healthy=0 catches all cases where the
-- socket is effectively dead but has not yet failed.
if ~is_dead_var and (C_SOCKET::is_healthy(id)=0) then close end;
return is_dead_var
end;
get_str:STR pre ~void(self) is
-- Return the next string from the peer SOCKET.
-- An empty-string result means the sender sent EITHER the empty
-- string or a void string.
-- May block, if no strings are available, until one is sent.
-- If an error occurs, the socket dies, and return void.
-- If socket is already dead: raise an exception if using exceptions,
-- else simply return void.
if is_dead_var then
if use_exceptions_var then
raise #SOCKET_EXCEPTION("Tried to read from a dead socket")
end;
return void
end;
res:STR;
len::=C_SOCKET::receive_len(id);
if len>0 then
res:="X".repeat(len);
status::=C_SOCKET::receive_str(id,res,len);
if status=0 then
die("Socket died receiving body of string");
res:=void
end
elsif len=0 then res:=#STR
else die("Socket died receiving length of string")
end;
return res
end;
plus(s:$STR) pre ~void(self) is
-- Send string version of `s' to the peer SOCKET.
-- If 's' is void, the receiver will get the empty string.
-- May block, if buffers are full, until receiver does a `get_str'.
-- If an error occurs, the socket dies.
-- If socket is already dead: raise an exception if using exceptions,
-- else simply return.
if is_dead_var then
if use_exceptions_var then
raise #SOCKET_EXCEPTION("Tried to write on a dead socket")
end;
return
end;
str_to_send:STR;
if void(s) then str_to_send:="" else str_to_send:=s.str end;
if (C_SOCKET::is_healthy(id)=0) then
die("Socket found dead when preparing to send string");
return
end;
status::=C_SOCKET::send_str(id,str_to_send,str_to_send.length);
if status=0 then die("Socket died while sending string") end
end;
plus(s:$STR):SAME is plus(s); return self end;
-- Same as above except return self, allowing chaining, eg, skt+x+y+z.
can_read_without_block:BOOL pre ~void(self) is
-- Return true if socket is alive and data is available
-- so that `get_str' would not block.
return ~is_dead_var and C_SOCKET::able_to_read(id)/=0
end;
can_write_without_block:BOOL pre ~void(self) is
-- Return true if socket is alive and 'plus' would accept a
-- string without blocking due to full buffers.
return ~is_dead_var and C_SOCKET::able_to_write(id)/=0
end;
block_until_can_read pre ~void(self) is
-- Blocks in a non-busy wait until socket can read.
-- If socket is already dead: raise an exception if using exceptions,
-- else return immediately.
if is_dead_var then
if use_exceptions_var then
raise #SOCKET_EXCEPTION("Tried to block_until_can_read on a "
"dead socket")
end;
return
end;
C_SOCKET::block_until_can_read(id)
end;
block_until_can_write pre ~void(self) is
-- Blocks in a non-busy wait until socket can write without blocking.
-- If socket is already dead: raise an exception if using exceptions,
-- else return immediately.
if is_dead_var then
if use_exceptions_var then
raise #SOCKET_EXCEPTION("Tried to block_until_can_write on a "
"dead socket")
end;
return
end;
C_SOCKET::block_until_can_write(id)
end;
reasonable_filename(s:STR):BOOL is
-- Return whether `s' is a reasonable Unix filename and hence
-- suitable for a Unix-domain socket name. Overly cautious, but
-- this ought to be implemented elsewhere anyway.
loop
c::=s.elt!;
if ~c.is_alphanum and c/='-' and c/='_' and c/='.' then
return false
end
end;
return true
end;
min_port_num:INT is return C_SOCKET::min_port_num end;
-- Internet socket port numbers must exceed this value.
private die(s:STR) is
-- Close the socket (thereby marking it dead), and raise an
-- exception if use_exceptions_var is set.
close;
if use_exceptions_var then raise #SOCKET_EXCEPTION(s) end
end;
private attr id:INT;
-- Unix descriptor for the socket.
private attr is_dead_var:BOOL;
-- Indicates whether socket is operational for `get_str' or `plus'.
private attr use_exceptions_var:BOOL;
-- Flag whether to raise an exception under the conditions described
-- in the routine `use_exceptions'.
--
shared num_connect_attempts:INT:=10;
-- Connecting sockets will look once per second for a matching
-- initator, this many times, before failing.
shared unix_socket_directory:STR:="/tmp/";
-- Where Unix-domain server sockets will live.
-- Probably should leave this alone; but if you must change it at
-- runtime, be sure to change it in both communicating processes!
end;
class SOCKET_EXCEPTION < $STR
class SOCKET_EXCEPTION < $STR is
create(s:STR):SAME is res::=new; res.s:=s; return res end;
str:STR is return "SOCKET_EXCEPTION: "+s end;
private attr s:STR;
end;
external class C_SOCKET
external class C_SOCKET is
make_initiating_socket_unix(name:STR):INT;
make_connecting_socket_unix(name:STR):INT;
make_initiating_socket_inet(port:INT):INT;
make_connecting_socket_inet(srvname:STR,srvport:INT):INT;
close_socket(fd:INT);
receive_len(id:INT):INT;
receive_str(id:INT,s:STR,len:INT):INT; -- modifies s
send_str(id:INT,s:STR,len:INT):INT;
able_to_read(fd:INT):INT;
able_to_write(fd:INT):INT;
is_healthy(fd:INT):INT;
block_until_can_read(fd:INT);
block_until_can_write(fd:INT);
min_port_num:INT;
ignore_broken_pipe_signals;
nconnatt:INT is return SOCKET::num_connect_attempts end;
end;