View source with formatted comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jeffrey Rosenwald
    4    E-mail:        jeffrose@acm.org
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2009-2020, Jeffrey Rosenwald
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(tipc,
   36          [ tipc_socket/2,              % -Socket  +Type
   37            tipc_close_socket/1,        % +Socket
   38            tipc_setopt/2,              % +Socket, +Option
   39            tipc_bind/3,                % +Socket, +Address +Scope
   40            tipc_listen/2,              % +Socket, +BackLog
   41            tipc_accept/3,              % +Master, -Slave, -PeerName
   42            tipc_open_socket/3,         % +Socket, -Read, -Write
   43            tipc_get_name/2,            % +Socket, -Address
   44            tipc_get_peer_name/2,       % +Socket, -Address
   45            tipc_connect/2,             % +Socket, +Address
   46            tipc_receive/4,             % +Socket, -Data, -Sender, +Options
   47            tipc_send/4,                % +Socket, +Data, +Receiver, +Options
   48%           tipc_subscribe/5, % +Socket, +Address, +Timeout, +Filter,
   49%           +Usr_handle
   50
   51%           tipc_receive_subscr_event/2, % +Socket, -Event
   52            tipc_canonical_address/2,   % -Address, +port_id/2
   53            tipc_service_probe/1,       % ?Address
   54            tipc_service_probe/2,       % ?Address, ?PortId
   55            tipc_service_port_monitor/2, % +Address, :Goal
   56            tipc_service_port_monitor/3, % +Address, :Goal, +Timeout
   57            tipc_service_exists/1,      % +Address
   58            tipc_service_exists/2,       % +Address +Timeout
   59            tipc_initialize/0            %
   60          ]).   61:- autoload(library(apply),[maplist/3]).   62:- autoload(library(lists),[member/2]).   63
   64:- use_foreign_library(foreign(tipc)).   65
   66:- multifile tipc_stack_initialize/0.   67
   68/** <module> TIPC Sockets
   69
   70Transparent Inter-Process Communication  (TIPC)   provides  a  flexible,
   71reliable, fault-tolerant, high-speed,  and   low-overhead  framework for
   72inter-process  communication  between  federations   of  trusted  peers,
   73operating as a unit. It was developed  by   Ericsson  AB,  as a means to
   74provide for communications between Common  Control Systems processes and
   75Network  Element  peers  in  telephone    switching  systems,  sometimes
   76operating at arm's  length  on  different   line  cards  or  mainframes.
   77Delegation of responsibility in  this  way   is  one  of the fundamental
   78precepts of the Erlang programming system,   also developed at Ericsson.
   79TIPC represents a more generalized version of the same behavioral design
   80pattern. For an overview, please see: tipc_overview.md.
   81
   82__Errors__
   83
   84The TIPC module uses the error   handling functions from library(socket)
   85and therefore all the functions below may throw error(socket_error(Code,
   86Message)) where `Code` is the  lowercase   version  of the C-macro error
   87code and `Message` is an atom describing   the error in a human friendly
   88format, depending on the current  locale.   See  the  socket library for
   89details.
   90
   91@author Jeffrey Rosenwald (JeffRose@acm.org)
   92@see    <http://tipc.sf.net>, <http://www.erlang.org>
   93@compat Linux only
   94*/
   95
   96%!   tipc_socket(-SocketId, +SocketType) is det.
   97%
   98%    Creates  a  TIPC-domain  socket  of    the  type  specified  by
   99%    SocketType, and unifies it to an  identifier, SocketId.
  100%
  101%    @param SocketType is one of  the   following  atoms:
  102%
  103%    * rdm - unnumbered, reliable datagram service,
  104%    * dgram - unnumbered, unreliable datagram service,
  105%    * seqpacket - numbered, reliable datagram service, and
  106%    * stream - reliable, connection-oriented byte-stream
  107%      service
  108
  109%!   tipc_close_socket(+SocketId) is det.
  110%
  111%    Closes the indicated socket, making SocketId invalid. In stream
  112%    applications, sockets are closed by closing both stream handles
  113%    returned by tipc_open_socket/3.  There  are   two  cases  where
  114%    tipc_close_socket/1   is   used   because     there    are   no
  115%    stream-handles:
  116%
  117%     * After tipc_accept/3, the server does  a fork/1 to handle the
  118%     client in a sub-process. In this   case the accepted socket is
  119%     not longer needed from the main   server and must be discarded
  120%     using tipc_close_socket/1.
  121%
  122%     *  If,  after  discovering   the    connecting   client   with
  123%     tipc_accept/3,  the  server  does  not   want  to  accept  the
  124%     connection, it should discard the  accepted socket immediately
  125%     using tipc_close_socket/1.
  126%
  127%    @param SocketId the socket identifier returned by tipc_socket/2
  128%    or tipc_accept/3.
  129
  130%!  tipc_subscribe(+SocketId, +NameSeqAddress, +Timeout, +Filter, +UserHandle) is det.
  131%
  132%    Subscribes to events related to a publisher that is bound to
  133%    the multi-cast address specified in NameSeqAddress.
  134%
  135%    Timeout  specifies  the  duration  of    the   subscription  in
  136%    milliseconds. Specifying a  Timeout  of   -1,  provides  for  a
  137%    subscription of infinite duration. Filter  specifies the events
  138%    of interest to the  subscriber.  It   is  an  integer bit field
  139%    where:
  140%
  141%    * (1 is X /\ 1) -> "notify port availability",
  142%    * (2 is X /\ 2) -> "notify service availability", and
  143%    * (4 is X /\ 4) -> "notify subscription cancellation".
  144%
  145%    UserHandle is an eight-byte, user-defined buffer that is passed
  146%    transparently to the subscriber  with   every  notification. An
  147%    application that uses this service must   be  prepared to parse
  148%    event notifications received from  the   TIPC  topology server.
  149%    tipc_receive_subscr_event/4, is provided for this purpose.
  150%
  151%    It is possible to have  multiple   subscriptions  active on the
  152%    same socket any one time, making periodic subscription renewal
  153%    easy.
  154%
  155%    _|This predicate is valid only on connections to the topology
  156%    server: name(1,1,0).|_
  157%
  158%    _|Please note that this predicate should be considered private.
  159%    It has been removed from the Prolog public API. Its use in user
  160%    programs is strongly discouraged. See the "tipc_service"
  161%    predicates for equivalent functionality.|_
  162%
  163%    @param SocketId the socket  identifier   that  was  provided by
  164%    tipc_socket/2,  and  is  connected  to   the  topology  server,
  165%    name(1,1,0), via tipc_connect/2.
  166%
  167%    @param NameSeqAddress the name_seq/3 address  of the service to
  168%    be monitored.
  169%
  170%    @param PortId the port-id of the socket   that  is bound to the
  171%    service
  172%
  173%    @param Timeout an integer that specifies   the  duration of the
  174%    subscription in milliseconds. A  duration   of  -1, specifies a
  175%    subscription of infinite duration.  @param   Filter  the  event
  176%    filter bit map.
  177%
  178%    @param UserHandle an eight-byte code that is passed
  179%    transparently to the user with each event notification.
  180
  181%!   tipc_open_socket(+SocketId, -InStream, -OutStream) is det.
  182%
  183%    Opens two SWI-Prolog I/O-streams, one to   deal with input from
  184%    the socket and one with output   to  the socket. If tipc_bind/3
  185%    has been called on the socket, OutStream is useless and will
  186%    not be created. After closing both InStream and OutStream, the
  187%    socket itself is discarded.
  188
  189%!   tipc_bind(+Socket, +Address, +ScopingOption) is det.
  190%
  191%    Associates/disassociates a socket with the name/3 or name_seq/3
  192%    address specified in Address. It also registers/unregisters it in
  193%    the  topology  server  name  table.   This  makes  the  address
  194%    visible/invisible to the rest of the   network according to the
  195%    scope specified in ScopingOption. ScopingOption   is a grounded
  196%    term that is one of:
  197%
  198%     $ scope(Scope) : where Scope is  one of: =zone=, =cluster=, or
  199%     =node=. Servers may bind to more   than  one address by making
  200%     successive calls to tipc_bind/3, one for  each address that it
  201%     wishes to advertise. The server will   receive traffic for all
  202%     of them. A server may, for  example, register one address with
  203%     node scope, another with cluster scope,  and a third with zone
  204%     scope. A client may then limit   the scope of its transmission
  205%     by specifying the appropriate address.
  206%
  207%     $ no_scope(Scope) : where Scope is as defined above. An
  208%     application may target a specific address for removal from its
  209%     collection of addresses by specifying the address and its
  210%     scope. The scoping option, =|no_scope(all)|=, may be used to
  211%     unbind the socket from all of its registered addresses. This
  212%     feature allows an application to gracefully exit from service.
  213%     Because the socket remains open, the application may continue
  214%     to service current transactions to completion. TIPC however,
  215%     will not schedule any new work for the server instance. If no
  216%     other servers are available, the work will be rejected or
  217%     dropped according to the socket options specified by the
  218%     client.
  219%
  220%    Connection-oriented, byte-stream services are  implemented with
  221%    this predicate combined with   tipc_listen/2 and tipc_accept/3.
  222%    Connectionless, datagram services  may   be  implemented  using
  223%    tipc_receive/4.
  224%
  225%    Note that clients do not  need  to   bind  to  any address. Its
  226%    port-id is sufficient for this role.   And server sockets (e.g.
  227%    those that are bound to name/3   or  name_seq/3, addresses) may
  228%    not act as clients. That is, they may not originate connections
  229%    from the socket using tipc_connect/2. Servers however, may
  230%    originate datagrams from bound sockets using tipc_send/4.
  231%    Please see the TIPC programmers's guide for other restrictions.
  232
  233%!   tipc_listen(+Socket,+Backlog) is det.
  234%
  235%    Listens for incoming requests for connections. Backlog
  236%    indicates how many pending connection requests are allowed.
  237%    Pending requests are requests that are not yet acknowledged
  238%    using tipc_accept/3. If the indicated number is exceeded, the
  239%    requesting client will be signalled that the service is
  240%    currently not available. A suggested default value is 5.
  241
  242%!   tipc_accept(+Socket, -Slave, -Peer) is det.
  243%
  244%    Blocks on a server socket  and   waits  for connection requests
  245%    from clients. On success,  it  creates   a  new  socket for the
  246%    client and binds the identifier to Slave.  Peer is bound to the
  247%    TIPC address, port_id/2, of the client.
  248
  249%!   tipc_connect(+Socket, +TIPC_address) is det.
  250%
  251%    Provides a connection-oriented, client-interface   to connect a
  252%    socket to a given TIPC_address.   After  successful completion,
  253%    tipc_open_socket/3 may be used  to   create  I/O-Streams to the
  254%    remote socket.
  255
  256%!   tipc_get_name(+Socket, -TIPC_address) is det.
  257%
  258%    Unifies TIPC_address with the port-id assigned to the socket.
  259
  260%!   tipc_get_peer_name(+Socket, -TIPC_address) is det.
  261%
  262%    Unifies TIPC_address with the port-id assigned to the socket
  263%    that this socket is connected to.
  264
  265%!   tipc_setopt(+Socket,+Option) is det.
  266%
  267%    Sets options on the socket. Defined options are:
  268%
  269%    $ importance(+Priority) :
  270%    Allow sockets to assign a priority   to their traffic. Priority
  271%    is one of : =low= (default), =medium=, =high=, or =critical=.
  272%
  273%    $ src_droppable(+Boolean) :
  274%    Allow TIPC to silently discard packets in congested situations,
  275%    rather than queuing them for later transmission.
  276%
  277%    $ dest_droppable(+Boolean) :
  278%    Allow TIPC to silently discard packets in congested situations,
  279%    rather than returning them to the sender as undeliverable.
  280%
  281%    $ conn_timeout(+Seconds) :
  282%    Specifies the time interval that tipc_connect/2 will use before
  283%    abandoning a connection attempt. Default: 8.000 sec.
  284
  285%!   tipc_receive(+Socket, -Data, -From, +OptionList) is det.
  286%
  287%    Waits  for,  and  returns  the  next  datagram.  Like  its  UDP
  288%    counterpart, the data are returned as   a  Prolog string object
  289%    (see string_codes/2). From is  an   address  structure of the
  290%    form port_id/2, indicating the sender of the message.
  291%
  292%     Defined options are:
  293%
  294%      * as(+Type)
  295%      Defines the returned term-type. Type is one of atom, codes or
  296%      string (default).
  297%
  298%      * nonblock
  299%      Poll the socket and return immediately. If a message is
  300%      present, it is returned. If not, then an exception,
  301%      error(socket_error(eagain, Message), _), will be thrown. Users
  302%      are cautioned not to "spin" unnecessarily on non-blocking
  303%      receives as they may prevent the system from servicing other
  304%      background activities such as XPCE event dispatching.
  305%
  306%    The typical sequence to receive a connectionless TIPC datagram is:
  307%
  308%    ==
  309%    receive :-
  310%            tipc_socket(S, dgram),
  311%            tipc_bind(S, name(18888, 10, 0), scope(zone)),
  312%            repeat,
  313%                tipc_receive(Socket, Data, From, [as(atom)]),
  314%                format('Got ~q from ~q~n', [Data, From]),
  315%                Data == quit,
  316%            !, tipc_close_socket(S).
  317%    ==
  318%
  319
  320%!   tipc_send(+Socket, +Data, +To, +Options) is det.
  321%
  322%    sends a TIPC datagram to one or more destinations. Like its UDP
  323%    counterpart, Data is a string, atom  or code-list providing the
  324%    data to be sent.  To  is   a  name/3,  name_seq/3, or port_id/2
  325%    address structure. See tipc_overview.txt, for more information
  326%    on TIPC Address Structures. Options is currently unused.
  327%
  328%    A simple example to send a connectionless TIPC datagram is:
  329%
  330%    ==
  331%    send(Message) :-
  332%            tipc_socket(S, dgram),
  333%            tipc_send(S, Message, name(18888, 10,0), []),
  334%            tipc_close_socket(S).
  335%    ==
  336%
  337%    Messages are delivered silently unless  some form of congestion
  338%    was encountered and the   =|dest_droppable(false)|=  option was
  339%    issued on the sender's socket. In  this case, the send succeeds
  340%    but a notification in the form of  an empty message is returned
  341%    to the sender from  the  receiver,   indicating  some  kind  of
  342%    delivery failure. The port-id of the   receiver  is returned in
  343%    congestion conditions. A =|port_id(0,0)|=, is   returned if the
  344%    destination address was invalid. Senders   and receivers should
  345%    beware of this possibility.
  346%
  347%
  348
  349%!  tipc_event(+Data, -Event, -Residue) is det.
  350%
  351%   Parses event notifications received from   the  topology server
  352%   into Prolog structures. This  predicate   has  been  permanently
  353%   removed.
  354%
  355
  356%!  tipc_receive_subscr_event(+Socket, -Event) is semidet.
  357%
  358%   Receives and parses event notifications received from the
  359%   TIPC Topology Server.
  360%
  361%   _|Please note that this predicate is considered private. Its use
  362%   in user programs is strongly discouraged. See the
  363%   tipc-service predicates for alternatives.|_
  364%
  365%   @param Socket is a TIPC socket that has been previously
  366%   connected to the topology server using tipc_connect/2, and for
  367%   which a subscription has been sent using tipc_subscribe/5,
  368%   above.
  369%
  370%   @param Event is the structure: =|tipc_event(-Action, -Subscr,
  371%   -Found, -Port_id)|=. On subscription timeout, the
  372%   atom, =subscr_timeout= is returned. Subscr is the name-sequence
  373%   address of the original subscription.
  374%
  375%   Action is one of:
  376%
  377%     $ published :
  378%     The socket specified by Port_id has been bound to
  379%     the name_seq/3 address specified in Found.
  380%
  381%     $ withdrawn :
  382%     The socket specified by Port_id has been
  383%     unbound from the name_seq/3 address specified in Found. See
  384%     the no_scope/1 option of tipc_bind/3.
  385
  386:- multifile
  387    prolog:message/3.  388
  389prolog:message(error(socket_error(_Code, Message), _)) -->
  390    [ 'Socket error: ~w'-[Message] ].
  391
  392%!  tipc_canonical_address(-CanonicalAddress, +PortId) is det.
  393%
  394%   Translates a port_id/2 address into canonical TIPC form:
  395%
  396%   * tipc_address(Zone, Cluster, Node, Reference)
  397%
  398%   It is provided for debugging an printing purposes only. The
  399%   canonical address is not used for any other purpose.
  400
  401integerAsU32(In, Out) :-
  402    nonvar(In),
  403    (   In < 0
  404    ->  Out is In + 0x100000000
  405    ;   Out is In
  406    ).
  407integerAsU32(In, Out) :-
  408    nonvar(Out),
  409    (   Out > 0x7fffffff
  410    ->  In is Out - 0x100000000
  411    ;   In is Out
  412    ).
  413
  414tipc_canonical_address(tipc_address(Z,C,N, Ref1), port_id(Ref, Node)) :-
  415    integerAsU32(Ref, Ref1),
  416    integerAsU32(Node, X),
  417    Z is (X >> 24) /\ 0xFF,
  418    C is (X >> 12) /\ 0xFFF,
  419    N is X /\ 0xFFF.
  420
  421user:portray(port_id(Ref, Node)) :-
  422    tipc_canonical_address(tipc_address(Z,C,N, Ref1), port_id(Ref, Node)),
  423    format('port_id(''<~w.~w.~w:~w>'')', [Z,C,N, Ref1]).
  424
  425%!  tipc_service_exists(+Address, +Timeout) is semidet.
  426%!  tipc_service_exists(+Address) is semidet.
  427%
  428%   Interrogates the TIPC topology server to see if a service is
  429%   available at an advertised Address.
  430%
  431%   @param Address is one of:   =|name(Type,  Instance, Domain)|= or
  432%   =|name_seq(Type,  Lower,  Upper)|=.   A    name/3,   address  is
  433%   translated to a name_seq/3, following, where Lower and Upper are
  434%   assigned the value of Instance. Domain is unused and must be
  435%   zero. A =|name_seq(Type, Lower, Upper)|= is a multi-cast
  436%   address. This predicate succeeds if there is at least one
  437%   service that would answer according to multi-cast addressing
  438%   rules.
  439%
  440%   @param Timeout is optional. It is a non-negative real number
  441%   that specifies the amount of time in seconds to block and wait
  442%   for a service to become available. Fractions of a second are
  443%   also permissible.
  444%
  445
  446tipc_address(name(T, I, 0), name_seq(T, I, I)).
  447tipc_address(name_seq(T, L, U), name_seq(T, L, U)).
  448tipc_address(mcast(T, L, U), name_seq(T, L, U)).
  449%
  450%
  451
  452tipc_service_exists(Address) :-
  453    tipc_service_exists(Address, 0.0).
  454
  455tipc_service_exists(Address, Timeout) :-
  456    tipc_address(Address, NameSeq),
  457    !,
  458    ITime is integer(Timeout * 1000),
  459    try_finally(tipc_socket(S, seqpacket),
  460                tipc_close_socket(S)),
  461    tipc_connect(S, name(1,1,0)),   % connect to the topology server
  462    tipc_subscribe(S, NameSeq, ITime, 2, "swipl"),
  463    repeat,
  464        tipc_receive_subscr_event(S, Data),
  465        (   Data == subscr_timeout
  466        ->  !, fail
  467        ;   Data = tipc_event(published, NameSeq, _FoundSeq, _Port_id)
  468        ),
  469    !.
  470
  471%!  tipc_service_probe(?Address) is nondet.
  472%!  tipc_service_probe(?Address, ?PortId) is nondet.
  473%   Allows a user to discover the instance ranges and/or port-ids
  474%   for a particular service.
  475%
  476%   @param Address is a name_seq/3 address. The address type must be
  477%   grounded.
  478%
  479%   @param PortId is  unified  with  the   port-id  for  a  specific
  480%   name_sequence address.
  481%
  482
  483try_finally(Setup, Cleanup) :-
  484    setup_call_cleanup(Setup, ( Solution = yes ; Solution = no ), Cleanup),
  485    Solution = yes.
  486
  487tipc_service_probe(Address) :-
  488    tipc_address(Address, name_seq(Type, Lower, Upper)),
  489    integer(Type),
  490    NameSeq = name_seq(Type, Lower, Upper),
  491    try_finally(tipc_socket(S, seqpacket), tipc_close_socket(S)),
  492    tipc_connect(S, name(1,1,0)),   % connect to the topology server
  493    tipc_subscribe(S, name_seq(Type, 0, 4294967295), 0, 2, "swipl"),  % look for everything
  494    sp_collect(S, Members),
  495    !,
  496    member([NameSeq, _], Members).
  497
  498tipc_service_probe(Address, PortId) :-
  499    tipc_address(Address, name_seq(Type, Lower, Upper)),
  500    integer(Type),
  501    NameSeq = name_seq(Type, Lower, Upper),
  502    try_finally(tipc_socket(S, seqpacket), tipc_close_socket(S)),
  503    tipc_connect(S, name(1,1,0)),   % connect to the topology server
  504    tipc_subscribe(S, name_seq(Type, 0, 4294967295), 0, 1, "swipl"),  % look for everything
  505    sp_collect(S, Members),
  506    !,
  507    member([NameSeq, PortId], Members).
  508
  509sp_collect(S, Members) :-
  510    findall([NameSeq, PortId],
  511        ( repeat,
  512          tipc_receive_subscr_event(S, Data),
  513          (   Data == subscr_timeout
  514              ->  !, fail
  515              ;   Data = tipc_event(published, _Service, NameSeq, PortId)
  516          )
  517        ), Members).
  518
  519%!  tipc_service_port_monitor(+Addresses, :Goal) is det.
  520%!  tipc_service_port_monitor(+Addresses, :Goal, ?Timeout) is det.
  521%
  522%   Monitors a collection of worker threads that are bound to a list
  523%   of Addresses. A single port monitor may be used to provide
  524%   surveillance over workers that are providing a number of
  525%   different services. For a given address type, discontiguous
  526%   port ranges may be specified, but overlapping port ranges may
  527%   not. Goal for example, may simply choose to broadcast the
  528%   notification, thus delegating the notification event handling to
  529%   others.
  530%
  531%   @param Addresses is a list of name/3 or name_seq/3 addresses
  532%   for the services to be monitored.
  533%
  534%   @param Goal is a predicate that will be called when a
  535%   worker's publication status changes. The Goal
  536%   is called exactly once per event with its the last argument
  537%   unified with the structure:
  538%
  539%   $ published(-NameSeq, -PortId) : when the worker binds
  540%   its socket to the address.
  541%
  542%   $ withdrawn(-NameSeq, -PortId) : when the worker
  543%   unbinds its socket from the address.
  544%
  545%   @param Timeout is optional.  It  is   one  of:
  546%
  547%   $ Timeout : a non-negative real number that specifies the
  548%   number of seconds that surveillance is to be continued.
  549%
  550%   $ infinite : causes the monitor to run forever in the current
  551%   thread (e.g. never returns).
  552%
  553%   $ detached(-ThreadId) : causes the monitor to run
  554%   forever as a separate thread. ThreadId is unified with the
  555%   thread identifier of the monitor thread. This is useful when the
  556%   monitor is required to provide continuous surveillance, while
  557%   operating in the background.
  558%
  559
  560spm_dispatch(_Goal, subscr_timeout) :- !.
  561spm_dispatch(Goal, tipc_event(Action, _Subscr, NameSeq, PortId)) :-
  562    Event =.. [Action, NameSeq, PortId],
  563    once(call(Goal, Event)), fail.
  564
  565:- meta_predicate
  566    tipc_service_port_monitor(+, 1),
  567    tipc_service_port_monitor(+, 1, +).  568
  569tipc_service_port_monitor(Address, Goal) :-
  570    tipc_service_port_monitor(Address, Goal, 0.0),
  571    !.
  572
  573tipc_service_port_monitor(Address, Goal, detached(ThreadId)) :-
  574    !,
  575    thread_create(tipc_service_port_monitor(Address, Goal, infinite),
  576                  ThreadId, [detached(true)]).
  577tipc_service_port_monitor(Address, Goal, infinite) :-
  578    tipc_service_port_monitor(Address, Goal, -0.001),
  579    !.
  580
  581tipc_service_port_monitor(Addresses, Goal, Timeout) :-
  582    maplist(tipc_address,Addresses, NameSeqs),
  583    !,
  584    ITime is integer(Timeout * 1000),
  585    try_finally(tipc_socket(S, seqpacket), tipc_close_socket(S)),
  586    tipc_connect(S, name(1,1,0)),   % connect to the topology server
  587    forall(member(NameSeq, NameSeqs),
  588           tipc_subscribe(S, NameSeq, ITime, 1, "swipl")),
  589    repeat,
  590        tipc_receive_subscr_event(S, Data),
  591        spm_dispatch(Goal, Data),
  592    !.
  593
  594%%     tipc_initialize is semidet.
  595%
  596%      causes the TIPC service and the TIPC stack to be initialized
  597%      and made ready for service. An application must call this
  598%      predicate as part of its initialization prior to any use of
  599%      TIPC predicates. _|Please note the change of the API.|_
  600
  601tipc_initialize :-
  602    with_mutex(tipc_mutex,
  603               forall(tipc_stack_initialize, true)).
  604
  605tipc_stack_initialize :-
  606    tipc_service_exists(name(1,1,0))