├── generate ├── .gitmodules ├── generate.bat ├── Marefile ├── README.md ├── MutexLockQueue.h ├── SpinLockQueue.h ├── LockFreeQueue.h ├── LockFreeQueueCpp11.h ├── LockFreeQueueSlow3.h ├── LockFreeQueueSlow2.h ├── LockFreeLifoQueue.h ├── LockFreeQueueSlow1.h ├── mpmc_bounded_queue.h ├── Test.cpp └── LICENSE /generate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [ ! -f "Build/Debug/.mare/mare" ] && Ext/mare/compile --buildDir=Build/Debug/.mare --outputDir=Build/Debug/.mare --sourceDir=Ext/mare/src 4 | Build/Debug/.mare/mare $@ 5 | 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Ext/libnstd"] 2 | path = Ext/libnstd 3 | url = https://github.com/craflin/libnstd.git 4 | [submodule "Ext/mare"] 5 | path = Ext/mare 6 | url = https://github.com/craflin/mare.git 7 | -------------------------------------------------------------------------------- /generate.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if not exist Build\Debug\.mare\mare.exe call Ext\mare\compile.bat --buildDir=Build/Debug/.mare --outputDir=Build/Debug/.mare --sourceDir=Ext/mare/src 4 | if not "%1"=="" (Build\Debug\.mare\mare.exe %*) else Build\Debug\.mare\mare.exe --vcxproj=2013 5 | -------------------------------------------------------------------------------- /Marefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | if(tool == "vcxproj") { 4 | platforms = { "Win32", "x64" } 5 | } 6 | 7 | buildDir = "Build/$(configuration)/.$(target)" 8 | 9 | targets = { 10 | 11 | LockFreeQueue = cppApplication + { 12 | dependencies = { "libnstd" } 13 | includePaths = { "Ext/libnstd/include" } 14 | libPaths = { "Build/$(configuration)/.libnstd" } 15 | libs = { "nstd" } 16 | root = "Src" 17 | files = { 18 | "*.cpp" = cppSource 19 | "*.h" 20 | } 21 | if tool == "vcxproj" { 22 | linkFlags += { "/SUBSYSTEM:CONSOLE" } 23 | } 24 | if platform == "Linux" { 25 | libs += { "pthread", "rt" } 26 | cppFlags += { "-std=c++11" } 27 | } 28 | defines -= "NDEBUG" 29 | } 30 | 31 | include "Ext/libnstd/libnstd.mare" 32 | libnstd += { 33 | folder = "Ext" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LockFreeQueue 2 | ============= 3 | 4 | Here, I am testing some multi-producer multi-consumer bounded ring buffer FIFO queue implementations for fun. 5 | 6 | * [LockFreeQueueCpp11.h](LockFreeQueueCpp11.h) - The fastest lock free queue I have managed to implement. 7 | * [LockFreeQueue.h](LockFreeQueue.h) - The fastest lock free queue I have managed to implement without c++11. It is equally fast as LockFreeQueueCpp11.h. 8 | * [mpmc_bounded_queue.h](mpmc_bounded_queue.h) - Bounded MPMC queue by [Dmitry Vyukov, 2011] 9 | * [LockFreeQueueSlow1.h](LockFreeQueueSlow1.h) - My first attempt at implementing a lock free queue. It is working correctly, but it is a lot slower than LockFreeQueue.h. 10 | * [LockFreeQueueSlow2.h](LockFreeQueueSlow2.h) - A lock free queue based on [John D. Valois, 1994]. The queue uses Valois' algorithm adapted to a ring buffer structure with some modifications to tackle the ABA-Problem. 11 | * [LockFreeQueueSlow3.h](LockFreeQueueSlow3.h) - Another lock free queue almost as fast as LockFreeQueue.h. 12 | * [MutexLockQueue.h](MutexLockQueue.h) - A naive queue implementation that uses a conventional mutex lock (CriticalSecion / pthread-Mutex). 13 | * [SpinLockQueue.h](SpinLockQueue.h) - A naive queue implementation that uses an atomic TestAndSet-lock. 14 | 15 | And for the fun of it, here is a multi-producer multi-consumer LIFO queue: 16 | 17 | * [LockFreeLifoQueue.h](LockFreeLifoQueue.h) - A lock free multi-producer multi-consumer bounded LIFO queue. 18 | 19 | #### References 20 | 21 | [John D. Valois, 1994] - Implementing Lock-Free Queues
22 | [Dmitry Vyukov, 2011] - [Bounded MPMC queue](http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue) -------------------------------------------------------------------------------- /MutexLockQueue.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class MutexLockQueue 8 | { 9 | public: 10 | explicit MutexLockQueue(usize capacity) 11 | { 12 | _capacityMask = capacity - 1; 13 | for(usize i = 1; i <= sizeof(void*) * 4; i <<= 1) 14 | _capacityMask |= _capacityMask >> i; 15 | _capacity = _capacityMask + 1; 16 | 17 | _queue = (Node*)Memory::alloc(sizeof(Node) * _capacity); 18 | 19 | _head = 0; 20 | _tail = 0; 21 | } 22 | 23 | ~MutexLockQueue() 24 | { 25 | for(usize i = _head; i != _tail; ++i) 26 | (&_queue[i & _capacityMask].data)->~T(); 27 | Memory::free(_queue); 28 | } 29 | 30 | usize capacity() const {return _capacity;} 31 | 32 | usize size() const 33 | { 34 | usize result; 35 | _mutex.lock(); 36 | result = _tail - _head; 37 | _mutex.unlock(); 38 | return result; 39 | } 40 | 41 | bool push(const T& data) 42 | { 43 | _mutex.lock(); 44 | if(_tail - _head == _capacity) 45 | { 46 | _mutex.unlock(); 47 | return false; // queue is full 48 | } 49 | Node& node = _queue[(_tail++) & _capacityMask]; 50 | new (&node.data)T(data); 51 | _mutex.unlock(); 52 | return true; 53 | } 54 | 55 | bool pop(T& result) 56 | { 57 | _mutex.lock(); 58 | if(_head == _tail) 59 | { 60 | _mutex.unlock(); 61 | return false; // queue is empty 62 | } 63 | Node& node = _queue[(_head++) & _capacityMask]; 64 | result = node.data; 65 | (&node.data)->~T(); 66 | _mutex.unlock(); 67 | return true; 68 | } 69 | 70 | private: 71 | struct Node 72 | { 73 | T data; 74 | }; 75 | 76 | private: 77 | usize _capacityMask; 78 | Node* _queue; 79 | usize _capacity; 80 | usize _head; 81 | usize _tail; 82 | mutable Mutex _mutex; 83 | }; 84 | -------------------------------------------------------------------------------- /SpinLockQueue.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class SpinLockQueue 8 | { 9 | public: 10 | explicit SpinLockQueue(usize capacity) 11 | { 12 | _capacityMask = capacity - 1; 13 | for(usize i = 1; i <= sizeof(void*) * 4; i <<= 1) 14 | _capacityMask |= _capacityMask >> i; 15 | _capacity = _capacityMask + 1; 16 | 17 | _queue = (Node*)Memory::alloc(sizeof(Node) * _capacity); 18 | 19 | _lock = 0; 20 | _head = 0; 21 | _tail = 0; 22 | } 23 | 24 | ~SpinLockQueue() 25 | { 26 | for(usize i = _head; i != _tail; ++i) 27 | (&_queue[i & _capacityMask].data)->~T(); 28 | Memory::free(_queue); 29 | } 30 | 31 | usize capacity() const {return _capacity;} 32 | 33 | usize size() const 34 | { 35 | usize result; 36 | while(Atomic::testAndSet(_lock) != 0); 37 | result = _tail - _head; 38 | Atomic::store(_lock, 0); 39 | return result; 40 | } 41 | 42 | bool push(const T& data) 43 | { 44 | while(Atomic::testAndSet(_lock) != 0); 45 | if(_tail - _head == _capacity) 46 | { 47 | _lock = 0; 48 | return false; // queue is full 49 | } 50 | Node& node = _queue[(_tail++) & _capacityMask]; 51 | new (&node.data)T(data); 52 | Atomic::store(_lock, 0); 53 | return true; 54 | } 55 | 56 | bool pop(T& result) 57 | { 58 | while(Atomic::testAndSet(_lock) != 0); 59 | if(_head == _tail) 60 | { 61 | _lock = 0; 62 | return false; // queue is empty 63 | } 64 | Node& node = _queue[(_head++) & _capacityMask]; 65 | result = node.data; 66 | (&node.data)->~T(); 67 | Atomic::store(_lock, 0); 68 | return true; 69 | } 70 | 71 | private: 72 | struct Node 73 | { 74 | T data; 75 | }; 76 | 77 | private: 78 | usize _capacityMask; 79 | Node* _queue; 80 | usize _capacity; 81 | usize _head; 82 | usize _tail; 83 | mutable volatile int32 _lock; 84 | }; 85 | -------------------------------------------------------------------------------- /LockFreeQueue.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class LockFreeQueue 8 | { 9 | public: 10 | explicit LockFreeQueue(usize capacity) 11 | { 12 | _capacityMask = capacity - 1; 13 | for(usize i = 1; i <= sizeof(void*) * 4; i <<= 1) 14 | _capacityMask |= _capacityMask >> i; 15 | _capacity = _capacityMask + 1; 16 | 17 | _queue = (Node*)Memory::alloc(sizeof(Node) * _capacity); 18 | for(usize i = 0; i < _capacity; ++i) 19 | { 20 | _queue[i].tail = i; 21 | _queue[i].head = -1; 22 | } 23 | 24 | _tail = 0; 25 | _head = 0; 26 | } 27 | 28 | ~LockFreeQueue() 29 | { 30 | for(usize i = _head; i != _tail; ++i) 31 | (&_queue[i & _capacityMask].data)->~T(); 32 | 33 | Memory::free(_queue); 34 | } 35 | 36 | usize capacity() const {return _capacity;} 37 | 38 | usize size() const 39 | { 40 | usize head = Atomic::load(_head); 41 | return _tail - head; 42 | } 43 | 44 | bool push(const T& data) 45 | { 46 | Node* node; 47 | usize next, tail = _tail; 48 | for(;; tail = next) 49 | { 50 | node = &_queue[tail & _capacityMask]; 51 | if(Atomic::load(node->tail) != tail) 52 | return false; 53 | if((next = Atomic::compareAndSwap(_tail, tail, tail + 1)) == tail) 54 | break; 55 | } 56 | new (&node->data)T(data); 57 | Atomic::store(node->head, tail); 58 | return true; 59 | } 60 | 61 | bool pop(T& result) 62 | { 63 | Node* node; 64 | usize next, head = _head; 65 | for(;; head = next) 66 | { 67 | node = &_queue[head & _capacityMask]; 68 | if(Atomic::load(node->head) != head) 69 | return false; 70 | if((next = Atomic::compareAndSwap(_head, head, head + 1)) == head) 71 | break; 72 | } 73 | result = node->data; 74 | (&node->data)->~T(); 75 | Atomic::store(node->tail, head + _capacity); 76 | return true; 77 | } 78 | 79 | private: 80 | struct Node 81 | { 82 | T data; 83 | usize tail; 84 | usize head; 85 | }; 86 | 87 | private: 88 | usize _capacityMask; 89 | Node* _queue; 90 | usize _capacity; 91 | char cacheLinePad1[64]; 92 | usize _tail; 93 | char cacheLinePad2[64]; 94 | usize _head; 95 | char cacheLinePad3[64]; 96 | }; 97 | -------------------------------------------------------------------------------- /LockFreeQueueCpp11.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class LockFreeQueueCpp11 8 | { 9 | public: 10 | explicit LockFreeQueueCpp11(size_t capacity) 11 | { 12 | _capacityMask = capacity - 1; 13 | for(size_t i = 1; i <= sizeof(void*) * 4; i <<= 1) 14 | _capacityMask |= _capacityMask >> i; 15 | _capacity = _capacityMask + 1; 16 | 17 | _queue = (Node*)new char[sizeof(Node) * _capacity]; 18 | for(size_t i = 0; i < _capacity; ++i) 19 | { 20 | _queue[i].tail.store(i, std::memory_order_relaxed); 21 | _queue[i].head.store(-1, std::memory_order_relaxed); 22 | } 23 | 24 | _tail.store(0, std::memory_order_relaxed); 25 | _head.store(0, std::memory_order_relaxed); 26 | } 27 | 28 | ~LockFreeQueueCpp11() 29 | { 30 | for(size_t i = _head; i != _tail; ++i) 31 | (&_queue[i & _capacityMask].data)->~T(); 32 | 33 | delete [] (char*)_queue; 34 | } 35 | 36 | size_t capacity() const {return _capacity;} 37 | 38 | size_t size() const 39 | { 40 | size_t head = _head.load(std::memory_order_acquire); 41 | return _tail.load(std::memory_order_relaxed) - head; 42 | } 43 | 44 | bool push(const T& data) 45 | { 46 | Node* node; 47 | size_t tail = _tail.load(std::memory_order_relaxed); 48 | for(;;) 49 | { 50 | node = &_queue[tail & _capacityMask]; 51 | if(node->tail.load(std::memory_order_relaxed) != tail) 52 | return false; 53 | if((_tail.compare_exchange_weak(tail, tail + 1, std::memory_order_relaxed))) 54 | break; 55 | } 56 | new (&node->data)T(data); 57 | node->head.store(tail, std::memory_order_release); 58 | return true; 59 | } 60 | 61 | bool pop(T& result) 62 | { 63 | Node* node; 64 | size_t head = _head.load(std::memory_order_relaxed); 65 | for(;;) 66 | { 67 | node = &_queue[head & _capacityMask]; 68 | if(node->head.load(std::memory_order_relaxed) != head) 69 | return false; 70 | if(_head.compare_exchange_weak(head, head + 1, std::memory_order_relaxed)) 71 | break; 72 | } 73 | result = node->data; 74 | (&node->data)->~T(); 75 | node->tail.store(head + _capacity, std::memory_order_release); 76 | return true; 77 | } 78 | 79 | private: 80 | struct Node 81 | { 82 | T data; 83 | std::atomic tail; 84 | std::atomic head; 85 | }; 86 | 87 | private: 88 | size_t _capacityMask; 89 | Node* _queue; 90 | size_t _capacity; 91 | char cacheLinePad1[64]; 92 | std::atomic _tail; 93 | char cacheLinePad2[64]; 94 | std::atomic _head; 95 | char cacheLinePad3[64]; 96 | }; 97 | -------------------------------------------------------------------------------- /LockFreeQueueSlow3.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class LockFreeQueueSlow3 8 | { 9 | public: 10 | explicit LockFreeQueueSlow3(usize capacity) 11 | { 12 | _capacityMask = capacity - 1; 13 | for(usize i = 1; i <= sizeof(void*) * 4; i <<= 1) 14 | _capacityMask |= _capacityMask >> i; 15 | _capacity = _capacityMask + 1; 16 | 17 | _queue = (Node*)Memory::alloc(sizeof(Node) * _capacity); 18 | for(usize i = 0; i < _capacity; ++i) 19 | _queue[i].state = 0; 20 | 21 | _tail = 0; 22 | _head = 0; 23 | } 24 | 25 | ~LockFreeQueueSlow3() 26 | { 27 | for(usize i = _head; i != _tail; ++i) 28 | (&_queue[i & _capacityMask].data)->~T(); 29 | 30 | Memory::free(_queue); 31 | } 32 | 33 | usize capacity() const {return _capacity;} 34 | 35 | usize size() const 36 | { 37 | usize head = Atomic::load(_head); 38 | return _tail - head; 39 | } 40 | 41 | bool push(const T& data) 42 | { 43 | for(;;) 44 | { 45 | usize tail = _tail; 46 | Node* node = &_queue[tail & _capacityMask]; 47 | switch(Atomic::compareAndSwap(node->state, 0, 2)) 48 | { 49 | case 2: 50 | continue; 51 | case 0: 52 | break; 53 | default: 54 | return false; 55 | } 56 | if(Atomic::compareAndSwap(_tail, tail, tail + 1) == tail) 57 | { 58 | new (&node->data)T(data); 59 | Atomic::store(node->state, 1); 60 | return true; 61 | } 62 | else 63 | node->state = 0; 64 | } 65 | } 66 | 67 | bool pop(T& result) 68 | { 69 | for(;;) 70 | { 71 | usize head = _head; 72 | Node* node = &_queue[head & _capacityMask]; 73 | switch(Atomic::compareAndSwap(node->state, 1, 3)) 74 | { 75 | case 3: 76 | continue; 77 | case 1: 78 | break; 79 | default: 80 | return false; 81 | } 82 | if(Atomic::compareAndSwap(_head, head, head + 1) == head) 83 | { 84 | result = node->data; 85 | (&node->data)->~T(); 86 | Atomic::store(node->state, 0); 87 | return true; 88 | } 89 | else 90 | node->state = 1; 91 | } 92 | } 93 | 94 | private: 95 | struct Node 96 | { 97 | T data; 98 | volatile int state; 99 | }; 100 | 101 | private: 102 | usize _capacityMask; 103 | Node* _queue; 104 | usize _capacity; 105 | char cacheLinePad1[64]; 106 | volatile usize _tail; 107 | char cacheLinePad2[64]; 108 | volatile usize _head; 109 | char cacheLinePad3[64]; 110 | }; 111 | -------------------------------------------------------------------------------- /LockFreeQueueSlow2.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class LockFreeQueueSlow2 8 | { 9 | public: 10 | explicit LockFreeQueueSlow2(usize capacity) 11 | { 12 | _capacityMask = capacity - 1; 13 | for(usize i = 1; i <= sizeof(void*) * 4; i <<= 1) 14 | _capacityMask |= _capacityMask >> i; 15 | _capacity = _capacityMask + 1; 16 | 17 | _queue = (Node*)Memory::alloc(sizeof(Node) * _capacity); 18 | for(usize i = 0; i < _capacity; ++i) 19 | { 20 | _queue[i].tail = i; 21 | _queue[i].head = i - 1; 22 | } 23 | 24 | _tail = 0; 25 | _head = 0; 26 | } 27 | 28 | ~LockFreeQueueSlow2() 29 | { 30 | for(usize i = _head; i != _tail; ++i) 31 | (&_queue[i & _capacityMask].data)->~T(); 32 | 33 | Memory::free(_queue); 34 | } 35 | 36 | usize capacity() const {return _capacity;} 37 | 38 | usize size() const 39 | { 40 | usize head = Atomic::load(_head); 41 | return _tail - head; 42 | } 43 | 44 | bool push(const T& data) 45 | { 46 | begin: 47 | usize tail = _tail; 48 | Node* node = &_queue[tail & _capacityMask]; 49 | usize newTail = tail + 1; 50 | usize nodeTail = node->tail; 51 | if(nodeTail == tail) 52 | { 53 | nodeTail = Atomic::compareAndSwap(node->tail, tail, newTail); 54 | if(nodeTail == tail) 55 | { 56 | Atomic::compareAndSwap(_tail, tail, newTail); 57 | new (&node->data)T(data); 58 | Atomic::store(node->head, tail); 59 | return true; 60 | } 61 | } 62 | if(nodeTail == newTail) 63 | { 64 | Atomic::compareAndSwap(_tail, tail, newTail); 65 | goto begin; 66 | } 67 | else 68 | return false; 69 | } 70 | 71 | bool pop(T& result) 72 | { 73 | begin: 74 | usize head = _head; 75 | Node* node = &_queue[head & _capacityMask]; 76 | usize newHead = head + 1; 77 | usize nodeHead = node->head; 78 | if(nodeHead == head) 79 | { 80 | nodeHead = Atomic::compareAndSwap(node->head, head, newHead); 81 | if(nodeHead == head) 82 | { 83 | Atomic::compareAndSwap(_head, head, newHead); 84 | result = node->data; 85 | (&node->data)->~T(); 86 | Atomic::store(node->tail, head + _capacity); 87 | return true; 88 | } 89 | } 90 | if(nodeHead == newHead) 91 | { 92 | Atomic::compareAndSwap(_head, head, newHead); 93 | goto begin; 94 | } 95 | else 96 | return false; 97 | } 98 | 99 | private: 100 | struct Node 101 | { 102 | T data; 103 | volatile usize head; 104 | volatile usize tail; 105 | }; 106 | 107 | private: 108 | usize _capacityMask; 109 | Node* _queue; 110 | usize _capacity; 111 | char cacheLinePad1[64]; 112 | volatile usize _head; 113 | char cacheLinePad2[64]; 114 | volatile usize _tail; 115 | char cacheLinePad3[64]; 116 | }; 117 | -------------------------------------------------------------------------------- /LockFreeLifoQueue.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class LockFreeLifoQueue 8 | { 9 | public: 10 | explicit LockFreeLifoQueue(usize capacity) 11 | : _capacity(capacity) 12 | { 13 | _indexMask = capacity; 14 | for(usize i = 1; i <= sizeof(void*) * 4; i <<= 1) 15 | _indexMask |= _indexMask >> i; 16 | _abaOffset = _indexMask + 1; 17 | 18 | _queue = (Node*)Memory::alloc(sizeof(Node) * (capacity + 1)); 19 | for(usize i = 1; i < capacity;) 20 | { 21 | Node& node = _queue[i]; 22 | node.abaNextFree = ++i; 23 | } 24 | _queue[capacity].abaNextFree = 0; 25 | 26 | _abaFree = 1; 27 | _abaPushed = 0; 28 | } 29 | 30 | ~LockFreeLifoQueue() 31 | { 32 | for(usize abaPushed = _abaPushed;;) 33 | { 34 | usize nodeIndex = abaPushed & _indexMask; 35 | if(!nodeIndex) 36 | break; 37 | Node& node = _queue[nodeIndex]; 38 | abaPushed = node.abaNextPushed; 39 | (&node.data)->~T(); 40 | } 41 | 42 | Memory::free(_queue); 43 | } 44 | 45 | usize capacity() const {return _capacity;} 46 | 47 | usize size() const {return 0;} 48 | 49 | bool push(const T& data) 50 | { 51 | Node* node; 52 | usize abaFree; 53 | for(;;) 54 | { 55 | abaFree = _abaFree; 56 | usize nodeIndex = abaFree & _indexMask; 57 | if(!nodeIndex) 58 | return false; 59 | node = &_queue[nodeIndex]; 60 | if(Atomic::compareAndSwap(_abaFree, abaFree, node->abaNextFree + _abaOffset) == abaFree) 61 | break; 62 | } 63 | 64 | new (&node->data)T(data); 65 | for(;;) 66 | { 67 | usize abaPushed = _abaPushed; 68 | node->abaNextPushed = abaPushed; 69 | if(Atomic::compareAndSwap(_abaPushed, abaPushed, abaFree) == abaPushed) 70 | return true; 71 | } 72 | } 73 | 74 | bool pop(T& result) 75 | { 76 | Node* node; 77 | usize abaPushed; 78 | for(;;) 79 | { 80 | abaPushed = _abaPushed; 81 | usize nodeIndex = abaPushed & _indexMask; 82 | if(!nodeIndex) 83 | return false; 84 | node = &_queue[nodeIndex]; 85 | if(Atomic::compareAndSwap(_abaPushed, abaPushed, node->abaNextPushed + _abaOffset) == abaPushed) 86 | break; 87 | } 88 | 89 | result = node->data; 90 | (&node->data)->~T(); 91 | abaPushed += _abaOffset; 92 | for(;;) 93 | { 94 | usize abaFree = _abaFree; 95 | node->abaNextFree = abaFree; 96 | if(Atomic::compareAndSwap(_abaFree, abaFree, abaPushed) == abaFree) 97 | return true; 98 | } 99 | } 100 | 101 | private: 102 | struct Node 103 | { 104 | T data; 105 | volatile usize abaNextFree; 106 | volatile usize abaNextPushed; 107 | }; 108 | 109 | private: 110 | usize _indexMask; 111 | Node* _queue; 112 | usize _abaOffset; 113 | usize _capacity; 114 | char cacheLinePad1[64]; 115 | volatile usize _abaFree; 116 | char cacheLinePad2[64]; 117 | volatile usize _abaPushed; 118 | char cacheLinePad3[64]; 119 | }; 120 | -------------------------------------------------------------------------------- /LockFreeQueueSlow1.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | template class LockFreeQueueSlow1 8 | { 9 | public: 10 | explicit LockFreeQueueSlow1(usize capacity) 11 | { 12 | _capacityMask = capacity - 1; 13 | for(usize i = 1; i <= sizeof(void*) * 4; i <<= 1) 14 | _capacityMask |= _capacityMask >> i; 15 | _capacity = _capacityMask + 1; 16 | 17 | _queue = (Node*)Memory::alloc(sizeof(Node) * _capacity); 18 | for(Node* node = _queue, * end = _queue + _capacity; node < end; ++node) 19 | node->state = Node::free; 20 | 21 | _freeNodes = _capacity; 22 | _occupiedNodes = 0; 23 | _writeIndex = -1; 24 | _safeWriteIndex = -1; 25 | _readIndex = -1; 26 | _safeReadIndex = -1; 27 | } 28 | 29 | ~LockFreeQueueSlow1() 30 | { 31 | for(Node* node = _queue, * end = _queue + _capacity; node < end; ++node) 32 | switch(node->state) 33 | { 34 | case Node::set: 35 | case Node::occupied: 36 | (&node->data)->~T(); 37 | break; 38 | default: 39 | break; 40 | } 41 | Memory::free(_queue); 42 | } 43 | 44 | usize capacity() const {return _capacity;} 45 | 46 | usize size() const {return _capacity - _freeNodes;} 47 | 48 | bool push(const T& data) 49 | { 50 | begin: 51 | usize freeNodes = _freeNodes; 52 | if(freeNodes == 0) 53 | return false; // queue is full 54 | if(Atomic::compareAndSwap(_freeNodes, freeNodes, freeNodes - 1) != freeNodes) 55 | goto begin; 56 | usize writeIndex = Atomic::increment(_writeIndex); 57 | Node* node = &_queue[writeIndex & _capacityMask]; 58 | ASSERT(node->state == Node::free); 59 | new (&node->data)T(data); 60 | Atomic::store(node->state, Node::set); 61 | commit: 62 | usize safeWriteIndex = _safeWriteIndex; 63 | usize nextSafeWriteIndex = safeWriteIndex + 1; 64 | commitNext: 65 | node = &_queue[nextSafeWriteIndex & _capacityMask]; 66 | if(node->state == Node::set && Atomic::compareAndSwap(node->state, Node::set, Node::occupied) == Node::set) 67 | { 68 | if(Atomic::compareAndSwap(_safeWriteIndex, safeWriteIndex, nextSafeWriteIndex) == safeWriteIndex) 69 | { 70 | Atomic::increment(_occupiedNodes); 71 | safeWriteIndex = nextSafeWriteIndex; 72 | ++nextSafeWriteIndex; 73 | goto commitNext; 74 | } 75 | else 76 | node->state = Node::set; 77 | goto commit; 78 | } 79 | return true; 80 | } 81 | 82 | bool pop(T& result) 83 | { 84 | begin: 85 | usize occupiedNodes = _occupiedNodes; 86 | if(occupiedNodes == 0) 87 | return false; // queue is empty 88 | if(Atomic::compareAndSwap(_occupiedNodes, occupiedNodes, occupiedNodes - 1) != occupiedNodes) 89 | goto begin; 90 | usize readIndex = Atomic::increment(_readIndex); 91 | Node* node = &_queue[readIndex & _capacityMask]; 92 | ASSERT(node->state == Node::occupied); 93 | result = node->data; 94 | (&node->data)->~T(); 95 | Atomic::store(node->state, Node::unset); 96 | release: 97 | usize safeReadIndex = _safeReadIndex; 98 | usize nextSafeReadIndex = safeReadIndex + 1; 99 | releaseNext: 100 | node = &_queue[nextSafeReadIndex & _capacityMask]; 101 | if(node->state == Node::unset && Atomic::compareAndSwap(node->state, Node::unset, Node::free) == Node::unset) 102 | { 103 | if(Atomic::compareAndSwap(_safeReadIndex, safeReadIndex, nextSafeReadIndex) == safeReadIndex) 104 | { 105 | Atomic::increment(_freeNodes); 106 | safeReadIndex = nextSafeReadIndex; 107 | ++nextSafeReadIndex; 108 | goto releaseNext; 109 | } 110 | else 111 | node->state = Node::unset; 112 | goto release; 113 | } 114 | return true; 115 | } 116 | 117 | private: 118 | struct Node 119 | { 120 | T data; 121 | enum State 122 | { 123 | free, 124 | set, 125 | occupied, 126 | unset, 127 | }; 128 | volatile int state; 129 | }; 130 | 131 | private: 132 | usize _capacityMask; 133 | Node* _queue; 134 | usize _capacity; 135 | volatile usize _freeNodes; 136 | volatile usize _occupiedNodes; 137 | volatile usize _writeIndex; 138 | volatile usize _safeWriteIndex; 139 | volatile usize _readIndex; 140 | volatile usize _safeReadIndex; 141 | }; 142 | -------------------------------------------------------------------------------- /mpmc_bounded_queue.h: -------------------------------------------------------------------------------- 1 | /* Multi-producer/multi-consumer bounded queue. 2 | * Copyright (c) 2010-2011, Dmitry Vyukov. All rights reserved. 3 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | * 1. Redistributions of source code must retain the above copyright notice, this list of 5 | * conditions and the following disclaimer. 6 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list 7 | * of conditions and the following disclaimer in the documentation and/or other materials 8 | * provided with the distribution. 9 | * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 10 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 11 | * DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 12 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 13 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 14 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | * The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted 16 | * as representing official policies, either expressed or implied, of Dmitry Vyukov. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | template 23 | class mpmc_bounded_queue 24 | { 25 | private: 26 | static inline size_t nextPowerOfTwo(size_t buffer_size) 27 | { 28 | size_t result = buffer_size - 1; 29 | for(size_t i = 1; i <= sizeof(void*) * 4; i <<= 1) 30 | result |= result >> i; 31 | return result + 1; 32 | } 33 | public: 34 | mpmc_bounded_queue(size_t buffer_size) 35 | : buffer_(new cell_t [nextPowerOfTwo(buffer_size)]) 36 | , buffer_mask_(nextPowerOfTwo(buffer_size) - 1) 37 | { 38 | buffer_size = buffer_mask_ + 1; 39 | assert((buffer_size >= 2) && 40 | ((buffer_size & (buffer_size - 1)) == 0)); 41 | for (size_t i = 0; i != buffer_size; i += 1) 42 | buffer_[i].sequence_.store(i, std::memory_order_relaxed); 43 | enqueue_pos_.store(0, std::memory_order_relaxed); 44 | dequeue_pos_.store(0, std::memory_order_relaxed); 45 | } 46 | 47 | ~mpmc_bounded_queue() 48 | { 49 | delete [] buffer_; 50 | } 51 | 52 | size_t size() const 53 | { 54 | size_t head = dequeue_pos_.load(std::memory_order_acquire);; 55 | return enqueue_pos_.load(std::memory_order_relaxed) - head; 56 | } 57 | 58 | size_t capacity() const 59 | { 60 | return buffer_mask_ + 1; 61 | } 62 | 63 | bool push(T const& data) 64 | { 65 | cell_t* cell; 66 | size_t pos = enqueue_pos_.load(std::memory_order_relaxed); 67 | for (;;) 68 | { 69 | cell = &buffer_[pos & buffer_mask_]; 70 | size_t seq = 71 | cell->sequence_.load(std::memory_order_acquire); 72 | intptr_t dif = (intptr_t)seq - (intptr_t)pos; 73 | if (dif == 0) 74 | { 75 | if (enqueue_pos_.compare_exchange_weak 76 | (pos, pos + 1, std::memory_order_relaxed)) 77 | break; 78 | } 79 | else if (dif < 0) 80 | return false; 81 | else 82 | pos = enqueue_pos_.load(std::memory_order_relaxed); 83 | } 84 | cell->data_ = data; 85 | cell->sequence_.store(pos + 1, std::memory_order_release); 86 | return true; 87 | } 88 | 89 | bool pop(T& data) 90 | { 91 | cell_t* cell; 92 | size_t pos = dequeue_pos_.load(std::memory_order_relaxed); 93 | for (;;) 94 | { 95 | cell = &buffer_[pos & buffer_mask_]; 96 | size_t seq = 97 | cell->sequence_.load(std::memory_order_acquire); 98 | intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1); 99 | if (dif == 0) 100 | { 101 | if (dequeue_pos_.compare_exchange_weak 102 | (pos, pos + 1, std::memory_order_relaxed)) 103 | break; 104 | } 105 | else if (dif < 0) 106 | return false; 107 | else 108 | pos = dequeue_pos_.load(std::memory_order_relaxed); 109 | } 110 | data = cell->data_; 111 | cell->sequence_.store 112 | (pos + buffer_mask_ + 1, std::memory_order_release); 113 | return true; 114 | } 115 | 116 | private: 117 | struct cell_t 118 | { 119 | std::atomic sequence_; 120 | T data_; 121 | }; 122 | static size_t const cacheline_size = 64; 123 | typedef char cacheline_pad_t [cacheline_size]; 124 | cacheline_pad_t pad0_; 125 | cell_t* const buffer_; 126 | size_t const buffer_mask_; 127 | cacheline_pad_t pad1_; 128 | std::atomic enqueue_pos_; 129 | cacheline_pad_t pad2_; 130 | std::atomic dequeue_pos_; 131 | cacheline_pad_t pad3_; 132 | mpmc_bounded_queue(mpmc_bounded_queue const&); 133 | void operator = (mpmc_bounded_queue const&); 134 | }; 135 | -------------------------------------------------------------------------------- /Test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "LockFreeQueueCpp11.h" 9 | #include "LockFreeQueue.h" 10 | #include "LockFreeQueueSlow1.h" 11 | #include "LockFreeQueueSlow2.h" 12 | #include "LockFreeQueueSlow3.h" 13 | #include "MutexLockQueue.h" 14 | #include "SpinLockQueue.h" 15 | #include "mpmc_bounded_queue.h" 16 | #include "LockFreeLifoQueue.h" 17 | 18 | static const int testItems = 250000 * 64 / 3 * 10; 19 | static const int testThreadConsumerThreads = 8; 20 | static const int testThreadProducerThreads = 8; 21 | static const int testItemsPerConsumerThread = testItems / testThreadConsumerThreads; 22 | static const int testItemsPerProducerThread = testItems / testThreadProducerThreads; 23 | 24 | template class IQueue 25 | { 26 | public: 27 | virtual usize size() const = 0; 28 | virtual usize capacity() const = 0; 29 | virtual bool push(const T& data) = 0; 30 | virtual bool pop(T& result) = 0; 31 | }; 32 | 33 | template class TestQueue : public IQueue 34 | { 35 | public: 36 | TestQueue(usize size) : queue(size) {} 37 | usize size() const {return queue.size();} 38 | usize capacity() const {return queue.capacity();} 39 | bool push(const T& data) {return queue.push(data);} 40 | bool pop(T& result) {return queue.pop(result);} 41 | private: 42 | Q queue; 43 | }; 44 | 45 | volatile usize producerSum; 46 | volatile usize consumerSum; 47 | volatile int64 maxPushDuration; 48 | volatile int64 maxPopDuration; 49 | 50 | uint producerThread(void* param) 51 | { 52 | IQueue* queue = (IQueue*)param; 53 | for(int i = 0; i < testItemsPerProducerThread; ++i) 54 | { 55 | for(;;) 56 | { 57 | int64 startTime = Time::microTicks(); 58 | while(!queue->push(i)) 59 | { 60 | Thread::yield(); 61 | startTime = Time::microTicks(); 62 | } 63 | { 64 | int64 duration = Time::microTicks() - startTime; 65 | for(;;) 66 | { 67 | int64 lmaxPushDuration = maxPushDuration; 68 | if(duration <= lmaxPushDuration || Atomic::compareAndSwap(maxPushDuration, lmaxPushDuration, duration) == lmaxPushDuration) 69 | break; 70 | } 71 | break; 72 | } 73 | } 74 | Atomic::fetchAndAdd(producerSum, i); 75 | } 76 | return 0; 77 | } 78 | 79 | uint consumerThread(void* param) 80 | { 81 | IQueue* queue = (IQueue*)param; 82 | int val; 83 | for(int i = 0; i < testItemsPerConsumerThread; ++i) 84 | { 85 | for(;;) 86 | { 87 | int64 startTime = Time::microTicks(); 88 | while(!queue->pop(val)) 89 | { 90 | Thread::yield(); 91 | startTime = Time::microTicks(); 92 | } 93 | { 94 | int64 duration = Time::microTicks() - startTime; 95 | for(;;) 96 | { 97 | int64 lmaxPopDuration = maxPopDuration; 98 | if(duration <= lmaxPopDuration || Atomic::compareAndSwap(maxPopDuration, lmaxPopDuration, duration) == lmaxPopDuration) 99 | break; 100 | } 101 | break; 102 | } 103 | } 104 | Atomic::fetchAndAdd(consumerSum, val); 105 | } 106 | return 0; 107 | } 108 | 109 | template void testQueue(const String& name, bool fifo = false) 110 | { 111 | Console::printf(_T("Testing %s... \n"), (const tchar*)name); 112 | 113 | volatile int32 int32_ = 0; 114 | volatile uint32 uint32_ = 0; 115 | ASSERT(Atomic::compareAndSwap(int32_, 0, 1) == 0); 116 | ASSERT(int32_ == 1); 117 | ASSERT(Atomic::compareAndSwap(uint32_, 0, 1) == 0); 118 | ASSERT(uint32_ == 1); 119 | 120 | volatile int64 int64_ = 0; 121 | volatile uint64 uint64_ = 0; 122 | ASSERT(Atomic::compareAndSwap(int64_, 0, 1) == 0); 123 | ASSERT(int64_ == 1); 124 | ASSERT(Atomic::compareAndSwap(uint64_, 0, 1) == 0); 125 | ASSERT(uint64_ == 1); 126 | 127 | { 128 | TestQueue queue(10000); 129 | int result; 130 | ASSERT(queue.capacity() >= 10000); 131 | ASSERT(!queue.pop(result)); 132 | ASSERT(queue.push(42)); 133 | ASSERT(queue.pop(result)); 134 | ASSERT(result == 42); 135 | ASSERT(!queue.pop(result)); 136 | } 137 | 138 | { 139 | TestQueue queue(2); 140 | int result; 141 | ASSERT(queue.capacity() >= 2); 142 | ASSERT(!queue.pop(result)); 143 | ASSERT(queue.push(42)); 144 | ASSERT(queue.push(43)); 145 | ASSERT(queue.pop(result)); 146 | ASSERT(result == (fifo ? 43 : 42)); 147 | ASSERT(queue.pop(result)); 148 | ASSERT(result == (fifo ? 42 : 43)); 149 | ASSERT(!queue.pop(result)); 150 | ASSERT(queue.push(44)); 151 | ASSERT(queue.push(45)); 152 | ASSERT(queue.pop(result)); 153 | ASSERT(result == (fifo ? 45 : 44)); 154 | ASSERT(queue.push(47)); 155 | } 156 | 157 | producerSum = 0; 158 | consumerSum = 0; 159 | maxPushDuration = 0; 160 | maxPopDuration = 0; 161 | 162 | int64 microStartTime = Time::microTicks(); 163 | { 164 | TestQueue queue(100); 165 | List threads; 166 | for(int i = 0; i < testThreadProducerThreads; ++i) 167 | { 168 | Thread* thread = new Thread; 169 | thread->start(producerThread, &queue); 170 | threads.append(thread); 171 | } 172 | for(int i = 0; i < testThreadConsumerThreads; ++i) 173 | { 174 | Thread* thread = new Thread; 175 | thread->start(consumerThread, &queue); 176 | threads.append(thread); 177 | } 178 | for(List::Iterator i = threads.begin(), end = threads.end(); i != end; ++i) 179 | { 180 | Thread* thread = *i; 181 | thread->join(); 182 | delete thread; 183 | } 184 | ASSERT(queue.size() == 0); 185 | ASSERT(producerSum == consumerSum); 186 | } 187 | int64 microDuration = Time::microTicks() - microStartTime; 188 | Console::printf(_T("%lld ms, maxPush: %lld microseconds, maxPop: %lld microseconds\n"), microDuration / 1000, maxPushDuration, maxPopDuration); 189 | } 190 | 191 | int main(int argc, char* argv[]) 192 | { 193 | for(int i = 0; i < 3; ++i) 194 | { 195 | Console::printf(_T("--- Run %d ---\n"), i); 196 | testQueue >("LockFreeQueueCpp11"); 197 | testQueue >("mpmc_bounded_queue"); 198 | testQueue >("LockFreeQueue"); 199 | testQueue >("LockFreeQueueSlow1"); 200 | testQueue >("LockFreeQueueSlow2"); 201 | testQueue >("LockFreeQueueSlow3"); 202 | testQueue >("MutexLockQueue"); 203 | testQueue >("SpinLockQueue"); 204 | testQueue >("LockFreeLifoQueue", true); 205 | } 206 | 207 | return 0; 208 | } 209 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------