TargetRegistry.h
// TargetRegistry.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007
@import "Object.h"
#include "Ceda/cxUtils/xvector.h"
/*
Targets
-------
Definition : Each DLL or EXE is a /target/. A target is uniquely identified by its /targetName/.
E.g. "Ceda/cxObject" is a target name. Note that target names make use of namespaces. A target name
shouldn't include version numbers.
Definition : C is a /sub-target/ of P (written C < P) if P statically links directly or indirectly
against C.
Note that the sub-target relation is irreflexive, anti-symmetric and transitive.
E.g. Ceda/cxObject is a sub-target of Ceda/cxPersistStore.
Note that we can distinguish between direct and indirect sub-targets.
Target Index Numbers
--------------------
cxObject contains a threadsafe singleton registry called the /TargetRegistry/ that can be used by
a target within a running process to obtain its unique /Target Index Number/ or (TIN), an integer of
type ssize_t.
The TargetRegistry records the 1-1 mapping between targetName and TIN. A target will register
itself, obtaining its TIN when it is first loaded into the process. A target records its TIN in a
static global variable named s_localTin so it is local to the target.
Definition : Let tin(T) be the TIN assigned to target T (within a given running process).
Release sequence numbers
------------------------
Each target has its own /Release Sequence Number/ (RSN) to uniquely identify each formal release of
the target. RSNs start at 0.
Consider target T4 with sub-targets T1,T2,T3. Then we can build a table for the releases of T4
showing the corresponding RSNs of T1,T2,T3. For example
T4 Date T1 T2 T3
RSN RSN RSN RSN
------------------------------------------
0 23 Feb 2008 14 3 0
1 18 Jan 2009 16 3 0
2 09 Mar 2009 17 3 4
3 14 Aug 2009 20 3 6
Definition: Given target T, CurrentRSN(T) is the last RSN in the table for T.
For example, CurrentRSN(T4) = 3
Definition: Given target T, Rsns(T) = { r | 0 <= r <= CurrentRSN(T) } is the set of valid RSNs
of T
Definition: Let S be a sub-target of T. Let r in Rsns(T). Then SubTargetRsn(T,r,S) gives the
RSN of S corresponding to release r of T.
Eg SubTargetRsn(T4,2,T3) = 4
T1,T2,T3 may in turn have sub-targets so they will have similar tables. Note for example
that if T1 is a sub-target of T3 then the above column of T1 RSNs would be redundant and would not be
directly recorded by the application programmer.
Mapping RSNs
------------
When a target is first loaded into a process, it allocates and populates a multidimensional array
used to map its RSNs to all direct or indirect sub-target RSNs.
Definition:
Given target T, let SubTargetRsnMap(T) be a pointer to an array of pointers to arrays of
integers (of type ssize_t), such that
SubTargetRsnMap(T)[r][s] = SubTargetRsn(T,r,S)
where S is a sub-target of T having s = tin(S), and r in Rsns(T)
Note that SubTargetRsnMap(T)[r] is the address of an array of integers
*/
namespace ceda
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// Date
$struct+ Date
{
int16 year;
int8 month; // 1-12
int8 day; // 1-31
};
@api void Serialise(Archive& ar, const Date& x);
@api void Deserialise(InputArchive& ar, Date& x);
@api xostream& operator<<(xostream& os, const Date& x);
///////////////////////////////////////////////////////////////////////////////////////////////////
// dsTargetRelease
struct dsTargetRelease0
{
ConstStringZ m_name; // Name of the release
Date m_date; // Date of the release
};
template <ssize_t n>
struct dsTargetRelease
{
ConstStringZ m_name; // Name of the release
Date m_date; // Date of the release
RSN m_directSubTgtRsns[n];
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// dsTarget
$struct+ dsTarget
{
ConstStringZ m_targetName; // Fully qualified name of the target
ssize_t m_numDirectSubTargets;
ConstStringZ const* m_directSubTargetNames;
ssize_t m_numReleases;
const dsTargetRelease<1>* m_releases;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// XTarget
$adt+ XTarget
{
void Unregister();
const dsTarget& GetdsTarget();
TIN GetTin();
@for (rType in mRegistryTypes)
{
@def R = Reflected@@rType
void RegisterReflected@@rType(const R* e);
const xvector<const R*>& Get@@rType@@Registry() const;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// Target registration
@api XTarget* RegisterTarget(const dsTarget& dst);
// Find the target with the given name. Returns nullptr if not found
@api XTarget* FindTarget(ConstStringZ targetName);
// Find the target with the given TIN.
@api XTarget* FindTarget(TIN tin);
@api void GetTargets(xvector<XTarget*>& targets);
@api void WriteDataSourceRsn(Archive& ar, RSN rsn);
// Called at the beginning of the deserialisation of a datasource, in order to read the RSN from
// the archive a set thread local storage as required so that subsequent calls to GetModelRsn()
// work as expected.
@api void ReadDataSourceRsn(InputArchive& ar, XTarget* tgt);
// Called when deserialising a model embedded under a datasource, perhaps in a different DLL to
// where the datasource is defined.
@api RSN GetModelRsn(TIN localTin);
@runpython
{
# convert the given targetName into a valid identifier by replacing forward slash characters
# with underscores
def targetNameAsIdentifier(s):
i = 0
r = ''
while i < len(s):
c = s[i]
if c == '/':
r = r + '_'
else:
r = r + c
i = i+1
return r
}
@defpython CEDA_TARGET_NAME_TO_IDENTIFIER(s) =
{
targetNameAsIdentifier(@str(s))
}
/*
Typically used for targets which don't define $models, and therefore have no need for declaring
subtargets and releases
*/
@def mRegisterTargetNotSupportingModels =
{
@def tgtNameId = CEDA_TARGET_NAME_TO_IDENTIFIER(@root_to_projdir_path)
namespace ceda
{
static dsTarget s_dsTarget =
{
@str(@root_to_projdir_path),
0,
nullptr,
0,
nullptr
};
static XTarget* s_xTarget = nullptr;
XTarget* tgtNameId@@_GetXTarget()
{
if (s_xTarget == nullptr)
{
s_xTarget = RegisterTarget(s_dsTarget);
}
return s_xTarget;
}
$init
{
tgtNameId@@_GetXTarget();
}
} // ceda
}
// Declare in a cpp file *outside a namespace* within a target in order to declare its s_localTin
// and have it registered when the DLL is loaded.
@def mRegisterTargetHelper(subTgtNames,tgtReleases, bool leaf) =
{
@def tgtNameId = CEDA_TARGET_NAME_TO_IDENTIFIER(@root_to_projdir_path)
namespace ceda
{
@if (leaf)
{
static dsTargetRelease0 s_targetReleases[] = tgtReleases;
static dsTarget s_dsTarget =
{
@str(@root_to_projdir_path),
0,
nullptr,
cxArraySize(s_targetReleases),
(dsTargetRelease<1>*) s_targetReleases
};
}
@else
{
static ConstStringZ s_subTargetNames[] = subTgtNames;
static dsTargetRelease<cxArraySize(s_subTargetNames)> s_targetReleases[] = tgtReleases;
static dsTarget s_dsTarget =
{
@str(@root_to_projdir_path),
cxArraySize(s_subTargetNames),
s_subTargetNames,
cxArraySize(s_targetReleases),
(dsTargetRelease<1>*) s_targetReleases
};
}
static TIN s_localTin = -1;
static RSN s_localRsn = cxArraySize(s_targetReleases) - 1;
static XTarget* s_xTarget = nullptr;
RSN tgtNameId@@_GetModelRsn()
{
return GetModelRsn(s_localTin);
}
void tgtNameId@@_WriteDataSourceRsn(Archive& ar)
{
WriteDataSourceRsn(ar,s_localRsn);
}
void tgtNameId@@_ReadDataSourceRsn(InputArchive& ar)
{
ReadDataSourceRsn(ar,s_xTarget);
}
XTarget* tgtNameId@@_GetXTarget()
{
if (s_xTarget == nullptr)
{
s_xTarget = RegisterTarget(s_dsTarget);
s_localTin = GetTin(s_xTarget);
}
return s_xTarget;
}
$init
{
tgtNameId@@_GetXTarget();
}
} // ceda
}
/*
Used for a target defining $models for which there are no subtargets defining $models
A new release must be declared when any $models within the target undergo schema evolution.
These releases may correspond to a proper subset of the formal releases of the target
because changes to functionality without involving schema evolution of a $model do not require
a release to be declared in mRegisterLeafTarget.
Example:
mRegisterLeafTarget(
{
// version year mon day
{ "0.1", {2015, 01, 01} }, // Release #0
{ "0.5", {2017, 03, 23} }, // Release #1
{ "1.0", {2018, 09, 06} }, // Release #2
})
*/
@def mRegisterLeafTarget(tgtReleases) = mRegisterTargetHelper(,tgtReleases,true)
/*
Used for a target defining $models for which there are subtargets defining $models
The declared subtargets are only the /direct subtargets/. The framework calculates the
transitive closure to give both the direct and indirect subtargets.
It is only necessary to define direct subtargets for libraries which define models which are
directly /embedded/ within models of this target. In theory one could link against a library
but not be embedding any of its models; there is no need to declare a dependency in such cases.
A new release must be declared when any models within the target or models embedded within the
target undergo schema evolution. That means that typically when a subtarget has a new release, it is
necessary to also add a release to this target (so there's kind of a domino effect)
Example:
mRegisterTarget(
// Names of the sub-targets
{
"Ceda/cxModel"
},
// Releases
{
// version year mon day cxModel
{ "0.1", {2015, 01, 01}, 0 }, // Release #0
{ "0.7", {2015, 02, 24}, 4 }, // Release #1
{ "0.23", {2015, 03, 12}, 5 }, // Release #2
{ "1.08", {2015, 07, 15}, 5 }, // Release #3
{ "2.04", {2015, 11, 31}, 6 }, // Release #4
})
*/
@def mRegisterTarget(subTgtNames,tgtReleases) = mRegisterTargetHelper(subTgtNames,tgtReleases,false)
} // namespace ceda