MultiWorkingSetIPC.h

// MultiWorkingSetIPC.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2015

@import "cxWorkingSetIpc.h"
@import "WsipcSessionProtocolId.h"
@import "WorkingSetIPC.h"

@if (false)
{
@import "Ceda/cxPersistStore/IPersistStore.h"
@import "Ceda/cxOperation/IWorkingSetMachine.h"
@import "Ceda/cxObject/Object.h"
#include "Ceda/cxMessage/MultiplexedMsg.h"
#include "Ceda/cxMessage/MClientServer.h"
#include "Ceda/cxMessage/Socket.h"
#include "Ceda/cxUtils/Guid.h"

/*
A MwsipcSession allows for synchronisation using DeltaRW/ROD messages of any number of working 
sets using a single stream oriented connection.

The set of working sets which can be synchronised over the connection is dynamic - i.e. it 
can change over the life of the session (this rules out directly using the multiplexing 
capabilities of the MultiplexedMsgConnection to multiplex messages across different working sets).
*/

namespace ceda
{
$class MultiplexedMsgConnection;

// Uniquely identifies a working set
$typedef+ Guid TWorkingSetId;

// MwsipcSession has a ptr<IMwsipcSessionHandler> in order to make callbacks into the caller
$interface+ IMwsipcSessionHandler : IObject
{
    /*
    Called when AddWorkingSet() has been called on the peer and a message has been received
    locally indicating that the peer wants to synchronise the working set with the given wsid.
    
    Called without any locks on any CSpaces.
    
    Return nullptr to reject synchronisation (in which case currently the connection is
    aborted), otherwise return a WorkingSetMachine to be added as a working set to be synchronised.

    Note that the MwsipcSession does not take on responsibility for closing the returned 
    WorkingSetMachine.  For every WorkingSetMachine that is returned by this function, there will 
    eventually be a call to ReleaseWorkingSetMachine to indicate when it is safe to close the
    WorkingSetMachine.  Note that it is sufficient to close the working set machines after the 
    MultiplexedMsgConnection has been closed.
    */
    WorkingSetMachine* OnReceiveUnmatchedWorkingSetId(const TWorkingSetId& wsid);

    /*
    Called when the MwsipcSession is no longer referencing the given WorkingSetMachine making 
    it safe for it to be closed.
    
    Called without any locks on any CSpaces.  This makes it safe to close the WorkingSetMachine 
    from within the implementation of this function.
    */
    void ReleaseWorkingSetMachine(WorkingSetMachine* wsm);
};

$adt+ MwsipcSession
{
    /*
    Working sets can be added/removed dynamically over the life of the session.

    These functions can be called with or without locks on the CSpace of the working set.
    There functions have an asynchronous nature.  They do not need to be paired. 
    
    There is a tendency for the last call to dominate previous calls. E.g. calling AddWorkingSet 
    then RemoveWorkingSet for a given working set probably means it ends up being removed, however 
    exactly what happens depends on the peer (which can also have called AddWorkingSet).

    These functions must not be called after the associated MultiplexedMsgConnection has been 
    closed.
    */
    
    /*
    Subject to various assumptions a call to AddWorkingSet() eventually causes a working set to be
    synchronised with the peer.  The peer doesn't need to also call AddWorkingSet().  If it does 
    that is fine, alternatively it can work passively and add a WorkingSetMachine when requested 
    in its implementation of OnReceiveUnmatchedWorkingSetId().

    An attempt to add a working set that is already being synchronised is not an error, the attempt
    to do so is simply ignored.

    Note that the MwsipcSession does not take on responsibility for closing the given 
    WorkingSetMachine.  The given WorkingSetMachine must not be closed until after the 
    MultiplexedMsgConnection is closed.
    */
    void AddWorkingSet(const TWorkingSetId& wsid, WorkingSetMachine* wsm);

    /*    
    Subject to various assumptions a call to RemoveWorkingSet() eventually stops the sending of 
    messages both locally and remotely to synchronise a working set, but this can take some time to
    eventuate.
    
    It is not necessary for the peer to also call RemoveWorkingSet() - syncing of a working set is 
    stopped when either or both sides call RemoveWorkingSet().
    
    It must not be assumed that the WorkingSetMachine can be closed after calling this function.  
    Currently the only safe thing to do is to close all the WorkingSetMachines after the 
    MultiplexedMsgConnection is closed.

    RemoveWorkingSet() can be called safely on a working set that was never added and it can safely 
    be called more than once on a given working set.  The implementation simply ignores redundant
    calls to RemoveWorkingSet().

    It is not necessary to remove all working sets before the MultiplexedMsgConnection is closed.
    */
    void RemoveWorkingSet(const TWorkingSetId& wsid);
};

/*
Performs the following:

*   Creates an implementation of IMessageWriter that sends working set deltas and ROD objects, 
    and calls AddWriter() on the given MultiplexedMsgConnection.

*   Creates an implementation of IMessageReader that receives and applies working set
    deltas and ROD objects, and calls AddReader() on the given MultiplexedMsgConnection.
    
*   Ensures that calls to NotifyMoreMessagesToWrite() will be made as required on the 
    given MultiplexedMsgConnection when there are changes to a working set that are 
    ready to be sent as a working set delta.

The created MwsipcSession is concerned with supporting interactive collaboration, by sending and 
receiving working set deltas on each WorkingSetMachine.  The protocol begins with an exchange of 
datasetids, siteids and vector times for each WorkingSetMachine.

It is expected that the peer will call the same function in order to set up their side of
the connection. 

Assumes the given MultiplexedMsgConnection has been created but OpenConnection() hasn't been 
called yet.  After calling OpenConnection() the client will need to make a single call to
NotifyMoreMessagesToWrite() in order to kick-start the message sending.

The given MultiplexedMsgConnection takes ownership of the returned MwsipcSession - i.e. the 
MwsipcSession is automatically deleted when the MultiplexedMsgConnection is closed.

WorkingSetMachines are pushed to the MwsipcSession by calling AddWorkingSet() and pulled by the
MwsipcSession when it calls OnReceiveUnmatchedWorkingSetId() on the handler.  In either case
it is very important not to close a WorkingSetMachine until after the MultiplexedMsgConnection
has been closed.
*/
$function+ MwsipcSession* CreateMwsipcSession(
    ptr<IMwsipcSessionHandler> handler,         // Can be null
    MultiplexedMsgConnection* c, 
    const WsipcSessionProtocolId& protocolId);

@api MultiplexedMsgConnection* CreateSimpleWorkingSetConnection(
    Iocp* iocp, 
    WorkingSetMachine* wsm, 
    SocketHandle socket,
    const WsipcSessionProtocolId& protocolId,
    const MessageReaderSettings& readerSettings,
    const MessageWriterSettings& writerSettings);

// Create a server that listens on the given port
// Returns NULL if server could not be created.  A more specific windows error can be obtained
// with a call to GetLastError().
$function+ MServer* CreateSimpleWsipcServer(
    Iocp* iocp, 
    WorkingSetMachine* wsm,
    const WsipcSessionProtocolId& protocolId,
    SOCKET_PORT port, 
    const TcpSettings& tcpSettings = TcpSettings());

// Create a client that tries to connect to the given host that is listening on the given port
// todo : discuss error conditions
$function+ MClient* CreateSimpleWsipcClient(
    Iocp* iocp, 
    WorkingSetMachine* wsm,
    const WsipcSessionProtocolId& protocolId, 
    ConstStringZ hostname, SOCKET_PORT port, 
    const TcpSettings& tcpSettings = TcpSettings());

} // namespace ceda
} // false