66 Skeleton

Example

Consider the following rpc interface Ix:


$interface rpc Ix
{
    void f1(int32 x, uint16 y);
    void f2(const X& x);
    void f3(const xstring& s);
};

Xcpp generates the following code to register a skeleton


struct SkeletonIx : ceda::Skeleton
{
    SkeletonIx() : ceda::Skeleton(GetSkeletonThunkFnTable(),3) {}
    ceda::ptr<Ix> Invoke()
    {
        return ceda::ptr<Ix>(m_delegate.m_self, m_delegate.m_table);
    }
    void Thunk0()
    {
        ceda::int32 x;
        ceda::uint16 y;
        Pop() >> x >> y;
        Invoke()->f1(x,y);
    }
    void Thunk1()
    {
        X x;
        Pop() >> x;
        Invoke()->f2(x);
    }
    void Thunk2()
    {
        ceda::string8 s;
        Pop() >> s;
        Invoke()->f3(s);
    }
    static void sThunk0(void* _self) { ((SkeletonIx*)_self)->Thunk0(); }
    static void sThunk1(void* _self) { ((SkeletonIx*)_self)->Thunk1(); }
    static void sThunk2(void* _self) { ((SkeletonIx*)_self)->Thunk2(); }
    static const ceda::SkeletonThunkFn* GetSkeletonThunkFnTable()
    {
        static const ceda::SkeletonThunkFn t[] =
        {
            &sThunk0,
            &sThunk1,
            &sThunk2,
        };
        return t;
    }
};
struct SkeletonIxFactory : public ceda::ISkeletonFactory
{
    SkeletonIxFactory()
    {
        ceda::RegisterSkeleton(ceda::GetIpcInterfaceName<Ix>(),this);
    }
    ~SkeletonIxFactory()
    {
        ceda::UnregisterSkeleton(ceda::GetIpcInterfaceName<Ix>());
    }
    virtual ceda::Skeleton* CreateSkeleton()
    {
        return new SkeletonIx;
    }
};
void _Register_SkeletonIx()
{
    static SkeletonIxFactory s;
}

Xcpp generates code such as the following for an RPC interface.


struct SkeletonIServer : public ceda::Skeleton
{
    SkeletonIServer() : ceda::Skeleton(GetSkeletonThunkFnTable(), 3) {}
    IServer& Invoke()
    {
        return reinterpret_cast(m_delegate);
    }
    void Thunk0()
    {
        int32 x;
        Pop() >> x;
        Invoke().PrintInt(x);
    }
    void Thunk1()
    {
        int32 a;
        int32 b;
        Pop() >> a;
        Invoke().Square(a,b);
        Push() << b;
    }
    void Thunk2()
    {
        int32 a;
        int32 b;
        float32 c;
        Pop() >> a >> b;
        Invoke().Sum(a,b,c);
        Push() << c;
    }

    static void sThunk0(void* self) { ((SkeletonIServer*)self)->Thunk0(); }
    static void sThunk1(void* self) { ((SkeletonIServer*)self)->Thunk1(); }
    static void sThunk2(void* self) { ((SkeletonIServer*)self)->Thunk2(); }

    static const SkeletonThunkFn* GetSkeletonThunkFnTable()
    {
        static const SkeletonThunkFn t[] =
        {
            &sThunk0,
            &sThunk1,
            &sThunk2,
        };
        return t;
    }
};

Skeleton.h

// Skeleton.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2020

@import "cxRpc.h"
@import "Ceda/cxObject/Object.h"
#include "Ceda/cxUtils/Archive.h"

namespace ceda
{
class Skeleton;

///////////////////////////////////////////////////////////////////////////////////////////////////
// ISkeletonFactory

struct ISkeletonFactory
{
    virtual Skeleton* CreateSkeleton() = 0;
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// Skeleton factory registry

// The SkeletonFactoryRegistry is a transient registry indexed by fully qualified name of the
// interface.
// This allows the system to automatically generate a suitable skeleton object for a given interface

// The following three functions can be called by different threads - they are fully threadsafe.

// Returns false if skeleton factory has already been registered
@api bool RegisterSkeleton(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory);

@api void UnregisterSkeleton(ConstStringZ qualifiedInterfaceName);

// Create a skeleton for the given interface for which a skeleton factory has previously been
// registered.
// This must be deleted using C++ delete keyword
// Returns nullptr if no skeleton factory for the given interface has been registered.
@api Skeleton* CreateSkeleton(ConstStringZ qualifiedInterfaceName);

///////////////////////////////////////////////////////////////////////////////////////////////////
// Skeleton

typedef void (*SkeletonThunkFn)(void* self);

/*
    template<typename T>
    struct SkeletonThunkFnTable3
    {
        static void sThunk0(void* self) { ((T*)self)->Thunk0(); }
        static void sThunk1(void* self) { ((T*)self)->Thunk1(); }
        static void sThunk2(void* self) { ((T*)self)->Thunk2(); }

        static const SkeletonThunkFn* Table()
        {
            static const SkeletonThunkFn t[] =
            {
                &sThunk0,
                &sThunk1,
                &sThunk2,
            };
            return t;
        }
    };
*/

class Skeleton
{
    cxNotCloneable(Skeleton)
public:
    explicit Skeleton(const SkeletonThunkFn* skeletonThunkFnTable, ssize_t numMethods) :
        m_ar(nullptr),
        m_skeletonThunkFnTable(skeletonThunkFnTable),
        numMethods(numMethods)
    {
    }

    // Returns read-archive for reading the in-parameters
    inline InputArchive& Pop() { return m_ar; }

    // Test whether the given index is valid
    inline bool ValidIndex(ssize_t index) const { return 0 <= index && index < numMethods; }

    // The ultimate receiver of the messages.   It is assumed this can be reinterpret cast to the
    // interface associated with this Skeleton.
    AnyInterface m_delegate;

    InputArchive m_ar;
    const SkeletonThunkFn* m_skeletonThunkFnTable;
    ssize_t numMethods;
};

} // namespace ceda

Skeleton.cpp

// Skeleton.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2020

@import "Skeleton.h"
#include "Ceda/cxUtils/xstring.h"
#include "Ceda/cxUtils/Tracer.h"
#include <map>
#include <mutex>

@def bool tf_SkeletonFactory = false

namespace ceda
{
/////////////////////////////////////////////////////////////////////////////////////////////////
// SkeletonFactoryRegistry

class SkeletonFactoryRegistry
{
public:
    static SkeletonFactoryRegistry& GetInstance()
    {
        static SkeletonFactoryRegistry s;
        return s;
    }

    SkeletonFactoryRegistry() { m_magic = MAGIC; }
    ~SkeletonFactoryRegistry() { m_magic = 0; }

    bool Register(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory);
    void Unregister(ConstStringZ qualifiedInterfaceName);
    Skeleton* CreateSkeleton(ConstStringZ qualifiedInterfaceName);

private:
    enum
    {
        MAGIC = 0xceda2009
    };
    int32 m_magic;

    // Map from ReflectedInterface to factory
    typedef std::map<xstring, ISkeletonFactory*> MAP;
    MAP m_map;

    // Mutex used to control access to the map
    std::mutex mapMutex_;
};

bool SkeletonFactoryRegistry::Register(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory)
{
    @if (tf_SkeletonFactory)
    {
        Tracer() << "SCION: Registering skeleton for interface " << qualifiedInterfaceName << '\n';
    }

    std::lock_guard<std::mutex> lock(mapMutex_);

    MAP::iterator i = m_map.find(qualifiedInterfaceName);
    if (i == m_map.end())
    {
        m_map[qualifiedInterfaceName] = factory;
        return true;
    }
    else
    {
        return false;
    }
}

void SkeletonFactoryRegistry::Unregister(ConstStringZ qualifiedInterfaceName)
{
    // When the application is closed ungracefully, Unregister can be called after
    // the SkeletonFactoryRegistry has already destructed, so we check for this using a magic
    // value.
    if (m_magic == MAGIC)
    {
        std::lock_guard<std::mutex> lock(mapMutex_);

        MAP::iterator i = m_map.find(qualifiedInterfaceName);
        cxAssert(i != m_map.end());
        m_map.erase(i);
    }
}

Skeleton* SkeletonFactoryRegistry::CreateSkeleton(ConstStringZ qualifiedInterfaceName)
{
    @if (tf_SkeletonFactory)
    {
        Tracer() << "SCION: Creating skeleton for interface " << qualifiedInterfaceName << '\n';
    }

    std::lock_guard<std::mutex> lock(mapMutex_);

    MAP::iterator i = m_map.find(qualifiedInterfaceName);
    if (i == m_map.end())
    {
        // Not found
        @if (tf_SkeletonFactory)
        {
            Tracer() << "SCION: *** ERROR : Skeleton interface " << qualifiedInterfaceName << " not registered\n";
        }
        return nullptr;
    }
    else
    {
        return i->second->CreateSkeleton();
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

@api bool RegisterSkeleton(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory)
{
    return SkeletonFactoryRegistry::GetInstance().Register(qualifiedInterfaceName,factory);
}

@api void UnregisterSkeleton(ConstStringZ qualifiedInterfaceName)
{
    SkeletonFactoryRegistry::GetInstance().Unregister(qualifiedInterfaceName);
}

@api Skeleton* CreateSkeleton(ConstStringZ qualifiedInterfaceName)
{
    return SkeletonFactoryRegistry::GetInstance().CreateSkeleton(qualifiedInterfaceName);
}

} // namespace ceda