# TrieadOwner reference

TrieadOwner is the thread's private interface used to control its state and interact with the App (the App uses the thread's identity to detect the deadlocks in these interactions). Whenever a Triead is constructed, its OS/Perl thread receives the TrieadOwner object for it.

## TrieadOwner construction

Normally the TrieadOwner object is constructed inside

    Triead::start()

or

    Triead::startHere()

and passed to the thread's main function. The following constructor is used inside

    start()

, and it's pretty much a private method. The only reason to use it would be if you want to do something very unusual, and even then you probably should write a wrapper method for your unusual thing and then call that wrapper method. The constructor constructs both Triead and TrieadOwner as a two sides of the same item, and registers the thread with the App.

    $to = Triceps::TrieadOwner::new($tid, $handle, $appOrName, $tname, $fragname);

Here

    $tid

is the Perl thread id where this TrieadOwner belongs (it can be obtained with

    $thr->tid()

).

    $handle

is the Perl thread's low-level handle (as in

    $thr->handle_()

), it's the underlying POSIX thread handle, used to interrupt the thread on shutdown (the long story is that in the Perl threads the

    kill()

call doesn't actually send a signal to another thread but just sets a flag; to interrupt a sleeping system call a real signal has to be delivered through the POSIX API).

    $handle

is a dangerous argument, and passing a wrong value there may cause a crash.

Both

    $tid

and

    $handle

may be undef. If

    $tid

is undef, the thread won't be joined by the harvester and you can either detach it or join it yourself. If either

    $tid

or

    $handle

is undef, the thread won't be interrupted on shutdown.

The signal used for interruption is

    SIGUSR2

. Triceps sets its default handler that does nothing on this signal. This is done in the method

    Triceps::sigusr2_setup()

, and it gets called during the Triceps module loading. Internally it translates to the &Cpp; method

    Sigusr2::setup()

that sets the dummy handler on the first call. The reason why it has to be done in &Cpp; is that Perl 5.19 crashes on SIGUSR2 if the handler is set in Perl. If you're interested in the details, see .

    $appOrName

is the App object or its name that would be automatically looked up (or will confess if not found).

    $tname

is the name for the thread, that must be unique within the App (though it might be declared before).

    $fragname

is the name of the fragment where the thread belongs, use for no fragment.

## TrieadOwner general methods

    $app = $to->app();

Get the App where this Triead belongs.

    $unit = $to->unit();

Whenever a Triead is constructed, a Unit is automatically created to execute its logic. This call returns that unit. When the Triead is destroyed, the unit will be cleaned and unreferenced.

The unit is named the same as the thread.

    $to->addUnit($moreUnit);

It's possible to split the Triead's logic into multiple units, all running in the same Perl thread. This call puts an extra unit under Triead's control, and has two effects: First, the unit will be referenced for the life of the Triead, and cleaned and unreferenced when the Triead is destroyed. Second, when the Triead's main loop runs, after each incoming rowop it will check all the controlled units for any rowops scheduled in them, and will run them until all such rowops are processed.

The names of the units are not checked in any way, it's your responsibility to name them sensibly and probably differently from each other.

The repeated calls with the same unit will have no effect.

    $to->forgetUnit($moreUnit);

Pull a unit out of Triead's control. After that the cleaning of the unit becomes your responsibility. The thread's main unit cannot be forgotten, the attempts to forget it will be simply ignored. The same goes for the units that aren't under the Triead's control in the first place, these calls are ignored.

    @units = $to->listUnits();

Get the list of units under Triead's control. The main unit (the same as returned with

    $to->unit()

) will always be the first in the list. The list contains only the unit references, not the name-value pairs (and you can always get the names from the unit objects themselves).

    $triead = $to->get();

Get the public API of this Triead.

    $name = $to->getName();

Get this Triead's name.

    $frag = $to->fragment();

Get the name of this Triead's fragment ( if not in a fragment).

    $to->markConstructed();

Advance the Triead to the Constructed state. After that point no more nexuses may be exported in the Triead. Any look-ups by other Trieads for the Nexuses of this Triead will proceed at this point, either succeeding or failing (if the requested nexus is not exported).

In all the

    mark*()

methods, the state advance is cumulative: it brings the thread through all the intermediate states.

If the Triead is already in the Constructed or later state, this call has no effect.

    $to->markReady();

Advance the Triead to the Ready (fully initialized) state. After that point no more nexuses may be imported into this Triead.

In all the

    mark*()

methods, the state advance is cumulative: it brings the thread through all the intermediate states.

If the App has been already shut down, this Triead will be immediately requested to die.

If this is the last Triead in the App to become ready, this method will invoke the check for the topological correctness of the App. If the check finds an error (a loop of nexuses of the same direction), it will abort the App and confess with a message describing the nature of the error.

If the Triead is already in the Ready or later state, this call has no effect.

    $to->readyReady();

Mark this Triead as Ready and wait for all the App's Trieads to become Ready. There is no method that just waits for readiness because that would be likely causing a deadlock. When the thread waits for readiness, it must be ready itself, so this call does both. All the error checks of

    markReady()

apply.

It is possible and reasonable to call this method repeatedly: more Trieads may be added to the App later, and it's a good idea to call

    readyReady()

again before communicating with these new threads. Otherwise any rowops sent before these threads become ready will never arrive to these threads.

    $to->markDead();

Mark this Triead as Dead. A dead thread will not receive any more input, and any output from it will be thrown away. This notifies the harvester that it needs to join the Perl thread, so there should not be too much of a delay between making this call and exiting the Perl thread. The repeated calls have no effect.

Normally the

    Triead::start()

and

    startHere()

call

    markDead()

automatically in their wrapper logic, and there is no need for a manual call. However if you decide to bypass them, you must call

    markDead()

manually before exiting the thread, or the harvester will be stuck forever waiting for this thread to die.

In all the

    mark*()

methods, the state advance is cumulative: it brings the thread through all the intermediate states. But with

    markDead()

there is an even more interesting twist. Suppose there is an application with an incorrect topology, and all the Trieads in it but one are ready. That last Triead then experiences an error, and proceeds directly to call

    markDead()

and then exit. This

    markDead()

will involve an intermediate step marking the Triead as ready. Since it's the last Triead to be ready, it will trigger the topology check, and since the topology is incorrect, it will fail. If it happened in

    markReady()

, the method would confess. But in

    markDead()

confessing doesn't make a whole lot of sense: after all, the thread is about to exit anyway. So

    markDead()

will catch all these confessions and throw them away, it will never fail. However the failed check will still abort the App, and the rest of the threads will wake up and fail as usual.

    markDead()

also clears and unreferences all the TrieadOwner's registered units, not waiting for the TrieadOwner object to be destroyed. This unravels any potential cyclic references where the code in a label might be referring back to the TrieadOwner.

    $to->abort($msg);

Abort the App with a message. This is a convenience wrapper that translates to

    App::abortBy()

.

    $result = $to->isRqDead();

Check whether the thread was requested to die. For most threads,

    mainLoop()

does this check automatically, and

    nextXtray()

also returns the same value. However in the special cases, such as doing some long processing in response to a rowop, or doing some timeouts, it's best to do a manual check of

    isRqDead()

periodically and abort the long operation if the thread has been requested to die, since any output will be thrown away anyway.

When a thread is requested to die, it gets immediately disconnected from the nexuses. It will not get any more input (though it might still go through the buffers in its input facets), and any output from it will be discarded.

Note that even when the Triead has been requested to die, it still must call

    markDead()

when it actually dies (normally the

    Triead::start()

or

    startHere()

takes care of it in its wrapper).

    $result = $to->isConstructed();
    $result = $to->isReady();
    $result = $to->isDead();
    $result = $to->isInputOnly();

Check the state of the Triead, the same as Triead methods.

    $facet = $to->makeNexus(@options);

Create, export and (optionally) import a nexus. The result is an imported Facet of this Nexus, except when the options specify the no-import mode (then the result will be

    undef

). Confesses on errors.

The options are:

    name => $name

  Name of the nexus, it will be used both as the export name and the local imported as-name of the facet.

    labels => [ @definitions ]

  Defines the labels similarly to FnReturn in a referenced array. The array contains the pairs of

      (label_name, label_definition)

  , with definitions in one of two forms:

      labels => [
      	name1 => $rowType1,
      	name2 => $fromLabel2,
      	...
      ]

  The definition may be either a RowType, and then a label of this row type will be created, or a Label, and then a label of the same row type will be created and chained from that original label. The created label objects can be later found from Facets, and used like normal labels, by chaining them or sending rowops to them (chaining from them is probably not the best idea, although it works anyway).

  Optional, or may be an empty array; the implicit labels

      _BEGIN_

  and

      _END_

  will allways be added automatically if not explicitly defined.

  The labels are used to construct an implicit FnReturn in the current Triead's main unit, and this is the FnReturn that will be visible in the Facet that gets imported back. If the import mode is none, the FnReturn will still be constructed and then abandoned (and freed by the reference count going to 0, as usual). The labels used as

      $fromLabel

  above must always belong to the Triead's main unit.

    rowTypes => [ @definitions ]

  Defines the row types exported in this Nexus as a referenced array of name-value pairs:

      rowTypes => [
      	name => $rowType,
      	...
      ]

  The types imported back into this Triead's facet will be references to the exact same type objects. Optional, or may be empty.

    tableTypes => [ @definitions ]

  Defines the table types exported in this Nexus as a referenced array of name-value pairs:

      tableTypes => [
      	name => $tableType,
      	...
      ]

  The types imported back into this Triead's facet will be references to the exact same type objects. Optional, or may be empty.

    reverse => 0/1

  Flag: this Nexus goes in the reverse direction. The reverse nexuses are used to break up the topological loops, to prevent the deadlocks on the queueing. They have no limit on the queue size, and the data is read from them at a higher priority than from the direct nexuses. Default: 0.

    chainFront => 0/1

  Flag: when the labels are specified as

      $fromLabel

  , chain them at the front of the original labels. Default: 1.

  The default is this way because chaining at the front is what is typically needed. The reasons are described at length in &xrsp;, but the short gist is that you might want to send the rows from both the inputs, intermediate points, and the end of processing into an output nexus. It's most convenient to create the nexus in one go, after the whole thread's computation is defined. But the rowops from the earlier stages of computations have to come to the nexus before the rowops from the later stage. Chaining at the front ensures that each such label will send the rowop into the nexus first, and only then to the next stage of the computation.

    queueLimit => $number

  Defines the size limit after which the writes to the queue of this Nexus block. In reality because of the double-buffering the queue may contain up to twice that many trays before the future writes block. This option has no effect on the reverse nexuses. Default:

      &Facet::DEFAULT_QUEUE_LIMIT

  , 500 or so.

    import => $importType

  A string value, essentially an enum, determining how this Nexus gets immediately imported back into this Triead. The supported values are:

  -     reader

    (or anything starting from read) - import for reading

  -     writer

    (or anything starting from write) - import for writing

  -     none

    (or anything starting from no) - do not import

  Case-insensitive. The use of the canonical strings is recommended.

    $facet = $to->importNexus(@options);

Import a nexus into this Triead. Returns the imported Facet. The repeated attempts to import the same Nexus will return references to the same Facet object. Confesses on errors. An attempt to import the same nexus for both reading and writing is an error.

The options are:

    from => "$thread_name/$nexus_name"

  Identifier of the nexus to import, consisting of two parts separated by a slash.

  The nexus name will also be used as the name of the local facet, unless overridden by the option as. The reason for slash separator is that normally both the thread name and the nexus name parts may contain further components separated by dots, and a different separator allows to find the boundary between them. If a dot were used, in a.b.c it would be impossible to say, does it mean the thread a and nexus b.c in it, or thread a.b and nexus c? However a/b.c or a.b/c have no such ambiguity. Mutually exclusive with options fromTriead and fromNexus.

    fromTriead => $t

    fromNexus => $n

  The alternative way to specify the source thread and nexus as separate options. Both options must be present or absent at the same time. Mutually exclusive with from.

    as => $name

  Specifies an override name for the local facet (and thus also for the FnReturn created in the facet). Logically similar to the SQL clause AS. Default is to reuse the nexus name.

    import => $importType

  A string value, essentially an enum, determining how this Nexus gets imported. The supported values are the same as for

      makeNexus()

  , except none, since there is no point in a no-op import:

  -     reader

    (or anything starting from read) - import for reading;

  -     writer

    (or anything starting from write) - import for writing.

  Case-insensitive. The use of the canonical strings is recommended.

    immed => 0/1

  Flag: do not wait for the thread that exported the nexus to be fully constructed. Waiting synchronizes with the exporter and prevents a race of an import attempt trying to find a nexus before it is made and failing. However if two threads are waiting for each other, it becomes a deadlock that gets caught and aborts the App. The immediate import allows to avoid such deadlocks for the circular topologies with helper threads.

  The helper threads are the blind alleys in the topology: the main thread outsources some computation to a helper thread, sending it the arguments, then later receiving the results and continuing with its logic.

  With the helper threads, the parent thread will import the resul nexus of the helper as usual but the helper will import the argument nexus from the parent immediately. This can be done because the parent is required in this situation to export the argument nexus first and only then create the helper thread.

  The sequence in &xrsp; shows it in detail.

  Default: 0, except if importing a nexus that has been exported from the same Triead. Importing from the same Triead is not used often, since the export also imports the nexus back right away, and there is rarely any use in importing separately. But it's possible, and importing back from the same Triead is always treated as immediate to avoid deadlocks.

    @exports = $to->exports();

The same as the method on the Triead, equivalent to

    $to->get()->exports()

. The returned array contains the name-value pairs of the nexus names and objects.

    @imports = $to->imports();

This method is different from the Triead method. It still returns an array of name-value pairs but the values are Facets (not Nexuses, as in Triead). It's a natural difference, since the facets are useful in the owner thread, and available only in it.

    $result = $to->flushWriters();

Flush any data collected in the writer facets, sending them to the appropriate nexuses. The data in each facet becomes a tray that is sent to the nexus (if there was no data collected on a facet, nothing will be sent from it). Returns 1 if the flush was completed, 0 if the thread was requested to die and thus the data was discarded. The data is never sent out of a facet by itself, it always must be flushed in one of the explicit ways (

    TrieadOwner::flushWriters()

,

    Facet::flushWriter()

, or enqueueing a rowop on the facet's labels

    _BEGIN_

and

    _END_

). The flush may get stuck if this is an input-only thread and a drain is active, it will wait until the drain is released.

    $to->requestMyselfDead();

Request this Triead itself to die. This is the way to disconnect from the nexuses while the thread is exiting on its own. For example, if this thread is going to dump its data before exit to a large file that takes half an hour to write, normally the data queued for this thread might fill up the queues in the nexuses, and it's a bad practice to keep the other threads stuck due to the overflowing buffers. Requesting this thread to die disconnects it from the nexuses and prevents the data from collecting. The thread could also be disconnected by marking it dead, but that would keep the harvester stuck waiting to join it while the thread completes its long write, and that's not so good either. This call provides the solution, avoiding both pitfalls.

    $result = $to->nextXtray();

Process one incoming tray from a single reader nexus (any nexus where data is available, respecting the priorities). A tray essentially embodies a transaction, and X stands for cross-thread. There actually is the Xtray type that represents the Tray in a special thread-safe format but it's used only inside the nexuses and not visible from outside.

If there is currently no data to process, this method will wait.

The return value is 1 normally, or 0 if the thread was requested to die. So the typical usage is:

    while($to->nextXtray()) { ... }

The method

    mainLoop()

encapsulates the most typical usage, and

    nextXtray()

needs to be used directly only in the more unusual circumstances.

The data is read from the reverse nexuses first, at a higher priority. If any reverse nexus has data available, it will always be read before the direct nexuses. A reverse nexus typically completes a topological loop, so this priority creates the preference to cycle the data through the loop until it comes out, before accepting more data into the loop. Since all the nexuses have non-zero-length queues, obviously, there will be multiple data items traveling through the loop, in different phases, but this priority solution limits the amount of data kept in the loop's queues and allows the queue flow control to prevent too much data from entering the loop.

The raised priority of the reverse nexuses can also be used to deliver the urgent messages. Remember, there is nothing preventing you from marking any nexus as reverse (as long as it doesn't create a loop consisting of only the reverse nexuses).

The downside of having the reverse nexuses connected to a thread is that it causes an extra overhead from the check with a mutex synchronization on each

    nextXtray()

. The regular-priority direct nexuses use double-buffering, with locking a mutex only when the second buffer runs dry, and refilling it by swapping its contents with the whole collected first buffer. But the high-priority reverse nexuses have to be checked every time, even if they have no incoming data.

Within the same priority the data is processed in the round-robin order. More exactly, each refill of the double-buffering grabs the data from the first buffer of each facet and moves it to the second buffer. Then the second buffer is processed in the round-robin fashion until it runs out and another refill becomes needed.

The

    nextXtray()

processes all the rowops from the incoming tray by calling them on the facet's FnReturn. Two special rowops are generated automatically even if they haven't been queued up explicitly, on the facet's labels

    _BEGIN_

and

    _END_

(to avoid extra overhead, they are actually generated only if there is any processing chained for them).

The

    nextXtray()

automatically flushes the writers after processing a tray.

If a fatal error is encountered during processing (such as some code in a label dies),

    nextXtray()

will catch the exception, discard the rest of the tray and confess itself, without flushing the writers.

    $result = $to->nextXtrayNoWait();

Similar to

    nextXtray()

, but returns immediately if there is no data to process. Returns 0 if there is either no input data or the thread was requested to die (the way to differentiate between these cases is to call

    $to->isRqDead()

).

    $result = $to->nextXtrayTimeout($timeout);

Similar to

    nextXtrayNoWait()

, only if there is no data, waits for up to the length of timeout. The timeout value is floating-point seconds. Returns 0 if the timeout has expired or the thread was requested to die.

    $result = $to->nextXtrayTimeLimit($deadline);

Similar to

    nextXtrayNoWait()

, only if there is no data, waits until the absolute deadline. The deadline value is time since epoch in floating-point seconds, such as returned by

    Triceps::now()

. Returns 0 if the wait reached the deadline or the thread was requested to die.

    $to->mainLoop();

Process the incoming trays until the thread is requested to die. The exact implementation of the main loop (in &Cpp;) is:

    void TrieadOwner::mainLoop()
    {
    	while (nextXtray())
    		{ }
    }

Nexus
Xtray
now

## TrieadOwner drains

The drain API of the TrieadOwner is very similar to the one in the App. The best way to do the drain is by the automatically-scoped AutoDrain class. If a drain doesn't need an automatic scoping, use the TrieadOwner API. And finally if you want to mess with drains from outside an App's Triead and thus don't have a TrieadOwner, only then use the App API.

    $to->requestDrainShared();
    $to->requestDrainExclusive();
    $to->waitDrain();
    $to->drainShared();
    $to->drainExclusive();
    $to->undrain();
    $result = $to->isDrained();

The methods are used in exactly the same way as the similar App methods, with only the difference of the names on the shared drains.

The exclusive drains always make the exclusion for this Triead. (Only one thread can be excluded from a drain). Normally the exclusive drains should be used only for the input-only threads. They could potentially be used to exclude a non-input-only thread too but I'm not sure, what's the point, and haven't worked out if it would work reliably (it might, or it might not).

    $result = $to->isRqDrain();

Check whether a drain request is active. This can be used in the threads that generate data based on the real-time clock yet aren't input-only: if they find that a drain is active, they should refrain from generating the data and go back to the waiting. There is no way for them to find when the drain is released, so they should just continue to the next timeout as usual. Such code must use

    nextXtrayTimeout()

or

    nextXtrayTimeLimit()

for the timeouts, or the drain would never complete. These two methods know how to handle the timeouts and also how to properly interact with the drains without causing the race conditions. The input-only threads don't have this limitation. And of course keep in mind that the better practice is to deal with the real time either in the input-only threads or by driving it from outside the model altogether.

## TrieadOwner file interrruption

The file interruption part of the API deals with how the thread handles a request to die. It should stop its work and exit, and for the normal threads that read from nexuses, this is fairly straightforward. The difficulty is with the threads that read from the outside sources (sockets and such). They may be in the middle of a read call, with no way to tell when the next chunk of data will arrive. These long calls on the file descriptors need to be interrupted when the thread is requested to die.

The interruption is done by revoking the file descriptors (dupping a

    /dev/null

into it) and sending the signal

    SIGUSR2

to the thread. Even if the dupping doesn't interrupt the file operation,

    SIGUSR2

does, and on restart it will find that the file descriptor now refers to

    /dev/null

and return immediately. Triceps defines a

    SIGUSR2

handler that does nothing, but you can override it with a custom one.

For this to work, Triceps needs to know, which file descriptors are to be revoked, which is achieved by registering them for tracking in the TrieadOwner. To avoid accidentally revoking the unrelated descriptors, the descriptors must be unregistered before closing. The normal sequence is:

- open a file descriptor;
- register it for tracking;
- do the file operations;
- unregister the file descriptor;
- close it.

The lowest-level calls deal with the raw tracking:

    $to->trackFd($fd);

Register the file descriptor (obtained with

    fileno()

or such) for tracking. The repeated calls for the same descriptor have no effect.

    $to->forgetFd($fd);

Unregister the file descriptor. If the descriptor is not registered, the call is ignored.

The next level of the API deals with the file handles, extracting the file descriptors from them as needed.

    $to->track(FILE);

Get a file descriptor from the file handle and track it.

    $to->forget(FILE);

Get a file descriptor from the file handle and unregister it.

    $to->close(FILE);

Unegister the file handle's descriptor then close the file handle. It's a convenience wrapper, to make the unregistering easier to remember.

The correct sequence might be hard to follow if the code involves some dying and evals catching these deaths. To handle that, the next level of the API provides the automatic tracking by scope. The scoping is done with the class Triceps::TrackedFile. Which is probably easier to describe right here.

## TrackedFile

A TrackedFile object keeps a reference of a file handle and also knows of its file descriptor being tracked by the TrieadOwner (so yes, it has a reference to the TrieadOwner too). Until the TrackedFile is destroyed, the file handle in it will never have its reference count go to 0, so it will never be automatically closed and destroyed. And when the TrackedFile is destroyed, first it tells the TrieadOwner to forget the file descriptor and only then unreferences the file handle, preserving the correct sequence.

And the scope of the TrackedFile is controlled by the scope of a variable that keeps a reference to it. If it's a local/my variable, TrackedFile will be destroyed on leaving the block, if it's a field in an object, it will be destroyed when the object is destroyed or the field is reset. Basically, the usual Perl scope business in Triceps.

If you want to close the file handle before leaving the scope of TrackedFile, don't call

    close()

on the file handle. Instead, call

    close()

on the TrackedFile:

    $trf->close();

This will again properly untrack the file descriptor, and then close the file handle, and remember that it has been closed, so no seconds attempt at that will be done when the TrackedFile gets destroyed.

There also are a couple of getter methods on the TrackedFile:

    $fd = $trf->fd();

Get the tracked file descriptor. If the file handle has been already closed, will return -1.

    $fh = $trf->get();

Get the tracked file handle. If the file handle had already been closed, will confess.

Now we get back to the TrieadOwner.

    $trf = $to->makeTrackedFile(FILE);

Create a TrackedFile object from a file handle.

    $trf = $to->makeTrackedFileFd(FILE, $fd);

The more low-level way to construct a TrackedFile, with specifying the file descriptor as a separate explicit argument.

    makeTrackedFile()

is pretty much a wrapper that calls

    fileno()

on the file handle and then calls

    makeTrackedFileFd()

.

And the following methods combine the loading of a file descriptor from the App and tracking it by the TrieadOwner. They are the most typically used interface for passing around the file handles through the App.

    ($trf, $file) = $to->trackDupFile($name, $mode);

Load the dupped file handle from the App, create a file handle object from it according to the IO::Handle subclass received from the App and

    $mode

(in either

    r/w/a/r+/w+/a+

or

    />>/+/+>>

format), and make a TrackedFile from it. Returns a pair of the TrackedFile object and the created file handle. The App still keeps the original file descriptor. The

    $mode

must be consistent with the original mode of the file stored into the App.

    $name

is the name that was used to store the file descriptor into the App.

    ($trf, $file) = $to->trackGetFile($name, $mode);

Similar to

    trackDupFile()

only the file descriptor is moved from the App, and the App forgets about it.

    ($trf, $file) = $to->trackDupClass($name, $mode, $class);
    ($trf, $file) = $to->trackGetClass($name, $mode, $class);

Similar to the File versions, only creates the file handle of an explicitly specified subclass of IO::Handle (per

    $class

).
TrackedFile
TrieadOwner
Triead