Nebula
Loading...
Searching...
No Matches
Threading Namespace Reference

Detailed Description

The Jobs2 system provides a set of threads and a pool of jobs from which threads can pickup work.

A frame pass prepares a rendering sequence, draws and subpasses must reside within one of these objects.

A spinlock is a lock which keeps the core busy while the lock is being held.

A mutex object which will assert instead of lock the thread.

(C) 2021 Individual contributors, see AUTHORS file

A lock-free queue implements a queue which completely relies on atomic operations.

All operations first performs a detachment of a node, followed by a modification of that node.

Removal:

1. Get the node you want to pop.
2. If non-null, get the next node from 1.
3. Do an interlocked comparison-swap, if we won the race, set head to the next node.
4. Otherwise, go back to 1.
5. oldHead is now a node which is detached and can not be reached by any other thread.

Node* oldHead;
Node* newHead;
while (true)
{
    oldHead = this->head;
    if (oldHead == nullptr)
        return false;

    newHead = oldHead->next;
    if (CompareAndSwap(&this->head, oldHead, newHead))
        break;
}

Insertion:

1. Get the value you expect to modify (head, tail, free head, free tail)
2. Do an interlocked comparison exchange, check if value from 1. matches current value of what you expect, exchange values.
3. If not, go back to 1.
4. oldTail is now detached and can not reached by any other thread.

Example:

Node* oldTail = nullptr;
while (true)
{
    oldTail = this->tail;

if we won the race (oldTail == this->tail), swap this->tail to newNode if (CompareAndSwap(&this->tail, oldTail, newNode) break; }

if (oldTail != nullptr) oldTail->next = newNode;

This queue implements two linked lists. One for the resident nodes and one for the free nodes. The free nodes and resident nodes follow identical patterns, howeven the resident nodes will attempt to allocate from the free nodes (and assert that it worked) or return a node to the free list.

TODO: Add support for ABA - make a new type of node which accompanies the node pointer with an integer allowing for knowing if a node has been modified even if it's state looks identical. For example, we might be Thread A running an Enqueue operation, and in the CompareAndSwap we assume we wont the race because this->tail is equivalent to oldTail, BUT! What can also happen is that between where we get oldTail and to the CompareAndSwap (CAS) operation, Thread B comes in and does Enqueue, Dequeue, leaving us in the same state but with changes having happened. If we instead accompany each node with an atomic counter, and increase it for every time we make a modification, we can safetly do the CAS and know for sure if the node has been tampered with or not.

Efficiently waits where we can guarantee the acquire/release happens within a short time.

Use with caution, as this is not a synchronization primitive, the OS won't be able to yield the CPU core to other threads with the same affinity, which may result in groups of cores with narrow affinity masks getting stuck and hung.

Namespaces

namespace  Interlocked
 

Classes

class  AssertingMutex
 
struct  AssertingScope
 
struct  CriticalScope
 
class  EventWithManualReset
 
class  LockFreeQueue
 
class  ObjectRef
 A thread-safe reference to a shared object. More...
 
class  SafeFlag
 A thread-safe flag variable. More...
 
class  SafePriorityQueue
 A thread-safe priority-sorted queue which protects itself with critical sections. More...
 
class  SafeQueue
 Thread-safe version of Util::Queue. More...
 
class  Spinlock
 
struct  SpinlockScope
 

Typedefs

typedef volatile int AtomicCounter
 
typedef volatile int64 AtomicCounter64
 
typedef pthread_t ThreadId
 
typedef int64_t ThreadIdStorage
 

Functions

 __ImplementClass (Threading::ObjectRef, 'OBJR', Core::RefCounted)
 

Variables

static const ThreadId InvalidThreadId = 0
 
static const ThreadId InvalidThreadId = 0
 
static const ThreadId InvalidThreadId = 0
 
Threading::ThreadId MainThreadId = Threading::InvalidThreadId
 
static const ThreadId InvalidThreadId = 0xffffffff
 

Typedef Documentation

◆ AtomicCounter

typedef volatile int Threading::AtomicCounter

◆ AtomicCounter64

◆ ThreadId

typedef DWORD Threading::ThreadId

◆ ThreadIdStorage

typedef int32_t Threading::ThreadIdStorage

Function Documentation

◆ __ImplementClass()

Threading::__ImplementClass ( Threading::ObjectRef ,
'OBJR' ,
Core::RefCounted  )

Variable Documentation

◆ InvalidThreadId [1/4]

const ThreadId Threading::InvalidThreadId = 0
static

◆ InvalidThreadId [2/4]

const ThreadId Threading::InvalidThreadId = 0
static

◆ InvalidThreadId [3/4]

const ThreadId Threading::InvalidThreadId = 0
static

◆ InvalidThreadId [4/4]

const ThreadId Threading::InvalidThreadId = 0xffffffff
static

◆ MainThreadId