123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- #pragma once
- #include "UnityPlatformConfigure.h"
- #include "ExtendedAtomicTypes.h"
- #include "AtomicNode.h"
- UNITY_PLATFORM_BEGIN_NAMESPACE;
- #if ATOMIC_HAS_QUEUE
- // clang has arm-specific bultins to implement LL/SC
- // NB the common pattern in implementation *there* seems to be chain of ifdef-s (not if-s!). So keep it same
- // if you compare arm+clang specific impl to generic cpp impl, you will notice that stricter memory ordering is used.
- // It was conscious decision. Performance-wise it is on-par (or faster) than generic cpp code anyway
- // some whining about DCAS define and its usage: AtomicQueue/AtomicStack do actually use DCAS
- // as in second "word" in atomic_word2 is for counter to avoid ABA problem
- // at the same time, in AtomicList atomic_word2 is used NOT for DCAS
- // as its second word is "tag" which has some meaning to the outside code (AtomicQueue),
- // so it is actually "please use two words atomically"
- // as opposed to "please use one word atomically and use second as implementation detail to fight ABA problem"
- // that means that for AtomicQueue/AtomicStack we can easily do specific impl with LL/SC
- // but for AtomicList we need to use atomic_word2
- #if (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) && UNITY_ATOMIC_USE_CLANG_ATOMICS
- #define UNITY_ATOMICQUEUE_USE_CLANG_ARM_ATOMICS 1
- #endif
- // A generic lockfree stack.
- // Any thread can Push / Pop nodes to the stack.
- // The stack is lockfree and highly optimized. It has different implementations for different architectures.
- // On intel / arm it is built with double CAS:
- // http://en.wikipedia.org/wiki/Double_compare-and-swap
- // On PPC it is built on LL/SC:
- // http://en.wikipedia.org/wiki/Load-link/store-conditional
- class AtomicStack
- {
- #if defined(ATOMIC_HAS_DCAS)
- volatile atomic_word2 _top;
- #else
- volatile atomic_word _top;
- #endif
- public:
- AtomicStack();
- ~AtomicStack();
- int IsEmpty() const;
- void Push(AtomicNode *node);
- void PushAll(AtomicNode *first, AtomicNode *last);
- AtomicNode *Pop();
- AtomicNode *PopAll();
- };
- AtomicStack* CreateAtomicStack();
- void DestroyAtomicStack(AtomicStack* s);
- // A generic lockfree queue FIFO queue.
- // Any thread can Enqueue / Dequeue in parallel.
- // We do guarantee that all 3 data pointer are the same after dequeuing.
- //
- // But when pushing / popping a node there is no guarantee that the pointer to the AtomicNode is the same.
- // Enqueue adds node to the head, and Dequeue pops it from the tail.
- // Implementation relies on dummy head node which allow to modify next pointer atomically.
- // Thus Dequeue pops not the enqueued node, but the next one.
- // Empty: [ head ] [next] [ tail ]
- // dummy 0 dummy
- // Enqueue: [ head ] [next] [next] [ tail ]
- // node1 dummy 0 dummy
- // Dequeue: [ head ] [next] [ tail ] -> dummy dequeued, but with node1 data[3]
- // node1 0 node1
- // Make sure to destroy nodes consistently.
- // The queue is lockfree and highly optimized. It has different implementations for different architectures.
- // On intel / arm it is built with double CAS:
- // http://en.wikipedia.org/wiki/Double_compare-and-swap
- // On PPC it is built on LL/SC:
- // http://en.wikipedia.org/wiki/Load-link/store-conditional
- class AtomicQueue
- {
- #if defined(ATOMIC_HAS_DCAS)
- volatile atomic_word2 _tail;
- #else
- volatile atomic_word _tail;
- #endif
- volatile atomic_word _head;
- public:
- AtomicQueue();
- ~AtomicQueue();
- int IsEmpty() const;
- void Enqueue(AtomicNode *node);
- void EnqueueAll(AtomicNode *first, AtomicNode *last);
- AtomicNode *Dequeue();
- };
- AtomicQueue* CreateAtomicQueue();
- void DestroyAtomicQueue(AtomicQueue* s);
- #elif IL2CPP_SUPPORT_THREADS
- #if IL2CPP_TARGET_JAVASCRIPT
- // Provide a dummy implementation for Emscripten that lets us build, but won't
- // work at runtime.
- class AtomicStack
- {
- public:
- AtomicStack() {}
- int IsEmpty() const
- {
- return 1;
- }
- void Push(AtomicNode *node)
- {
- }
- void PushAll(AtomicNode *first, AtomicNode *last)
- {
- }
- AtomicNode *Pop()
- {
- return NULL;
- }
- AtomicNode *PopAll()
- {
- return NULL;
- }
- };
- #else
- #error Platform is missing atomic queue implementation
- #endif // IL2CPP_TARGET_JAVASCRIPT
- #endif
- //
- // Special concurrent list for JobQueue
- // This code is not meant to be general purpose and should not be used outside of the job queue.
- class AtomicList
- {
- #if defined(ATOMIC_HAS_DCAS) || defined(UNITY_ATOMICQUEUE_USE_CLANG_ARM_ATOMICS)
- volatile atomic_word2 _top;
- #else
- volatile atomic_word _top;
- volatile atomic_word _ver;
- #endif
- public:
- void Init();
- atomic_word Tag();
- AtomicNode *Peek();
- AtomicNode *Load(atomic_word &tag);
- AtomicNode *Clear(AtomicNode *old, atomic_word tag);
- bool Add(AtomicNode *first, AtomicNode *last, atomic_word tag);
- AtomicNode* Touch(atomic_word tag);
- void Reset(AtomicNode *node, atomic_word tag);
- };
- UNITY_PLATFORM_END_NAMESPACE;
|