DeleteObjects.cpp

// DeleteObjects.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2012

@import "Store.h"
#include "Ceda/cxUtils/Environ.h"
#include "Ceda/cxUtils/HPTime.h"

/*
Example generated output:

    DeleteObjects
    {
        Create new store
          Num check points          = 0
          Num recoveries            = 0
          Num transactions          = 2
          Segment size              = 512 kB
          Num serial elements       = 5
          Num live overflow packets = 0
          Num live RPM packets      = 0
          Num obsolete packets      = 2
          Total utilisation         = 0000000000000083 = 131
          File size                 = 0000000000080000 = 524288
            % utilisation           = 0.025%
        Open store and insert a child object
          root = X(100,01010101.01010106:DeleteObjects::X:0000000001D7B960)
        Open store and delete the child object
          root = X(100,01010101.01010106:DeleteObjects::X:0000000001D7B840)
          c = X(0,null)
    }
*/

namespace DeleteObjects
{
    $struct X isa ceda::IPersistable :
        model
        {
            ceda::int32 m_value;
            ceda::cref<X> m_child;
        }
    {
    };

    void Run()
    {
        ceda::TraceGroup g("DeleteObjects");
        
        ceda::xstring path = ceda::GetCedaTestPath("DeleteObjects.ced");
        
        {
            Tracer() << "Create new store\n";
            ceda::TraceIndenter indent;

            Store store(path, ceda::OM_CREATE_ALWAYS);
            X* root = store.BootstrapRoot<X>();

            #if CEDA_LSS_USE_CONTIGUOUS_SEID_SPACE
                /*
                The PersistStore creates two special serial elements:
                   RootPersistStoreObject: oid = 1.0
                      PSpaceMap          : oid = 1.1
                
                The PSpace creates two special serial elements:
                    PSpaceRoots          : oid = 1.2
                    DeletionQueue        : oid = 1.3

                The call to BootstrapRoot<X> allocates another oid:
                    root                 : oid = 1.4
                */
                cxAssert(GetOid(root) == ceda::OID(4,1));
            #else
                /*
                The PersistStore creates two special serial elements:
                   RootPersistStoreObject: oid = 01010101.01010101
                      PSpaceMap          : oid = 01010101.01010102
                
                The PSpace creates two special serial elements:
                    PSpaceRoots          : oid = 01010101.01010103
                    DeletionQueue        : oid = 01010101.01010104

                The call to BootstrapRoot<X> allocates another oid:
                    root                 : oid = 01010101.01010105
                */
                cxAssert(GetOid(root) == ceda::OID(0x01010105, 0x01010101));
            #endif
            
            cxAssert(store.GetNumPersistableObjects() == 1);
            cxAssert(store.GetNumPSpaces() == 1);

            store.ShowLssStats();
        }
        
        {
            Tracer() << "Open store and insert a child object\n";
            ceda::TraceIndenter indent;

            Store store(path);
            X* root = store.BootstrapRoot<X>();
            
            {
                ceda::CSpaceTxn txn;
                root->m_value = 100;

                // Create an object.  This is registered in the CSpace but is not yet reachable 
                // from the persitent store so doesn't have an OID yet
                X* c = $new X;
                cxAssert(!GetOid(c));
                
                // This assignment causes the object to become persistent, so it is allocated an OID
                // (in this case 00000001.00000006).
                root->m_child = c;

                Tracer() << "GetOid(c) = " << GetOid(c) << '\n';
                
                #if CEDA_LSS_USE_CONTIGUOUS_SEID_SPACE
                    // todo : this is no longer the case. Why?
                    /*
                    // The object with OID 00000001.00000005 is the ceda::WorkingSetMgr
                    ceda::pref<ceda::IPersistable> p;
                    p.SetOid( ceda::OID(5,1) );
                    Tracer() << "Object with oid 00000001.00000005 = " << p.Get() << '\n';
                    */
                    cxAssert(GetOid(c) == ceda::OID(5,1));
                #else
                    cxAssert(GetOid(c) == ceda::OID(0x01010106, 0x01010101));
                #endif
                
                Tracer() << "root = " << *root << '\n';
            }
            cxAssert(store.GetNumPersistableObjects() == 2);
        }
        
        {
            Tracer() << "Open store and delete the child object\n";
            ceda::TraceIndenter indent;

            Store store(path);
            X* root = store.BootstrapRoot<X>();

            {
                ceda::CSpaceTxn txn;

                cxAssert(root->m_value == 100);
            
                X* c = root->m_child.Get();
                cxAssert(c);
                
                Tracer() << "root = " << *root << '\n';
                Tracer() << "c = " << *c << '\n';
                
                // The following assignment causes the child object to be permanently deleted from
                // the store, but this happens asychronously.
                root->m_child = nullptr;
            }
            
            cxAssert(store.GetNumPersistableObjects() == 2);

            // Set the period for the DosWriter to 100msec, and wait for long enough for the async 
            // deletion to be completed.
            cxAssert( GetDosPeriod(store.m_store) == 1000 );
            SetDosPeriod(store.m_store, 100);
            Sleep(200);
            
            cxAssert(store.GetNumPersistableObjects() == 1);
        }
    }
}


/*
Example generated output:

    DeleteObjects2
    {
        Create new store
        Open store and insert a child object
          root = (100,(0,null))
        Open store and delete the child object
          root = (100,(0,null))
          c = (0,null)
          root = (100,null)
    }
*/

namespace DeleteObjects2
{
    $struct X <<os>> isa ceda::IPersistable
    {
        X() : m_value(0) {}
        explicit X(int v) : m_value(v) {}
        
        // Implementation of IObject
        void VisitObjects(ceda::IObjectVisitor& v) const { v << m_child; }

        // Implementation of IPersistable
        void VisitPrefs(ceda::IPrefVisitor& v) const { v << m_child; }

        void Write(ceda::xostream& os) const
        {
            os << '(' << m_value << ',';
            if (m_child)
            {
                m_child->Write(os);
            }
            else
            {
                os << "null";
            }
            os << ')';
        }

        void Serialise(ceda::Archive& ar) const
        {
            ar << m_value << m_child;
        }

        void Deserialise(ceda::InputArchive& ar)
        {
            ar >> m_value >> m_child;
        }
    
        ceda::int32 m_value;
        ceda::cref<X> m_child;
    };

    void Run()
    {
        ceda::TraceGroup g("DeleteObjects2");
        
        ceda::xstring path = ceda::GetCedaTestPath("DeleteObjects2.ced");
        
        {
            Tracer() << "Create new store\n";
            ceda::TraceIndenter indent;

            Store store(path, ceda::OM_CREATE_ALWAYS);
            X* root = store.BootstrapRoot<X>();

            #if CEDA_LSS_USE_CONTIGUOUS_SEID_SPACE
                cxAssert(GetOid(root) == ceda::OID(4,1));
            #else
                cxAssert(GetOid(root) == ceda::OID(0x01010105, 0x01010101));
            #endif
            cxAssert(store.GetNumPersistableObjects() == 1);
        }
        
        {
            Tracer() << "Open store and insert a child object\n";
            ceda::TraceIndenter indent;

            Store store(path);
            X* root = store.BootstrapRoot<X>();
            
            {
                ceda::CSpaceTxn txn;

                // Create an object.  This is registered in the CSpace but is not yet reachable 
                // from the persitent store so doesn't have an OID yet
                X* c = $new X(25);
                cxAssert(!GetOid(c));
                cxAssert(!c->IsDirty());
                
                // These assignments don't mark the root as dirty, nor do they cause c to be allocated
                // an oid.
                root->m_value = 100;
                root->m_child = c;
                cxAssert(!root->IsDirty());
                cxAssert(!GetOid(c));
                
                root->MarkAsDirtyWithoutTrace();
                cxAssert(root->IsDirty());
                
                // Causes c to become persistent, so it is allocated an OID (in this case 
                // 00000001.00000005 (was 01010101.01010106)).
                DeclareReachable(root,c);
                
                #if CEDA_LSS_USE_CONTIGUOUS_SEID_SPACE
                    ceda::OID EXPECTED_OID_OF_CHILD(5,1);
                #else
                    ceda::OID EXPECTED_OID_OF_CHILD(0x01010106, 0x01010101);
                #endif
                cxAssert(GetOid(c) == EXPECTED_OID_OF_CHILD);
                
                Tracer() << "root = " << *root << '\n';
            }
            cxAssert(store.GetNumPersistableObjects() == 2);
        }
        
        {
            Tracer() << "Open store and delete the child object\n";
            ceda::TraceIndenter indent;

            Store store(path);
            X* root = store.BootstrapRoot<X>();

            {
                ceda::CSpaceTxn txn;

                cxAssert(root->m_value == 100);
            
                X* c = root->m_child.Get();
                cxAssert(c);
                
                Tracer() << "root = " << *root << '\n';
                Tracer() << "c = " << *c << '\n';

                // This assignment doesn't mark the root as dirty, nor does it cause c to be 
                // deleted from the store.
                root->m_child = nullptr;
                cxAssert(!root->IsDirty());
                
                #if CEDA_LSS_USE_CONTIGUOUS_SEID_SPACE
                    ceda::OID EXPECTED_OID_OF_CHILD(5,1);
                #else
                    ceda::OID EXPECTED_OID_OF_CHILD(0x01010106, 0x01010101);
                #endif
                
                cxAssert(GetOid(c) == EXPECTED_OID_OF_CHILD);

                root->MarkAsDirtyWithoutTrace();    
                cxAssert(root->IsDirty());

                Tracer() << "root = " << *root << '\n';

                cxAssert(TryBindObjectGivenOid(EXPECTED_OID_OF_CHILD) == c);
                cxAssert(TryBindObjectGivenOid(EXPECTED_OID_OF_CHILD) != root);
                
                cxAssert(store.GetNumPersistableObjects() == 2);
                
                SyncPermanentlyDeleteObject(c);
                
                // After the call to SyncPermanentlyDeleteObject(c) the following conditions are met:
                //    1)    c no longer has an oid
                //    2)    c is marked as clean
                //    2)    TryBindObjectGivenOid() now returns null
                //cxAssert(!GetOid(c));  todo: still has the same oid
                cxAssert(TryBindObjectGivenOid(EXPECTED_OID_OF_CHILD) == ceda::null);

                // This works because GetNumPersistableObjects() first ensures all changes to the PSpace 
                // are flushed to the LSS
                cxAssert(store.GetNumPersistableObjects() == 1);
            }
        }
    }
}