Thread

  • LXi_SIMD_MAXRUN

ILxWorkList

class ILxWorkList

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

LxResult IsEmpty(LXtObjectID self)

A worklist is a opaque container for units of work. Typically elements are processed one at a time, and can be split into new spawned lists.

void *Next(LXtObjectID self)
LxResult Split(LXtObjectID self, unsigned mode, void **ppvObj)
void Clear(LXtObjectID self)
bool Split(CLxLoc_WorkList &wlist, unsigned mode = LXiWLSPLIT_NONE)

User Class Only:

  • LXiWLSPLIT_NONE

  • LXiWLSPLIT_ONE

  • LXiWLSPLIT_HALF

ILxThreadMutex

class ILxThreadMutex

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

void Enter(LXtObjectID self)

Mutexes are used for synchronization of threads. Only a single thread may be “in” a mutex at a time. And other thread calling Enter will block until the owning thread has released the mutex with a Leave call. NOTE: Calling Enter from a thread that already has the mutex will result in deadlock. Critical sections are closely related to mutexes with the one (significant) difference that a thread can re-enter a critical section it owns. These are usually not necessary and have a greater performance cost than mutexes. Critical sections share the THREADMUTEX interface.

void Leave(LXtObjectID self)

ILxThreadService

class ILxThreadService

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

LxResult CreateMutex(LXtObjectID self, void **ppvObj)

The thread service allows you to create mutexes.

LxResult CreateCS(LXtObjectID self, void **ppvObj)

… and critical sections.

LxResult CreateGroup(LXtObjectID self, void **ppvObj)

The thread service interface allows new groups to be created.

unsigned int NumProcs(LXtObjectID self)

There are also a couple of simple methods to determine the state of the threading systems.

unsigned int IsMainThread(LXtObjectID self)
LxResult CreateSlot(LXtObjectID self, size_t size, LXtObjectID client, void **ppvObj)

Finally this method allows allocating a new thread slot.

LxResult ProcessShared(LXtObjectID self, LXtObjectID shared)

You start with a single one of these objects that contains all the work. You then call the ProcesShared() method in the service. This will spawn enough of the shared work objects to populate the available computing resources. Each one will then process all the work it has, getting more from the main shared work object when they are empty. When all the work is done the sub-objects are destroyed.

LxResult ProcessRange(LXtObjectID self, void *data, int startIndex, int endIndex, LXtObjectID rangeWorker)

Multi-processing over a range of indices (such as scanlines in an image).

LxResult InitThread(LXtObjectID self)

If a plugin creates its own threads, using some external library (like pthreads or OpenMP), it needs to initialize itself before it can call Nexus functions. This function allows that. If the thread has already initialized itself, this function will do nothing.

LxResult CleanupThread(LXtObjectID self)

Once the thread has finished executing Nexus code, it needs to free the Nexus specific thread-data, so it must call CleanupThread, or there will be a memory leak.

LxResult ProcessWaterfall(LXtObjectID self, LXtObjectID waterfall, unsigned threads)

This method takes a waterfall object and processes all the work it contains. New instances will be spawned to fill out the given number of threads, or one for each processor is the thread count is zero.

int AtomicIncrement(LXtObjectID self, volatile int *addr)

This section defines functions that are assurably atomic, meaning that there are no possible race conditions if multiple threads call them on the same data at once. All values needs to be aligned to their size (i.e. 32-bit ints needs to be 32-bit aligned, and 64-bit ints need to be 64-bit aligned). If they’re not aligned, the functions do nothing. All functions return the result of the increment or decrement. These functions atomically increment and decrement an address-aligned integer.

int AtomicDecrement(LXtObjectID self, volatile int *addr)
int AtomicIntegerAdd(LXtObjectID self, volatile int *addr, int val)

These functions are the same but they allow you to add and subtract any number. They return the result of the addition or subtraction.

int AtomicIntegerSubtract(LXtObjectID self, volatile int *addr, int val)
bool NewMutex(CLxLoc_ThreadMutex &mux)

User Class Only:

bool NewCritSec(CLxLoc_ThreadMutex &cs)

User Class Only:

bool NewGroup(CLxLoc_ThreadGroup &tg)

User Class Only:

bool NewSlot(CLxLoc_ThreadSlot &ts, size_t size)

User Class Only:

bool NewSlot(CLxLoc_ThreadSlot &ts, ILxUnknownID client)

ILxThreadJob

class ILxThreadJob

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

void Execute(LXtObjectID self)

Clients specify the work they wish to perform by creating a Job. The job is a very simple object that has a single method - Execute. Jobs can be added to other ThreadService objects which can in turn control their threaded execution.

ILxThreadGroup

class ILxThreadGroup

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

void AddJob(LXtObjectID self, LXtObjectID job)

A more useful higher level construct than the Job is the thread group. Thread Groups allow the client launch several threads at once. Each thread in the group gets its own ThreadFunc and data. Usually, all the threads in the group will have the same ThreadFunc but different pieces of data. They may run either synchronously or asynchronously.

unsigned NumJobs(LXtObjectID self)
void Clear(LXtObjectID self)
void Execute(LXtObjectID self)
void Wait(LXtObjectID self)
LxResult Running(LXtObjectID self)
void Kill(LXtObjectID self)

ILxThreadSlot

class ILxThreadSlot

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

LxResult Set(LXtObjectID self, void *value)

Slots are a way to store data locally for each thread.

LxResult Get(LXtObjectID self, void **value)
LxResult Clear(LXtObjectID self)

ILxThreadSlotClient

class ILxThreadSlotClient

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

LxResult Alloc(LXtObjectID self, void **value)

Defining a slot can use a client class for allocating and freeing the data.

LxResult Free(LXtObjectID self, void *value)

ILxSharedWork

class ILxSharedWork

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

LxResult Evaluate(LXtObjectID self)

Shared work represents a batch of work that can be done in parallel.

  • Evaluate Process one unit of work and return a result code. An error will terminate the whole process. If there is no more work left to do return LXe_FALSE.

  • Spawn Create a new shared work object of the same type. The new object should have no work.

  • Share Move work from this object to the other. This is typically only one unit of work, but if the work units are especially small, more than one at a time can be moved to reduce contention. When there is no work left return LXe_FALSE.

LxResult Spawn(LXtObjectID self, void **ppvObj)
LxResult Share(LXtObjectID self, LXtObjectID other, unsigned int split)

ILxThreadRangeWorker

class ILxThreadRangeWorker

ILxThreadService service provides a simple interface into basic threading constructs and, more importantly, a way to synchronize with the host process and other threads.

Public Functions

LxResult Execute(LXtObjectID self, int index, void *sharedData)

A range worker performs processing over a range of indices (such as scanlines in an image).

  • LXiWFALL_DONE

  • LXiWFALL_HASWORK

  • LXiWFALL_NEXT_WORK

  • LXiWFALL_NEXT_STAGE

ILxWaterfall

class ILxWaterfall

A waterfall is a model of threaded computation that combines parallel and sequential elements. The metaphor is of a waterfall consisting of a set of pools at different levels. Water fills the first set of pools in parallel, but the pools at the next level don’t start to fill until the first level is completely full. Likewise the waterfall object presents a parade of work units in stages. The work at each stage has to be complete before the next stage can begin. The object manages not only the work list but the processing, which it does by allowing multiple copies of itself to work on the same list.

Public Functions

LxResult Spawn(LXtObjectID self, void **ppvObj)

Spawn() creates a new waterfall object taking the first work in the current stage. If there’s no more work in this stage this should fail.

unsigned State(LXtObjectID self)

Query the state of the waterfall object. This returns one of the LXiWFALL state values.

  • DONE indicates that the last work in the last stage has been completed.

  • HASWORK means that this waterfall object has work ready to be processed.

  • NEXT_WORK, NEXT_STAGE if the waterfall object has no work it can indicate that there is more work in this stage, or that the current stage is complete but there is more work in the next stage.

LxResult ProcessWork(LXtObjectID self)

If this object has work loaded it can be discharged with this method. This is the only method that will be called concurrently; all other methods will be called in one thread at a time and so don’t need to protect themselves.

LxResult GetWork(LXtObjectID self)

If there is work available in the current stage this is used to take one unit of work and load it into this object.

LxResult Advance(LXtObjectID self)

If there is no work in the current stage this is called to advance the waterfall to the next stage. All the work in the previous stage will have been completely processed.

CLxMutexLock

class CLxMutexLock : public CLxUser_ThreadMutex

We also provide a couple of simple classes for self-allocating locks and automatically scoped enter and leave.

Public Functions

CLxMutexLock()

CLxCritSecLock

class CLxCritSecLock : public CLxUser_ThreadMutex

Public Functions

CLxCritSecLock()

CLxArmLockedMutex

class CLxArmLockedMutex : public CLxArm

Public Functions

CLxArmLockedMutex(CLxUser_ThreadMutex &mux)
~CLxArmLockedMutex()

Public Members

CLxUser_ThreadMutex &lock