// Copyright (C) 2012  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#undef DLIB_BsP_ABSTRACT_Hh_
#ifdef DLIB_BsP_ABSTRACT_Hh_

#include "../noncopyable.h"
#include "../sockets/sockets_extensions_abstract.h"
#include <vector>

namespace dlib
{

// ----------------------------------------------------------------------------------------

    class bsp_context : noncopyable
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This is a tool used to implement algorithms using the Bulk Synchronous
                Parallel (BSP) computing model.  A BSP algorithm is composed of a number of
                processing nodes, each executing in parallel.  The general flow of
                execution in each processing node is the following:
                    1. Do work locally on some data.
                    2. Send some messages to other nodes.
                    3. Receive messages from other nodes.
                    4. Go to step 1 or terminate if complete.

                To do this, each processing node needs an API used to send and receive
                messages.  This API is implemented by the bsp_connect object which provides
                these services to a BSP node.  

                Note that BSP processing nodes are spawned using the bsp_connect() and
                bsp_listen() routines defined at the bottom of this file.  For example, to
                start a BSP algorithm consisting of N processing nodes, you would make N-1
                calls to bsp_listen() and one call to bsp_connect().  The call to
                bsp_connect() then initiates the computation on all nodes.

                Finally, note that there is no explicit barrier synchronization function
                you call at the end of step 3.  Instead, you can simply call a method such
                as try_receive() until it returns false.  That is, the bsp_context's
                receive methods incorporate a barrier synchronization that happens once all
                the BSP nodes are blocked on receive calls and there are no more messages
                in flight. 


            THREAD SAFETY
                This object is not thread-safe.  In particular, you should only ever have
                one thread that works with an instance of this object.  This means that,
                for example, you should not spawn sub-threads from within a BSP processing
                node and have them invoke methods on this object.  Instead, you should only
                invoke this object's methods from within the BSP processing node's main
                thread (i.e. the thread that executes the user supplied function funct()).
        !*/

    public:

        template <typename T>
        void send(
            const T& item,
            unsigned long target_node_id
        );
        /*!
            requires
                - item is serializable 
                - target_node_id < number_of_nodes()
                - target_node_id != node_id()
            ensures
                - sends a copy of item to the node with the given id.
            throws
                - dlib::socket_error:
                    This exception is thrown if there is an error which prevents us from
                    delivering the message to the given node.  One way this might happen is
                    if the target node has already terminated its execution or has lost
                    network connectivity. 
        !*/

        template <typename T>
        void broadcast (
            const T& item
        );
        /*!
            ensures
                - item is serializable
                - sends a copy of item to all other processing nodes.   
            throws
                - dlib::socket_error
                    This exception is thrown if there is an error which prevents us from
                    delivering a message to one of the other nodes.  This might happen, for
                    example, if one of the nodes has terminated its execution or has lost
                    network connectivity.
        !*/

        unsigned long node_id (
        ) const; 
        /*!
            ensures
                - Returns the id of the current processing node.  That is, 
                  returns a number N such that:
                    - N < number_of_nodes()
                    - N == the node id of the processing node that called node_id().  This
                      is a number that uniquely identifies the processing node.
        !*/

        unsigned long number_of_nodes (
        ) const; 
        /*!
            ensures
                - returns the number of processing nodes participating in the BSP
                  computation.
        !*/

        template <typename T>
        bool try_receive (
            T& item
        );
        /*!
            requires
                - item is serializable
            ensures
                - if (this function returns true) then
                    - #item == the next message which was sent to the calling processing
                      node.
                - else
                    - The following must have been true for this function to return false:
                        - All other nodes were blocked on calls to receive(),
                          try_receive(), or have terminated.
                        - There were not any messages in flight between any nodes.  
                        - That is, if all the nodes had continued to block on receive
                          methods then they all would have blocked forever.  Therefore,
                          this function only returns false once there are no more messages
                          to process by any node and there is no possibility of more being
                          generated until control is returned to the callers of receive
                          methods. 
                    - When one BSP node's receive method returns because of the above
                      conditions then all of them will also return.  That is, it is NOT the
                      case that just a subset of BSP nodes unblock.  Moreover, they all
                      unblock at the same time.  
            throws
                - dlib::socket_error:
                    This exception is thrown if some error occurs which prevents us from
                    communicating with other processing nodes.
                - dlib::serialization_error or any exception thrown by the global
                  deserialize(T) routine:
                    This is thrown if there is a problem in deserialize().  This might
                    happen if the message sent doesn't match the type T expected by
                    try_receive().
        !*/

        template <typename T>
        void receive (
            T& item
        );
        /*!
            requires
                - item is serializable
            ensures
                - #item == the next message which was sent to the calling processing
                  node.
                - This function is just a wrapper around try_receive() that throws an
                  exception if a message is not received (i.e. if try_receive() returns
                  false).
            throws
                - dlib::socket_error:
                    This exception is thrown if some error occurs which prevents us from
                    communicating with other processing nodes or if there was not a message
                    to receive.
                - dlib::serialization_error or any exception thrown by the global
                  deserialize(T) routine:
                    This is thrown if there is a problem in deserialize().  This might
                    happen if the message sent doesn't match the type T expected by
                    receive().
        !*/

        template <typename T>
        bool try_receive (
            T& item,
            unsigned long& sending_node_id
        ); 
        /*!
            requires
                - item is serializable
            ensures
                - if (this function returns true) then
                    - #item == the next message which was sent to the calling processing
                      node.
                    - #sending_node_id == the node id of the node that sent this message.
                    - #sending_node_id < number_of_nodes()
                - else
                    - The following must have been true for this function to return false:
                        - All other nodes were blocked on calls to receive(),
                          try_receive(), or have terminated.
                        - There were not any messages in flight between any nodes.  
                        - That is, if all the nodes had continued to block on receive
                          methods then they all would have blocked forever.  Therefore,
                          this function only returns false once there are no more messages
                          to process by any node and there is no possibility of more being
                          generated until control is returned to the callers of receive
                          methods. 
                    - When one BSP node's receive method returns because of the above
                      conditions then all of them will also return.  That is, it is NOT the
                      case that just a subset of BSP nodes unblock.  Moreover, they all
                      unblock at the same time.  
            throws
                - dlib::socket_error:
                    This exception is thrown if some error occurs which prevents us from
                    communicating with other processing nodes.
                - dlib::serialization_error or any exception thrown by the global
                  deserialize(T) routine:
                    This is thrown if there is a problem in deserialize().  This might
                    happen if the message sent doesn't match the type T expected by
                    try_receive().
        !*/

        template <typename T>
        void receive (
            T& item,
            unsigned long& sending_node_id
        ); 
        /*!
            requires
                - item is serializable
            ensures
                - #item == the next message which was sent to the calling processing node.
                - #sending_node_id == the node id of the node that sent this message.
                - #sending_node_id < number_of_nodes()
                - This function is just a wrapper around try_receive() that throws an
                  exception if a message is not received (i.e. if try_receive() returns
                  false).
            throws
                - dlib::socket_error:
                    This exception is thrown if some error occurs which prevents us from
                    communicating with other processing nodes or if there was not a message
                    to receive.
                - dlib::serialization_error or any exception thrown by the global
                  deserialize(T) routine:
                    This is thrown if there is a problem in deserialize().  This might
                    happen if the message sent doesn't match the type T expected by
                    receive().
        !*/

        void receive (
        );
        /*!
            ensures
                - Waits for the following to all be true:
                    - All other nodes were blocked on calls to receive(), try_receive(), or
                      have terminated.
                    - There are not any messages in flight between any nodes.  
                    - That is, if all the nodes had continued to block on receive methods
                      then they all would have blocked forever.  Therefore, this function
                      only returns once there are no more messages to process by any node
                      and there is no possibility of more being generated until control is
                      returned to the callers of receive methods. 
                - When one BSP node's receive method returns because of the above
                  conditions then all of them will also return.  That is, it is NOT the
                  case that just a subset of BSP nodes unblock.  Moreover, they all unblock
                  at the same time.  
            throws
                - dlib::socket_error:
                    This exception is thrown if some error occurs which prevents us from
                    communicating with other processing nodes or if a message is received
                    before this function would otherwise return.

        !*/

    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    template <
        typename funct_type
        >
    void bsp_connect (
        const std::vector<network_address>& hosts,
        funct_type funct
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function spawns a BSP job consisting of hosts.size()+1 processing nodes.
            - The processing node with a node ID of 0 will run locally on the machine
              calling bsp_connect().  In particular, this node will execute funct(CONTEXT),
              which is expected to carry out this node's portion of the BSP computation.
            - The other processing nodes are executed on the hosts indicated by the input
              argument.  In particular, this function interprets hosts as a list addresses
              identifying machines running the bsp_listen() or bsp_listen_dynamic_port()
              routines.  
            - This call to bsp_connect() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1
        >
    void bsp_connect (
        const std::vector<network_address>& hosts,
        funct_type funct,
        ARG1 arg1
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function spawns a BSP job consisting of hosts.size()+1 processing nodes.
            - The processing node with a node ID of 0 will run locally on the machine
              calling bsp_connect().  In particular, this node will execute funct(CONTEXT,arg1),
              which is expected to carry out this node's portion of the BSP computation.
            - The other processing nodes are executed on the hosts indicated by the input
              argument.  In particular, this function interprets hosts as a list addresses
              identifying machines running the bsp_listen() or bsp_listen_dynamic_port()
              routines.  
            - This call to bsp_connect() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1,
        typename ARG2
        >
    void bsp_connect (
        const std::vector<network_address>& hosts,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function spawns a BSP job consisting of hosts.size()+1 processing nodes.
            - The processing node with a node ID of 0 will run locally on the machine
              calling bsp_connect().  In particular, this node will execute funct(CONTEXT,arg1,arg2),
              which is expected to carry out this node's portion of the BSP computation.
            - The other processing nodes are executed on the hosts indicated by the input
              argument.  In particular, this function interprets hosts as a list addresses
              identifying machines running the bsp_listen() or bsp_listen_dynamic_port()
              routines.  
            - This call to bsp_connect() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1,
        typename ARG2,
        typename ARG3
        >
    void bsp_connect (
        const std::vector<network_address>& hosts,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2,
        ARG3 arg3
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2,arg3) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function spawns a BSP job consisting of hosts.size()+1 processing nodes.
            - The processing node with a node ID of 0 will run locally on the machine
              calling bsp_connect().  In particular, this node will execute funct(CONTEXT,arg1,arg2,arg3),
              which is expected to carry out this node's portion of the BSP computation.
            - The other processing nodes are executed on the hosts indicated by the input
              argument.  In particular, this function interprets hosts as a list addresses
              identifying machines running the bsp_listen() or bsp_listen_dynamic_port()
              routines.  
            - This call to bsp_connect() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1,
        typename ARG2,
        typename ARG3,
        typename ARG4
        >
    void bsp_connect (
        const std::vector<network_address>& hosts,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2,
        ARG3 arg3,
        ARG4 arg4
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2,arg3,arg4) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function spawns a BSP job consisting of hosts.size()+1 processing nodes.
            - The processing node with a node ID of 0 will run locally on the machine
              calling bsp_connect().  In particular, this node will execute funct(CONTEXT,arg1,arg2,arg3,arg4),
              which is expected to carry out this node's portion of the BSP computation.
            - The other processing nodes are executed on the hosts indicated by the input
              argument.  In particular, this function interprets hosts as a list addresses
              identifying machines running the bsp_listen() or bsp_listen_dynamic_port()
              routines.  
            - This call to bsp_connect() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    template <
        typename funct_type
        >
    void bsp_listen (
        unsigned short listening_port,
        funct_type funct
    );
    /*!
        requires
            - listening_port != 0
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT) will be executed and it will
              then be able to participate in the BSP computation as one of the processing
              nodes.  
            - This function will listen on TCP port listening_port for a connection from
              bsp_connect().  Once the connection is established, it will close the
              listening port so it is free for use by other applications.  The connection
              and BSP computation will continue uninterrupted.
            - This call to bsp_listen() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1
        >
    void bsp_listen (
        unsigned short listening_port,
        funct_type funct,
        ARG1 arg1
    );
    /*!
        requires
            - listening_port != 0
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1) will be executed and it will
              then be able to participate in the BSP computation as one of the processing
              nodes.  
            - This function will listen on TCP port listening_port for a connection from
              bsp_connect().  Once the connection is established, it will close the
              listening port so it is free for use by other applications.  The connection
              and BSP computation will continue uninterrupted.
            - This call to bsp_listen() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1,
        typename ARG2
        >
    void bsp_listen (
        unsigned short listening_port,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2
    );
    /*!
        requires
            - listening_port != 0
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1,arg2) will be executed and
              it will then be able to participate in the BSP computation as one of the
              processing nodes.  
            - This function will listen on TCP port listening_port for a connection from
              bsp_connect().  Once the connection is established, it will close the
              listening port so it is free for use by other applications.  The connection
              and BSP computation will continue uninterrupted.
            - This call to bsp_listen() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1,
        typename ARG2,
        typename ARG3
        >
    void bsp_listen (
        unsigned short listening_port,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2,
        ARG3 arg3
    );
    /*!
        requires
            - listening_port != 0
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2,arg3) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1,arg2,arg3) will be
              executed and it will then be able to participate in the BSP computation as
              one of the processing nodes.  
            - This function will listen on TCP port listening_port for a connection from
              bsp_connect().  Once the connection is established, it will close the
              listening port so it is free for use by other applications.  The connection
              and BSP computation will continue uninterrupted.
            - This call to bsp_listen() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename funct_type,
        typename ARG1,
        typename ARG2,
        typename ARG3,
        typename ARG4
        >
    void bsp_listen (
        unsigned short listening_port,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2,
        ARG3 arg3,
        ARG4 arg4
    );
    /*!
        requires
            - listening_port != 0
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2,arg3,arg4) must be a valid expression 
                  (i.e. funct must be a function or function object)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1,arg2,arg3,arg4) will be
              executed and it will then be able to participate in the BSP computation as
              one of the processing nodes.  
            - This function will listen on TCP port listening_port for a connection from
              bsp_connect().  Once the connection is established, it will close the
              listening port so it is free for use by other applications.  The connection
              and BSP computation will continue uninterrupted.
            - This call to bsp_listen() blocks until the BSP computation has completed on
              all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    template <
        typename port_notify_function_type,
        typename funct_type
        >
    void bsp_listen_dynamic_port (
        unsigned short listening_port,
        port_notify_function_type port_notify_function,
        funct_type funct
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT) must be a valid expression 
                  (i.e. funct must be a function or function object)
            - port_notify_function((unsigned short) 1234) must be a valid expression
              (i.e. port_notify_function() must be a function or function object taking an 
              unsigned short)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT) will be executed and it will
              then be able to participate in the BSP computation as one of the processing
              nodes.  
            - if (listening_port != 0) then
                - This function will listen on TCP port listening_port for a connection
                  from bsp_connect().  
            - else
                - An available TCP port number is automatically selected and this function
                  will listen on it for a connection from bsp_connect(). 
            - Once a listening port is opened, port_notify_function() is called with the
              port number used.  This provides a mechanism to find out what listening port
              has been used if it is automatically selected.  It also allows you to find
              out when the routine has begun listening for an incoming connection from
              bsp_connect().
            - Once a connection is established, we will close the listening port so it is
              free for use by other applications.  The connection and BSP computation will
              continue uninterrupted.
            - This call to bsp_listen_dynamic_port() blocks until the BSP computation has
              completed on all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename port_notify_function_type,
        typename funct_type,
        typename ARG1
        >
    void bsp_listen_dynamic_port (
        unsigned short listening_port,
        port_notify_function_type port_notify_function,
        funct_type funct,
        ARG1 arg1
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1) must be a valid expression 
                  (i.e. funct must be a function or function object)
            - port_notify_function((unsigned short) 1234) must be a valid expression
              (i.e. port_notify_function() must be a function or function object taking an 
              unsigned short)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1) will be executed and it
              will then be able to participate in the BSP computation as one of the
              processing nodes.  
            - if (listening_port != 0) then
                - This function will listen on TCP port listening_port for a connection
                  from bsp_connect().  
            - else
                - An available TCP port number is automatically selected and this function
                  will listen on it for a connection from bsp_connect(). 
            - Once a listening port is opened, port_notify_function() is called with the
              port number used.  This provides a mechanism to find out what listening port
              has been used if it is automatically selected.  It also allows you to find
              out when the routine has begun listening for an incoming connection from
              bsp_connect().
            - Once a connection is established, we will close the listening port so it is
              free for use by other applications.  The connection and BSP computation will
              continue uninterrupted.
            - This call to bsp_listen_dynamic_port() blocks until the BSP computation has
              completed on all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename port_notify_function_type,
        typename funct_type,
        typename ARG1,
        typename ARG2
        >
    void bsp_listen_dynamic_port (
        unsigned short listening_port,
        port_notify_function_type port_notify_function,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2) must be a valid expression 
                  (i.e. funct must be a function or function object)
            - port_notify_function((unsigned short) 1234) must be a valid expression
              (i.e. port_notify_function() must be a function or function object taking an 
              unsigned short)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1,arg2) will be executed and
              it will then be able to participate in the BSP computation as one of the
              processing nodes.  
            - if (listening_port != 0) then
                - This function will listen on TCP port listening_port for a connection
                  from bsp_connect().  
            - else
                - An available TCP port number is automatically selected and this function
                  will listen on it for a connection from bsp_connect(). 
            - Once a listening port is opened, port_notify_function() is called with the
              port number used.  This provides a mechanism to find out what listening port
              has been used if it is automatically selected.  It also allows you to find
              out when the routine has begun listening for an incoming connection from
              bsp_connect().
            - Once a connection is established, we will close the listening port so it is
              free for use by other applications.  The connection and BSP computation will
              continue uninterrupted.
            - This call to bsp_listen_dynamic_port() blocks until the BSP computation has
              completed on all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename port_notify_function_type,
        typename funct_type,
        typename ARG1,
        typename ARG2,
        typename ARG3
        >
    void bsp_listen_dynamic_port (
        unsigned short listening_port,
        port_notify_function_type port_notify_function,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2,
        ARG3 arg3
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2,arg3) must be a valid expression 
                  (i.e. funct must be a function or function object)
            - port_notify_function((unsigned short) 1234) must be a valid expression
              (i.e. port_notify_function() must be a function or function object taking an 
              unsigned short)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1,arg2,arg3) will be
              executed and it will then be able to participate in the BSP computation as
              one of the processing nodes.  
            - if (listening_port != 0) then
                - This function will listen on TCP port listening_port for a connection
                  from bsp_connect().  
            - else
                - An available TCP port number is automatically selected and this function
                  will listen on it for a connection from bsp_connect(). 
            - Once a listening port is opened, port_notify_function() is called with the
              port number used.  This provides a mechanism to find out what listening port
              has been used if it is automatically selected.  It also allows you to find
              out when the routine has begun listening for an incoming connection from
              bsp_connect().
            - Once a connection is established, we will close the listening port so it is
              free for use by other applications.  The connection and BSP computation will
              continue uninterrupted.
            - This call to bsp_listen_dynamic_port() blocks until the BSP computation has
              completed on all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------

    template <
        typename port_notify_function_type,
        typename funct_type,
        typename ARG1,
        typename ARG2,
        typename ARG3,
        typename ARG4
        >
    void bsp_listen_dynamic_port (
        unsigned short listening_port,
        port_notify_function_type port_notify_function,
        funct_type funct,
        ARG1 arg1,
        ARG2 arg2,
        ARG3 arg3,
        ARG4 arg4
    );
    /*!
        requires
            - let CONTEXT be an instance of a bsp_context object.  Then:
                - funct(CONTEXT,arg1,arg2,arg3,arg4) must be a valid expression 
                  (i.e. funct must be a function or function object)
            - port_notify_function((unsigned short) 1234) must be a valid expression
              (i.e. port_notify_function() must be a function or function object taking an 
              unsigned short)
        ensures
            - This function listens for a connection from the bsp_connect() routine.  Once
              this connection is established, funct(CONTEXT,arg1,arg2,arg3,arg4) will be
              executed and it will then be able to participate in the BSP computation as
              one of the processing nodes.  
            - if (listening_port != 0) then
                - This function will listen on TCP port listening_port for a connection
                  from bsp_connect().  
            - else
                - An available TCP port number is automatically selected and this function
                  will listen on it for a connection from bsp_connect(). 
            - Once a listening port is opened, port_notify_function() is called with the
              port number used.  This provides a mechanism to find out what listening port
              has been used if it is automatically selected.  It also allows you to find
              out when the routine has begun listening for an incoming connection from
              bsp_connect().
            - Once a connection is established, we will close the listening port so it is
              free for use by other applications.  The connection and BSP computation will
              continue uninterrupted.
            - This call to bsp_listen_dynamic_port() blocks until the BSP computation has
              completed on all processing nodes.
        throws
            - dlib::socket_error
                This exception is thrown if there is an error which prevents the BSP
                job from executing.  
            - Any exception thrown by funct() will be propagated out of this call to
              bsp_connect().
    !*/

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

}

#endif // DLIB_BsP_ABSTRACT_Hh_