├── 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 |
--------------------------------------------------------------------------------