/** * The conditional directives in this file are for two reasons: * - In C++ sources, the headers will qualify the type and function * definitions with the ::EVB:: namepsace. * - The implementations of the support functions are in C * * All of this is to support C programmers as well as C++. */ #ifdef __cplusplus namespace EVB { #endif /* * Below are valid barrier types. * These are #defines rather than enums so that the * known width data types are used to make data transportable * between 32/64 bit system. */ #define BARRIER_NOTBARRIER 0 /* Not a barrier event. */ #define BARRIER_START 1 /* Data taking starting (BEGIN/RESUME) */ #define BARRIER_END 2 /* Data taking endng (END/PAUSE) */ #define BARRIER_SYNCH 3 /* time synchronization barrier */ #ifdef UINT64_C #define NULL_TIMESTAMP UINT64_C(0xffffffffffffffff) #else #define NULL_TIMESTAMP __UINT64_C(0xffffffffffffffff) #endif typedef struct __attribute__((__packed__))_FragmentHeader { uint64_t s_timestamp; //< Fragment time relative to globally synchronized clock. uint32_t s_sourceId ; //< Unique source identifier. uint32_t s_size; // Bytes in fragment payload. uint32_t s_barrier; // Non zero for barrier events - the barrier type. } FragmentHeader, *pFragmentHeader; typedef struct __attribute__((__packed__)) _Fragment { FragmentHeader s_header; void* s_pBody; } Fragment, *pFragment; typedef struct __attribute__((__packed__)) _FragmentChain { struct _FragmentChain* s_pNext; pFragment s_pFragment; } FragmentChain, *pFragmentChain; typedef struct __attribute__((__packed__)) _FlatFragment { FragmentHeader s_header; int s_body[0]; } FlatFragment, *pFlatFragment; #ifdef __cplusplus } #endif #ifdef __cplusplus extern "C" { #define NS(type) EVB::type #else #define NS(type) type #endif void freeFragment(NS(pFragment) p); NS(pFragment) allocateFragment(const NS(FragmentHeader*) pHeader); NS(pFragment) newFragment(uint64_t timestamp, uint32_t sourceId, uint32_t size); size_t fragmentChainLength(NS(pFragmentChain) p); #ifdef __cplusplus } #endif #undef NS #endif
fragment.h is a header that is deployed for NSCLDAQ. It was first first deployed for NSCLDAQ-V10.2 and has, for the most poart, not evolved since.
fragment.h provides constants, data type definitions and unbound functions that can be used in either C++ Or C code. When compiledin C++, all data types live in the ::EVB namespace. In C, since namespaces don't exist, this namespace specification is turned off. The functions live in the top level namespace (::) in C++ and have C bindings so that they can be used from either C or C++ software.
The primary purpose of the fragment.h header is to provide definitions of data structures that are used both externally and internally by the NSCLDAQ event building pipeline.
The first of these structs, FragmentHeader
(and its pointer pFragmentHeader) contain headers
that are used by the event builder to process fragment payloads.
By extracting all such data into a FragmentHeader
,
the event builder and related software can perform many operations
on event fragments while knowing nothing about their contents.
The fields of this tightly packed struct are:
s_timestamp
The 64 bit timestamp of the fragment. The event builder first orders fragments by timestamp and then glues together timestamps that occur within a prescribed interval of the first unattached fragment into events.
The first part of this process is called Ordering, the second part glomming.
s_sourceId
Is a value that should be unique among data sources and event builder outputs. It represents the data source from which this data came from. Since the NSCLDAQ supports multi-level event building, an event building pipeline must be assigned a data source as well.
s_size
Number of bytes in the fragment payload. The actual location of the fragment payload depends on the struct used to hold both the fragment and the payload.
s_barrier
The barrier type of the fragment. The event builder supports
a data synchroniziation mechanism called barrier synchronization.
Barrier synchronization means that if a fragment is encountered
from a data source with a non-zero
s_barrier
, no more data
will be emitted from that source until fragments at
the front of all data source queues have fragments with a
matching value for s_barrier
.
Barriers can thus be though of as markers that separate data before they were emitted in all sources from data (if any) after they were emitted.
The next struct we will describe is a struct that allows fragments
and their payloads to be constructed without copying the bulk
fragment data (the payload). This type is called a
Fragment
and has a pointer type
pFragment
.
Fragment
is a packed struct with
the following fields.
s_header
A fragment header as described above.
s_pBody
A pointer to the payload. The number of bytes of payload
are described by the s_size
field of the s_header
struct.
An ordered list of fragments may be held by
FragmentChain
items. FragmentChain
and its pointer type,
pFragmentChain, provide for a linked list of
fragments where each element of the list is a
FragmentChain
FragmentChain
is a packed struct containing
the following fields:
s_pNext
Pointer to the next fragment in the chain. This pointer should be nullptr (C++) or NULL (C) at the end of the chain.
s_pFragment
Pointer to the fragment at this element in the chain.
Sometimes it's useful to have the fragment payload inline with the
fragment header. This is, for example, the structure of fragments
in an assembled event. The packet FlatFragment
and its pointer type pFlatFragment describes this
structure.
FlatFragment
has the fields:
s_header
Contains the fragment header.
s_body[0]
Contains the body. The actual size of this field is
held in the s_size
field of the
s_header
struct.
Several constants are defined by this header:
When placed in a the s_barrier
field of a FragmentHeader
,
this value indicates the fragment does not require any barrier
synchronization.
When placed in the s_barrier
field of a FragmentHeader
,
this indicates the fragment should be syncrhonized as
start of runs.
When placed in the s_barrier
field of a FragmentHeader
,
this indicates the fragment should be syncrhonized as
end of runs.
When placed in the s_barrier
field of a FragmentHeader
,
this indicates an external time syncrhonization was
triggered and barrier synchronization should accompany it.
Note that this is not currently used.
When placed in the
s_timestamp
field of
FragmentHeader
, indicates
to the event orderer the data source cannot assign a timestamp
to this fragment. The event orderer will then assign
the most recently used event timestamp from the same
data source.
Four functions are exported by fragment.h. In the descriptions below the notation [EVB::] means that when used from C++, the types described live in the EVB namespace. Conditional compilation directives in fragment.h minimize the
Given a pointer to a fragment where both the header and the body are dynamically allocated, frees the body had header. The free operation is done in critical section making it thread-safe.
The fragment must have been allocated with
newFragment
,
or allocateFragment
as fragment
header and fragment body pools are used to
minimize the actual number of calls to
malloc
and
free
to improve performance
of programs that do a lot of dynamic fragment
allocation and deallocation.
Given a pointer to a fragment header that has
already been filled in, allocates a fragment]
header and fragment body linking them together
in a [EVB::]Fragment. The
header of that object will be filled in by copying
the data in pHeader
.
The body must be filled in by the caller.
The resulting fragment must be freed using
freeFragment
function.
Fragment dynamic alocation using the functions described in this section is managed by maintaining and re-using storage from a pool of the fixed length fragments (header + pointer) and several pools of fixed size fragment bodies. Allocating a fragment means allocating an object from the header pool (or malloc-ing it), locating the pool of the smallest bodies large enough to accomodate pHeader->s_size and allocating one of its bodies or mallocing a new one if that pool is empty.
Freeing a fragment means returning the [EVB::]Fragment to the fixed length pool of fragment structs and the body back to its original pool. This scheme turns out to provide a significant performance advantage over mallocing and freeing objects each time they're needed.
Allocates a fragment and its body from the
and fills it in from the functions parameters.
The header's s_barrier
is set to BARRIER_NOTBARRIER
(the most common case). If you want to create
a barrier, allocate the fragment and the
explicitly set the s_barrier
field of the resulting fragment to the
correct barrier type.
The returned value is a pointer to a dynamically
allocated fragment that must be freed
using freeFragment
when it is no longer needed.top
Returns the amount of stroage required
to convert the fragment chain p
int a soup of flat fragments. This can be used
to size the buffer needed to hold a chain of
fragments when sending it to the orderer or other
recipient over some communication media.