17 \$cache functions using DGAsyncIndepNode

Parameterless $cache functions are treated specially because there is no need for a cache map. Instead they are more like an individual async dependent variable.

Consider the following example:


@import "Ceda/cxObject/DGAsyncNode.h"

$struct X isa ceda::IObject
{
    $cache <<async>> int y() const
    {
        return 7;
    }
};

Xcpp generates the following C++ code:

struct X : public ceda::IObjectBaseMixin<X,ceda::EmptyBase>
{
    typedef ceda::IObjectBaseMixin<X,ceda::EmptyBase> BaseClass;

    int _calc_y() const
    {
        return 7;
    }

    struct _depnode_y : public ceda::DGAsyncIndepNode<_depnode_y,X,int>
    {
        typedef ceda::DGAsyncIndepNode<_depnode_y,X,int> base;
        using typename base::output_type;
        static void CalcOutput(const X* _self, output_type& _output)
        {
            _output = _self->_calc_y();
        }
        X const* _GetSelf() const { return CONST_ATTRIB_CAST(X,_dn_y); }
        X const* _GetFinalSelf() const { return _GetSelf(); }
        virtual ceda::xstring Name() const { return "X::y()"; }
    } _dn_y;

    int const& y() const
    {
        return _dn_y.read();
    }

    void EvictDgsNodes() const
    {
        BaseClass::EvictDgsNodes();
        _dn_y.TryEvict(false);
    }

    ceda::AnyInterface QueryInterface(const ceda::ReflectedInterface&);
    ceda::ReflectedClass const* GetReflectedClass() const;
};

There is a nested struct named _depnode_y which represents an asynchronously calculated variable.

_depnode_y is a subclass of ceda::DGAsyncIndepNode which is defined in DGNode.h

DGAsyncIndepNode

template <typename FinalClass, typename Self, typename Output>
class DGAsyncIndepNode : public DGIndepNode
{
public:
    typedef Output output_type;

    virtual ssize_t ByteSize() const
    {
        // This is the *overhead* of caching the output, it is assumed when OnEvict() calls
        // ClearCacheValue(_output), CacheValueAdditionalSize() gives the number of bytes that is recovered.
        return CacheValueAdditionalSize(_output);
    }

    virtual void VisitContainingObject(IObjectVisitor& _v) const
    {
        _v << _output << static_cast<const FinalClass*>(this)->_GetFinalSelf();
    }

    virtual void OnEvict() const
    {
        OnEvictCacheValue(_output);
        ClearCacheValue(_output);
    }

    virtual bool IsEvictable() const
    {
        return !Enabled(DF_ASYNC_RUNNING) && CacheValueIsEvictable(_output);
    }

    const output_type& read() const
    {
        if (Enabled(DF_CALLED_READ))
        {
            if (Enabled(DF_ASYNC_RUNNING))
            {
                // read() has already been called, and an async output is currently being calculated.
                // Attach out-nodes so when the output is updated the out-nodes will be invalidated
                ReadBarrier();
            }
            else
            {
                // Calls to read() don't invoke the read barrier once the output has been
                // calculated, because it cannot change once it is calculated.
            }
        }
        else
        {
            // read() has been called for the first time.
            SetFlag(DF_CALLED_READ);

            // Attach out-nodes - these will be invalidated when the async output has been calculated.
            ReadBarrier();

            CSpace* cspace = GetThreadPtr<CSpace>();

            cxAssert(!Enabled(DF_ASYNC_RUNNING));
            SetFlag(DF_ASYNC_RUNNING);

            // _output hasn't been calculated before so post a task to calculate it.
            PostAsyncTask(cspace, [this]()
            {
                PrepareThreadLocalStorage(static_cast<const FinalClass*>(this)->_GetFinalSelf());

                // Calculate output from key without a CSpace lock, hold output in a local variable
                output_type localOutput;
                FinalClass::CalcOutput(static_cast<const FinalClass*>(this)->_GetSelf(), localOutput);

                {
                    // Use CSpace lock to swap in the output, then invalidate the out-nodes
                    CSpaceTxn txn;
                    cxAssert(Enabled(DF_ASYNC_RUNNING));
                    UpdateOutput(_output, localOutput);
                    OnChange();
                    ClearFlag(DF_ASYNC_RUNNING);
                    if (TypeHasAdditionalSize<output_type>()) UpdateByteSize();
                }
            });
        }
        return _output;
    }
protected:
    mutable output_type _output;        // Protected by a CSpace lock
};