fragment.h

Name

fragment.h -- Define event builder fragment structures.

Synopsis


/**
 * 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
                
            

DESCRIPTION

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.

DATA STRUCTURES

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:

uint64_ts_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.

uint32_ts_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.

uint32_ts_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.

uint32_ts_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.

FragmentHeaders_header

A fragment header as described above.

void*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:

pFragmentChains_pNext

Pointer to the next fragment in the chain. This pointer should be nullptr (C++) or NULL (C) at the end of the chain.

pFragments_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:

FlatFragment s_header

Contains the fragment header.

ints_body[0]

Contains the body. The actual size of this field is held in the s_size field of the s_header struct.

CONSTANTS

Several constants are defined by this header:

BARRIER_NOTBARRIER

When placed in a the s_barrier field of a FragmentHeader, this value indicates the fragment does not require any barrier synchronization.

BARRIER_START

When placed in the s_barrier field of a FragmentHeader, this indicates the fragment should be syncrhonized as start of runs.

BARRIER_END

When placed in the s_barrier field of a FragmentHeader, this indicates the fragment should be syncrhonized as end of runs.

BARRIER_SYNCH

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.

NULL_TIMESTAMP

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.

FUNCTIONS

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

voidfreeFragment( [EVB::]pFragment p );

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 allocateFragmentas 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.

[EVB::]pFragmentallocateFragment( const [EVB::]FragmentHeader* pHeader );

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.

[EVB::]pFragmentnewFragment( uint64_t timestamp uint32_t sourceId uint32_t size );

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

size_tfragmentChainLength( [EVB::]pFragmentChain p );

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.