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