├── .gitignore
├── .nuget
├── NuGet.Config
├── NuGet.exe
└── NuGet.targets
├── ConcurrentPriorityQueue.sln
├── ConcurrentPriorityQueue.sln.DotSettings
├── ConcurrentPriorityQueue
├── AbstractPriorityQueue.cs
├── ConcurrentFixedSizePriorityQueue.cs
├── ConcurrentPriorityQueue.cs
├── ConcurrentPriorityQueue.csproj
├── ConcurrentPriorityQueue.nuspec
├── IPriorityQueue.cs
└── Properties
│ └── AssemblyInfo.cs
├── License.md
├── README.md
└── Tests
└── ConcurrentPriorityQueueTests
├── AssertEx.cs
├── ConcurrentPriorityQueueTests.csproj
├── ExecWithStats.cs
├── FunctionalTests
├── AbstractPriorityQueueTests.cs
├── ConcurrentFixedSizePriorityQueueTests.cs
└── ConcurrentPriorityQueueTests.cs
├── PerformanceTests
├── ConcurrentFixedSizePriorityQueueTests.cs
└── ConcurrentPriorityQueueTests.cs
└── Properties
└── AssemblyInfo.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | # User-specific files
2 | *.suo
3 | *.user
4 | *.sln.docstates
5 |
6 | # Build results
7 |
8 | [Dd]ebug/
9 | [Rr]elease/
10 | x64/
11 | build/
12 | [Bb]in/
13 | [Oo]bj/
14 |
15 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
16 | !packages/*/build/
17 |
18 | # MSTest test Results
19 | [Tt]est[Rr]esult*/
20 | [Bb]uild[Ll]og.*
21 |
22 | *_i.c
23 | *_p.c
24 | *.ilk
25 | *.meta
26 | *.obj
27 | *.pch
28 | *.pdb
29 | *.pgc
30 | *.pgd
31 | *.rsp
32 | *.sbr
33 | *.tlb
34 | *.tli
35 | *.tlh
36 | *.tmp
37 | *.tmp_proj
38 | *.log
39 | *.vspscc
40 | *.vssscc
41 | .builds
42 | *.pidb
43 | *.log
44 | *.scc
45 |
46 | # OS generated files #
47 | .DS_Store*
48 | ehthumbs.db
49 | Icon?
50 | Thumbs.db
51 |
52 | # Visual C++ cache files
53 | ipch/
54 | *.aps
55 | *.ncb
56 | *.opensdf
57 | *.sdf
58 | *.cachefile
59 |
60 | # Visual Studio profiler
61 | *.psess
62 | *.vsp
63 | *.vspx
64 |
65 | # Guidance Automation Toolkit
66 | *.gpState
67 |
68 | # ReSharper is a .NET coding add-in
69 | _ReSharper*/
70 | *.[Rr]e[Ss]harper
71 |
72 | # TeamCity is a build add-in
73 | _TeamCity*
74 |
75 | # DotCover is a Code Coverage Tool
76 | *.dotCover
77 |
78 | # NCrunch
79 | *.ncrunch*
80 | .*crunch*.local.xml
81 |
82 | # Installshield output folder
83 | [Ee]xpress/
84 |
85 | # DocProject is a documentation generator add-in
86 | DocProject/buildhelp/
87 | DocProject/Help/*.HxT
88 | DocProject/Help/*.HxC
89 | DocProject/Help/*.hhc
90 | DocProject/Help/*.hhk
91 | DocProject/Help/*.hhp
92 | DocProject/Help/Html2
93 | DocProject/Help/html
94 |
95 | # Click-Once directory
96 | publish/
97 |
98 | # Publish Web Output
99 | *.Publish.xml
100 |
101 | # NuGet Packages Directory
102 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
103 | #packages/
104 |
105 | # Windows Azure Build Output
106 | csx
107 | *.build.csdef
108 |
109 | # Windows Store app package directory
110 | AppPackages/
111 |
112 | # Others
113 | sql/
114 | *.Cache
115 | ClientBin/
116 | [Ss]tyle[Cc]op.*
117 | ~$*
118 | *~
119 | *.dbmdl
120 | *.[Pp]ublish.xml
121 | *.pfx
122 | *.publishsettings
123 | modulesbin/
124 | tempbin/
125 |
126 | # EPiServer Site file (VPP)
127 | AppData/
128 |
129 | # RIA/Silverlight projects
130 | Generated_Code/
131 |
132 | # Backup & report files from converting an old project file to a newer
133 | # Visual Studio version. Backup files are not needed, because we have git ;-)
134 | _UpgradeReport_Files/
135 | Backup*/
136 | UpgradeLog*.XML
137 | UpgradeLog*.htm
138 |
139 | # vim
140 | *.txt~
141 | *.swp
142 | *.swo
143 |
144 | # svn
145 | .svn
146 |
147 | # SQL Server files
148 | **/App_Data/*.mdf
149 | **/App_Data/*.ldf
150 | **/App_Data/*.sdf
151 |
152 |
153 | #LightSwitch generated files
154 | GeneratedArtifacts/
155 | _Pvt_Extensions/
156 | ModelManifest.xml
157 |
158 | # =========================
159 | # Windows detritus
160 | # =========================
161 |
162 | # Windows image file caches
163 | Thumbs.db
164 | ehthumbs.db
165 |
166 | # Folder config file
167 | Desktop.ini
168 |
169 | # Recycle Bin used on file shares
170 | $RECYCLE.BIN/
171 |
172 | # Mac desktop service store files
173 | .DS_Store
174 |
175 | # SASS Compiler cache
176 | .sass-cache
177 |
178 | # Visual Studio 2014 CTP
179 | **/*.sln.ide
180 | /packages/NUnit.2.6.3
181 | *.nupkg
182 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dshulepov/ConcurrentPriorityQueue/1628a40d3d9634c05b13aa20fc5d7343bb326b3c/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 |
32 |
33 |
34 |
35 | $(SolutionDir).nuget
36 |
37 |
38 |
39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
41 |
42 |
43 |
44 | $(MSBuildProjectDirectory)\packages.config
45 | $(PackagesProjectConfig)
46 |
47 |
48 |
49 |
50 | $(NuGetToolsPath)\NuGet.exe
51 | @(PackageSource)
52 |
53 | "$(NuGetExePath)"
54 | mono --runtime=v4.0.30319 "$(NuGetExePath)"
55 |
56 | $(TargetDir.Trim('\\'))
57 |
58 | -RequireConsent
59 | -NonInteractive
60 |
61 | "$(SolutionDir) "
62 | "$(SolutionDir)"
63 |
64 |
65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
67 |
68 |
69 |
70 | RestorePackages;
71 | $(BuildDependsOn);
72 |
73 |
74 |
75 |
76 | $(BuildDependsOn);
77 | BuildPackage;
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 |
100 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConcurrentPriorityQueue", "ConcurrentPriorityQueue\ConcurrentPriorityQueue.csproj", "{4C5838DE-B350-4268-83DE-D94CC0738F00}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{5DC01D8D-E5AB-474E-A34F-C31D29C037AB}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConcurrentPriorityQueueTests", "Tests\ConcurrentPriorityQueueTests\ConcurrentPriorityQueueTests.csproj", "{B67A2F2D-08FB-4458-9299-A323941AAF19}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{8178047C-7099-463E-BE54-311EC08DAA42}"
13 | ProjectSection(SolutionItems) = preProject
14 | .nuget\NuGet.Config = .nuget\NuGet.Config
15 | .nuget\NuGet.exe = .nuget\NuGet.exe
16 | .nuget\NuGet.targets = .nuget\NuGet.targets
17 | EndProjectSection
18 | EndProject
19 | Global
20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
21 | Debug|Any CPU = Debug|Any CPU
22 | Release|Any CPU = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {4C5838DE-B350-4268-83DE-D94CC0738F00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {4C5838DE-B350-4268-83DE-D94CC0738F00}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {4C5838DE-B350-4268-83DE-D94CC0738F00}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {4C5838DE-B350-4268-83DE-D94CC0738F00}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {B67A2F2D-08FB-4458-9299-A323941AAF19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {B67A2F2D-08FB-4458-9299-A323941AAF19}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {B67A2F2D-08FB-4458-9299-A323941AAF19}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {B67A2F2D-08FB-4458-9299-A323941AAF19}.Release|Any CPU.Build.0 = Release|Any CPU
33 | EndGlobalSection
34 | GlobalSection(SolutionProperties) = preSolution
35 | HideSolutionNode = FALSE
36 | EndGlobalSection
37 | GlobalSection(NestedProjects) = preSolution
38 | {B67A2F2D-08FB-4458-9299-A323941AAF19} = {5DC01D8D-E5AB-474E-A34F-C31D29C037AB}
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
3 | <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
4 | <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
5 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
6 | <data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="ConcurrentPriorityQueueTests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data>
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue/AbstractPriorityQueue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace ConcurrentPriorityQueue
6 | {
7 | ///
8 | /// Heap-based implementation of priority queue.
9 | ///
10 | public abstract class AbstractPriorityQueue : IPriorityQueue where TPriority : IComparable
11 | {
12 | internal sealed class Node
13 | {
14 | public TPriority Priority { get; internal set; }
15 | public readonly TElement Element;
16 |
17 | public Node(TElement element, TPriority priority)
18 | {
19 | Priority = priority;
20 | Element = element;
21 | }
22 | }
23 |
24 | internal Node[] _nodes;
25 | internal int _count;
26 | internal readonly NodeComparer _comparer;
27 | private readonly bool _dataIsValueType;
28 |
29 | ///
30 | /// Create an empty max priority queue of given capacity.
31 | ///
32 | /// Queue capacity. Greater than 0.
33 | /// Priority comparer. Default type comparer will be used unless custom is provided.
34 | ///
35 | internal AbstractPriorityQueue(int capacity, IComparer comparer = null)
36 | {
37 | if (capacity <= 0) throw new ArgumentOutOfRangeException("capacity", "Expected capacity greater than zero.");
38 |
39 | _nodes = new Node[capacity + 1]; // first element at 1
40 | _count = 0;
41 | _comparer = new NodeComparer(comparer ?? Comparer.Default);
42 | _dataIsValueType = typeof (TElement).IsValueType;
43 | }
44 |
45 | ///
46 | /// Create a new priority queue from the given nodes storage and comparer.
47 | /// Used to create existing queue copies.
48 | ///
49 | /// Heap with data.
50 | /// Count of items in the heap.
51 | /// Node comparer for nodes in the queue.
52 | internal AbstractPriorityQueue(Node[] nodes, int count, NodeComparer comparer)
53 | {
54 | _nodes = nodes;
55 | _count = count;
56 | _comparer = comparer;
57 | _dataIsValueType = typeof(TElement).IsValueType;
58 | }
59 |
60 | public int Capacity { get { return _nodes.Length - 1; } }
61 |
62 | public int Count { get { return _count; } }
63 |
64 | ///
65 | /// Returns true if there is at least one item, which is equal to given.
66 | /// TElement.Equals is used to compare equality.
67 | ///
68 | public virtual bool Contains(TElement item)
69 | {
70 | return GetItemIndex(item) > 0;
71 | }
72 |
73 | ///
74 | /// Returns index of the first occurrence of the given item or 0.
75 | /// TElement.Equals is used to compare equality.
76 | ///
77 | private int GetItemIndex(TElement item)
78 | {
79 | for (int i = 1; i <= _count; i++)
80 | {
81 | if (Equals(_nodes[i].Element, item)) return i;
82 | }
83 | return 0;
84 | }
85 |
86 | ///
87 | /// Check if given data items are equal using TD.Equals.
88 | /// Handles null values for object types.
89 | ///
90 | internal bool Equals(TElement a, TElement b)
91 | {
92 | if (_dataIsValueType)
93 | {
94 | return a.Equals(b);
95 | }
96 |
97 | var objA = a as object;
98 | var objB = b as object;
99 | if (objA == null && objB == null) return true; // null == null because equality should be symmetric
100 | if (objA == null || objB == null) return false;
101 | return objA.Equals(objB);
102 | }
103 |
104 | public virtual void Enqueue(TElement item, TPriority priority)
105 | {
106 | int index = _count + 1;
107 | _nodes[index] = new Node(item, priority);
108 |
109 | _count = index; // update count after the element is really added but before Sift
110 |
111 | Sift(index); // move item "up" while heap principles are not met
112 | }
113 |
114 | public virtual TElement Dequeue()
115 | {
116 | if (_count == 0) throw new InvalidOperationException("Unable to dequeue from empty queue.");
117 |
118 | TElement item = _nodes[1].Element; // first element at 1
119 | Swap(1, _count); // last element at _count
120 | _nodes[_count] = null; // release hold on the object
121 |
122 | _count--; // update count after the element is really gone but before Sink
123 |
124 | Sink(1); // move item "down" while heap principles are not met
125 |
126 | return item;
127 | }
128 |
129 | ///
130 | /// Returns the first element in the queue (element with max priority) without removing it from the queue.
131 | ///
132 | ///
133 | public virtual TElement Peek()
134 | {
135 | if (_count == 0) throw new InvalidOperationException("Unable to peek from empty queue.");
136 |
137 | return _nodes[1].Element; // first element at 1
138 | }
139 |
140 | ///
141 | /// Remove all items from the queue. Capacity is not changed.
142 | ///
143 | public virtual void Clear()
144 | {
145 | for (int i = 1; i <= _count; i++)
146 | {
147 | _nodes[i] = null;
148 | }
149 | _count = 0;
150 | }
151 |
152 | ///
153 | /// Update priority of the first occurrence of the given item
154 | ///
155 | /// Item, which priority should be updated.
156 | /// New priority
157 | ///
158 | public virtual void UpdatePriority(TElement item, TPriority priority)
159 | {
160 | var index = GetItemIndex(item);
161 | if (index == 0) throw new ArgumentException("Item is not found in the queue.");
162 |
163 | var priorityCompare = _comparer.Compare(_nodes[index].Priority, priority);
164 | if (priorityCompare < 0)
165 | {
166 | _nodes[index].Priority = priority;
167 | Sift(index); // priority is increased, so item should go "up" the heap
168 | }
169 | else if (priorityCompare > 0)
170 | {
171 | _nodes[index].Priority = priority;
172 | Sink(index); // priority is decreased, so item should go "down" the heap
173 | }
174 | }
175 |
176 | ///
177 | /// Returns a copy of internal heap array. Number of elements is _count + 1;
178 | ///
179 | ///
180 | internal Node[] CopyNodes()
181 | {
182 | var nodesCopy = new Node[_count + 1];
183 | Array.Copy(_nodes, 0, nodesCopy, 0, _count + 1);
184 | return nodesCopy;
185 | }
186 |
187 | private bool GreaterOrEqual(Node i, Node j)
188 | {
189 | return _comparer.Compare(i, j) >= 0;
190 | }
191 |
192 | ///
193 | /// Moves the item with given index "down" the heap while heap principles are not met.
194 | ///
195 | private void Sink(int i)
196 | {
197 | while (true)
198 | {
199 | int leftChildIndex = 2 * i;
200 | int rightChildIndex = 2 * i + 1;
201 | if (leftChildIndex > _count) return; // reached last item
202 |
203 | var item = _nodes[i];
204 | var left = _nodes[leftChildIndex];
205 | var right = rightChildIndex > _count ? null : _nodes[rightChildIndex];
206 |
207 | // if item is greater than children - exit
208 | if (GreaterOrEqual(item, left) && (right == null || GreaterOrEqual(item, right))) return;
209 |
210 | // else exchange with greater of children
211 | int greaterChild = right == null || GreaterOrEqual(left, right) ? leftChildIndex : rightChildIndex;
212 | Swap(i, greaterChild);
213 |
214 | // continue at new position
215 | i = greaterChild;
216 | }
217 | }
218 |
219 | ///
220 | /// Moves the item with given index "up" the heap while heap principles are not met.
221 | ///
222 | private void Sift(int i)
223 | {
224 | while (true)
225 | {
226 | if (i <= 1) return; // reached root
227 | int parent = i / 2; // get parent
228 |
229 | // if root is greater or equal - exit
230 | if (GreaterOrEqual(_nodes[parent], _nodes[i])) return;
231 |
232 | Swap(parent, i);
233 | i = parent;
234 | }
235 | }
236 |
237 | private void Swap(int i, int j)
238 | {
239 | var tmp = _nodes[i];
240 | _nodes[i] = _nodes[j];
241 | _nodes[j] = tmp;
242 | }
243 |
244 | public abstract IEnumerator GetEnumerator();
245 |
246 | IEnumerator IEnumerable.GetEnumerator()
247 | {
248 | return GetEnumerator();
249 | }
250 |
251 | ///
252 | /// Compare nodes based on priority or just priorities
253 | ///
254 | internal sealed class NodeComparer : IComparer, IComparer
255 | {
256 | private readonly IComparer _comparer;
257 |
258 | public NodeComparer(IComparer comparer)
259 | {
260 | _comparer = comparer;
261 | }
262 |
263 | public int Compare(Node x, Node y)
264 | {
265 | if (x == null && y == null) return 0;
266 | if (x == null) return -1;
267 | if (y == null) return 1;
268 |
269 | return _comparer.Compare(x.Priority, y.Priority);
270 | }
271 |
272 | public int Compare(TPriority x, TPriority y)
273 | {
274 | return _comparer.Compare(x, y);
275 | }
276 | }
277 |
278 | ///
279 | /// Queue items enumerator. Returns items in the oder of queue priority.
280 | ///
281 | internal sealed class PriorityQueueEnumerator : IEnumerator
282 | {
283 | private readonly TElement[] _items;
284 | private int _currentIndex;
285 |
286 | internal PriorityQueueEnumerator(AbstractPriorityQueue queueCopy)
287 | {
288 | _items = new TElement[queueCopy.Count];
289 |
290 | // dequeue the given queue copy to extract items in order of priority
291 | // enumerator is based on the new array to allow reset and multiple enumerations
292 | for (int i = 0; i < _items.Length; i++)
293 | {
294 | _items[i] = queueCopy.Dequeue();
295 | }
296 |
297 | Reset();
298 | }
299 |
300 | public void Dispose()
301 | {
302 | }
303 |
304 | public bool MoveNext()
305 | {
306 | _currentIndex++;
307 | return _currentIndex < _items.Length;
308 | }
309 |
310 | public void Reset()
311 | {
312 | _currentIndex = -1;
313 | }
314 |
315 | public TElement Current { get { return _items[_currentIndex]; } }
316 |
317 | object IEnumerator.Current
318 | {
319 | get { return Current; }
320 | }
321 | }
322 | }
323 | }
324 |
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue/ConcurrentFixedSizePriorityQueue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace ConcurrentPriorityQueue
5 | {
6 | ///
7 | /// Heap-based implementation of concurrent, fixed-size priority queue. Max priority is on top of the heap.
8 | ///
9 | public class ConcurrentFixedSizePriorityQueue : AbstractPriorityQueue where TPriority : IComparable
10 | {
11 | private readonly object _sync = new object();
12 |
13 | ///
14 | /// Create a new instance of priority queue with given fixed capacity.
15 | ///
16 | /// Maximum queue capacity. Should be greater than 0.
17 | /// Priority comparer. Default for type will be used unless custom is provided.
18 | public ConcurrentFixedSizePriorityQueue(int capacity, IComparer comparer = null):base(capacity, comparer)
19 | {
20 | }
21 |
22 | private ConcurrentFixedSizePriorityQueue(Node[] nodes, int count, NodeComparer comparer):base(nodes, count, comparer)
23 | { }
24 |
25 | ///
26 | /// Add new item to the queue.
27 | ///
28 | public override void Enqueue(TElement item, TPriority priority)
29 | {
30 | lock (_sync)
31 | {
32 | while (_count >= Capacity) base.Dequeue();
33 |
34 | base.Enqueue(item, priority);
35 | }
36 | }
37 |
38 | ///
39 | /// Remove and return the item with max priority from the queue.
40 | ///
41 | ///
42 | public override TElement Dequeue()
43 | {
44 | TElement item;
45 | lock (_sync)
46 | {
47 | item = base.Dequeue();
48 | }
49 |
50 | return item;
51 | }
52 |
53 | ///
54 | /// Remove all items from the queue. Capacity is not changed.
55 | ///
56 | public override void Clear()
57 | {
58 | lock (_sync)
59 | {
60 | base.Clear();
61 | }
62 | }
63 |
64 | ///
65 | /// Returns true if there is at least one item, which is equal to given.
66 | /// TD.Equals is used to compare equality.
67 | ///
68 | public override bool Contains(TElement item)
69 | {
70 | lock (_sync)
71 | {
72 | return base.Contains(item);
73 | }
74 | }
75 |
76 | ///
77 | /// Returns the first element in the queue (element with max priority) without removing it from the queue.
78 | ///
79 | ///
80 | public override TElement Peek()
81 | {
82 | lock (_sync)
83 | {
84 | return base.Peek();
85 | }
86 | }
87 |
88 | ///
89 | /// Update priority of the first occurrence of the given item
90 | ///
91 | /// Item, which priority should be updated.
92 | /// New priority
93 | ///
94 | public override void UpdatePriority(TElement item, TPriority priority)
95 | {
96 | lock (_sync)
97 | {
98 | base.UpdatePriority(item, priority);
99 | }
100 | }
101 |
102 | public override IEnumerator GetEnumerator()
103 | {
104 | Node[] nodesCopy;
105 | lock (_sync)
106 | {
107 | nodesCopy = CopyNodes();
108 | }
109 | // queue copy is created to be able to extract the items in the priority order
110 | // using the already existing dequeue method
111 | // (because they are not exactly in priority order in the underlying array)
112 | var queueCopy = new ConcurrentFixedSizePriorityQueue(nodesCopy, nodesCopy.Length - 1, _comparer);
113 |
114 | return new PriorityQueueEnumerator(queueCopy);
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue/ConcurrentPriorityQueue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace ConcurrentPriorityQueue
5 | {
6 | ///
7 | /// Heap-based implementation of concurrent priority queue. Max priority is on top of the heap.
8 | ///
9 | public class ConcurrentPriorityQueue : AbstractPriorityQueue where TPriority : IComparable
10 | {
11 | private readonly object _sync = new object();
12 | private const int _defaultCapacity = 10;
13 | private const int _shrinkRatio = 4;
14 | internal const int _resizeFactor = 2;
15 |
16 | private int _shrinkBound;
17 |
18 | ///
19 | /// Create a new instance of priority queue with given initial capacity.
20 | ///
21 | /// Initial queue capacity. Should be greater than 0.
22 | /// Priority comparer. Default for type will be used unless custom is provided.
23 | public ConcurrentPriorityQueue(int capacity, IComparer comparer = null):base(capacity, comparer)
24 | {
25 | _shrinkBound = Capacity / _shrinkRatio;
26 | }
27 |
28 | ///
29 | /// Create a new instance of priority queue with default initial capacity.
30 | ///
31 | /// Priority comparer. Default for type will be used unless custom is provided.
32 | public ConcurrentPriorityQueue(IComparer comparer = null): this(_defaultCapacity, comparer)
33 | {
34 | }
35 |
36 | private ConcurrentPriorityQueue(Node[] nodes, int count, NodeComparer comparer):base(nodes, count, comparer)
37 | { }
38 |
39 | ///
40 | /// Add new item to the queue.
41 | ///
42 | public override void Enqueue(TElement item, TPriority priority)
43 | {
44 | lock (_sync)
45 | {
46 | if (_count == Capacity) GrowCapacity();
47 |
48 | base.Enqueue(item, priority);
49 | }
50 | }
51 |
52 | ///
53 | /// Remove and return the item with max priority from the queue.
54 | ///
55 | ///
56 | public override TElement Dequeue()
57 | {
58 | TElement item;
59 | lock (_sync)
60 | {
61 | item = base.Dequeue();
62 |
63 | if (_count <= _shrinkBound && _count > _defaultCapacity) ShrinkCapacity();
64 | }
65 |
66 | return item;
67 | }
68 |
69 | ///
70 | /// Trim queue capacity to count of items in the queue
71 | ///
72 | public void Trim()
73 | {
74 | lock (_sync)
75 | {
76 | int newCapacity = _count;
77 | Array.Resize(ref _nodes, newCapacity + 1); // first element is at position 1
78 | _shrinkBound = newCapacity / _shrinkRatio;
79 | }
80 | }
81 |
82 | ///
83 | /// Remove all items from the queue. Capacity is not changed.
84 | ///
85 | public override void Clear()
86 | {
87 | lock (_sync)
88 | {
89 | base.Clear();
90 | }
91 | }
92 |
93 | ///
94 | /// Returns true if there is at least one item, which is equal to given.
95 | /// TD.Equals is used to compare equality.
96 | ///
97 | public override bool Contains(TElement item)
98 | {
99 | lock (_sync)
100 | {
101 | return base.Contains(item);
102 | }
103 | }
104 |
105 | ///
106 | /// Returns the first element in the queue (element with max priority) without removing it from the queue.
107 | ///
108 | ///
109 | public override TElement Peek()
110 | {
111 | lock (_sync)
112 | {
113 | return base.Peek();
114 | }
115 | }
116 |
117 | ///
118 | /// Update priority of the first occurrence of the given item
119 | ///
120 | /// Item, which priority should be updated.
121 | /// New priority
122 | ///
123 | public override void UpdatePriority(TElement item, TPriority priority)
124 | {
125 | lock (_sync)
126 | {
127 | base.UpdatePriority(item, priority);
128 | }
129 | }
130 |
131 | public override IEnumerator GetEnumerator()
132 | {
133 | Node[] nodesCopy;
134 | lock (_sync)
135 | {
136 | nodesCopy = CopyNodes();
137 | }
138 | // queue copy is created to be able to extract the items in the priority order
139 | // using the already existing dequeue method
140 | // (because they are not exactly in priority order in the underlying array)
141 | var queueCopy = new ConcurrentPriorityQueue(nodesCopy, nodesCopy.Length - 1, _comparer);
142 |
143 | return new PriorityQueueEnumerator(queueCopy);
144 | }
145 |
146 | private void GrowCapacity()
147 | {
148 | int newCapacity = Capacity * _resizeFactor;
149 | Array.Resize(ref _nodes, newCapacity + 1); // first element is at position 1
150 | _shrinkBound = newCapacity / _shrinkRatio;
151 | }
152 |
153 | private void ShrinkCapacity()
154 | {
155 | int newCapacity = Capacity / _resizeFactor;
156 | Array.Resize(ref _nodes, newCapacity + 1); // first element is at position 1
157 | _shrinkBound = newCapacity / _shrinkRatio;
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue/ConcurrentPriorityQueue.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {4C5838DE-B350-4268-83DE-D94CC0738F00}
8 | Library
9 | Properties
10 | ConcurrentPriorityQueue
11 | ConcurrentPriorityQueue
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue/ConcurrentPriorityQueue.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PriorityQueue
5 | 0.1.0
6 | Concurrent Priority Queue
7 | Denis Shulepov
8 | Denis Shulepov
9 | https://github.com/dshulepov/ConcurrentPriorityQueue/blob/master/License.md
10 | https://github.com/dshulepov/ConcurrentPriorityQueue
11 | false
12 | C# implementation of generic heap-based concurrent priority queue for .NET
13 | Copyright 2015
14 | PriorityQueue Heap Concurrent Generics
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue/IPriorityQueue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace ConcurrentPriorityQueue
5 | {
6 | public interface IPriorityQueue : IEnumerable where TPriority : IComparable
7 | {
8 | int Capacity { get; }
9 | int Count { get; }
10 | bool Contains(TElement item);
11 | void Enqueue(TElement item, TPriority priority);
12 | TElement Dequeue();
13 | TElement Peek();
14 | void Clear();
15 | void UpdatePriority(TElement item, TPriority priority);
16 | }
17 | }
--------------------------------------------------------------------------------
/ConcurrentPriorityQueue/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ConcurrentPriorityQueue")]
9 | [assembly: AssemblyDescription("C# implementation of generic heap-based concurrent priority queue for .NET")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Denis Shulepov")]
12 | [assembly: AssemblyProduct("ConcurrentPriorityQueue")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("f0cdc9a7-8044-4c35-b33e-34e90af917a7")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.1.0.0")]
36 | [assembly: AssemblyFileVersion("0.1.0.0")]
37 |
38 | [assembly: InternalsVisibleTo("ConcurrentPriorityQueueTests")]
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | #The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Denis Shulepov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Not supported
2 | Moved to https://github.com/dshulepov/DataStructures
3 |
4 | ##ConcurrentPriorityQueue
5 |
6 | C# implementation of generic heap-based concurrent [priority queue](http://en.wikipedia.org/wiki/Priority_queue) for .NET
7 |
8 | >Priority queue is an abstract data type which is like a regular queue or
9 | >stack data structure, but where additionally each element has a "priority"
10 | >associated with it. In a priority queue, an element with high priority is
11 | >served before an element with low priority. If two elements have the same
12 | >priority, they are served according to their order in the queue.
13 |
14 | ###Features
15 | - Generic
16 | - Concurrent (i.e. supports multi-threading)
17 | - Performant ( `O(n) = nlog(n)` for enqueue and dequeue )
18 | - Fixed size support (items are dequeued before enqueueing when queue is full)
19 | - Resizing support (queue grows and shrinks depending on the number of items)
20 | - Alter priority of already enqueued item
21 |
22 | ###NuGet
23 | - Install `PM> Install-Package PriorityQueue`
24 | - [https://www.nuget.org/packages/PriorityQueue](https://www.nuget.org/packages/PriorityQueue/)
25 |
26 | ###Applications
27 |
28 | - Bandwidth management
29 | - [Discrete event simulation](http://en.wikipedia.org/wiki/Discrete_event_simulation)
30 | - [Huffman coding](http://en.wikipedia.org/wiki/Huffman_coding)
31 | - [Best-first search algorithms](http://en.wikipedia.org/wiki/Best-first_search)
32 | - [ROAM triangulation algorithm](http://en.wikipedia.org/wiki/ROAM)
33 | - [Dijkstra's algorithm](http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
34 | - [Prim's Algorithm](http://en.wikipedia.org/wiki/Prim%27s_algorithm) for Minimum Spanning Tree
35 |
36 | ##License
37 | Released under the MIT license.
38 |
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/AssertEx.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace ConcurrentPriorityQueueTests
5 | {
6 | public static class AssertEx
7 | {
8 | public static T Throws(Action action) where T:Exception
9 | {
10 | try
11 | {
12 | action();
13 | }
14 | catch (Exception ex)
15 | {
16 | var expectedException = ex as T;
17 | if (expectedException != null) return expectedException;
18 | Assert.Fail(string.Format("Expected {0}, but {1} was thrown.", typeof(T), ex.GetType().Name));
19 | }
20 | Assert.Fail("Failed to thow exception {0}", typeof(T));
21 | return null;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/ConcurrentPriorityQueueTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {B67A2F2D-08FB-4458-9299-A323941AAF19}
8 | Library
9 | Properties
10 | ConcurrentPriorityQueueTests
11 | ConcurrentPriorityQueueTests
12 | v4.5
13 | 512
14 | ..\..\
15 | true
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | {4c5838de-b350-4268-83de-d94cc0738f00}
53 | ConcurrentPriorityQueue
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
64 |
65 |
66 |
67 |
74 |
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/ExecWithStats.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading;
6 |
7 | namespace ConcurrentPriorityQueueTests
8 | {
9 | internal sealed class ExecWithStats
10 | {
11 | private readonly Action _action;
12 | private readonly CountdownEvent _doneEvent;
13 | private readonly int _count;
14 | private int _exceptionsCount;
15 | private readonly string _name;
16 | private readonly TimeSpan[] _times;
17 | private readonly Stopwatch _stopwatch;
18 |
19 | public ExecWithStats(string name, int count, Action action, CountdownEvent doneEvent = null)
20 | {
21 | _action = action;
22 | _doneEvent = doneEvent;
23 | _name = name;
24 | _count = count;
25 | _exceptionsCount = 0;
26 | _times = new TimeSpan[_count];
27 | _stopwatch = new Stopwatch();
28 | }
29 |
30 | public void Exec()
31 | {
32 | for (var i = 0; i < _count; i++)
33 | {
34 | _stopwatch.Start();
35 | try
36 | {
37 | _action();
38 | }
39 | catch (Exception)
40 | {
41 | _exceptionsCount++;
42 | }
43 | finally
44 | {
45 | _stopwatch.Stop();
46 | _times[i] = _stopwatch.Elapsed;
47 | _stopwatch.Reset();
48 | }
49 | }
50 | if (_doneEvent != null) _doneEvent.Signal();
51 | }
52 |
53 | public Stats GetStats()
54 | {
55 | return new Stats(_name, _count, _times, _exceptionsCount);
56 | }
57 |
58 | public sealed class Stats
59 | {
60 | public readonly TimeSpan Min; // Minimum
61 | public readonly TimeSpan Med; // Median
62 | public readonly TimeSpan Avg; // Average
63 | public readonly TimeSpan Max; // Maximum
64 | public readonly int ExceptionsCount;
65 | public readonly string Name;
66 |
67 | public Stats(string name, int count, TimeSpan[] times, int exceptionsCount)
68 | {
69 | Array.Sort(times, 0, count);
70 |
71 | Min = times[0];
72 | Med = times[count/2];
73 | Max = times[count-1];
74 | Avg = new TimeSpan((int) times.Average(_ => _.Ticks));
75 | ExceptionsCount = exceptionsCount;
76 | Name = name;
77 | }
78 | }
79 |
80 | internal static void OutputStatsSummary(IEnumerable execStats)
81 | {
82 | foreach (var stats in execStats.Select(_ => _.GetStats())
83 | .GroupBy(_ => _.Name)
84 | .Select(
85 | _ =>
86 | new
87 | {
88 | Name = _.Key,
89 | ThreadsCount = _.Count(),
90 | Min = _.Min(x => x.Min),
91 | Med = _.OrderBy(x => x.Med).ElementAt(_.Count() / 2).Med,
92 | Avg = new TimeSpan((int)_.Average(x => x.Avg.Ticks)),
93 | Max = _.Max(x => x.Max),
94 | Exceptions = _.Sum(x => x.ExceptionsCount)
95 | }))
96 | {
97 | Console.WriteLine("Name:{0}", stats.Name);
98 | Console.WriteLine("ThreadsCount:{0}", stats.ThreadsCount);
99 | Console.WriteLine("Time: Min: {0}, Median:{1}, Average: {2}, Max {3}", stats.Min, stats.Med, stats.Avg,
100 | stats.Max);
101 | Console.WriteLine("Exceptions: {0}\n", stats.Exceptions);
102 | }
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/FunctionalTests/AbstractPriorityQueueTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Linq;
4 | using ConcurrentPriorityQueue;
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 |
7 | namespace ConcurrentPriorityQueueTests.FunctionalTests
8 | {
9 | public abstract class AbstractPriorityQueueTests
10 | {
11 | protected abstract IPriorityQueue CreateQueue(int capacity) where TPriority : IComparable;
12 |
13 | [TestMethod]
14 | public void Initialize()
15 | {
16 | var target = CreateQueue(5);
17 |
18 | Assert.AreEqual(0, target.Count);
19 | Assert.AreEqual(5, target.Capacity);
20 |
21 | AssertEx.Throws(() => target = CreateQueue(0));
22 | }
23 |
24 | [TestMethod]
25 | public void Enqueue()
26 | {
27 | var target = CreateQueue(4);
28 |
29 | Assert.AreEqual(0, target.Count);
30 |
31 | target.Enqueue("a", 1);
32 | Assert.AreEqual(1, target.Count);
33 |
34 | target.Enqueue("b", 2);
35 | Assert.AreEqual(2, target.Count);
36 |
37 | target.Enqueue("c", 3);
38 | Assert.AreEqual(3, target.Count);
39 |
40 | target.Enqueue("d", 4);
41 | Assert.AreEqual(4, target.Count);
42 | }
43 |
44 | [TestMethod]
45 | public void Dequeue()
46 | {
47 | var target = CreateQueue(4);
48 | target.Enqueue("a", 1);
49 | target.Enqueue("b", 4);
50 | target.Enqueue("c", 3);
51 | target.Enqueue("d", 2);
52 |
53 | Assert.AreEqual(4, target.Count);
54 | Assert.AreEqual("b", target.Dequeue());
55 | Assert.AreEqual(3, target.Count);
56 | Assert.AreEqual("c", target.Dequeue());
57 | Assert.AreEqual(2, target.Count);
58 | Assert.AreEqual("d", target.Dequeue());
59 | Assert.AreEqual(1, target.Count);
60 | Assert.AreEqual("a", target.Dequeue());
61 | Assert.AreEqual(0, target.Count);
62 |
63 | AssertEx.Throws(() => target.Dequeue());
64 | }
65 |
66 | [TestMethod]
67 | public void Peek()
68 | {
69 | var target = CreateQueue(4);
70 |
71 | AssertEx.Throws(() => target.Peek());
72 |
73 | target.Enqueue("a", 1);
74 |
75 | Assert.AreEqual("a", target.Peek());
76 | Assert.AreEqual(1, target.Count);
77 |
78 | target.Enqueue("b", 4);
79 |
80 | Assert.AreEqual("b", target.Peek());
81 | Assert.AreEqual(2, target.Count);
82 |
83 | target.Enqueue("c", 3);
84 |
85 | Assert.AreEqual("b", target.Peek());
86 | Assert.AreEqual(3, target.Count);
87 |
88 | target.Enqueue("d", 2);
89 |
90 | Assert.AreEqual("b", target.Peek());
91 | Assert.AreEqual(4, target.Count);
92 |
93 | target.Dequeue();
94 |
95 | Assert.AreEqual("c", target.Peek());
96 | Assert.AreEqual(3, target.Count);
97 | }
98 |
99 | [TestMethod]
100 | public void GetEnumerator()
101 | {
102 | var target = CreateQueue(6);
103 | target.Enqueue("a", 1);
104 | target.Enqueue("b", 2);
105 | target.Enqueue("c", 3);
106 | target.Enqueue("d", 4);
107 | target.Enqueue("e", 5);
108 |
109 | var enumerator1 = target.GetEnumerator();
110 | var enumerator2 = ((IEnumerable)target).GetEnumerator();
111 |
112 | Assert.AreNotEqual(enumerator1, enumerator2);
113 |
114 | string result = string.Join(",", target);
115 | Assert.AreEqual("e,d,c,b,a", result);
116 | }
117 |
118 | [TestMethod]
119 | public void UpdatePriority()
120 | {
121 | var target = CreateQueue(4);
122 | target.Enqueue("a", 1);
123 | target.Enqueue("b", 2);
124 | target.Enqueue("c", 3);
125 | target.Enqueue("d", 4);
126 |
127 | Assert.AreEqual("d", target.Peek());
128 | Assert.AreEqual("d,c,b,a", string.Join(",", target));
129 |
130 | target.UpdatePriority("a", 5);
131 | Assert.AreEqual("a", target.Peek());
132 | Assert.AreEqual("a,d,c,b", string.Join(",", target));
133 |
134 | target.UpdatePriority("d", 1);
135 | Assert.AreEqual("a", target.Peek());
136 | Assert.AreEqual("a,c,b,d", string.Join(",", target));
137 |
138 | AssertEx.Throws(() => target.UpdatePriority("x", 23));
139 | }
140 |
141 | [TestMethod]
142 | public void Clear()
143 | {
144 | var target = CreateQueue(7);
145 |
146 | target.Enqueue("a", 7);
147 | target.Enqueue("b", 6);
148 | target.Enqueue("c", 5);
149 |
150 | target.Clear();
151 | Assert.AreEqual(0, target.Count);
152 | Assert.AreEqual(0, target.ToArray().Length);
153 | }
154 |
155 |
156 | [TestMethod]
157 | public void Contains()
158 | {
159 | var target = CreateQueue(5);
160 | target.Enqueue("a", 1);
161 | target.Enqueue("b", 1);
162 | target.Enqueue("c", 1);
163 | target.Enqueue("a", 1);
164 | target.Enqueue(null, 4);
165 |
166 | Assert.IsTrue(target.Contains("a"));
167 | Assert.IsTrue(target.Contains("b"));
168 | Assert.IsTrue(target.Contains("c"));
169 | Assert.IsFalse(target.Contains("d"));
170 | Assert.IsTrue(target.Contains(null));
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/FunctionalTests/ConcurrentFixedSizePriorityQueueTests.cs:
--------------------------------------------------------------------------------
1 | using ConcurrentPriorityQueue;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace ConcurrentPriorityQueueTests.FunctionalTests
5 | {
6 | [TestClass]
7 | public class ConcurrentFixedSizePriorityQueueTests : AbstractPriorityQueueTests
8 | {
9 | protected override IPriorityQueue CreateQueue(int capacity)
10 | {
11 | return new ConcurrentFixedSizePriorityQueue(capacity);
12 | }
13 |
14 | [TestMethod]
15 | public void EnqueueAfterExceedingCapacity()
16 | {
17 | var target = new ConcurrentFixedSizePriorityQueue(3);
18 |
19 | Assert.AreEqual(0, target.Count);
20 |
21 | target.Enqueue("a", 1);
22 | target.Enqueue("b", 2);
23 | target.Enqueue("c", 3);
24 |
25 | target.Enqueue("d", 4);
26 | Assert.AreEqual(3, target.Count);
27 | string result = string.Join(",", target);
28 | Assert.AreEqual("d,b,a", result);
29 |
30 | target.Enqueue("e", 0);
31 | Assert.AreEqual(3, target.Count);
32 | result = string.Join(",", target);
33 | Assert.AreEqual("b,a,e", result);
34 | }
35 |
36 | [TestMethod]
37 | public void EnqueueDequeue()
38 | {
39 | var target = new ConcurrentFixedSizePriorityQueue(7);
40 |
41 | target.Enqueue("a", 7);
42 | target.Enqueue("b", 6);
43 | target.Enqueue("c", 5);
44 | Assert.AreEqual("a", target.Dequeue());
45 | target.Enqueue("d", 4);
46 | Assert.AreEqual("b", target.Dequeue());
47 | target.Enqueue("a", 7);
48 | Assert.AreEqual("a", target.Dequeue());
49 | Assert.AreEqual("c", target.Dequeue());
50 | Assert.AreEqual("d", target.Dequeue());
51 | Assert.AreEqual(0, target.Count);
52 | }
53 |
54 | [TestMethod]
55 | public void EqualsForObjects()
56 | {
57 | var target = new ConcurrentFixedSizePriorityQueue(4);
58 |
59 | Assert.IsTrue(target.Equals("a", "a"));
60 | Assert.IsFalse(target.Equals("a", "b"));
61 | Assert.IsFalse(target.Equals("a", null));
62 | Assert.IsFalse(target.Equals(null, "a"));
63 | Assert.IsTrue(target.Equals(null, null));
64 | }
65 |
66 | [TestMethod]
67 | public void EqualsForValueTypes()
68 | {
69 | var target = new ConcurrentFixedSizePriorityQueue(4);
70 |
71 | Assert.IsTrue(target.Equals(1, 1));
72 | Assert.IsFalse(target.Equals(1, 2));
73 | Assert.IsFalse(target.Equals(2, 1));
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/FunctionalTests/ConcurrentPriorityQueueTests.cs:
--------------------------------------------------------------------------------
1 | using ConcurrentPriorityQueue;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace ConcurrentPriorityQueueTests.FunctionalTests
5 | {
6 | [TestClass]
7 | public class ConcurrentPriorityQueueTests : AbstractPriorityQueueTests
8 | {
9 | protected override IPriorityQueue CreateQueue(int capacity)
10 | {
11 | return new ConcurrentPriorityQueue(capacity);
12 | }
13 |
14 | [TestMethod]
15 | public void EnqueueAfterExceedingCapacity()
16 | {
17 | var target = new ConcurrentPriorityQueue(3);
18 |
19 | Assert.AreEqual(0, target.Count);
20 |
21 | target.Enqueue("a", 1);
22 | target.Enqueue("b", 2);
23 | target.Enqueue("c", 3);
24 |
25 | target.Enqueue("d", 4);
26 | Assert.AreEqual(4, target.Count);
27 | Assert.AreEqual(6, target.Capacity);
28 | string result = string.Join(",", target);
29 | Assert.AreEqual("d,c,b,a", result);
30 |
31 | target.Enqueue("e", 0);
32 | Assert.AreEqual(5, target.Count);
33 | Assert.AreEqual(6, target.Capacity);
34 | result = string.Join(",", target);
35 | Assert.AreEqual("d,c,b,a,e", result);
36 | }
37 |
38 | [TestMethod]
39 | public void DequeueWithTooBigCapacity()
40 | {
41 | var target = new ConcurrentPriorityQueue(50);
42 |
43 | for (var i = 0; i < 13; i++)
44 | {
45 | target.Enqueue("a", i);
46 | }
47 | Assert.AreEqual(50, target.Capacity);
48 | Assert.AreEqual(13, target.Count);
49 | target.Dequeue();
50 | Assert.AreEqual(25, target.Capacity);
51 | Assert.AreEqual(12, target.Count);
52 | target.Dequeue();
53 | Assert.AreEqual(25, target.Capacity);
54 | Assert.AreEqual(11, target.Count);
55 | }
56 |
57 | [TestMethod]
58 | public void EnqueueDequeue()
59 | {
60 | var target = new ConcurrentPriorityQueue(7);
61 |
62 | target.Enqueue("a", 7);
63 | target.Enqueue("b", 6);
64 | target.Enqueue("c", 5);
65 | Assert.AreEqual("a", target.Dequeue());
66 | target.Enqueue("d", 4);
67 | Assert.AreEqual("b", target.Dequeue());
68 | target.Enqueue("a", 7);
69 | Assert.AreEqual("a", target.Dequeue());
70 | Assert.AreEqual("c", target.Dequeue());
71 | Assert.AreEqual("d", target.Dequeue());
72 | Assert.AreEqual(0, target.Count);
73 | }
74 |
75 | [TestMethod]
76 | public void Trim()
77 | {
78 | var target = new ConcurrentPriorityQueue(2);
79 | int expectedCapacity = target.Capacity;
80 | for (var i = 0; i < 10; i++)
81 | {
82 | if (target.Count == target.Capacity) expectedCapacity = expectedCapacity * ConcurrentPriorityQueue._resizeFactor;
83 | target.Enqueue("a", i);
84 | }
85 | Assert.AreEqual(expectedCapacity, target.Capacity);
86 | string items = string.Join(",", target);
87 | target.Trim();
88 | Assert.AreEqual(10, target.Capacity);
89 | Assert.AreEqual(items, string.Join(",", target));
90 | }
91 |
92 | [TestMethod]
93 | public void EqualsForObjects()
94 | {
95 | var target = new ConcurrentPriorityQueue(4);
96 |
97 | Assert.IsTrue(target.Equals("a", "a"));
98 | Assert.IsFalse(target.Equals("a", "b"));
99 | Assert.IsFalse(target.Equals("a", null));
100 | Assert.IsFalse(target.Equals(null, "a"));
101 | Assert.IsTrue(target.Equals(null, null));
102 | }
103 |
104 | [TestMethod]
105 | public void EqualsForValueTypes()
106 | {
107 | var target = new ConcurrentPriorityQueue(4);
108 |
109 | Assert.IsTrue(target.Equals(1, 1));
110 | Assert.IsFalse(target.Equals(1, 2));
111 | Assert.IsFalse(target.Equals(2, 1));
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/PerformanceTests/ConcurrentFixedSizePriorityQueueTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using ConcurrentPriorityQueue;
7 | using Microsoft.VisualStudio.TestTools.UnitTesting;
8 |
9 | namespace ConcurrentPriorityQueueTests.PerformanceTests
10 | {
11 | [TestClass]
12 | public class ConcurrentFixedSizePriorityQueueTests
13 | {
14 |
15 | [TestMethod]
16 | public void SingleThreadTiming()
17 | {
18 | const int capacity = 1000000;
19 | var target = new ConcurrentFixedSizePriorityQueue(capacity);
20 | var watcher = new Stopwatch();
21 |
22 | watcher.Start();
23 | for (int i = 0; i < capacity; i++)
24 | {
25 | target.Enqueue("a", 1);
26 | }
27 | watcher.Stop();
28 | Assert.AreEqual(capacity, target.Count);
29 | Assert.AreEqual(capacity, target.Capacity);
30 | Console.WriteLine("Enqueue {0} elements: {1}", capacity, watcher.Elapsed);
31 |
32 | watcher.Restart();
33 | // ReSharper disable once UnusedVariable
34 | var enumerator = target.GetEnumerator();
35 | watcher.Stop();
36 | Console.WriteLine("Get enumerator for {0} elements: {1}", capacity, watcher.Elapsed);
37 |
38 | watcher.Restart();
39 | for (int i = 0; i < capacity; i++)
40 | {
41 | target.Dequeue();
42 | }
43 | watcher.Stop();
44 | Assert.AreEqual(0, target.Count);
45 | Console.WriteLine("Dequeue {0} elements: {1}", capacity, watcher.Elapsed);
46 |
47 | watcher.Start();
48 | for (int i = 0; i < 2 * capacity; i++)
49 | {
50 | target.Enqueue("a", 1);
51 | }
52 | watcher.Stop();
53 | Assert.AreEqual(capacity, target.Count);
54 | Assert.AreEqual(capacity, target.Capacity);
55 | Console.WriteLine("Enqueue twice the capacity of {0} elements: {1}", capacity, watcher.Elapsed);
56 | }
57 |
58 | [TestMethod]
59 | public void MultiThreadEnqueue()
60 | {
61 | const int capacity = 1000000;
62 | const int threadsCount = 100;
63 | const int count = capacity / threadsCount;
64 | var target = new ConcurrentFixedSizePriorityQueue(capacity);
65 |
66 | var execStats = new ExecWithStats[threadsCount];
67 |
68 | var watcher = new Stopwatch();
69 |
70 | // several threads enqueue elements
71 | watcher.Start();
72 | Parallel.For(0, threadsCount, index =>
73 | {
74 | execStats[index] = new ExecWithStats(string.Format("Enqueue {0}", count), count, () => target.Enqueue("a", new DateTime()));
75 | execStats[index].Exec();
76 | });
77 |
78 | watcher.Stop();
79 | Assert.AreEqual(capacity, target.Count);
80 | Console.WriteLine("{0} threads each enqueue {1} elements. total time: {2}\n", threadsCount, count, watcher.Elapsed);
81 | ExecWithStats.OutputStatsSummary(execStats);
82 |
83 | // several threads dequeue elements
84 | watcher.Start();
85 | Parallel.For(0, threadsCount, index =>
86 | {
87 | execStats[index] = new ExecWithStats(string.Format("Dequeue {0}", count), count, () => target.Dequeue());
88 | execStats[index].Exec();
89 | });
90 | watcher.Stop();
91 | Assert.AreEqual(0, target.Count);
92 | Console.WriteLine("\n{0} threads each dequeue {1} elements. total time: {2}\n", threadsCount, count, watcher.Elapsed);
93 | ExecWithStats.OutputStatsSummary(execStats);
94 |
95 | // several threads enqueue double amount of elements
96 | // so on the second half each enqueue will have to do a dequeue because the queue will be full
97 | watcher.Start();
98 | Parallel.For(0, threadsCount, index =>
99 | {
100 | execStats[index] = new ExecWithStats(string.Format("Enqueue {0}", 2 * count), 2 * count, () => target.Enqueue("a", new DateTime()));
101 | execStats[index].Exec();
102 | });
103 | watcher.Stop();
104 | Assert.AreEqual(capacity, target.Count);
105 | Console.WriteLine("\n{0} threads each enqueue {1} elements. total time: {2}\n", threadsCount, 2 * count, watcher.Elapsed);
106 | ExecWithStats.OutputStatsSummary(execStats);
107 | }
108 |
109 | [TestMethod]
110 | public void RaceWithStats()
111 | {
112 | const int capacity = 1000000;
113 | const int threadsCount = 100;
114 | const int count = capacity / threadsCount;
115 | var target = new ConcurrentFixedSizePriorityQueue(capacity);
116 | var execStats = new List();
117 | var threadWait = new CountdownEvent(threadsCount);
118 |
119 | // odd threads will enqueue elements, while even threads will dequeue
120 | // obviously there will be a race condition and especially in the beginning dequeue will throw, because queue will often be empty
121 | // the total number of exceptions on dequeue threads will correspond the the number of items left in the queue
122 |
123 | for (var i = 0; i < threadsCount; i++)
124 | {
125 | ExecWithStats exec;
126 | if (i % 2 != 0)
127 | {
128 | exec = new ExecWithStats(string.Format("Enqueue {0} elements", count), count, () => target.Enqueue("a", new DateTime()), threadWait);
129 | }
130 | else
131 | {
132 | exec = new ExecWithStats(string.Format("Dequeue {0} elements", count), count, () => target.Dequeue(), threadWait);
133 | }
134 |
135 | execStats.Add(exec);
136 |
137 | var thread = new Thread(() => exec.Exec());
138 | thread.Start();
139 | }
140 |
141 | // Wait for all threads in pool to calculate.
142 | threadWait.Wait();
143 |
144 | // Output stats summary
145 | ExecWithStats.OutputStatsSummary(execStats);
146 |
147 | // Output queue state
148 | Console.WriteLine("Queue count:{0}, capacity:{1}", target.Count, target.Capacity);
149 |
150 | // Un-comment for a detailed list of stats
151 | //Console.WriteLine("---------------------");
152 | //foreach (var execStat in execStats)
153 | //{
154 | // var stats = execStat.GetStats();
155 | // Console.WriteLine("Name:{0}, Min: {1}, Median: {2}, Max {3}, Exceptions: {4}", stats.Name, stats.Min, stats.Med, stats.Max, stats.ExceptionsCount);
156 | //}
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/PerformanceTests/ConcurrentPriorityQueueTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using ConcurrentPriorityQueue;
7 | using Microsoft.VisualStudio.TestTools.UnitTesting;
8 |
9 | namespace ConcurrentPriorityQueueTests.PerformanceTests
10 | {
11 | [TestClass]
12 | public class ConcurrentPriorityQueueTests
13 | {
14 | [TestMethod]
15 | public void SingleThreadTiming()
16 | {
17 | const int count = 1000000;
18 | var target = new ConcurrentPriorityQueue(2);
19 | var watcher = new Stopwatch();
20 |
21 | watcher.Start();
22 | for (int i = 0; i < count; i++)
23 | {
24 | target.Enqueue("a", 1);
25 | }
26 | watcher.Stop();
27 | Assert.AreEqual(count, target.Count);
28 | Console.WriteLine("Enqueue {0} elements: {1}", count, watcher.Elapsed);
29 |
30 | watcher.Restart();
31 | // ReSharper disable once UnusedVariable
32 | var enumerator = target.GetEnumerator();
33 | watcher.Stop();
34 | Console.WriteLine("Get enumerator for {0} elements: {1}", count, watcher.Elapsed);
35 |
36 | watcher.Restart();
37 | for (int i = 0; i < count; i++)
38 | {
39 | target.Dequeue();
40 | }
41 | watcher.Stop();
42 | Assert.AreEqual(0, target.Count);
43 | Console.WriteLine("Dequeue {0} elements: {1}", count, watcher.Elapsed);
44 |
45 | watcher.Start();
46 | for (int i = 0; i < 2*count; i++)
47 | {
48 | target.Enqueue("a", 1);
49 | }
50 | watcher.Stop();
51 | Assert.AreEqual(2*count, target.Count);
52 | Console.WriteLine("Enqueue twice the capacity of {0} elements: {1}", count, watcher.Elapsed);
53 | }
54 |
55 | [TestMethod]
56 | public void MultiThreadEnqueue()
57 | {
58 | const int capacity = 1000000;
59 | const int threadsCount = 100;
60 | const int count = capacity/threadsCount;
61 | var target = new ConcurrentPriorityQueue();
62 |
63 | var execStats = new ExecWithStats[threadsCount];
64 |
65 | var watcher = new Stopwatch();
66 |
67 | // several threads enqueue elements
68 | watcher.Start();
69 | Parallel.For(0, threadsCount, index =>
70 | {
71 | execStats[index] = new ExecWithStats(string.Format("Enqueue {0}", count), count, () => target.Enqueue("a", new DateTime()));
72 | execStats[index].Exec();
73 | });
74 |
75 | watcher.Stop();
76 | Assert.AreEqual(capacity, target.Count);
77 | Console.WriteLine("{0} threads each enqueue {1} elements. total time: {2}\n", threadsCount, count, watcher.Elapsed);
78 | ExecWithStats.OutputStatsSummary(execStats);
79 |
80 | // several threads dequeue elements
81 | watcher.Start();
82 | Parallel.For(0, threadsCount, index =>
83 | {
84 | execStats[index] = new ExecWithStats(string.Format("Dequeue {0}", count), count, () => target.Dequeue());
85 | execStats[index].Exec();
86 | });
87 | watcher.Stop();
88 | Assert.AreEqual(0, target.Count);
89 | Console.WriteLine("\n{0} threads each dequeue {1} elements. total time: {2}\n", threadsCount, count, watcher.Elapsed);
90 | ExecWithStats.OutputStatsSummary(execStats);
91 | }
92 |
93 | [TestMethod]
94 | public void RaceWithStats()
95 | {
96 | const int capacity = 1000000;
97 | const int threadsCount = 100;
98 | const int count = capacity/threadsCount;
99 | var target = new ConcurrentPriorityQueue();
100 | var execStats = new List();
101 | var threadWait = new CountdownEvent(threadsCount);
102 |
103 | // odd threads will enqueue elements, while even threads will dequeue
104 | // obviously there will be a race condition and especially in the beginning dequeue will throw, because queue will often be empty
105 | // the total number of exceptions on dequeue threads will correspond the the number of items left in the queue
106 |
107 | for (var i = 0; i < threadsCount; i++)
108 | {
109 | ExecWithStats exec;
110 | if (i%2 != 0)
111 | {
112 | exec = new ExecWithStats(string.Format("Enqueue {0} elements", count), count, () => target.Enqueue("a", new DateTime()), threadWait);
113 | }
114 | else
115 | {
116 | exec = new ExecWithStats(string.Format("Dequeue {0} elements", count), count, () => target.Dequeue(), threadWait);
117 | }
118 |
119 | execStats.Add(exec);
120 |
121 | var thread = new Thread(() => exec.Exec());
122 | thread.Start();
123 | }
124 |
125 | // Wait for all threads in pool to calculate.
126 | threadWait.Wait();
127 |
128 | // Output stats summary
129 | ExecWithStats.OutputStatsSummary(execStats);
130 |
131 | // Output queue state
132 | Console.WriteLine("Queue count:{0}, capacity:{1}", target.Count, target.Capacity);
133 |
134 | // Un-comment for a detailed list of stats
135 | //Console.WriteLine("---------------------");
136 | //foreach (var execStat in execStats)
137 | //{
138 | // var stats = execStat.GetStats();
139 | // Console.WriteLine("Name:{0}, Min: {1}, Median: {2}, Max {3}, Exceptions: {4}", stats.Name, stats.Min, stats.Med, stats.Max, stats.ExceptionsCount);
140 | //}
141 | }
142 | }
143 | }
--------------------------------------------------------------------------------
/Tests/ConcurrentPriorityQueueTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ConcurrentPriorityQueueTests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ConcurrentPriorityQueueTests")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("58bae800-d2fa-4a9d-afd7-d537897b93fa")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------