TransferObjects.cpp

// TransferObjects.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007

@import "Ceda/cxPersistStore/IPersistStore.h"
@import "Ceda/cxObject/IObjectVisitor.h"
#include "Ceda/cxUtils/TracerUtils.h"
#include "Ceda/cxUtils/Environ.h"

/*
Demonstration of transfer of objects between PSpaces
----------------------------------------------------

The main reason to use multiple PSpaces is to promote greater concurrency.  This example shows 
one such appproach that is appropriate when creation of new object assemblies is time consuming,
and would otherwise have negative impacts on performance.

The idea is to prepare new object assemblies in an independent "child" PSpace to avoid a lock on
the parent PSpace.  Only when the assembly is completed (which could take a very long time), is 
a lock gained on the parent PSpace in order to transfer the assembly into the parent PSpace.  Often
this can be achieved very quickly.
*/

namespace TransferObjects
{
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // A node of a forwards linked list of ints
    
    $struct Node isa ceda::IPersistable
    {
        Node(int v = 0) : m_value(v) {}

        // Implementation of IObject
        void VisitObjects(ceda::IObjectVisitor& v) const { v << m_next; }

        // Implementation of IPersistable
        void VisitPrefs(ceda::IPrefVisitor& v) const { v << m_next; }
        void Serialise(ceda::Archive& ar) const
        {
            ar << m_next << m_value;
        }
        void Deserialise(ceda::InputArchive& ar) 
        { 
            ar >> m_next >> m_value; 
        }
        
        ceda::cref<Node> m_next;  // Next node in the forward linked list
        int m_value;        // Value of this node
    };
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    // A forwards linked list of int
    
    $struct List <<os>> isa ceda::IPersistable 
    {
        // Implementation of IObject
        void VisitObjects(ceda::IObjectVisitor& v) const { v << m_first << m_last; }

        // Implementation of IPersistable
        void VisitPrefs(ceda::IPrefVisitor& v) const { v << m_first; }
        void Serialise(ceda::Archive& ar) const
        {
            ar << m_first << m_last;
        }
        void Deserialise(ceda::InputArchive& ar) 
        { 
            ar >> m_first >> m_last; 
        }
        
        // Initialise with values [0,1,2,...,n-1]
        void Create(int n)
        {
            if (n > 0)
            {
                Node* p = $new Node(0);
                cxAssert(!p->IsDirty());
                DeclareReachable(this,p);
                cxAssert(p->IsDirty());

                m_first = p;
                {
                    for (int i=1 ; i < n ; ++i)
                    {
                        Node* q = $new Node(i);
                        cxAssert(!q->IsDirty());
                        DeclareReachable(p,q);
                        cxAssert(q->IsDirty());
                        p->m_next = q;
                        p = q;
                    }
                }
                m_last = p;
            }
            MarkAsDirtyWithoutTrace();
        }
        
        void Append(List& rhs)
        {
            if (m_last)
            {
                m_last->m_next = rhs.m_first;
                m_last->MarkAsDirtyWithoutTrace();
                
                m_last = rhs.m_last;
            }
            else
            {
                m_first = rhs.m_first;
                m_last = rhs.m_last;
            }
            MarkAsDirtyWithoutTrace();
            
            rhs.m_first = ceda::null;
            rhs.m_last = ceda::null;
            rhs.MarkAsDirtyWithoutTrace();
        }
        
        void Write(ceda::xostream& os) const
        {
            os << '[';
            Node* p = m_first.Get();
            while(p)
            {
                if (m_first != p) os << ',';
                os << p->m_value;
                p = p->m_next.Get();
            }
            os << ']';
        }

        ceda::cref<Node> m_first;
        ceda::cref<Node> m_last;
    };
    
    ///////////////////////////////////////////////////////////////////////////////////////////////
    
    /*
    todo: currently have a bug that is revealed by for the foo loop below.  Causes assertion in
    CacheMap::Transfer() because OID is already present in P1's ROT on the second iteration.
    
    Problem is caused by transfer being too eager in what it transfers.  The following two objects
    must not be transferred:
    
        1.  PSpaceRoots
        2.  DeletionQueue.
        
    These objects must remain in the ROT, DOS, etc.  This is a bit of a pain because it means we 
    need to filter them.
    */
    
    void Run()
    {
        ceda::TraceGroup g("TransferObjects example");

        // Open or create store
        ceda::xstring path = ceda::GetCedaTestPath("TransferObjects.ced");
        ceda::close_ptr<ceda::PersistStore> pstore(ceda::OpenPersistStore(path.c_str() /*, ceda::OM_CREATE_ALWAYS*/));

        // Open or create main PSpace named p1
        ceda::WPSpace p1(ceda::OpenPSpace(pstore.get(), "p1"));

        // Bootstrap root list L1 in p1
        List* L1 = ceda::BootstrapPSpaceRoot<List>("L1");

        for (int i=0 ; i < 3 ; ++i)
        {
            // Create a subordinate PSpace named p2
            ceda::WPSpace p2(ceda::OpenPSpace(pstore.get(), "p2"));
            
            // Bootstrap root list L2 in p2
            List* L2 = ceda::BootstrapPSpaceRoot<List>("L2");
            
            // In a transaction on p2, populate L2.  This illustrates a time consuming creation of
            // a new object assembly,  avoiding the need to lock the parent PSpace.
            {
                // Declare transaction on p2
                ceda::CSpaceTxn txn;
                
                // Make L2 = [0,1,2,...,9]
                L2->Create(10);
                cxAssert(L2->IsDirty());
                
                Tracer() << "L2 = " << *L2 << '\n';
            }

            // Quickly transfer nodes from child list into parent space
            @if (false)     // todo
            {
                {
                    // Declare transaction on p1 and p2
                    ceda::PSpaceTransferTxn txn(p1,p2);
                
                    // Transfer nodes from L1 to L2
                    L1->Append(*L2);
                    cxAssert(L1->IsDirty());

                    Tracer() << "L1 = " << *L1 << '\n';
                    Tracer() << '\n';
                }
            }
        }
    }
}