# Unit reference

The Unit is declared in

    sched/Unit.h

. It represents an execution unit that controls the execution order of a sequential part of a model. An unit is an Mtarget but in reality it may be used in one thread only. The multithreading support in the references is needed to build the multithreaded applications but each Unit in the application belongs to one thread (possibly sharing it with other Units).

    Unit(const string &name);

Constructs an execution unit.

    const string &getName() const;

Get back the name.

    void schedule(Onceref rop);
    void scheduleTray(const_Onceref tray);
    void fork(Onceref rop);
    void forkTray(const_Onceref tray);
    void call(Onceref rop);
    void callTray(const_Onceref tray);

    void enqueue(int em, Onceref rop);
    void enqueueTray(int em, const_Onceref tray);

Schedule, fork or call a rowop or tray, like in Perl. Unlike Perl, the methods with a tray argument have different names. And the enqueueing mode is always an integer constant. These constants are defined in the enum Gadget::EnqMode (the Gadget class is described in &xrsp;), and is one of

    Gadget::EM_SCHEDULE

,

    Gadget::EM_FORK

,

    Gadget::EM_CALL

and

    Gadget::EM_IGNORE

.

    void enqueueDelayedTray(const_Onceref tray);

Enqueue (schedule, fork or call) the rowops from a tray built by Gadget's method

    sendDelayed()

. The Rowop has a semi-undocumented field to store the enqueuing mode for that particular Rowop, that gets populated by

    Gadget::sendDelayed()

. The

    enqueueDelayedTray()

uses the value from this field to determine the enqueueing method individually for each Rowop. The whole arraggement is largely obsolete: it used to be used for the aggregators in a Table, mixing the outputs from multiple aggregators with potentially different enqueueing methods in one tray. But now everything just uses

    EM_CALL

and there is no need for this any more.

    bool empty() const;

Check whether all the Unit's frames are empty.

    bool isFrameEmpty() const;

Check whether the current inner frame is empty.

    bool isInOuterFrame() const;

Check whether the unit's current inner frame is the same as its outer frame, which means that the unit is not in the middle of a call.

    void callNext();
    void drainFrame();

Execute the next rowop from the current (inner) frame, or all the rowops on the current frame. The semantics is the same as in the Perl code.

    void setMark(Onceref mark);

Set a mark on the current frame, same as in Perl.

    void loopAt(FrameMark *mark, Onceref rop);
    void loopTrayAt(FrameMark *mark, const_Onceref tray);

Enqueue a rowop or a tray at the marked frame.

    void callAsChained(const Label *label, Rowop *rop, const Label *chainedFrom);

This method was introduced in Triceps version 2, and hasn't propagated to Perl yet. I'm not even sure that I want it visible in Perl, since it's kind of low-level. It executes a label call, assuming that it was chained from another label (before version 2 the functionality itself had obviously existed but was not visible in the API).

The row types of all the arguments must be matching, or the method would likely crash.

The label will be called with rowop, just as if this label was chained from another label. Since the chaining might be multi-level, the

    chainedFrom

label is not necessarily the one in the rowop, it can itself be chained from the label in the rowop, directly or indirectly.

All the tracing for the chained calls is automatically invoked. Keeping the consistency of the tracing is up to you: if you use the

    chainedFrom

argument label that hasn't actually been called through the chain, the trace will look surprising.

This method is used in Triceps in such places as calling a label in FnBinding through an FnReturn. You can use it directly as well, just be very careful.

    void clearLabels();

Clear all the unit's labels, same semantics as when called from Perl.

    void rememberLabel(Label *lab);

The method that connects a label to the unit. Normally you don't need to call it manually, the label constructor calls it (and that's why it's not in the Perl API). The only real reason to use this method manually is if you've disconnected a label manually from the unit, and want to reconnect it back (and I'm not sure if anyone would ever want that). Calling this method repeatedly with the same label and unit will have no effect. Remembering the same label in multiple units is not a good idea.

    void  forgetLabel(Label *lab);

Make the unit forget a label, so on

    clearLabels()

that label won't be cleared. This is another dangerous low-level method, since only the unit will forget about the label but the label will still keep the pointer to the unit, unless it's cleared. Because of the danger, it's also not in the Perl API. The reason to use it would be if you want to disassemble and discard a part of the unit without disturbing the rest of it. However a safer alternative is to just create multiple units in one thread and discard by a whole unit.

    RowType *getEmptyRowType() const;

A convenience method to get a reference to a row type with no fields. Such row type is useful for creation of pseudo-labels that have the user-defined clearing handlers that clear some user data.

Each unit has its own instance of an empty row type. The separate instances (instead of having a single global one) are purely for the conveniece of memory management in the threads, they are all equivalent. You could also create your own empty row type.

    void setMaxStackDepth(int v);
    int maxStackDepth() const;
    void setMaxRecursionDepth(int v);
    int maxRecursionDepth() const;

Set and get the maximal unit stack depth and recursion depth, works the same as in Perl.

    int getStackDepth() const;

Returns the current depth of the execution stack. It isn't of any use for the model logic as such but comes handy for debugging, to check in the loops that you haven't accidentally created a stack growing with iterations. When the unit is not running, the stack depth is 1, since the outermost frame always stays on the stack. When a rowop is being executed, the stack depth is at least 2.

    void clearLabels();

Clear all the labels in the unit, then drop the references from unit to them. Normally should be called only when the unit is about to be destroyed, and is called automatically when the unit gets destroyed. However in case of cyclic references to the unit, may need to be called manually or through a UnitClearingTrigger object to break these cycles. For more information, see &xrsp;.

The Unit has a few helper classes: the tracers and the clearing triggers.

The tracer classes inherit from the nested virtual class Unit::Tracer. They will be described in &xrsp;. The Unit class contains the definition of the enums of the tracers and provides a way to set the tracer in the unit.

    enum TracerWhen {
    	// The values go starting from 0 in before-after pairs
    	TW_BEFORE, // before calling the label's execution as such
    	TW_AFTER, // after all the execution is done
    	TW_BEFORE_CHAINED, // after execution, before calling the chained labels (if they are present)
    	TW_AFTER_CHAINED, // after calling the chained labels (if they were present)
    	TW_BEFORE_DRAIN, // before draining the label's frame if it's not empty
    	TW_AFTER_DRAIN, // after draining the label's frame if was not empty
    };

This enum describes the point of the tracer invocation. The meaning of the values is described in detail in &xrsp;.

    static const char *tracerWhenString(int when, const char *def = "???");
    static int stringTracerWhen(const char *when);

The usual conversions between the integer constants and their string representations.

    def

is as usual the default placeholder that will be used for an invalid value. And the conversion from string would return a -1 on an invalid value.

    static const char *tracerWhenHumanString(int when, const char *def = "???");
    static int humanStringTracerWhen(const char *when);

The same kind of conversion as above, only the strings are more human-readable. Basically, the same thing, only in the lowercase words. For example, TW_BEFORE_CHAINED would become "before-chained". This conversion is used by the provided tracer classes in their traces.

    static bool tracerWhenIsBefore(int when);
    static bool tracerWhenIsAfter(int when);

Check a TracerWhen value for whether it represents a point before or after calling a label. Right now all the valid values fall into one or the other classification. But more trace points that are neither before or after could get added in the future, so a good practice is to check explicitly for both conditions rather than a simple if/else with one condition.

    void setTracer(Onceref tracer);
    Onceref getTracer() const;

Set and get back the tracer object for the Unit.

    NULL

means that no tracer is set. It's possible to use the same tracer object for multiple units, as long as these units are in the same threads; that will combine the traces from these units into one trace. If you really want, it's even possible to use the same tracer object for the units in different threads but for that you must define your own tracer class that does the proper synchronization. None of the pre-defined tracers have the synchronization, since using them from multiple threads is usually not a good idea.

The clearing trigger objects are constructed with:

    UnitClearingTrigger(Unit *unit);

The trigger is an Mtarget, so the typical use would be:

    {
      Autoref ctrig = new UnitClearingTrigger(myunit);
      ...
    }

At the block exit when the Autoref will get destroyed, it will destroy the trigger, which would in turn cause the clearing of the unit. Of course, you can also place the Autoref into another object, and then the destruction of that object would cause the clearing, instead of the end of the block.

Unit
Gadget
Rowop
Table
streaming function
FnReturn
FnBinding
label
UnitClearingTrigger
tracing