├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE.txt ├── LockFreeDoublyLinkedList.sln ├── README.md ├── src ├── Counter.cs ├── ILockFreeDoublyLinkedList.cs ├── ILockFreeDoublyLinkedListNode.cs ├── LockFreeDoublyLinkedList.cs ├── LockFreeDoublyLinkedList.csproj ├── LockFreeDoublyLinkedListAdditions.cs └── ThreadingAdditions.cs └── test ├── Counter.cs ├── LinqHelper.cs ├── Program.cs ├── Tests ├── Test001.cs ├── Test001.lfdllOperationExecutor.cs ├── Test001.linkedListOperationExecutor.cs ├── Test001.operationExecutionInfo.cs ├── Test001.operationTiming.cs └── Test001_ │ ├── ExecutionState.cs │ ├── IOperationResultComparer.cs │ ├── LfdllExecutionState.cs │ ├── LinkedListExecutionState.cs │ ├── LinkedListItem.cs │ ├── ListItemData.cs │ ├── ObjectIdGenerator.cs │ ├── Operation.cs │ ├── OperationResultComparerCreators │ ├── IOperationResultComparerCreator.cs │ └── SeedAcceptingOperationComparerCreator.cs │ ├── OperationResultComparers │ ├── BoolReturningOperationComparer.cs │ ├── ItemDataReturningOperationComparer.cs │ ├── NodeReturningOperationComparer.cs │ └── VoidOperationComparer.cs │ ├── Operations │ ├── BoolReturningOperation.cs │ ├── CompareExchangeValue.cs │ ├── GetValue.cs │ ├── InsertAfter.cs │ ├── InsertAfterIf.cs │ ├── InsertBefore.cs │ ├── ItemDataReturningOperation.cs │ ├── Next.cs │ ├── NodeCreationOperation.cs │ ├── NodeReturningOperation.cs │ ├── PopRightNode.cs │ ├── Previous.cs │ ├── PushLeft.cs │ ├── PushRight.cs │ ├── Remove.cs │ ├── SelectRandomKnownNode.cs │ ├── SetValue.cs │ └── VoidOperation.cs │ └── TestIterationParameters.cs └── test.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | *.bak 3 | .~lock.* 4 | *~ 5 | *# 6 | /.project 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | build/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | 28 | # Roslyn cache directories 29 | *.ide/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | #NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | *_i.c 45 | *_p.c 46 | *_i.h 47 | *.ilk 48 | *.meta 49 | *.obj 50 | *.pch 51 | *.pdb 52 | *.pgc 53 | *.pgd 54 | *.rsp 55 | *.sbr 56 | *.tlb 57 | *.tli 58 | *.tlh 59 | *.tmp 60 | *.tmp_proj 61 | *.log 62 | *.vspscc 63 | *.vssscc 64 | .builds 65 | *.pidb 66 | *.svclog 67 | *.scc 68 | 69 | # Chutzpah Test files 70 | _Chutzpah* 71 | 72 | # Visual C++ cache files 73 | ipch/ 74 | *.aps 75 | *.ncb 76 | *.opensdf 77 | *.sdf 78 | *.cachefile 79 | 80 | # Visual Studio profiler 81 | *.psess 82 | *.vsp 83 | *.vspx 84 | 85 | # TFS 2012 Local Workspace 86 | $tf/ 87 | 88 | # Guidance Automation Toolkit 89 | *.gpState 90 | 91 | # ReSharper is a .NET coding add-in 92 | _ReSharper*/ 93 | *.[Rr]e[Ss]harper 94 | *.DotSettings.user 95 | 96 | # JustCode is a .NET coding addin-in 97 | .JustCode 98 | 99 | # TeamCity is a build add-in 100 | _TeamCity* 101 | 102 | # DotCover is a Code Coverage Tool 103 | *.dotCover 104 | 105 | # NCrunch 106 | _NCrunch_* 107 | .*crunch*.local.xml 108 | 109 | # MightyMoose 110 | *.mm.* 111 | AutoTest.Net/ 112 | 113 | # Web workbench (sass) 114 | .sass-cache/ 115 | 116 | # Installshield output folder 117 | [Ee]xpress/ 118 | 119 | # DocProject is a documentation generator add-in 120 | DocProject/buildhelp/ 121 | DocProject/Help/*.HxT 122 | DocProject/Help/*.HxC 123 | DocProject/Help/*.hhc 124 | DocProject/Help/*.hhk 125 | DocProject/Help/*.hhp 126 | DocProject/Help/Html2 127 | DocProject/Help/html 128 | 129 | # Click-Once directory 130 | publish/ 131 | 132 | # Publish Web Output 133 | *.[Pp]ublish.xml 134 | *.azurePubxml 135 | # TODO: Comment the next line if you want to checkin your web deploy settings 136 | # but database connection strings (with potential passwords) will be unencrypted 137 | *.pubxml 138 | *.publishproj 139 | 140 | # NuGet Packages 141 | *.nupkg 142 | # The packages folder can be ignored because of Package Restore 143 | **/packages/* 144 | # except build/, which is used as an MSBuild target. 145 | !**/packages/build/ 146 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 147 | #!**/packages/repositories.config 148 | 149 | # Windows Azure Build Output 150 | csx/ 151 | *.build.csdef 152 | 153 | # Windows Store app package directory 154 | AppPackages/ 155 | 156 | # Others 157 | sql/ 158 | *.Cache 159 | ClientBin/ 160 | [Ss]tyle[Cc]op.* 161 | ~$* 162 | *~ 163 | *.dbmdl 164 | *.dbproj.schemaview 165 | *.pfx 166 | *.publishsettings 167 | node_modules/ 168 | 169 | # RIA/Silverlight projects 170 | Generated_Code/ 171 | 172 | # Backup & report files from converting an old project file 173 | # to a newer Visual Studio version. Backup files are not needed, 174 | # because we have git ;-) 175 | _UpgradeReport_Files/ 176 | Backup*/ 177 | UpgradeLog*.XML 178 | UpgradeLog*.htm 179 | 180 | # SQL Server files 181 | *.mdf 182 | *.ldf 183 | 184 | # Business Intelligence projects 185 | *.rdl.data 186 | *.bim.layout 187 | *.bim_*.settings 188 | 189 | # Microsoft Fakes 190 | FakesAssemblies/ 191 | 192 | docfx_project/ 193 | docs/ 194 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/test/bin/Debug/netcoreapp2.0/test.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/test", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false, 19 | "internalConsoleOptions": "openOnSessionStart" 20 | }, 21 | { 22 | "name": ".NET Core Attach", 23 | "type": "coreclr", 24 | "request": "attach", 25 | "processId": "${command:pickProcess}" 26 | } 27 | ,] 28 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/LockFreeDoublyLinkedList.sln" 11 | ], 12 | "problemMatcher": "$msCompile", 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | } 17 | }, 18 | { 19 | "label": "release build", 20 | "command": "dotnet", 21 | "type": "process", 22 | "args": [ 23 | "pack", 24 | "--configuration=Release", 25 | "${workspaceFolder}/LockFreeDoublyLinkedList.sln" 26 | ], 27 | "problemMatcher": "$msCompile", 28 | "group": "build" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /LockFreeDoublyLinkedList.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LockFreeDoublyLinkedList", "src\LockFreeDoublyLinkedList.csproj", "{BD42123A-D4D1-45AF-8F7C-975C3116F7B8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{07E88CB1-A843-4062-991D-BFD1EC08D9E1}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(SolutionProperties) = preSolution 16 | HideSolutionNode = FALSE 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {BD42123A-D4D1-45AF-8F7C-975C3116F7B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {BD42123A-D4D1-45AF-8F7C-975C3116F7B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {BD42123A-D4D1-45AF-8F7C-975C3116F7B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {BD42123A-D4D1-45AF-8F7C-975C3116F7B8}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {07E88CB1-A843-4062-991D-BFD1EC08D9E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {07E88CB1-A843-4062-991D-BFD1EC08D9E1}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {07E88CB1-A843-4062-991D-BFD1EC08D9E1}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {07E88CB1-A843-4062-991D-BFD1EC08D9E1}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LockFreeDoublyLinkedList 2 | ======================== 3 | 4 | A lock-free doubly linked list implementation in C#. 5 | 6 | This work is based on the concept of the paper “Lock-free deques and doubly linked lists” 7 | by Håkan Sundell and Philippas Tsigas (2008). 8 | 9 | This project is also available as a [NuGet package](https://www.nuget.org/packages/LockFreeDoublyLinkedList/). 10 | 11 | To create the NuGet package from source, install [.Net Core](https://www.microsoft.com/net/learn/get-started/windows) and run the following command: 12 | 13 | dotnet pack --configuration=Release 14 | 15 | Tests can be run using: 16 | 17 | dotnet run --project=test/test.csproj 18 | -------------------------------------------------------------------------------- /src/Counter.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | #if SynchronizedLfdll 23 | using System.Text; 24 | using System.Threading; 25 | 26 | namespace LockFreeDoublyLinkedLists 27 | { 28 | /// 29 | /// A threadsafe Counter. 30 | /// 31 | public class Counter 32 | { 33 | /// 34 | /// The current counter value. 35 | /// 36 | public long Current => Interlocked.Read(ref value); 37 | 38 | /// 39 | /// Inkrement the counter value. 40 | /// 41 | /// The new counter value. 42 | public long Count() 43 | { 44 | return Interlocked.Increment(ref value); 45 | } 46 | 47 | private long value = 0; 48 | } 49 | } 50 | #endif -------------------------------------------------------------------------------- /src/ILockFreeDoublyLinkedList.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | #if SynchronizedLfdll 17 | #if Verbose 18 | #define SynchronizedLfdll_Verbose 19 | #endif 20 | #endif 21 | 22 | using System; 23 | using System.Collections.Generic; 24 | using System.Linq; 25 | using System.Threading; 26 | using System.Threading.Tasks; 27 | 28 | namespace LockFreeDoublyLinkedLists 29 | { 30 | /// 31 | /// A lock free doubly linked list for high concurrency. 32 | /// 33 | /// The type of the values. 34 | public interface ILockFreeDoublyLinkedList : IEnumerable 35 | where T : class 36 | { 37 | #if SynchronizedLfdll 38 | ThreadLocal NextStepWaitHandle { get; } 39 | AutoResetEvent StepCompletedWaitHandle { get; } 40 | #if SynchronizedLfdll_Verbose 41 | void LogNode(ILockFreeDoublyLinkedListNode inode); 42 | #endif 43 | #endif 44 | 45 | /// 46 | /// The dummy head node (leftmost). 47 | /// 48 | ILockFreeDoublyLinkedListNode Head { get; } 49 | /// 50 | /// The dummy tail node (rightmost). 51 | /// 52 | ILockFreeDoublyLinkedListNode Tail { get; } 53 | 54 | /// 55 | /// Removes the rightmost non-dummy node, if it exists. 56 | /// 57 | /// 58 | /// null, if the list is empty; else the removed node. 59 | /// 60 | ILockFreeDoublyLinkedListNode PopRightNode(); 61 | 62 | /// 63 | /// Inserts a new node at the head position. 64 | /// 65 | /// The initial value of the new node. 66 | /// The new inserted node. 67 | ILockFreeDoublyLinkedListNode PushLeft(T value); 68 | 69 | /// 70 | /// Inserts a new node at the tail position. 71 | /// 72 | /// The initial value of the new node. 73 | /// The new inserted node. 74 | ILockFreeDoublyLinkedListNode PushRight(T value); 75 | 76 | #if PopRight 77 | /// Removes the rightmost non-dummy node if it exists 78 | /// and returns its value after the removal. 79 | /// 80 | /// 81 | /// null, if the list is empty; else a Tuple<T>, 82 | /// which contains the value of the removed node. 83 | /// 84 | [Obsolete("This method is not atomar. For clarity use PopRightNode instead.", 85 | false)] 86 | Tuple PopRight(); 87 | #endif 88 | #if PopLeft 89 | /// 90 | /// Removes the leftmost non-dummy node and returns its value. 91 | /// 92 | /// The value of the removed node. 93 | [Obsolete("This method is not supported.", false)] 94 | Tuple PopLeft(); 95 | #endif 96 | } 97 | } -------------------------------------------------------------------------------- /src/ILockFreeDoublyLinkedListNode.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace LockFreeDoublyLinkedLists 23 | { 24 | /// 25 | /// A node of a LockFreeDoublyLinkedList instance. 26 | /// 27 | public interface ILockFreeDoublyLinkedListNode where T : class 28 | { 29 | #if DEBUG 30 | long Id { get; } 31 | 32 | #endif 33 | /// 34 | /// The value stored by the current node instance. 35 | /// 36 | T Value { get; set; } 37 | 38 | /// 39 | /// Returns the corresponding LockFreeDoublyLinkedList 40 | /// of the current node instance. 41 | /// 42 | ILockFreeDoublyLinkedList List { get; } 43 | 44 | /// 45 | /// The right neighbor node or null, 46 | /// if the current node is the dummy tail node. 47 | /// 48 | ILockFreeDoublyLinkedListNode Next { get; } 49 | /// 50 | /// The left neighbor node or null, 51 | /// if the current node is the dummy head node. 52 | /// 53 | ILockFreeDoublyLinkedListNode Prev { get; } 54 | 55 | /// 56 | /// Returns, if the current node has been removed. 57 | /// 58 | bool Removed { get; } 59 | 60 | /// 61 | /// Returns, if the current node is 62 | /// the dummy head or dummy tail node of List. 63 | /// 64 | bool IsDummyNode { get; } 65 | 66 | /// 67 | /// Inserts a new node left beside the current node instance. 68 | /// 69 | /// The initial value of the new node. 70 | /// The new inserted node. 71 | ILockFreeDoublyLinkedListNode InsertBefore(T newValue); 72 | /// 73 | /// Inserts a new node right beside the current node instance. 74 | /// 75 | /// The initial value of the new node. 76 | /// The new inserted node. 77 | ILockFreeDoublyLinkedListNode InsertAfter(T newValue); 78 | 79 | /// 80 | /// Inserts a new node after the current node instance 81 | /// if and only if: 82 | /// 83 | /// 84 | /// the current value is no dummy node, 85 | /// 86 | /// 87 | /// the current node has not yet been deleted 88 | /// 89 | /// 90 | /// and the 91 | /// for the current node’s value is satisfied. 92 | /// 93 | /// 94 | /// 95 | /// 96 | /// The new value for the node to insert. 97 | /// 98 | /// 99 | /// The condition which the current node’s value 100 | /// needs to satisfy for the insertion to take place. 101 | /// 102 | /// 103 | /// The inserted node, 104 | /// if the insertion could be performed. 105 | /// null else. 106 | /// 107 | ILockFreeDoublyLinkedListNode InsertAfterIf(T newValue, Func condition); 108 | 109 | /// 110 | /// Removes the current node 111 | /// from the corresponding LockFreeDoublyLinkedList instance. 112 | /// 113 | /// 114 | /// Whether the current node has been deleted by this thread. 115 | /// Also returns false if the current node is a dummy node. 116 | /// 117 | bool Remove(); 118 | 119 | /// 120 | /// Compares the Value property to comparand 121 | /// and replaces the prevalent value with newValue 122 | /// if and only if comparand reference equals the prevalent value. 123 | /// This happens in a single atomic operation. 124 | /// 125 | /// The value to write. 126 | /// The original value 127 | /// 128 | /// The prevalent value, 129 | /// regardless of whether the replacement took place. 130 | /// 131 | T CompareExchangeValue(T newValue, T comparand); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/LockFreeDoublyLinkedList.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | #if SynchronizedLfdll 17 | #if Verbose 18 | #define SynchronizedLfdll_Verbose 19 | #endif 20 | #endif 21 | 22 | using System; 23 | using System.Collections; 24 | using System.Collections.Generic; 25 | using System.Linq; 26 | using System.Threading; 27 | using System.Threading.Tasks; 28 | 29 | namespace LockFreeDoublyLinkedLists 30 | { 31 | /// 32 | /// Provides an implementation of a LockFree DoublyLinkedList. 33 | /// 34 | public static class LockFreeDoublyLinkedList 35 | { 36 | /// 37 | /// Creates a new . 38 | /// 39 | /// 40 | /// The type of the elements in the created 41 | /// . 42 | /// 43 | /// The newly created instance. 44 | public static ILockFreeDoublyLinkedList Create() 45 | where T : class 46 | { 47 | return new lockFreeDoublyLinkedList(); 48 | } 49 | 50 | /// 51 | /// Creates a new . 52 | /// 53 | /// 54 | /// The type of the elements in the created 55 | /// . 56 | /// 57 | /// 58 | /// The initial elements in the created list. 59 | /// 60 | /// The newly created instance. 61 | public static ILockFreeDoublyLinkedList Create( 62 | IEnumerable initial) 63 | where T : class 64 | { 65 | return new lockFreeDoublyLinkedList(initial); 66 | } 67 | 68 | #region private 69 | private static bool compareExchangeNodeLink(ref nodeLink location, 70 | nodeLink value, nodeLink comparandByValue) where T : class 71 | { 72 | return ThreadingAdditions 73 | .ConditionalCompareExchange(ref location, 74 | value, original => original.Equals(comparandByValue)); 75 | } 76 | 77 | private static bool compareExchangeNodeLinkInPair( 78 | ref valueNodeLinkPair location, nodeLink newLink, 79 | nodeLink comparadByValue) where T : class 80 | { 81 | Thread.MemoryBarrier(); 82 | T currentValue = location.Value; 83 | return ThreadingAdditions 84 | .ConditionalCompareExchange(ref location, 85 | new valueNodeLinkPair(currentValue, newLink), 86 | original => 87 | original.Link.Equals(comparadByValue) 88 | && ReferenceEquals(original.Value, currentValue)); 89 | } 90 | 91 | private class lockFreeDoublyLinkedList : 92 | ILockFreeDoublyLinkedList where T : class 93 | { 94 | public readonly node HeadNode; 95 | public readonly node TailNode; 96 | 97 | public IEnumerator GetEnumerator() 98 | { 99 | ILockFreeDoublyLinkedListNode current = Head; 100 | while (true) 101 | { 102 | current = current.Next; 103 | if (current == Tail) 104 | yield break; 105 | yield return current.Value; 106 | } 107 | } 108 | 109 | public ILockFreeDoublyLinkedListNode Head => HeadNode; 110 | 111 | public ILockFreeDoublyLinkedListNode Tail => TailNode; 112 | 113 | public ILockFreeDoublyLinkedListNode PushLeft(T value) 114 | { 115 | SpinWait spin = new SpinWait(); 116 | node node = new node(this); 117 | node prev = HeadNode; 118 | 119 | Thread.MemoryBarrier(); 120 | EnterTestSynchronizedBlock(); 121 | node next = prev.Next_.Link.P; 122 | #if SynchronizedLfdll_Verbose 123 | Console.WriteLine("PushLeft {0} Step 0", value); 124 | Console.WriteLine("next ="); 125 | LogNode(next); 126 | #endif 127 | LeaveTestSynchronizedBlock(); 128 | 129 | while (true) 130 | { 131 | /* node has not been made public yet, 132 | * so no synchronization constructs are necessary. */ 133 | node.Prev_ = new nodeLink(prev, false); 134 | node.Next_ = new valueNodeLinkPair(value, new nodeLink(next, false)); 135 | 136 | EnterTestSynchronizedBlock(); 137 | bool b = compareExchangeNodeLinkInPair(ref prev.Next_, 138 | new nodeLink(node, false), 139 | new nodeLink(next, false)); 140 | #if SynchronizedLfdll_Verbose 141 | Console.WriteLine("PushLeft {0} Step 1", value); 142 | Console.WriteLine( 143 | "compareExchangeNlIp(ref prev.Next_, {0}, {1}) = {2}", 144 | NodeLinkDescription(new nodeLink(node, false)), 145 | NodeLinkDescription(new nodeLink(next, false)), 146 | b); 147 | Console.WriteLine("prev = "); 148 | LogNode(prev); 149 | LogState(); 150 | #endif 151 | LeaveTestSynchronizedBlock(); 152 | 153 | if (b) 154 | break; 155 | 156 | /* Not necessary because of preceding compareExchange. */ 157 | // Thread.MemoryBarrier(); 158 | 159 | EnterTestSynchronizedBlock(); 160 | next = prev.Next_.Link.P; 161 | #if SynchronizedLfdll_Verbose 162 | Console.WriteLine("PushLeft {0} Step 2", value); 163 | Console.WriteLine("next ="); 164 | LogNode(next); 165 | #endif 166 | LeaveTestSynchronizedBlock(); 167 | 168 | spin.SpinOnce(); 169 | } 170 | pushEnd(node, next, spin); 171 | Thread.MemoryBarrier(); 172 | return node; 173 | } 174 | 175 | public ILockFreeDoublyLinkedListNode PushRight(T value) 176 | { 177 | SpinWait spin = new SpinWait(); 178 | node node = new node(this); 179 | node next = TailNode; 180 | 181 | Thread.MemoryBarrier(); 182 | EnterTestSynchronizedBlock(); 183 | node prev = next.Prev_.P; 184 | #if SynchronizedLfdll_Verbose 185 | Console.WriteLine("PushRight {0} Step 0", value); 186 | Console.WriteLine("prev = "); 187 | LogNode(prev); 188 | #endif 189 | LeaveTestSynchronizedBlock(); 190 | 191 | while (true) 192 | { 193 | /* node has not been made public yet, 194 | * so no threading constructs are necessary. */ 195 | node.Prev_ = new nodeLink(prev, false); 196 | node.Next_ = new valueNodeLinkPair(value, new nodeLink(next, false)); 197 | 198 | EnterTestSynchronizedBlock(); 199 | bool b = compareExchangeNodeLinkInPair(ref prev.Next_, 200 | new nodeLink(node, false), 201 | new nodeLink(next, false)); 202 | #if SynchronizedLfdll_Verbose 203 | Console.WriteLine("PushRight {0} Step 1", value); 204 | Console.WriteLine( 205 | "compareExchangeNlIp(ref prev.Next_, {0}, {1}) = {2}", 206 | NodeLinkDescription(new nodeLink(node, false)), 207 | NodeLinkDescription(new nodeLink(next, false)), 208 | b); 209 | Console.WriteLine("prev = "); 210 | LogNode(prev); 211 | LogState(); 212 | #endif 213 | LeaveTestSynchronizedBlock(); 214 | 215 | if (b) 216 | break; 217 | 218 | prev = CorrectPrev(prev, next); 219 | spin.SpinOnce(); 220 | } 221 | pushEnd(node, next, spin); 222 | Thread.MemoryBarrier(); 223 | return node; 224 | } 225 | 226 | #if PopLeft 227 | [Obsolete("This method is not supported.", false)] 228 | public Tuple PopLeft() 229 | { 230 | SpinWait spin = new SpinWait(); 231 | node prev = HeadNode; 232 | while (true) 233 | { 234 | Thread.MemoryBarrier(); 235 | EnterTestSynchronizedBlock(); 236 | node node = prev.Next_.Link.P; 237 | #if SynchronizedLfdll_Verbose 238 | Console.WriteLine("PopLeft Step 0"); 239 | Console.WriteLine("node = "); 240 | LogNode(node); 241 | #endif 242 | LeaveTestSynchronizedBlock(); 243 | 244 | if (node == TailNode) 245 | { 246 | Thread.MemoryBarrier(); 247 | return null; 248 | } 249 | 250 | Thread.MemoryBarrier(); 251 | EnterTestSynchronizedBlock(); 252 | nodeLink next = node.Next_.Link; 253 | #if SynchronizedLfdll_Verbose 254 | Console.WriteLine("PopLeft Step 1"); 255 | Console.WriteLine("next = {0}", NodeLinkDescription(next)); 256 | #endif 257 | LeaveTestSynchronizedBlock(); 258 | 259 | if (next.D) 260 | { 261 | SetMark(ref node.Prev_); 262 | 263 | EnterTestSynchronizedBlock(); 264 | // ReSharper disable once UnusedVariable 265 | bool b1 = compareExchangeNodeLinkInPair(ref prev.Next_, 266 | new nodeLink(next.P, false), 267 | (nodeLink)node); 268 | #if SynchronizedLfdll_Verbose 269 | Console.WriteLine("PopLeft Step 2"); 270 | Console.WriteLine("compareExchangeNlIp(ref prev.Next_, {0}, {1}) = {2}", 271 | NodeLinkDescription(new nodeLink(next.P, false)), 272 | NodeLinkDescription((nodeLink)node), 273 | b1); 274 | Console.WriteLine("prev = "); 275 | LogNode(prev); 276 | LogState(); 277 | #endif 278 | LeaveTestSynchronizedBlock(); 279 | 280 | continue; 281 | } 282 | 283 | EnterTestSynchronizedBlock(); 284 | bool b = compareExchangeNodeLinkInPair(ref node.Next_, 285 | new nodeLink(next.P, true), next); 286 | #if SynchronizedLfdll_Verbose 287 | Console.WriteLine("PopLeft Step 3"); 288 | Console.WriteLine("compareExchangeNlIp(ref node.Next_, {0}, {1}) = {2}", 289 | NodeLinkDescription(new nodeLink(next.P, true)), 290 | NodeLinkDescription(next), 291 | b); 292 | Console.WriteLine("node = "); 293 | LogNode(node); 294 | LogState(); 295 | #endif 296 | LeaveTestSynchronizedBlock(); 297 | 298 | if (b) 299 | { 300 | CorrectPrev(prev, next.P); 301 | 302 | Thread.MemoryBarrier(); 303 | EnterTestSynchronizedBlock(); 304 | T value = node.Next_.Value; 305 | #if SynchronizedLfdll_Verbose 306 | Console.WriteLine("PopLeft Step 4"); 307 | Console.WriteLine("value = {0}", value); 308 | #endif 309 | LeaveTestSynchronizedBlock(); 310 | 311 | Thread.MemoryBarrier(); 312 | return new Tuple(value); 313 | } 314 | 315 | spin.SpinOnce(); 316 | } 317 | } 318 | #endif 319 | 320 | #if PopRight 321 | [Obsolete("This method is not atomar. For clarity use PopRightNode instead.", false)] 322 | public Tuple PopRight() 323 | { 324 | ILockFreeDoublyLinkedListNode node = PopRightNode(); 325 | if (node == null) 326 | return null; 327 | return Tuple.Create(node.Value); 328 | } 329 | #endif 330 | 331 | public ILockFreeDoublyLinkedListNode PopRightNode() 332 | { 333 | SpinWait spin = new SpinWait(); 334 | node next = TailNode; 335 | 336 | Thread.MemoryBarrier(); 337 | EnterTestSynchronizedBlock(); 338 | node node = next.Prev_.P; 339 | #if SynchronizedLfdll_Verbose 340 | Console.WriteLine("PopRightNode Step 0"); 341 | Console.WriteLine("node ="); 342 | LogNode(node); 343 | #endif 344 | LeaveTestSynchronizedBlock(); 345 | 346 | while (true) 347 | { 348 | 349 | Thread.MemoryBarrier(); 350 | EnterTestSynchronizedBlock(); 351 | bool b = !node.Next_.Link.Equals(new nodeLink(next, false)); 352 | #if SynchronizedLfdll_Verbose 353 | Console.WriteLine("PopRightNode Step 1"); 354 | Console.WriteLine("b = {0}", b); 355 | #endif 356 | LeaveTestSynchronizedBlock(); 357 | 358 | if (b) 359 | { 360 | node = CorrectPrev(node, next); 361 | continue; 362 | } 363 | 364 | if (node == HeadNode) 365 | { 366 | Thread.MemoryBarrier(); 367 | return null; 368 | } 369 | 370 | EnterTestSynchronizedBlock(); 371 | bool b1 = compareExchangeNodeLinkInPair(ref node.Next_, 372 | new nodeLink(next, true), 373 | new nodeLink(next, false)); 374 | #if SynchronizedLfdll_Verbose 375 | Console.WriteLine("PopRightNode Step 2"); 376 | Console.WriteLine( 377 | "compareExchangeNlIp(ref node.Next_, {0}, {1}) = {2}", 378 | NodeLinkDescription(new nodeLink(next, true)), 379 | NodeLinkDescription(new nodeLink(next, false)), 380 | b1); 381 | Console.WriteLine("node = "); 382 | LogNode(node); 383 | LogState(); 384 | #endif 385 | LeaveTestSynchronizedBlock(); 386 | 387 | if (b1) 388 | { 389 | /* Not necessary because of preceding compareExchange. */ 390 | // Thread.MemoryBarrier(); 391 | 392 | EnterTestSynchronizedBlock(); 393 | node prev = node.Prev_.P; 394 | #if SynchronizedLfdll_Verbose 395 | Console.WriteLine("PopRightNode Step 3"); 396 | Console.WriteLine("prev ="); 397 | LogNode(prev); 398 | #endif 399 | LeaveTestSynchronizedBlock(); 400 | 401 | CorrectPrev(prev, next); 402 | 403 | Thread.MemoryBarrier(); 404 | return node; 405 | } 406 | 407 | spin.SpinOnce(); 408 | } 409 | } 410 | 411 | public void SetMark(ref nodeLink link) 412 | { 413 | Thread.MemoryBarrier(); 414 | EnterTestSynchronizedBlock(); 415 | nodeLink node = link; 416 | #if SynchronizedLfdll_Verbose 417 | Console.WriteLine("SetMark Step 0"); 418 | Console.WriteLine("node = {0}", NodeLinkDescription(node)); 419 | #endif 420 | LeaveTestSynchronizedBlock(); 421 | 422 | while (true) 423 | { 424 | if (node.D) 425 | break; 426 | 427 | EnterTestSynchronizedBlock(); 428 | nodeLink prevalent = Interlocked.CompareExchange( 429 | ref link, new nodeLink(node.P, true), node); 430 | #if SynchronizedLfdll_Verbose 431 | Console.WriteLine("SetMark Step 1"); 432 | Console.WriteLine("compareExchange(link, {0}, {1}) = {2}", 433 | NodeLinkDescription(new nodeLink(node.P, true)), 434 | NodeLinkDescription(node), 435 | NodeLinkDescription(prevalent)); 436 | Console.WriteLine("link = {0}", NodeLinkDescription(link)); 437 | #endif 438 | LeaveTestSynchronizedBlock(); 439 | 440 | // ReSharper disable once PossibleUnintendedReferenceComparison 441 | if (prevalent == node) 442 | break; 443 | node = prevalent; 444 | } 445 | } 446 | 447 | public node CorrectPrev(node prev, node node) 448 | { 449 | // ReSharper disable once IntroduceOptionalParameters.Local 450 | return CorrectPrev(prev, node, new SpinWait()); 451 | } 452 | 453 | public node CorrectPrev(node prev, node node, SpinWait spin) 454 | { 455 | node lastLink = null; 456 | while (true) 457 | { 458 | 459 | Thread.MemoryBarrier(); 460 | EnterTestSynchronizedBlock(); 461 | nodeLink link1 = node.Prev_; 462 | #if SynchronizedLfdll_Verbose 463 | Console.WriteLine("CorrectPrev Step 0"); 464 | Console.WriteLine("link1 = {0}", 465 | NodeLinkDescription(link1)); 466 | #endif 467 | LeaveTestSynchronizedBlock(); 468 | 469 | if (link1.D) 470 | break; 471 | 472 | Thread.MemoryBarrier(); 473 | EnterTestSynchronizedBlock(); 474 | nodeLink prev2 = prev.Next_.Link; 475 | #if SynchronizedLfdll_Verbose 476 | Console.WriteLine("CorrectPrev Step 1"); 477 | Console.WriteLine("prev2 = {0}", 478 | NodeLinkDescription(prev2)); 479 | #endif 480 | LeaveTestSynchronizedBlock(); 481 | 482 | if (prev2.D) 483 | { 484 | if (lastLink != null) 485 | { 486 | SetMark(ref prev.Prev_); 487 | 488 | EnterTestSynchronizedBlock(); 489 | 490 | // ReSharper disable once UnusedVariable 491 | bool b1 = compareExchangeNodeLinkInPair(ref lastLink.Next_, 492 | (nodeLink)prev2.P, (nodeLink)prev); 493 | #if SynchronizedLfdll_Verbose 494 | Console.WriteLine("CorrectPrev Step 2"); 495 | Console.WriteLine( 496 | "compareExchangeNlIp(lastLink.Next_, {0}, {1}) = {2}", 497 | NodeLinkDescription((nodeLink)prev2.P), 498 | NodeLinkDescription((nodeLink)prev), 499 | b1); 500 | Console.WriteLine("lastLink = "); 501 | LogNode(lastLink); 502 | LogState(); 503 | #endif 504 | LeaveTestSynchronizedBlock(); 505 | 506 | prev = lastLink; 507 | lastLink = null; 508 | continue; 509 | } 510 | 511 | Thread.MemoryBarrier(); 512 | EnterTestSynchronizedBlock(); 513 | prev2 = prev.Prev_; 514 | #if SynchronizedLfdll_Verbose 515 | Console.WriteLine("CorrectPrev Step 3"); 516 | Console.WriteLine("prev2 = {0}", 517 | NodeLinkDescription(prev2)); 518 | #endif 519 | LeaveTestSynchronizedBlock(); 520 | 521 | /* A conversion would probably sometimes 522 | * lead to errors. */ 523 | prev = prev2.P; 524 | continue; 525 | } 526 | /* The paper simply states „Prev_ != node“, 527 | * but the types are different. 528 | * It is probably assumed 529 | * that the comaparison is performed as follows: 530 | * !(prev2.P == node && !prev2.D). 531 | * Since prev2.D is always false here, 532 | * simplification is possible. **/ 533 | if (prev2.P != node) 534 | { 535 | lastLink = prev; 536 | prev = prev2.P; 537 | continue; 538 | } 539 | 540 | EnterTestSynchronizedBlock(); 541 | bool b = compareExchangeNodeLink(ref node.Prev_, 542 | new nodeLink(prev, false), link1); 543 | #if SynchronizedLfdll_Verbose 544 | Console.WriteLine("CorrectPrev Step 4"); 545 | Console.WriteLine("compareExchangeNl(node.Prev_, {0}, {1}) = {2}", 546 | NodeLinkDescription(new nodeLink(prev, false)), 547 | NodeLinkDescription(link1), 548 | b); 549 | Console.WriteLine("node = "); 550 | LogNode(node); 551 | LogState(); 552 | #endif 553 | LeaveTestSynchronizedBlock(); 554 | 555 | if (b) 556 | { 557 | /* Not necessary because of preceding compareExchange. */ 558 | // Thread.MemoryBarrier(); 559 | 560 | EnterTestSynchronizedBlock(); 561 | bool b1 = prev.Prev_.D; 562 | #if SynchronizedLfdll_Verbose 563 | Console.WriteLine("CorrectPrev Step 5"); 564 | Console.WriteLine("b1 = {0}", b1); 565 | #endif 566 | LeaveTestSynchronizedBlock(); 567 | 568 | if (b1) 569 | continue; 570 | break; 571 | } 572 | 573 | spin.SpinOnce(); 574 | } 575 | return prev; 576 | } 577 | 578 | public void EnterTestSynchronizedBlock() 579 | { 580 | #if SynchronizedLfdll 581 | if (NextStepWaitHandle.IsValueCreated) 582 | { 583 | NextStepWaitHandle.Value.WaitOne(); 584 | 585 | #if SynchronizedLfdll_Verbose 586 | Console.WriteLine("({0}) Step {1}", 587 | Thread.CurrentThread.Name, StepCounter.Count()); 588 | #endif 589 | } 590 | #endif 591 | } 592 | 593 | public void LeaveTestSynchronizedBlock() 594 | { 595 | #if SynchronizedLfdll 596 | if (NextStepWaitHandle.IsValueCreated) 597 | StepCompletedWaitHandle.Set(); 598 | #endif 599 | } 600 | 601 | /// 602 | /// Creates a new empty lockFreeDoublyLinkedList. 603 | /// 604 | public lockFreeDoublyLinkedList() 605 | { 606 | HeadNode = new node(this); 607 | TailNode = new node(this); 608 | 609 | HeadNode.Prev_ = new nodeLink(null, false); 610 | HeadNode.Next_ = new valueNodeLinkPair(null, new nodeLink(TailNode, false)); 611 | TailNode.Prev_ = new nodeLink(HeadNode, false); 612 | TailNode.Next_ = new valueNodeLinkPair(null, new nodeLink(null, false)); 613 | Thread.MemoryBarrier(); 614 | } 615 | 616 | /// 617 | /// Creates a new lockFreeDoublyLinkedList 618 | /// which contains the contents of the enumeration initial. 619 | /// 620 | /// The enumeration to copy. 621 | public lockFreeDoublyLinkedList(IEnumerable initial) 622 | : this() 623 | { 624 | if (initial == null) 625 | throw new ArgumentNullException(nameof(initial)); 626 | foreach (T value in initial) 627 | PushRight(value); 628 | } 629 | 630 | #region private 631 | IEnumerator IEnumerable.GetEnumerator() 632 | { 633 | return GetEnumerator(); 634 | } 635 | 636 | private void pushEnd(node node, node next, SpinWait spin) 637 | { 638 | while (true) 639 | { 640 | Thread.MemoryBarrier(); 641 | EnterTestSynchronizedBlock(); 642 | nodeLink link1 = next.Prev_; 643 | #if SynchronizedLfdll_Verbose 644 | Console.WriteLine("pushEnd Step 0"); 645 | Console.WriteLine("link1 = {0}", 646 | NodeLinkDescription(link1)); 647 | #endif 648 | LeaveTestSynchronizedBlock(); 649 | 650 | bool b = link1.D; 651 | if (!b) 652 | { 653 | Thread.MemoryBarrier(); 654 | EnterTestSynchronizedBlock(); 655 | b |= !node.Next_.Link.Equals(new nodeLink(next, false)); 656 | #if SynchronizedLfdll_Verbose 657 | Console.WriteLine("pushEnd Step 1"); 658 | Console.WriteLine( 659 | "!node.Next_.Link.Equals(new nodeLink(next, false) = {0}", 660 | !node.Next_.Link.Equals(new nodeLink(next, false))); 661 | #endif 662 | LeaveTestSynchronizedBlock(); 663 | } 664 | if (b) 665 | break; 666 | 667 | EnterTestSynchronizedBlock(); 668 | bool b1 = compareExchangeNodeLink(ref next.Prev_, 669 | new nodeLink(node, false), link1); 670 | #if SynchronizedLfdll_Verbose 671 | Console.WriteLine("pushEnd Step 2"); 672 | Console.WriteLine("compareExchangeNl(next.Prev_, {0}, {1}) = {2}", 673 | NodeLinkDescription(new nodeLink(node, false)), 674 | NodeLinkDescription(link1), 675 | b1); 676 | Console.WriteLine("next = "); 677 | LogNode(next); 678 | LogState(); 679 | #endif 680 | LeaveTestSynchronizedBlock(); 681 | 682 | if (b1) 683 | { 684 | 685 | /* Not necessary because of preceding compareExchange. */ 686 | // Thread.MemoryBarrier(); 687 | 688 | EnterTestSynchronizedBlock(); 689 | bool b2 = node.Prev_.D; 690 | #if SynchronizedLfdll_Verbose 691 | Console.WriteLine("pushEnd Step 3"); 692 | Console.WriteLine("b1 = {0}", b2); 693 | #endif 694 | LeaveTestSynchronizedBlock(); 695 | 696 | if (b2) 697 | CorrectPrev(node, next); 698 | break; 699 | } 700 | 701 | spin.SpinOnce(); 702 | } 703 | } 704 | #endregion 705 | 706 | #if SynchronizedLfdll 707 | public ThreadLocal NextStepWaitHandle { get; } 708 | = new ThreadLocal(); 709 | 710 | public AutoResetEvent StepCompletedWaitHandle { get; } 711 | = new AutoResetEvent(false); 712 | 713 | public Counter StepCounter { get; } = new Counter(); 714 | 715 | #if SynchronizedLfdll_Verbose 716 | public void LogState() 717 | { 718 | node current = HeadNode; 719 | while (current != null) 720 | { 721 | LogNode(current); 722 | current = current.Next_.Link.P; 723 | } 724 | } 725 | 726 | public void LogNode(ILockFreeDoublyLinkedListNode inode) 727 | { 728 | node node = (node)inode; 729 | Console.WriteLine(nodeName(node)); 730 | if (node != null) 731 | { 732 | Console.WriteLine(" .Prev_ = " 733 | + NodeLinkDescription(node.Prev_)); 734 | Console.WriteLine(" .Next_ = " 735 | + ValueNodeLinkPairDescription(node.Next_)); 736 | } 737 | } 738 | #endif 739 | 740 | #if SynchronizedLfdll_Verbose 741 | private string nodeName(node node) 742 | { 743 | if (node == null) 744 | return "null"; 745 | if (node == HeadNode) 746 | return "HeadNode"; 747 | if (node == TailNode) 748 | return "TailNode"; 749 | return "Node " + node.Value; 750 | } 751 | 752 | public string NodeLinkDescription(nodeLink link) 753 | { 754 | return "(" + nodeName(link.P) 755 | + ", " + link.D + ")"; 756 | } 757 | 758 | public string ValueNodeLinkPairDescription 759 | (valueNodeLinkPair pair) 760 | { 761 | return "(" + pair.Value + ", " 762 | + NodeLinkDescription(pair.Link) + ")"; 763 | } 764 | 765 | // ReSharper disable once UnusedMember.Local 766 | private void logNodeLink(nodeLink link) 767 | { 768 | Console.WriteLine(NodeLinkDescription(link)); 769 | } 770 | 771 | public void LogValueNodeLinkPair(valueNodeLinkPair pair) 772 | { 773 | Console.WriteLine(ValueNodeLinkPairDescription(pair)); 774 | } 775 | #endif 776 | #endif // SynchronizedLfdll 777 | } 778 | 779 | private class node : ILockFreeDoublyLinkedListNode where T : class 780 | { 781 | // ReSharper disable once InconsistentNaming 782 | public valueNodeLinkPair Next_; 783 | 784 | // ReSharper disable once InconsistentNaming 785 | public nodeLink Prev_; 786 | 787 | // ReSharper disable once InconsistentNaming 788 | public readonly lockFreeDoublyLinkedList List_; 789 | 790 | #if DEBUG 791 | public long Id { get; } 792 | 793 | #endif 794 | 795 | public ILockFreeDoublyLinkedList List => List_; 796 | 797 | public bool IsDummyNode => 798 | this == List_.HeadNode || this == List_.TailNode; 799 | 800 | public T Value 801 | { 802 | get 803 | { 804 | if (IsDummyNode) 805 | throwIsDummyNodeException(); 806 | /* At the commented out code it is assumed 807 | * that Value_ is not allowed to be readout 808 | * once the node was deleted. 809 | * However, this behaviour does not seem useful. */ 810 | //T val = this.newValue; 811 | //if (this.Next_.D) 812 | // return default(T); 813 | 814 | Thread.MemoryBarrier(); 815 | T value = Next_.Value; 816 | Thread.MemoryBarrier(); 817 | return value; 818 | } 819 | set 820 | { 821 | if (IsDummyNode) 822 | throwIsDummyNodeException(); 823 | Thread.MemoryBarrier(); 824 | while (true) 825 | { 826 | valueNodeLinkPair currentPair = Next_; 827 | if (Interlocked.CompareExchange( 828 | ref Next_, 829 | new valueNodeLinkPair(value, currentPair.Link), 830 | currentPair) == currentPair) 831 | { 832 | break; 833 | } 834 | } 835 | } 836 | } 837 | 838 | public ILockFreeDoublyLinkedListNode Next 839 | { 840 | get 841 | { 842 | node cursor = this; 843 | bool b = toNext(ref cursor); 844 | Thread.MemoryBarrier(); 845 | if (b) 846 | return cursor; 847 | return null; 848 | } 849 | } 850 | 851 | public ILockFreeDoublyLinkedListNode Prev 852 | { 853 | get 854 | { 855 | node cursor = this; 856 | bool b = toPrev(ref cursor); 857 | Thread.MemoryBarrier(); 858 | if (b) 859 | return cursor; 860 | return null; 861 | } 862 | } 863 | 864 | public bool Removed 865 | { 866 | get 867 | { 868 | Thread.MemoryBarrier(); 869 | bool result = Next_.Link.D; 870 | Thread.MemoryBarrier(); 871 | return result; 872 | } 873 | } 874 | 875 | public ILockFreeDoublyLinkedListNode InsertBefore(T newValue) 876 | { 877 | ILockFreeDoublyLinkedListNode result = insertBefore(newValue, this); 878 | Thread.MemoryBarrier(); 879 | return result; 880 | } 881 | 882 | public ILockFreeDoublyLinkedListNode InsertAfter(T newValue) 883 | { 884 | ILockFreeDoublyLinkedListNode result = insertAfter(newValue, this); 885 | Thread.MemoryBarrier(); 886 | return result; 887 | } 888 | 889 | public ILockFreeDoublyLinkedListNode InsertAfterIf(T newValue, Func condition) 890 | { 891 | if (IsDummyNode) 892 | return null; 893 | 894 | SpinWait spin = new SpinWait(); 895 | node cursor = this; 896 | node node = new node(List_); 897 | node prev = cursor; 898 | node next; 899 | while (true) 900 | { 901 | 902 | Thread.MemoryBarrier(); 903 | List_.EnterTestSynchronizedBlock(); 904 | valueNodeLinkPair nextLink = prev.Next_; 905 | #if SynchronizedLfdll_Verbose 906 | Console.WriteLine("insertAfterIf Step 0"); 907 | Console.WriteLine("nextLink ="); 908 | List_.LogValueNodeLinkPair(nextLink); 909 | #endif 910 | List_.LeaveTestSynchronizedBlock(); 911 | 912 | next = nextLink.Link.P; 913 | node.Prev_ = new nodeLink(prev, false); 914 | node.Next_ = new valueNodeLinkPair(newValue, 915 | new nodeLink(next, false)); 916 | 917 | bool cexSuccess; 918 | valueNodeLinkPair currentPair = nextLink; 919 | while (true) 920 | { 921 | if (!condition(currentPair.Value)) 922 | { 923 | Thread.MemoryBarrier(); 924 | return null; 925 | } 926 | if (!currentPair.Link.Equals( 927 | new nodeLink(next, false))) 928 | { 929 | cexSuccess = false; 930 | break; 931 | } 932 | 933 | List_.EnterTestSynchronizedBlock(); 934 | valueNodeLinkPair prevalent 935 | = Interlocked.CompareExchange 936 | (ref cursor.Next_, 937 | new valueNodeLinkPair(currentPair.Value, 938 | new nodeLink(node, false)), 939 | currentPair); 940 | #if SynchronizedLfdll_Verbose 941 | Console.WriteLine("InsertAfterIf Step 1"); 942 | Console.WriteLine( 943 | "CompareExchange(ref cursor.Next_, {0}, {1}) = {2}", 944 | new valueNodeLinkPair(currentPair.Value, 945 | new nodeLink(node, false)), 946 | currentPair, 947 | prevalent); 948 | Console.WriteLine("cursor ="); 949 | List_.LogNode(cursor); 950 | #endif 951 | List_.LeaveTestSynchronizedBlock(); 952 | 953 | if (ReferenceEquals(prevalent, currentPair)) 954 | { 955 | cexSuccess = true; 956 | break; 957 | } 958 | currentPair = prevalent; 959 | } 960 | 961 | if (cexSuccess) 962 | break; 963 | 964 | if (currentPair.Link.D) 965 | { 966 | Thread.MemoryBarrier(); 967 | return null; 968 | } 969 | spin.SpinOnce(); 970 | } 971 | List_.CorrectPrev(prev, next); 972 | Thread.MemoryBarrier(); 973 | return node; 974 | } 975 | 976 | 977 | public bool Remove() // out T lastValue 978 | { 979 | if (IsDummyNode) 980 | { 981 | // lastValue = default(T); 982 | return false; 983 | } 984 | while (true) 985 | { 986 | 987 | Thread.MemoryBarrier(); 988 | List_.EnterTestSynchronizedBlock(); 989 | nodeLink next = Next_.Link; 990 | #if SynchronizedLfdll_Verbose 991 | Console.WriteLine("Remove Step 0"); 992 | Console.WriteLine("next = {0}", List_.NodeLinkDescription(next)); 993 | #endif 994 | List_.LeaveTestSynchronizedBlock(); 995 | 996 | if (next.D) 997 | { 998 | // lastValue = default(T); 999 | Thread.MemoryBarrier(); 1000 | return false; 1001 | } 1002 | 1003 | List_.EnterTestSynchronizedBlock(); 1004 | bool b = compareExchangeNodeLinkInPair(ref Next_, 1005 | new nodeLink(next.P, true), next); 1006 | #if SynchronizedLfdll_Verbose 1007 | Console.WriteLine("Remove Step 1"); 1008 | Console.WriteLine("compareExchangeNl(ref this.Next_, {0}, {1}) = {2}", 1009 | List_.NodeLinkDescription(new nodeLink(next.P, true)), 1010 | List_.NodeLinkDescription(next), 1011 | b); 1012 | Console.WriteLine("this ="); 1013 | List_.LogNode(this); 1014 | List_.LogState(); 1015 | #endif 1016 | List_.LeaveTestSynchronizedBlock(); 1017 | 1018 | if (b) 1019 | { 1020 | nodeLink prev; 1021 | while (true) 1022 | { 1023 | 1024 | /* Not necessary because of preceding compareExchange. */ 1025 | // Thread.MemoryBarrier(); 1026 | 1027 | List_.EnterTestSynchronizedBlock(); 1028 | prev = Prev_; 1029 | #if SynchronizedLfdll_Verbose 1030 | Console.WriteLine("Remove Step 2"); 1031 | Console.WriteLine("prev = {0}", List_.NodeLinkDescription(Prev_)); 1032 | #endif 1033 | List_.LeaveTestSynchronizedBlock(); 1034 | 1035 | if (prev.D) 1036 | break; 1037 | 1038 | List_.EnterTestSynchronizedBlock(); 1039 | bool b1 = compareExchangeNodeLink(ref Prev_, 1040 | new nodeLink(prev.P, true), prev); 1041 | #if SynchronizedLfdll_Verbose 1042 | Console.WriteLine("Remove Step 3"); 1043 | Console.WriteLine("compareExchangeNl(ref this.Prev_, {0}, {1}) = {2}", 1044 | List_.NodeLinkDescription(new nodeLink(prev.P, true)), 1045 | List_.NodeLinkDescription(prev), 1046 | b1); 1047 | Console.WriteLine("this ="); 1048 | List_.LogNode(this); 1049 | List_.LogState(); 1050 | #endif 1051 | List_.LeaveTestSynchronizedBlock(); 1052 | 1053 | if (b1) 1054 | break; 1055 | } 1056 | List_.CorrectPrev(prev.P, next.P); 1057 | //lastValue = this.newValue; 1058 | Thread.MemoryBarrier(); 1059 | return true; 1060 | } 1061 | } 1062 | } 1063 | 1064 | public T CompareExchangeValue(T newValue, T comparand) 1065 | { 1066 | valueNodeLinkPair currentPair; 1067 | Thread.MemoryBarrier(); 1068 | while (true) 1069 | { 1070 | currentPair = Next_; 1071 | if (!ReferenceEquals(currentPair.Value, comparand)) 1072 | return currentPair.Value; 1073 | if (ReferenceEquals( 1074 | Interlocked.CompareExchange( 1075 | ref Next_, 1076 | new valueNodeLinkPair( 1077 | newValue, currentPair.Link), 1078 | currentPair), 1079 | currentPair)) 1080 | { 1081 | break; 1082 | } 1083 | } 1084 | return currentPair.Value; 1085 | } 1086 | 1087 | public node(lockFreeDoublyLinkedList list) 1088 | { 1089 | List_ = list; 1090 | #if DEBUG 1091 | 1092 | Id = Interlocked.Increment(ref nextId) - 1; 1093 | #endif 1094 | /* Value_ is flushed 1095 | * at the moment the current node instance is published 1096 | * (by CompareExchange). */ 1097 | } 1098 | 1099 | #region private 1100 | #if DEBUG 1101 | 1102 | // ReSharper disable once StaticFieldInGenericType 1103 | private static long nextId = 0; 1104 | 1105 | #endif 1106 | 1107 | private void throwIsDummyNodeException() 1108 | { 1109 | throw new InvalidOperationException( 1110 | "The current node is the dummy head or dummy tail node " + 1111 | "of the current List, so it may not store any value."); 1112 | } 1113 | 1114 | private bool toNext(ref node cursor) 1115 | { 1116 | while (true) 1117 | { 1118 | if (cursor == List_.TailNode) 1119 | return false; 1120 | 1121 | Thread.MemoryBarrier(); 1122 | List_.EnterTestSynchronizedBlock(); 1123 | node next = cursor.Next_.Link.P; 1124 | #if SynchronizedLfdll_Verbose 1125 | Console.WriteLine("toNext Step 0"); 1126 | Console.WriteLine("next ="); 1127 | List_.LogNode(next); 1128 | #endif 1129 | List_.LeaveTestSynchronizedBlock(); 1130 | 1131 | Thread.MemoryBarrier(); 1132 | List_.EnterTestSynchronizedBlock(); 1133 | bool d = next.Next_.Link.D; 1134 | #if SynchronizedLfdll_Verbose 1135 | Console.WriteLine("toNext Step 1"); 1136 | Console.WriteLine("d = {0}", d); 1137 | #endif 1138 | List_.LeaveTestSynchronizedBlock(); 1139 | 1140 | bool b = d; 1141 | if (b) 1142 | { 1143 | 1144 | Thread.MemoryBarrier(); 1145 | List_.EnterTestSynchronizedBlock(); 1146 | b &= !cursor.Next_.Link.Equals(new nodeLink(next, true)); 1147 | #if SynchronizedLfdll_Verbose 1148 | Console.WriteLine("toNext Step 2"); 1149 | Console.WriteLine("!cursor.Next_.Link.Equals(new nodeLink(next, true)) = {0}", 1150 | !cursor.Next_.Link.Equals(new nodeLink(next, true))); 1151 | #endif 1152 | List_.LeaveTestSynchronizedBlock(); 1153 | 1154 | } 1155 | if (b) 1156 | { 1157 | List_.SetMark(ref next.Prev_); 1158 | 1159 | Thread.MemoryBarrier(); 1160 | List_.EnterTestSynchronizedBlock(); 1161 | node p = next.Next_.Link.P; 1162 | #if SynchronizedLfdll_Verbose 1163 | Console.WriteLine("toNext Step 3"); 1164 | Console.WriteLine("p ="); 1165 | List_.LogNode(p); 1166 | #endif 1167 | List_.LeaveTestSynchronizedBlock(); 1168 | 1169 | List_.EnterTestSynchronizedBlock(); 1170 | // ReSharper disable once UnusedVariable 1171 | bool b1 = compareExchangeNodeLinkInPair(ref cursor.Next_, 1172 | (nodeLink)p, (nodeLink)next); 1173 | #if SynchronizedLfdll_Verbose 1174 | Console.WriteLine("Remove Step 4"); 1175 | Console.WriteLine("compareExchangeNlIp(ref cursor.Next_, {0}, {1}) = {2}", 1176 | List_.NodeLinkDescription((nodeLink)p), 1177 | List_.NodeLinkDescription((nodeLink)next), 1178 | b1); 1179 | Console.WriteLine("cursor ="); 1180 | List_.LogNode(cursor); 1181 | List_.LogState(); 1182 | #endif 1183 | List_.LeaveTestSynchronizedBlock(); 1184 | 1185 | continue; 1186 | } 1187 | cursor = next; 1188 | if (!d) 1189 | return true; 1190 | } 1191 | } 1192 | 1193 | private bool toPrev(ref node cursor) 1194 | { 1195 | while (true) 1196 | { 1197 | if (cursor == List_.HeadNode) 1198 | return false; 1199 | 1200 | Thread.MemoryBarrier(); 1201 | List_.EnterTestSynchronizedBlock(); 1202 | node prev = cursor.Prev_.P; 1203 | #if SynchronizedLfdll_Verbose 1204 | Console.WriteLine("toPrev Step 0"); 1205 | Console.WriteLine("prev ="); 1206 | List_.LogNode(prev); 1207 | #endif 1208 | List_.LeaveTestSynchronizedBlock(); 1209 | 1210 | Thread.MemoryBarrier(); 1211 | List_.EnterTestSynchronizedBlock(); 1212 | bool b = prev.Next_.Link.Equals(new nodeLink(cursor, false)); 1213 | #if SynchronizedLfdll_Verbose 1214 | Console.WriteLine("toPrev Step 1"); 1215 | Console.WriteLine("b = {0}", b); 1216 | #endif 1217 | List_.LeaveTestSynchronizedBlock(); 1218 | 1219 | if (b) 1220 | { 1221 | 1222 | Thread.MemoryBarrier(); 1223 | List_.EnterTestSynchronizedBlock(); 1224 | b &= !cursor.Next_.Link.D; 1225 | #if SynchronizedLfdll_Verbose 1226 | Console.WriteLine("toPrev Step 2"); 1227 | Console.WriteLine("!cursor.Next_.Link.D = {0}", !cursor.Next_.Link.D); 1228 | #endif 1229 | List_.LeaveTestSynchronizedBlock(); 1230 | 1231 | } 1232 | if (b) 1233 | { 1234 | cursor = prev; 1235 | return true; 1236 | } 1237 | else 1238 | { 1239 | Thread.MemoryBarrier(); 1240 | List_.EnterTestSynchronizedBlock(); 1241 | bool b1 = cursor.Next_.Link.D; 1242 | #if SynchronizedLfdll_Verbose 1243 | Console.WriteLine("toPrev Step 3"); 1244 | Console.WriteLine("b1 = {0}", b1); 1245 | #endif 1246 | List_.LeaveTestSynchronizedBlock(); 1247 | 1248 | if (b1) 1249 | toNext(ref cursor); 1250 | else 1251 | List_.CorrectPrev(prev, cursor); 1252 | } 1253 | } 1254 | } 1255 | 1256 | private ILockFreeDoublyLinkedListNode insertBefore( 1257 | T value, node cursor, SpinWait spin = new SpinWait()) 1258 | { 1259 | if (cursor == List_.HeadNode) 1260 | return InsertAfter(value); 1261 | node node = new node(List_); 1262 | 1263 | Thread.MemoryBarrier(); 1264 | List_.EnterTestSynchronizedBlock(); 1265 | node prev = cursor.Prev_.P; 1266 | #if SynchronizedLfdll_Verbose 1267 | Console.WriteLine("insertBefore Step 0"); 1268 | Console.WriteLine("prev ="); 1269 | List_.LogNode(prev); 1270 | #endif 1271 | List_.LeaveTestSynchronizedBlock(); 1272 | 1273 | node next; 1274 | while (true) 1275 | { 1276 | while (true) 1277 | { 1278 | 1279 | Thread.MemoryBarrier(); 1280 | List_.EnterTestSynchronizedBlock(); 1281 | bool b = !cursor.Next_.Link.D; 1282 | #if SynchronizedLfdll_Verbose 1283 | Console.WriteLine("insertBefore Step 1"); 1284 | Console.WriteLine("b = {0}", b); 1285 | #endif 1286 | List_.LeaveTestSynchronizedBlock(); 1287 | 1288 | if (b) 1289 | break; 1290 | 1291 | /* Since cursor was deleted 1292 | * the method correctPrev has not returned a node 1293 | * which is logically before cursor; 1294 | * the return value shall not have semantic meaning. 1295 | * As correctPrev apparently exprects 1296 | * a logical predecessor of node / cursor, 1297 | * prev cannot be passed to the method. 1298 | * This is dire for program execution 1299 | * especially when prev == List_.tailNode. */ 1300 | 1301 | toNext(ref cursor); 1302 | 1303 | #region Bugfix 1 1304 | /* Ascertain a new predecessor of cursor. */ 1305 | Thread.MemoryBarrier(); 1306 | List_.EnterTestSynchronizedBlock(); 1307 | prev = cursor.Prev_.P; 1308 | #if SynchronizedLfdll_Verbose 1309 | Console.WriteLine("insertBefore Step 1.1"); 1310 | Console.WriteLine("prev ="); 1311 | List_.LogNode(prev); 1312 | #endif 1313 | List_.LeaveTestSynchronizedBlock(); 1314 | #endregion 1315 | 1316 | prev = List_.CorrectPrev(prev, cursor); 1317 | } 1318 | next = cursor; 1319 | node.Prev_ = new nodeLink(prev, false); 1320 | node.Next_ = new valueNodeLinkPair(value, 1321 | new nodeLink(next, false)); 1322 | 1323 | List_.EnterTestSynchronizedBlock(); 1324 | bool b1 = compareExchangeNodeLinkInPair(ref prev.Next_, 1325 | new nodeLink(node, false), 1326 | new nodeLink(cursor, false)); 1327 | #if SynchronizedLfdll_Verbose 1328 | Console.WriteLine("insertBefore Step 2"); 1329 | Console.WriteLine("compareExchangeNlIp(ref prev.Next_, {0}, {1}) = {2}", 1330 | List_.NodeLinkDescription(new nodeLink(node, false)), 1331 | List_.NodeLinkDescription(new nodeLink(cursor, false)), 1332 | b1); 1333 | Console.WriteLine("prev ="); 1334 | List_.LogNode(prev); 1335 | List_.LogState(); 1336 | #endif 1337 | List_.LeaveTestSynchronizedBlock(); 1338 | 1339 | if (b1) 1340 | break; 1341 | 1342 | prev = List_.CorrectPrev(prev, cursor); 1343 | spin.SpinOnce(); 1344 | } 1345 | 1346 | List_.CorrectPrev(prev, next); 1347 | return node; 1348 | } 1349 | 1350 | private ILockFreeDoublyLinkedListNode insertAfter(T value, node cursor) 1351 | { 1352 | SpinWait spin = new SpinWait(); 1353 | 1354 | if (cursor == List_.TailNode) 1355 | return insertBefore(value, cursor, spin); 1356 | node node = new node(List_); 1357 | node prev = cursor; 1358 | node next; 1359 | while (true) 1360 | { 1361 | 1362 | Thread.MemoryBarrier(); 1363 | List_.EnterTestSynchronizedBlock(); 1364 | next = prev.Next_.Link.P; 1365 | #if SynchronizedLfdll_Verbose 1366 | Console.WriteLine("insertAfter Step 0"); 1367 | Console.WriteLine("next ="); 1368 | List_.LogNode(next); 1369 | #endif 1370 | List_.LeaveTestSynchronizedBlock(); 1371 | 1372 | node.Prev_ = new nodeLink(prev, false); 1373 | node.Next_ = new valueNodeLinkPair(value, 1374 | new nodeLink(next, false)); 1375 | 1376 | List_.EnterTestSynchronizedBlock(); 1377 | bool b1 = compareExchangeNodeLinkInPair(ref cursor.Next_, 1378 | new nodeLink(node, false), 1379 | new nodeLink(next, false)); 1380 | #if SynchronizedLfdll_Verbose 1381 | Console.WriteLine("insertAfter Step 1"); 1382 | Console.WriteLine("compareExchangeNlIp(ref cursor.Next_, {0}, {1}) = {2}", 1383 | List_.NodeLinkDescription(new nodeLink(node, false)), 1384 | List_.NodeLinkDescription(new nodeLink(next, false)), 1385 | b1); 1386 | Console.WriteLine("cursor ="); 1387 | List_.LogNode(cursor); 1388 | List_.LogState(); 1389 | #endif 1390 | List_.LeaveTestSynchronizedBlock(); 1391 | 1392 | if (b1) 1393 | break; 1394 | 1395 | /* Not necessary because of preceding compareExchange. */ 1396 | // Thread.MemoryBarrier(); 1397 | 1398 | List_.EnterTestSynchronizedBlock(); 1399 | bool b = prev.Next_.Link.D; 1400 | #if SynchronizedLfdll_Verbose 1401 | Console.WriteLine("insertAfter Step 2"); 1402 | Console.WriteLine("b = {0}", b); 1403 | List_.LogNode(next); 1404 | #endif 1405 | List_.LeaveTestSynchronizedBlock(); 1406 | 1407 | if (b) 1408 | return insertBefore(value, cursor, spin); 1409 | spin.SpinOnce(); 1410 | } 1411 | List_.CorrectPrev(prev, next); 1412 | return node; 1413 | } 1414 | #endregion 1415 | } 1416 | 1417 | private class nodeLink where T : class 1418 | { 1419 | public node P { get; } 1420 | public bool D { get; } 1421 | 1422 | public override bool Equals(object obj) 1423 | { 1424 | if (!(obj is nodeLink)) 1425 | return false; 1426 | 1427 | var other = (nodeLink)obj; 1428 | return D == other.D && P == other.P; 1429 | } 1430 | 1431 | public bool Equals(nodeLink other) 1432 | { 1433 | if (other == null) 1434 | return false; 1435 | 1436 | return D == other.D && P == other.P; 1437 | } 1438 | 1439 | public override int GetHashCode() 1440 | { 1441 | int hash = 17; 1442 | hash = hash * 486187739 + P.GetHashCode(); 1443 | hash = hash * 486187739 + D.GetHashCode(); 1444 | return hash; 1445 | } 1446 | 1447 | public static explicit operator nodeLink(node node) 1448 | { 1449 | return new nodeLink(node, false); 1450 | } 1451 | 1452 | public static explicit operator node(nodeLink link) 1453 | { 1454 | #if DEBUG 1455 | /* I am not sure, 1456 | * whether it is simply assumed in the document 1457 | * that the conversion is always possible. */ 1458 | if (link.D) 1459 | throw new ArgumentException(); 1460 | #endif 1461 | return link.P; 1462 | } 1463 | 1464 | public nodeLink(node p, bool d) 1465 | { 1466 | P = p; 1467 | D = d; 1468 | } 1469 | } 1470 | 1471 | private class valueNodeLinkPair where T : class 1472 | { 1473 | public T Value { get; } 1474 | public nodeLink Link { get; } 1475 | 1476 | public valueNodeLinkPair(T value, nodeLink link) 1477 | { 1478 | Value = value; 1479 | Link = link; 1480 | } 1481 | } 1482 | #endregion 1483 | } 1484 | } 1485 | -------------------------------------------------------------------------------- /src/LockFreeDoublyLinkedList.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.1 4 | 5 | Lock free doubly linked list 6 | Lock free doubly linked list 7 | LockFreeDoublyLinkedList 8 | A lock free doubly linked list for high concurrency. 9 | 4.0.0.1 10 | Christoph Müller (iblzm@hotmail.de) 11 | Christoph Müller (iblzm@hotmail.de) 12 | Copyright © Christoph Müller 2018–2020 13 | en 14 | true 15 | https://github.com/c7hm4r/LockFreeDoublyLinkedList 16 | Apache-2.0 17 | LICENSE.txt 18 | lock-free thread-safe linked list concurrency 19 | false 20 | 21 | 22 | true 23 | full 24 | false 25 | TRACE;DEBUG; 26 | 27 | 28 | true 29 | snupkg 30 | pdbonly 31 | true 32 | 33 | prompt 34 | 4 35 | bin\$(Configuration)\$(TargetFramework)\LockFreeDoublyLinkedList.xml 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/LockFreeDoublyLinkedListAdditions.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace LockFreeDoublyLinkedLists 23 | { 24 | /// 25 | /// Extension methods for the class 26 | /// 27 | /// 28 | public static class LockFreeDoublyLinkedListAdditions 29 | { 30 | /// 31 | /// Exchanges the value of a LockFreeLinkedList node 32 | /// with a new Value generated from the old value 33 | /// if and only if 34 | /// for the old value is not null. 35 | /// If not, nothing is performed and 36 | /// false within the result Tuple is returned. 37 | /// 38 | /// 39 | /// The type of the LockFreeLinkedList contents. 40 | /// 41 | /// The node whose value shall be replaced. 42 | /// 43 | /// The function which creates a new value encapsulated 44 | /// in a Tuple<T> based on the prevalent value. 45 | /// If the value shall not be replaced, return null; 46 | /// 47 | /// 48 | /// The prevalent value together with 49 | /// a success flag encapsulated in a Tuple<T, bool>. 50 | /// 51 | public static Tuple CompareExchangeValueIf( 52 | this ILockFreeDoublyLinkedListNode node, 53 | Func> newValue) 54 | where T : class 55 | { 56 | T old = node.Value; 57 | while (true) 58 | { 59 | Tuple @new = newValue(old); 60 | if (@new == null) 61 | return new Tuple(old, false); 62 | T prevalent = node.CompareExchangeValue(@new.Item1, old); 63 | if (prevalent == old) 64 | return new Tuple(old, true); 65 | old = prevalent; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/ThreadingAdditions.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading; 21 | using System.Threading.Tasks; 22 | 23 | namespace LockFreeDoublyLinkedLists 24 | { 25 | internal static class ThreadingAdditions 26 | { 27 | /// 28 | /// Replaces the value under location 29 | /// if a distinct condition is fulfilled for the prevalent value. 30 | /// 31 | /// The type of the value to replace. 32 | /// 33 | /// The place, where the value shall be replaced. 34 | /// 35 | /// 36 | /// The value, which is assigned, when the condition is fulfilled. 37 | /// 38 | /// 39 | /// The condition, 40 | /// which shall be fulfilled when the condition is being performed. 41 | /// 42 | /// 43 | /// The value, which has been prevalent before the replacement. 44 | /// 45 | /// 46 | /// Whether the replacement has occurred. 47 | /// 48 | public static bool ConditionalCompareExchange( 49 | ref T location, T value, Func condition, out T current) 50 | where T : class 51 | { 52 | Thread.MemoryBarrier(); 53 | current = location; 54 | while (true) 55 | { 56 | if (!condition(current)) 57 | return false; 58 | T prevalent = Interlocked.CompareExchange( 59 | ref location, value, current); 60 | if (ReferenceEquals(prevalent, current)) 61 | return true; 62 | current = prevalent; 63 | } 64 | } 65 | 66 | /// 67 | /// Replaces the value under location 68 | /// if a distinct condition is fulfilled for the prevalent value. 69 | /// 70 | /// The type of the value to replace. 71 | /// 72 | /// The place, where the value shall be replaced. 73 | /// 74 | /// 75 | /// The value, which is assigned, when the condition is fulfilled. 76 | /// 77 | /// 78 | /// The condition, 79 | /// which shall be fulfilled when the condition is being performed. 80 | /// 81 | /// 82 | /// Whether the replacement has occurred. 83 | /// 84 | public static bool ConditionalCompareExchange( 85 | ref T location, T value, Func condition) 86 | where T : class 87 | { 88 | T current; 89 | return ConditionalCompareExchange( 90 | ref location, value, condition, out current); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /test/Counter.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading; 21 | using System.Threading.Tasks; 22 | 23 | namespace Test 24 | { 25 | /// 26 | /// A threadsafe Counter. 27 | /// 28 | public class Counter 29 | { 30 | /// 31 | /// The current counter value. 32 | /// 33 | public long Current 34 | { 35 | get { return Interlocked.Read(ref value); } 36 | } 37 | 38 | /// 39 | /// Inkrement the counter value. 40 | /// 41 | /// The new counter value. 42 | public long Count() 43 | { 44 | return Interlocked.Increment(ref value) - 1; 45 | } 46 | 47 | public Counter(long initial = 0) 48 | { 49 | value = initial; 50 | Thread.MemoryBarrier(); 51 | } 52 | 53 | #region private 54 | private long value; 55 | #endregion 56 | } 57 | } -------------------------------------------------------------------------------- /test/LinqHelper.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test 23 | { 24 | internal static class LinqHelper 25 | { 26 | public static IEqualityComparer ToEqualityComparer 27 | (Func comparator, Func getHashCode) 28 | { 29 | return new equalityComparer(comparator, getHashCode); 30 | } 31 | 32 | public static IEnumerable Repeat(int count, Func generator) 33 | { 34 | for (int i = 0; i < count; i++) 35 | yield return generator(); 36 | } 37 | 38 | public static IEnumerable Repeat(Func generator) 39 | { 40 | while (true) 41 | yield return generator(); 42 | } 43 | 44 | #region private 45 | private class equalityComparer : IEqualityComparer 46 | { 47 | public bool Equals(T x, T y) 48 | { 49 | return comparator(x, y); 50 | } 51 | 52 | public int GetHashCode(T obj) 53 | { 54 | return getHashCode(obj); 55 | } 56 | 57 | public equalityComparer(Func comparator, Func getHashCode) 58 | { 59 | this.comparator = comparator; 60 | this.getHashCode = getHashCode; 61 | } 62 | 63 | #region private 64 | private readonly Func comparator; 65 | private readonly Func getHashCode; 66 | #endregion 67 | } 68 | #endregion 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/Program.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Diagnostics; 20 | using System.Linq; 21 | using System.Threading; 22 | using System.Threading.Tasks; 23 | using Test.Tests; 24 | 25 | namespace Test 26 | { 27 | internal class Program 28 | { 29 | #region private 30 | private static void Main(string[] args) 31 | { 32 | Random rand = new Random(); 33 | 34 | long remainingTests = 20000; 35 | var logIntervalStopwatch = new Stopwatch(); 36 | var testBatchStopwatch = new Stopwatch(); 37 | long testBatchSize; 38 | logIntervalStopwatch.Start(); 39 | testBatchStopwatch.Start(); 40 | while (true) 41 | { 42 | testBatchSize = remainingTests; 43 | var testRunsStopwatch = new Stopwatch(); 44 | while (remainingTests > 0) 45 | { 46 | if (logIntervalStopwatch.Elapsed.TotalSeconds >= 10) 47 | { 48 | Console.WriteLine("Number of remaining tests: " 49 | + remainingTests); 50 | if (testBatchSize - remainingTests != 0) 51 | Console.WriteLine("Remaining time: " 52 | + (double)remainingTests 53 | / (testBatchSize - remainingTests) 54 | * testBatchStopwatch.Elapsed.TotalSeconds + " s"); 55 | logIntervalStopwatch.Restart(); 56 | } 57 | remainingTests--; 58 | var test = new Test001(); 59 | test.Seed = rand.Next(int.MinValue, int.MaxValue); 60 | //Console.WriteLine("seed: {0}", test.Seed); 61 | 62 | try 63 | { 64 | Thread.Yield(); 65 | testRunsStopwatch.Start(); 66 | Thread.MemoryBarrier(); 67 | 68 | test.Main(args); 69 | 70 | Thread.MemoryBarrier(); 71 | testRunsStopwatch.Stop(); 72 | } 73 | catch 74 | { 75 | Console.WriteLine($"The test failed. The seed was: {test.Seed}."); 76 | throw; 77 | } 78 | } 79 | if (testBatchSize > 0) 80 | { 81 | Console.WriteLine($"Time per test: {testRunsStopwatch.Elapsed.TotalSeconds / testBatchSize} s."); 82 | } 83 | 84 | Console.Write("Enter 'r' or 'n', to restart the test. ?"); 85 | string input = Console.ReadLine(); 86 | Console.WriteLine(); 87 | if (input == "r") 88 | remainingTests = 1; 89 | else if (input == "n") 90 | { 91 | Console.Write("Enter number of restarts. ?"); 92 | long i; 93 | string text = Console.ReadLine(); 94 | if (long.TryParse(text, out i)) 95 | { 96 | remainingTests = i; 97 | } 98 | } 99 | if (remainingTests == 0) 100 | break; 101 | testBatchStopwatch.Restart(); 102 | logIntervalStopwatch.Restart(); 103 | } 104 | } 105 | #endregion 106 | } 107 | 108 | internal abstract class Test 109 | { 110 | public abstract void Main(string[] args); 111 | public int Seed { get; set; } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /test/Tests/Test001.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | #define CheckCorrectness 17 | /* If a thread misses, cancel immediately 18 | * and don’t become entangled in a deadlock. 19 | * Additionally, output the seed number. 20 | * May exacerbate the exception handling in the IDE, though. */ 21 | #define HandleTaskExceptionImmediately 22 | /* PopLeft is not atomic supported by the current LFDLL implementation. */ 23 | //#define PopLeft 24 | 25 | 26 | #if SynchronizedLfdll 27 | #if Verbose 28 | #define SynchronizedLfdll_verbose 29 | #endif 30 | #undef RunOperationsSequentially 31 | #endif 32 | 33 | using System; 34 | using System.Collections.Generic; 35 | using System.Linq; 36 | using System.Threading; 37 | using System.Threading.Tasks; 38 | using LockFreeDoublyLinkedLists; 39 | using Test.Tests.Test001_; 40 | using Test.Tests.Test001_.OperationResultComparers; 41 | using Test.Tests.Test001_.Operations; 42 | 43 | namespace Test.Tests 44 | { 45 | internal partial class Test001 : Test 46 | { 47 | public override void Main(string[] args) 48 | { 49 | #if Verbose 50 | const bool verbose = true; 51 | #else 52 | const bool verbose = false; 53 | #endif 54 | 55 | int operationSequencesNumber = 3; 56 | const int operationNumberPerSequence = 4; 57 | const int listSize = 4; 58 | 59 | //Seed = 669052551; 60 | Random rand2 = new Random(Seed); 61 | 62 | TestIterationParameters iterationParameters = 63 | newIterationParameters( 64 | listSize, operationSequencesNumber, 65 | operationNumberPerSequence, 66 | rand2); 67 | 68 | #if Verbose 69 | Console.WriteLine("Test of LockFreeDoublyLinkedList"); 70 | 71 | Console.WriteLine("Number of executors (Threads): " + operationSequencesNumber); 72 | Console.WriteLine("Operations per executor: " 73 | + operationNumberPerSequence); 74 | Console.WriteLine("List size: " + listSize); 75 | 76 | Console.WriteLine("seed: " + Seed); 77 | 78 | Console.WriteLine("startIndex-es: " 79 | + string.Join(" \t", iterationParameters.OperationSequences.Select(eParam => eParam.StartIndex))); 80 | 81 | Console.WriteLine("operations: "); 82 | for (int i = 0; i < iterationParameters.OperationSequences.Count; i++) 83 | { 84 | ExecutionSequenceParameters executionSequenceParameters 85 | = iterationParameters.OperationSequences[i]; 86 | Console.WriteLine("\t{0}", i); 87 | foreach (IOperationResultComparer op in executionSequenceParameters.Operations) 88 | { 89 | Console.WriteLine("\t\t{0}", op); 90 | } 91 | } 92 | #endif 93 | 94 | #if SynchronizedLfdll 95 | Tuple>, ILockFreeDoublyLinkedList> lfdllResult 96 | = runOnLfdll(iterationParameters); 97 | #else 98 | Tuple>, ILockFreeDoublyLinkedList> lfdllResult 99 | = runOnLfdll(iterationParameters); 100 | #endif 101 | 102 | List lfdllResultList = lfdllResult.Item2.ToList(); 103 | 104 | List timings 105 | = lfdllResult.Item1 106 | .SelectMany((opTimings, i) => opTimings.Select( 107 | opTiming => new operationExecutionInfo(opTiming, i))) 108 | .ToList(); 109 | 110 | IEqualityComparer equalityComparer 111 | = LinqHelper.ToEqualityComparer( 112 | (ListItemData item1, ListItemData item2) => 113 | item1.NodeId == item2.NodeId 114 | && item1.Value == item2.Value, 115 | item => 116 | (0x51ed270b + item.GetHashCode()) * -1521134295); 117 | 118 | #if CheckCorrectness 119 | bool found = 120 | permutations(timings) 121 | .Select( 122 | permTimings => 123 | runOnLinkedList( 124 | iterationParameters, 125 | permTimings.Select(oei => oei.ExecutorIndex) 126 | .ToList())) 127 | .Any( 128 | llResult => 129 | llResult.SequenceEqual(lfdllResultList, equalityComparer) 130 | && iterationParameters.OperationSequences.All( 131 | os => os.Operations.All(o => o.LastResultsEqual))); 132 | // ReSharper disable once RedundantLogicalConditionalExpressionOperand 133 | if (verbose && found) 134 | Console.WriteLine("Gefunden."); 135 | #pragma warning disable 162 // Unreachable code 136 | // ReSharper disable once ConditionIsAlwaysTrueOrFalse 137 | if (verbose || !found) 138 | #pragma warning restore 162 139 | { 140 | Console.WriteLine("Test of LockFreeDoublyLinkedList"); 141 | 142 | Console.WriteLine("Number of executors (Threads): " + operationSequencesNumber); 143 | Console.WriteLine("Operations per executor: " 144 | + operationNumberPerSequence); 145 | Console.WriteLine("List size: " + listSize); 146 | 147 | Console.WriteLine("seed: " + Seed); 148 | 149 | Random initializationRandom 150 | = new Random(iterationParameters.InitializationSeed); 151 | Console.WriteLine("initial: \t" 152 | + string.Join(" \t", Enumerable.Range(0, listSize).Select( 153 | i => new ListItemData(i, initializationRandom.Next( 154 | listItemValueRange))))); 155 | 156 | Console.WriteLine("lfdllResult: \t" 157 | + string.Join("\t", 158 | lfdllResultList.Select(o => o.ToString()))); 159 | Console.WriteLine("linked list results: \t"); 160 | var enumerationEqualityComparer = LinqHelper 161 | .ToEqualityComparer>( 162 | (e1, e2) => 163 | { 164 | var el1 = e1 as ListItemData[] ?? e1.ToArray(); 165 | var el2 = e2 as ListItemData[] ?? e2.ToArray(); 166 | return el1.Length == el2.Length && 167 | el1.Zip(el2, (i1, i2) => i1.NodeId == i2.NodeId && 168 | i1.Value == i2.Value) 169 | .All(i => i); 170 | }, 171 | e1 => e1.Aggregate( 172 | 0x51ed270b, 173 | (hash, next) => (hash + next.Value.GetHashCode()) * -1521134295)); 174 | IEnumerable> perms 175 | = permutations(timings) 176 | .Select( 177 | permTimings => 178 | runOnLinkedList( 179 | iterationParameters, 180 | permTimings.Select(oei => oei.ExecutorIndex) 181 | .ToList())) 182 | .Distinct(enumerationEqualityComparer); 183 | foreach (IEnumerable sequentialResult 184 | in perms) 185 | { 186 | Console.WriteLine(string.Join("", sequentialResult.Select(value => " \t" + value))); 187 | } 188 | Console.WriteLine("startIndex-es: " + string.Join(" \t", iterationParameters.OperationSequences.Select(eParam => eParam.StartIndex))); 189 | Console.WriteLine("operations:"); 190 | foreach (operationExecutionInfo timing in timings) 191 | { 192 | Console.WriteLine(" " + timing.ExecutorIndex 193 | + " \t" + timing.Start 194 | + " \t" + timing.End 195 | + " \t" + timing.Operation 196 | #if RunOperationsSequentially 197 | + " \t" + timing.Operation.LastResultsEqual 198 | #endif 199 | ); 200 | } 201 | if (!found) 202 | { 203 | Console.Write("?"); 204 | Console.ReadLine(); 205 | throw new Exception(); 206 | } 207 | } 208 | #endif 209 | } 210 | 211 | public Test001() 212 | { 213 | operationComparerCreators 214 | = new List>> 216 | { 217 | new List> 218 | { 219 | (oig, seed) => new SelectRandomKnownNode(oig, seed) 220 | } 221 | .Select(f => 222 | (Func) ( 224 | (rand, oig, counter) => 225 | { 226 | int seed = rand.Next(); 227 | return new VoidOperationComparer(f(oig, seed)); 228 | } 229 | ) 230 | ), 231 | new List> 233 | { 234 | (oig, v) => new PushLeft(oig, v), 235 | (oig, v) => new PushRight(oig, v), 236 | (oig, v) => new InsertBefore(oig, v), 237 | (oig, v) => new InsertAfter(oig, v) 238 | } 239 | .Select(f => 240 | (Func) 242 | ( 243 | (rand, oig, counter) => 244 | new NodeReturningOperationComparer( 245 | f(oig, nextListItemData(rand, counter))) 246 | ) 247 | ), 248 | new List>> 249 | { 250 | new List> 251 | { 252 | oig => new Next(oig), 253 | oig => new Previous(oig) 254 | } 255 | .Select(f => (Func) ( 256 | oig => new VoidOperationComparer(f(oig)))), 257 | new List> 258 | { oig => new PopRightNode(oig) } 259 | .Select(f => (Func) ( 260 | oig => new NodeReturningOperationComparer(f(oig)))), 261 | new List> 262 | { oig => new Remove(oig) } 263 | .Select(f => (Func) ( 264 | oig => new BoolReturningOperationComparer(f(oig)))), 265 | } 266 | .SelectMany(e => e, (e, f) => 267 | (Func) 269 | ((rand, oig, counter) => f(oig)) 270 | ), 271 | new List> 273 | { 274 | (rand, idGenerator, counter) => 275 | new NodeReturningOperationComparer( 276 | new InsertAfterIf(idGenerator, 277 | nextListItemData(rand, counter), 278 | nextItemValue(rand))), 279 | (rand, idGenerator, counter) => 280 | new ItemDataReturningOperationComparer( 281 | new CompareExchangeValue(idGenerator, 282 | nextItemValue(rand), nextItemValue(rand))), 283 | (rand, idGenerator, counter) => 284 | new ItemDataReturningOperationComparer( 285 | new GetValue(idGenerator)), 286 | (rand, idGenerator, counter) => 287 | new VoidOperationComparer( 288 | new SetValue(idGenerator, nextItemValue(rand))) 289 | } 290 | } 291 | .SelectMany(e => e) 292 | .ToList(); 293 | } 294 | 295 | private const int listItemValueRange = 3; 296 | 297 | private readonly List> 298 | operationComparerCreators; 299 | 300 | private ListItemData nextListItemData(Random rand, Counter counter) 301 | { 302 | return new ListItemData(counter.Count(), nextItemValue(rand)); 303 | } 304 | 305 | private int nextItemValue(Random rand) 306 | { 307 | return rand.Next(listItemValueRange); 308 | } 309 | 310 | private IEnumerable> permutations( 311 | List executionInfos) 312 | { 313 | int i; 314 | 315 | /* Sortieren nach Startzeitpunkt */ 316 | executionInfos.Sort( 317 | (t1, t2) => t1.Start.CompareTo(t2.Start)); 318 | 319 | var permutation = 320 | new LinkedList(executionInfos); 321 | 322 | yield return permutation.ToList(); 323 | 324 | var nodes = 325 | new LinkedListNode 326 | [executionInfos.Count]; 327 | LinkedListNode node = permutation.First; 328 | for (i = 0; i < executionInfos.Count; i++) 329 | { 330 | nodes[i] = node; 331 | // ReSharper disable once PossibleNullReferenceException 332 | node = node.Next; 333 | } 334 | 335 | i = permutation.Count - 1; 336 | LinkedListNode lastNode = nodes[i]; 337 | node = lastNode; 338 | bool seen = true; 339 | while (true) 340 | { 341 | /* node ist der Knoten mit dem höchsten Startzeitpunkt, 342 | * der sich in der Liste befindet, falls er sich in der Liste befindet. */ 343 | if (seen) 344 | { 345 | LinkedListNode prev = node.Previous; 346 | if (prev == null || prev.Value.End < node.Value.Start) 347 | { 348 | permutation.Remove(node); 349 | i--; 350 | if (i == 0) 351 | yield break; 352 | node = nodes[i]; 353 | } 354 | else 355 | { 356 | permutation.Remove(node); 357 | permutation.AddBefore(prev, node); 358 | if (node == lastNode) 359 | yield return permutation.ToList(); 360 | else 361 | seen = false; 362 | } 363 | } 364 | else 365 | { 366 | if (node == lastNode) 367 | { 368 | yield return permutation.ToList(); 369 | seen = true; 370 | } 371 | else 372 | { 373 | i++; 374 | node = nodes[i]; 375 | permutation.AddLast(node); 376 | } 377 | } 378 | } 379 | } 380 | 381 | private Tuple>, 382 | ILockFreeDoublyLinkedList> 383 | runOnLfdll(TestIterationParameters parameters) 384 | { 385 | ILockFreeDoublyLinkedList lfdll 386 | = LockFreeDoublyLinkedLists.LockFreeDoublyLinkedList. 387 | Create(); 388 | 389 | Random initializationRandom 390 | = new Random(parameters.InitializationSeed); 391 | 392 | foreach (int o in Enumerable.Range(0, parameters.InitialListLength)) 393 | lfdll.PushRight( 394 | new ListItemData( 395 | o, initializationRandom.Next(listItemValueRange))); 396 | 397 | var timer = new Counter(); 398 | 399 | List executors = 400 | parameters.OperationSequences.Select( 401 | (operationParams, name) => 402 | new lfdllOperationExecutor(operationParams, lfdll, 403 | timer, name)).ToList(); 404 | 405 | foreach (lfdllOperationExecutor executor in executors) 406 | executor.Initialize(); 407 | 408 | #if SynchronizedLfdll 409 | List nextStepWaitHandles = executors.Select( 410 | executor => new AutoResetEvent(false)).ToList(); 411 | #endif 412 | 413 | var executorTasks = new List>>(); 414 | for (int i = 0; i < executors.Count; i++) 415 | { 416 | lfdllOperationExecutor executor = executors[i]; 417 | #if SynchronizedLfdll 418 | AutoResetEvent nextStepWaitHandle = nextStepWaitHandles[i]; 419 | #endif 420 | Task> task = new Task>( 421 | () => 422 | { 423 | List timings; 424 | #if HandleTaskExceptionImmediately 425 | try 426 | { 427 | #endif 428 | #if SynchronizedLfdll 429 | lfdll.NextStepWaitHandle.Value 430 | = nextStepWaitHandle; 431 | #endif 432 | timings = executor.Run(); 433 | #if SynchronizedLfdll 434 | lfdll.NextStepWaitHandle.Value.WaitOne(); 435 | nextStepWaitHandles.Remove( 436 | lfdll.NextStepWaitHandle.Value); 437 | lfdll.StepCompletedWaitHandle.Set(); 438 | #endif 439 | #if HandleTaskExceptionImmediately 440 | } 441 | catch (Exception e) 442 | { 443 | Console.WriteLine(e.StackTrace); 444 | Console.WriteLine("Exception in Thread " + Thread.CurrentThread.Name); 445 | throw; 446 | } 447 | #endif 448 | return timings; 449 | }); 450 | executorTasks.Add(task); 451 | } 452 | 453 | foreach (Task> task in executorTasks) 454 | { 455 | #if RunOperationsSequentially 456 | task.RunSynchronously(); 457 | #else 458 | task.Start(); 459 | #endif 460 | } 461 | 462 | Random executionRandom = new Random(parameters.ExecutionSeed); 463 | #if SynchronizedLfdll_verbose 464 | List handleIndexes = new List(); 465 | #endif 466 | 467 | #if SynchronizedLfdll 468 | while (true) 469 | { 470 | if (nextStepWaitHandles.Count == 0) 471 | break; 472 | int nextHandleIndex = executionRandom.Next(nextStepWaitHandles.Count); 473 | #if SynchronizedLfdll_verbose 474 | handleIndexes.Add(nextHandleIndex); 475 | #endif 476 | 477 | AutoResetEvent nextHandle 478 | = nextStepWaitHandles[nextHandleIndex]; 479 | nextHandle.Set(); 480 | lfdll.StepCompletedWaitHandle.WaitOne(); 481 | } 482 | #endif 483 | 484 | // ReSharper disable once CoVariantArrayConversion 485 | Task.WaitAll(executorTasks.ToArray()); 486 | 487 | #if SynchronizedLfdll_verbose 488 | Console.WriteLine(string.Join(" ", handleIndexes)); 489 | #endif 490 | 491 | return new Tuple>, 492 | ILockFreeDoublyLinkedList>( 493 | executorTasks.Select(t => t.Result).ToList(), lfdll); 494 | } 495 | 496 | private IEnumerable runOnLinkedList( 497 | TestIterationParameters parameters, 498 | List executorStepOrder) 499 | { 500 | Random initializationRandom = 501 | new Random(parameters.InitializationSeed); 502 | 503 | var list = new LinkedList( 504 | Enumerable.Range(0, parameters.InitialListLength) 505 | .Select( 506 | i => 507 | new LinkedListItem( 508 | new ListItemData( 509 | i, 510 | initializationRandom.Next( 511 | listItemValueRange)), 512 | isDummy: false))); 513 | list.AddFirst(new LinkedListItem(null, true)); 514 | list.AddLast(new LinkedListItem(null, true)); 515 | 516 | List executors = 517 | parameters.OperationSequences.Select( 518 | eParams => new linkedListOperationExecutor(eParams, list)) 519 | .ToList(); 520 | 521 | foreach (linkedListOperationExecutor executor in executors) 522 | executor.Initialize(); 523 | 524 | foreach (int executorIndex in executorStepOrder) 525 | { 526 | executors[executorIndex].RunSingleOperation(); 527 | } 528 | 529 | return list 530 | .Skip(1) 531 | .SkipLast(1) 532 | .Where(value => !value.Deleted) 533 | .Select(item => item.Data) 534 | .ToList(); 535 | } 536 | 537 | private TestIterationParameters newIterationParameters(int listSize, int operationSequencesNumber, int operationNumberPerSequence, Random rand) 538 | { 539 | var counter = new Counter(listSize); 540 | var idGenerator = new ObjectIdGenerator(); 541 | return new TestIterationParameters() 542 | { 543 | InitialListLength = listSize, 544 | OperationSequences = LinqHelper.Repeat( 545 | operationSequencesNumber, 546 | () => new ExecutionSequenceParameters 547 | { 548 | StartIndex = rand.Next(listSize + 2), // with head and tail node 549 | Operations = 550 | LinqHelper.Repeat( 551 | operationNumberPerSequence, 552 | () => newRandomOperationResultComparer( 553 | rand, counter, idGenerator)).ToList(), 554 | } 555 | ) 556 | .ToList(), 557 | InitializationSeed = rand.Next(), 558 | ExecutionSeed = rand.Next(), 559 | }; 560 | } 561 | 562 | private IOperationResultComparer newRandomOperationResultComparer 563 | (Random rand2, Counter c, ObjectIdGenerator idGenerator) 564 | { 565 | return operationComparerCreators[ 566 | rand2.Next(operationComparerCreators.Count)]( 567 | rand2, idGenerator, c); 568 | } 569 | 570 | private static Tuple measureTime(Action action, Counter counter) 571 | { 572 | long start = counter.Count(); 573 | action(); 574 | long end = counter.Count(); 575 | 576 | return new Tuple(start, end); 577 | } 578 | } 579 | } -------------------------------------------------------------------------------- /test/Tests/Test001.lfdllOperationExecutor.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | #define CheckCorrectness 17 | /* If a thread misses, cancel immediately 18 | * and don’t become entangled in a deadlock. 19 | * Additionally, output the seed number. 20 | * May exacerbate the exception handling in the IDE, though. */ 21 | #define HandleTaskExceptionImmediately 22 | /* PopLeft is not atomic supported by the current LFDLL implementation. */ 23 | //#define PopLeft 24 | 25 | 26 | #if SynchronizedLfdll 27 | #if Verbose 28 | #define SynchronizedLfdll_verbose 29 | #endif 30 | #undef RunOperationsSequentially 31 | #endif 32 | 33 | using System; 34 | using System.Collections.Generic; 35 | using System.Linq; 36 | using System.Threading; 37 | using LockFreeDoublyLinkedLists; 38 | using Test.Tests.Test001_; 39 | 40 | namespace Test.Tests 41 | { 42 | internal partial class Test001 43 | { 44 | private class lfdllOperationExecutor 45 | { 46 | public int Name { get; } 47 | 48 | /* Should be executed before a 49 | * LFDLLOperationExecutor modifies list. */ 50 | 51 | public void Initialize() 52 | { 53 | ILockFreeDoublyLinkedListNode current = 54 | state.List.Head; 55 | for (int i = 0; i < eParams.StartIndex; i++) 56 | current = current.Next; 57 | state.Current = current; 58 | } 59 | 60 | public List Run() 61 | { 62 | #if !RunOperationsSequentially 63 | Thread.CurrentThread.Name = Name.ToString(); 64 | #endif 65 | return eParams.Operations 66 | .Select(operation => 67 | new operationTiming( 68 | operation, 69 | processOperation(operation, counter))) 70 | .ToList(); 71 | } 72 | 73 | public lfdllOperationExecutor( 74 | ExecutionSequenceParameters eParams, 75 | ILockFreeDoublyLinkedList list, Counter counter, 76 | int name) 77 | { 78 | this.eParams = eParams; 79 | this.counter = counter; 80 | Name = name; 81 | state = new LfdllExecutionState(list); 82 | state.AddToKnownNodes(list.Head); 83 | state.AddingToKnownNodes(list.Tail); 84 | } 85 | 86 | #region private 87 | private readonly ExecutionSequenceParameters eParams; 88 | private readonly LfdllExecutionState state; 89 | private readonly Counter counter; 90 | 91 | private Tuple processOperation( 92 | IOperationResultComparer op, Counter ctr) 93 | { 94 | #if SynchronizedLfdll_verbose 95 | Console.WriteLine("({0}) Nächste Operation: {1}", Name, op); 96 | Console.WriteLine("({0}) Aktueller Knoten:", Name); 97 | state.List.LogNode(state.Current); 98 | #endif 99 | 100 | Tuple timing = measureTime( 101 | () => 102 | { 103 | op.RunOnLfdll(state); 104 | }, 105 | ctr); 106 | 107 | #if SynchronizedLfdll_verbose 108 | Console.WriteLine("({0}) Beendete Operation: {1}", Name, op); 109 | #endif 110 | 111 | return timing; 112 | } 113 | #endregion 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /test/Tests/Test001.linkedListOperationExecutor.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | #define CheckCorrectness 17 | /* If a thread misses, cancel immediately 18 | * and don’t become entangled in a deadlock. 19 | * Additionally, output the seed number. 20 | * May exacerbate the exception handling in the IDE, though. */ 21 | #define HandleTaskExceptionImmediately 22 | /* PopLeft is not atomic supported by the current LFDLL implementation. */ 23 | //#define PopLeft 24 | 25 | 26 | #if SynchronizedLfdll 27 | #if Verbose 28 | #define SynchronizedLfdll_verbose 29 | #endif 30 | #undef RunOperationsSequentially 31 | #endif 32 | 33 | using System.Collections.Generic; 34 | using System.Linq; 35 | using Test.Tests.Test001_; 36 | 37 | namespace Test.Tests 38 | { 39 | internal partial class Test001 40 | { 41 | private class linkedListOperationExecutor 42 | { 43 | public void Initialize() 44 | { 45 | LinkedListNode current = state.List.First; 46 | for (int i = 0; i < eParams.StartIndex; i++) 47 | { 48 | // ReSharper disable once PossibleNullReferenceException 49 | current = current.Next; 50 | } 51 | state.Current = current; 52 | } 53 | 54 | public void RunSingleOperation() 55 | { 56 | IOperationResultComparer operation 57 | = eParams.Operations[nextOperation]; 58 | operation.RunOnLinkedList(state); 59 | nextOperation++; 60 | } 61 | 62 | public linkedListOperationExecutor( 63 | ExecutionSequenceParameters eParams, 64 | LinkedList list) 65 | { 66 | state = new LinkedListExecutionState(list); 67 | state.AddToKnownNodes(list.First); 68 | state.AddToKnownNodes(list.Last); 69 | this.eParams = eParams; 70 | } 71 | 72 | #region private 73 | private readonly ExecutionSequenceParameters eParams; 74 | private readonly LinkedListExecutionState state; 75 | private int nextOperation = 0; 76 | #endregion 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /test/Tests/Test001.operationExecutionInfo.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | #define CheckCorrectness 17 | /* If a thread misses, cancel immediately 18 | * and don’t become entangled in a deadlock. 19 | * Additionally, output the seed number. 20 | * May exacerbate the exception handling in the IDE, though. */ 21 | #define HandleTaskExceptionImmediately 22 | /* PopLeft is not atomic supported by the current LFDLL implementation. */ 23 | //#define PopLeft 24 | 25 | 26 | #if SynchronizedLfdll 27 | #if Verbose 28 | #define SynchronizedLfdll_verbose 29 | #endif 30 | #undef RunOperationsSequentially 31 | #endif 32 | 33 | using Test.Tests.Test001_; 34 | 35 | namespace Test.Tests 36 | { 37 | internal partial class Test001 38 | { 39 | private class operationExecutionInfo 40 | { 41 | public readonly int ExecutorIndex; 42 | public readonly IOperationResultComparer Operation; 43 | public readonly long Start, End; 44 | 45 | public operationExecutionInfo( 46 | operationTiming operationTiming, int executorIndex) 47 | { 48 | ExecutorIndex = executorIndex; 49 | Operation = operationTiming.Operation; 50 | Start = operationTiming.Start; 51 | End = operationTiming.End; 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /test/Tests/Test001.operationTiming.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | #define CheckCorrectness 17 | /* If a thread misses, cancel immediately 18 | * and don’t become entangled in a deadlock. 19 | * Additionally, output the seed number. 20 | * May exacerbate the exception handling in the IDE, though. */ 21 | #define HandleTaskExceptionImmediately 22 | /* PopLeft is not atomic supported by the current LFDLL implementation. */ 23 | //#define PopLeft 24 | 25 | 26 | #if SynchronizedLfdll 27 | #if Verbose 28 | #define SynchronizedLfdll_verbose 29 | #endif 30 | #undef RunOperationsSequentially 31 | #endif 32 | 33 | using System; 34 | using Test.Tests.Test001_; 35 | 36 | namespace Test.Tests 37 | { 38 | internal partial class Test001 39 | { 40 | private class operationTiming 41 | { 42 | public readonly IOperationResultComparer Operation; 43 | public readonly long Start, End; 44 | 45 | public operationTiming(IOperationResultComparer operation, 46 | Tuple timing) 47 | { 48 | Operation = operation; 49 | Start = timing.Item1; 50 | End = timing.Item2; 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /test/Tests/Test001_/ExecutionState.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_ 24 | { 25 | internal abstract class ExecutionState : IExecutionState 26 | { 27 | public ListT List { get; private set; } 28 | public List KnownNodes { get; private set; } 29 | 30 | public ICollection KnownNodesCollection 31 | { 32 | get { return KnownNodes; } 33 | } 34 | 35 | public int CurrentIndex { get; set; } 36 | 37 | /// 38 | /// Adds a node to the list of known nodes. 39 | /// 40 | /// The node to add. 41 | /// 42 | /// The index of the node in the list of known nodes 43 | /// after the addition. 44 | /// 45 | public int AddToKnownNodes(NodeT node) 46 | { 47 | int i = KnownNodes.IndexOf(node); 48 | if (i < 0) 49 | { 50 | i = KnownNodes.Count; 51 | KnownNodes.Add(node); 52 | } 53 | return i; 54 | } 55 | 56 | /// 57 | /// Adds a node to the list of known nodes 58 | /// and returns the same node. 59 | /// 60 | /// The node to add. 61 | /// The node to add. 62 | public NodeT AddingToKnownNodes(NodeT node) 63 | { 64 | AddToKnownNodes(node); 65 | return node; 66 | } 67 | 68 | public NodeT Current 69 | { 70 | get { return KnownNodes[CurrentIndex]; } 71 | set { CurrentIndex = AddToKnownNodes(value); } 72 | } 73 | 74 | public ExecutionState(ListT list) 75 | { 76 | List = list; 77 | KnownNodes = new List(); 78 | } 79 | } 80 | 81 | internal interface IExecutionState 82 | { 83 | ICollection KnownNodesCollection { get; } 84 | int CurrentIndex { get; set; } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/Tests/Test001_/IOperationResultComparer.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_ 23 | { 24 | internal abstract class OperationResultComparer 25 | : IOperationResultComparer 26 | where TOperation : IOperation 27 | { 28 | public abstract bool LastResultsEqual { get; } 29 | public TOperation Operation { get; private set; } 30 | 31 | public void RunOnLfdll(LfdllExecutionState state) 32 | { 33 | LfdllResult = Operation.RunOnLfdll(state); 34 | } 35 | 36 | public void RunOnLinkedList(LinkedListExecutionState state) 37 | { 38 | LlResult = Operation.RunOnLinkedList(state); 39 | } 40 | 41 | public TLfdllResult LfdllResult { get; private set; } 42 | public TLlResult LlResult { get; private set; } 43 | 44 | public override string ToString() 45 | { 46 | return Operation.ToString() + ": " + LfdllResult + ", " + LlResult; 47 | } 48 | 49 | public OperationResultComparer(TOperation operation) 50 | { 51 | Operation = operation; 52 | } 53 | } 54 | 55 | internal interface IOperationResultComparer 56 | { 57 | void RunOnLfdll(LfdllExecutionState state); 58 | void RunOnLinkedList(LinkedListExecutionState state); 59 | 60 | bool LastResultsEqual { get; } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/Tests/Test001_/LfdllExecutionState.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using LockFreeDoublyLinkedLists; 18 | 19 | namespace Test.Tests.Test001_ 20 | { 21 | internal class LfdllExecutionState 22 | : ExecutionState, 23 | ILockFreeDoublyLinkedListNode> 24 | { 25 | public LfdllExecutionState( 26 | ILockFreeDoublyLinkedList list) 27 | : base(list) 28 | { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/Tests/Test001_/LinkedListExecutionState.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System.Collections.Generic; 18 | 19 | namespace Test.Tests.Test001_ 20 | { 21 | internal class LinkedListExecutionState 22 | : ExecutionState, 23 | LinkedListNode> 24 | { 25 | public LinkedListExecutionState( 26 | LinkedList list) 27 | : base(list) 28 | { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/Tests/Test001_/LinkedListItem.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_ 23 | { 24 | internal class LinkedListItem 25 | { 26 | public bool IsDummy { get; private set; } 27 | public bool Deleted { get; private set; } 28 | public ListItemData Data { get; private set; } 29 | 30 | public void Delete() 31 | { 32 | if (IsDummy) 33 | { 34 | throw new InvalidOperationException(); 35 | } 36 | Deleted = true; 37 | } 38 | 39 | public LinkedListItem NewWithData(ListItemData data) 40 | { 41 | if (IsDummy) 42 | { 43 | throw new InvalidOperationException(); 44 | } 45 | return new LinkedListItem(data) 46 | { 47 | Deleted = Deleted, 48 | }; 49 | } 50 | 51 | public override string ToString() 52 | { 53 | return "<" + Data + ">" + (Deleted ? "D" : ""); 54 | } 55 | 56 | public LinkedListItem(ListItemData data, bool isDummy = false) 57 | { 58 | Data = data; 59 | IsDummy = isDummy; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/Tests/Test001_/ListItemData.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_ 23 | { 24 | internal class ListItemData 25 | { 26 | public long NodeId { get; private set; } 27 | public int Value { get; private set; } 28 | 29 | public ListItemData NewWithValue(int value) 30 | { 31 | return new ListItemData(NodeId, value); 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return "<" + NodeId + ", " + Value.ToString() + ">"; 37 | } 38 | 39 | // override object.Equals 40 | public override bool Equals(object obj) 41 | { 42 | if (obj == null || GetType() != obj.GetType()) 43 | { 44 | return false; 45 | } 46 | 47 | ListItemData objAsLid = obj as ListItemData; 48 | 49 | return NodeId == objAsLid.NodeId 50 | && Value == objAsLid.Value; 51 | } 52 | 53 | // override object.GetHashCode 54 | public override int GetHashCode() 55 | { 56 | return ((0x51ed270b + Value) * -1521134295 57 | + NodeId.GetHashCode()) * -1521134295; 58 | } 59 | 60 | public ListItemData(long nodeId, int value) 61 | { 62 | NodeId = nodeId; 63 | Value = value; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/Tests/Test001_/ObjectIdGenerator.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Runtime.Serialization; 21 | using System.Threading.Tasks; 22 | 23 | namespace Test.Tests.Test001_ 24 | { 25 | internal class ObjectIdGenerator 26 | { 27 | public long GetId(object o) 28 | { 29 | bool b; 30 | return generator.GetId(o, out b); 31 | } 32 | 33 | #region private 34 | private readonly ObjectIDGenerator generator = new ObjectIDGenerator(); 35 | #endregion 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operation.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_ 23 | { 24 | internal interface IOperation 25 | { 26 | T RunOnLinkedList(LinkedListExecutionState state); 27 | U RunOnLfdll(LfdllExecutionState state); 28 | } 29 | 30 | internal abstract class Operation : IOperation 31 | { 32 | public abstract T1 RunOnLinkedList( 33 | LinkedListExecutionState state); 34 | 35 | public abstract T2 RunOnLfdll( 36 | LfdllExecutionState state); 37 | 38 | public override string ToString() 39 | { 40 | return GetType().Name; 41 | } 42 | 43 | public Operation(ObjectIdGenerator idGenerator) 44 | { 45 | IdGenerator = idGenerator; 46 | } 47 | 48 | protected ObjectIdGenerator IdGenerator { get; private set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/Tests/Test001_/OperationResultComparerCreators/IOperationResultComparerCreator.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.OperationResultComparerCreators 23 | { 24 | internal interface IOperationResultComparerCreator 25 | { 26 | IOperationResultComparer CreateOperationResultComparer( 27 | Random rand, ObjectIdGenerator idGenerator, Counter counter); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Tests/Test001_/OperationResultComparerCreators/SeedAcceptingOperationComparerCreator.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using Test.Tests.Test001_.OperationResultComparers; 22 | using Test.Tests.Test001_.Operations; 23 | 24 | namespace Test.Tests.Test001_.OperationResultComparerCreators 25 | { 26 | internal class SeedAcceptingOperationComparerCreator 27 | : IOperationResultComparerCreator 28 | { 29 | public IOperationResultComparer CreateOperationResultComparer( 30 | Random rand, ObjectIdGenerator idGenerator, Counter counter) 31 | { 32 | int seed = rand.Next(); 33 | return new VoidOperationComparer( 34 | operationCreator(idGenerator, seed)); 35 | } 36 | 37 | public SeedAcceptingOperationComparerCreator( 38 | Func operationCreator) 39 | { 40 | this.operationCreator = operationCreator; 41 | } 42 | 43 | #region private 44 | private readonly Func operationCreator; 45 | #endregion 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/Tests/Test001_/OperationResultComparers/BoolReturningOperationComparer.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using Test.Tests.Test001_.Operations; 22 | 23 | namespace Test.Tests.Test001_.OperationResultComparers 24 | { 25 | internal class BoolReturningOperationComparer : OperationResultComparer 26 | { 27 | public override bool LastResultsEqual 28 | { 29 | get { return LfdllResult == LlResult; } 30 | } 31 | 32 | public BoolReturningOperationComparer(BoolReturningOperation operation) 33 | : base(operation) 34 | { 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/Tests/Test001_/OperationResultComparers/ItemDataReturningOperationComparer.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using Test.Tests.Test001_.Operations; 22 | 23 | namespace Test.Tests.Test001_.OperationResultComparers 24 | { 25 | internal class ItemDataReturningOperationComparer 26 | : OperationResultComparer 27 | { 28 | public override bool LastResultsEqual 29 | { 30 | get { return Equals(LfdllResult, LlResult); } 31 | } 32 | 33 | public ItemDataReturningOperationComparer( 34 | ItemDataReturningOperation operation) : base(operation) 35 | { 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Tests/Test001_/OperationResultComparers/NodeReturningOperationComparer.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | using Test.Tests.Test001_.Operations; 23 | 24 | namespace Test.Tests.Test001_.OperationResultComparers 25 | { 26 | internal class NodeReturningOperationComparer 27 | : OperationResultComparer, 29 | LinkedListNode> 30 | { 31 | public override bool LastResultsEqual 32 | { 33 | get 34 | { 35 | if (LlResult == null) 36 | return LfdllResult == null; 37 | if (LfdllResult == null) 38 | return false; 39 | return LlResult.Value.Data.Equals(LfdllResult.Value); 40 | } 41 | } 42 | 43 | public NodeReturningOperationComparer(NodeReturningOperation operation) 44 | : base(operation) 45 | { 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/Tests/Test001_/OperationResultComparers/VoidOperationComparer.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using Test.Tests.Test001_.Operations; 22 | 23 | namespace Test.Tests.Test001_.OperationResultComparers 24 | { 25 | internal class VoidOperationComparer : OperationResultComparer 26 | { 27 | public override bool LastResultsEqual 28 | { 29 | get { return true; } 30 | } 31 | 32 | public VoidOperationComparer(VoidOperation operation) 33 | : base(operation) 34 | { 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/BoolReturningOperation.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal abstract class BoolReturningOperation : Operation 25 | { 26 | public BoolReturningOperation(ObjectIdGenerator idGenerator) 27 | : base(idGenerator) 28 | { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/CompareExchangeValue.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal class CompareExchangeValue : ItemDataReturningOperation 25 | { 26 | public override ListItemData RunOnLinkedList( 27 | LinkedListExecutionState state) 28 | { 29 | if (state.Current == null || state.Current.Value.IsDummy) 30 | return null; 31 | ListItemData prevalentData = state.Current.Value.Data; 32 | if (prevalentData.Value != oldValue) 33 | return prevalentData; 34 | state.Current.Value = 35 | state.Current.Value.NewWithData( 36 | prevalentData.NewWithValue(newValue)); 37 | return prevalentData; 38 | } 39 | 40 | public override ListItemData RunOnLfdll(LfdllExecutionState state) 41 | { 42 | if (state.Current == null || state.Current.IsDummyNode) 43 | return null; 44 | ListItemData oldData = state.Current.Value; 45 | while (true) 46 | { 47 | if (oldData.Value != oldValue) 48 | return oldData; 49 | ListItemData prevalentData = state.Current.CompareExchangeValue( 50 | oldData.NewWithValue(newValue), oldData); 51 | if (ReferenceEquals(prevalentData, oldData)) 52 | return prevalentData; 53 | oldData = prevalentData; 54 | } 55 | } 56 | 57 | public override string ToString() 58 | { 59 | return base.ToString() + " " + oldValue + " " + newValue; 60 | } 61 | 62 | public CompareExchangeValue(ObjectIdGenerator idGenerator, int oldValue, int newValue) 63 | : base(idGenerator) 64 | { 65 | this.oldValue = oldValue; 66 | this.newValue = newValue; 67 | } 68 | 69 | #region private 70 | private readonly int oldValue; 71 | private readonly int newValue; 72 | #endregion 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/GetValue.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal class GetValue : ItemDataReturningOperation 25 | { 26 | public override ListItemData RunOnLinkedList(LinkedListExecutionState state) 27 | { 28 | return state.Current == null || state.Current.Value.IsDummy ? 29 | null : 30 | state.Current.Value.Data; 31 | } 32 | 33 | public override ListItemData RunOnLfdll(LfdllExecutionState state) 34 | { 35 | return state.Current == null || state.Current.IsDummyNode ? 36 | null : 37 | state.Current.Value; 38 | } 39 | 40 | public GetValue(ObjectIdGenerator idGenerator) 41 | : base(idGenerator) 42 | { 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/InsertAfter.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class InsertAfter : NodeCreationOperation 26 | { 27 | public override LinkedListNode RunOnLinkedList( 28 | LinkedListExecutionState state) 29 | { 30 | LinkedListNode current = state.Current; 31 | if (current == null || current == state.List.Last) 32 | return null; 33 | 34 | if (!current.Value.Deleted) 35 | return state.AddingToKnownNodes( 36 | state.List.AddAfter(current, new LinkedListItem(Value))); 37 | LinkedListNode prev = current; 38 | while (prev.Previous.Value.Deleted) 39 | { 40 | prev = prev.Previous; 41 | } 42 | return state.AddingToKnownNodes(state.List.AddBefore(prev, new LinkedListItem(Value))); 43 | } 44 | 45 | public override ILockFreeDoublyLinkedListNode RunOnLfdll( 46 | LfdllExecutionState state) 47 | { 48 | ILockFreeDoublyLinkedListNode current = state.Current; 49 | if (current == null || current == state.List.Tail) 50 | return null; 51 | return state.AddingToKnownNodes(current.InsertAfter(Value)); 52 | } 53 | 54 | public InsertAfter(ObjectIdGenerator idGenerator, ListItemData value) 55 | : base(idGenerator, value) 56 | { } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/InsertAfterIf.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class InsertAfterIf : NodeCreationOperation 26 | { 27 | public override LinkedListNode RunOnLinkedList(LinkedListExecutionState state) 28 | { 29 | if (state.Current == null 30 | || state.Current == state.List.Last 31 | || state.Current == state.List.First) 32 | return null; 33 | ListItemData oldData = state.Current.Value.Data; 34 | if (oldData.Value != prevalentValue 35 | || state.Current.Value.Deleted) 36 | { 37 | state.AddToKnownNodes(null); 38 | return null; 39 | } 40 | return 41 | state.AddingToKnownNodes( 42 | state.List.AddAfter(state.Current, new LinkedListItem(Value))); 43 | } 44 | 45 | public override ILockFreeDoublyLinkedListNode RunOnLfdll( 46 | LfdllExecutionState state) 47 | { 48 | if (state.Current == null 49 | || state.Current == state.List.Head 50 | || state.Current == state.List.Tail) 51 | return null; 52 | return 53 | state.AddingToKnownNodes( 54 | state.Current.InsertAfterIf( 55 | Value, data => data.Value == prevalentValue)); 56 | } 57 | 58 | public override string ToString() 59 | { 60 | return base.ToString() + " if value == " + prevalentValue; 61 | } 62 | 63 | public InsertAfterIf( 64 | ObjectIdGenerator idGenerator, ListItemData value, 65 | int prevalentValue) 66 | : base(idGenerator, value) 67 | { 68 | this.prevalentValue = prevalentValue; 69 | } 70 | 71 | #region private 72 | private readonly int prevalentValue; 73 | #endregion 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/InsertBefore.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class InsertBefore : NodeCreationOperation 26 | { 27 | public override LinkedListNode 28 | RunOnLinkedList( 29 | LinkedListExecutionState state) 30 | { 31 | LinkedListNode current = state.Current; 32 | if (current == null || current == state.List.First) 33 | return null; 34 | LinkedListNode successor = current; 35 | while (successor.Previous.Value.Deleted) 36 | { 37 | successor = successor.Previous; 38 | } 39 | return state.AddingToKnownNodes(state.List.AddBefore(successor, new LinkedListItem(Value))); 40 | } 41 | 42 | public override ILockFreeDoublyLinkedListNode RunOnLfdll( 43 | LfdllExecutionState state) 44 | { 45 | ILockFreeDoublyLinkedListNode current 46 | = state.Current; 47 | if (current == null || current == state.List.Head) 48 | return null; 49 | return state.AddingToKnownNodes(current.InsertBefore(Value)); 50 | } 51 | 52 | public InsertBefore(ObjectIdGenerator idGenerator, ListItemData value) 53 | : base(idGenerator, value) 54 | { } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/ItemDataReturningOperation.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal abstract class ItemDataReturningOperation 25 | : Operation 26 | { 27 | public ItemDataReturningOperation(ObjectIdGenerator idGenerator) 28 | : base(idGenerator) 29 | { 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/Next.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class Next : VoidOperation 26 | { 27 | public override object RunOnLinkedList( 28 | LinkedListExecutionState state) 29 | { 30 | LinkedListNode current = state.Current; 31 | if (current == null || current == state.List.Last) 32 | { 33 | current = state.List.Last; 34 | } 35 | else 36 | { 37 | do 38 | { 39 | current = current.Next; 40 | } while (current != null && current.Value.Deleted); 41 | } 42 | state.Current = current; 43 | return null; 44 | } 45 | 46 | public override object RunOnLfdll( 47 | LfdllExecutionState state) 48 | { 49 | ILockFreeDoublyLinkedListNode current = state.Current; 50 | if (current == null || current == state.List.Tail) 51 | { 52 | current = state.List.Tail; 53 | } 54 | else 55 | { 56 | current = current.Next; 57 | } 58 | state.Current = current; 59 | return null; 60 | } 61 | 62 | public Next(ObjectIdGenerator idGenerator) : base(idGenerator) 63 | { 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/NodeCreationOperation.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal abstract class NodeCreationOperation 25 | : NodeReturningOperation 26 | { 27 | public ListItemData Value { get; private set; } 28 | 29 | public override string ToString() 30 | { 31 | return base.ToString() + " " + Value; 32 | } 33 | 34 | public NodeCreationOperation( 35 | ObjectIdGenerator idGenerator, ListItemData value) 36 | : base(idGenerator) 37 | { 38 | Value = value; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/NodeReturningOperation.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal abstract class NodeReturningOperation : Operation< 26 | LinkedListNode, 27 | ILockFreeDoublyLinkedListNode> 28 | { 29 | public NodeReturningOperation(ObjectIdGenerator idGenerator) : base(idGenerator) 30 | { 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/PopRightNode.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class PopRightNode : NodeReturningOperation 26 | { 27 | // ReSharper disable once RedundantAssignment 28 | public override LinkedListNode RunOnLinkedList( 29 | LinkedListExecutionState state) 30 | { 31 | LinkedListNode last = state.List.Last; 32 | do 33 | { 34 | last = last.Previous; 35 | } 36 | while (last != state.List.First && last.Value.Deleted); 37 | if (last == state.List.First) 38 | return null; 39 | last.Value.Delete(); 40 | return state.AddingToKnownNodes(last); 41 | } 42 | 43 | public override ILockFreeDoublyLinkedListNode RunOnLfdll( 44 | LfdllExecutionState state) 45 | { 46 | ILockFreeDoublyLinkedListNode node 47 | = state.List.PopRightNode(); 48 | if (node != null) 49 | state.AddToKnownNodes(node); 50 | return node; 51 | } 52 | 53 | public PopRightNode(ObjectIdGenerator idGenerator) 54 | : base(idGenerator) 55 | { 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/Previous.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class Previous : VoidOperation 26 | { 27 | public override object RunOnLinkedList( 28 | LinkedListExecutionState state) 29 | { 30 | LinkedListNode previous; 31 | if (state.Current == null || state.Current == state.List.First) 32 | { 33 | previous = state.List.First; 34 | } 35 | else 36 | { 37 | previous = state.Current.Previous; 38 | while (previous.Value.Deleted) 39 | previous = previous.Previous; 40 | } 41 | state.Current = previous; 42 | return null; 43 | } 44 | 45 | public override object RunOnLfdll( 46 | LfdllExecutionState state) 47 | { 48 | ILockFreeDoublyLinkedListNode current = state.Current; 49 | if (current == null || current == state.List.Head) 50 | { 51 | current = state.List.Head; 52 | } 53 | else 54 | { 55 | current = current.Prev; 56 | } 57 | state.Current = current; 58 | return null; 59 | } 60 | 61 | public Previous(ObjectIdGenerator idGenerator) : base(idGenerator) 62 | { 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/PushLeft.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class PushLeft : NodeCreationOperation 26 | { 27 | public override LinkedListNode RunOnLinkedList( 28 | LinkedListExecutionState state) 29 | { 30 | return state.AddingToKnownNodes( 31 | state.List.AddAfter(state.List.First, new LinkedListItem(Value))); 32 | } 33 | 34 | public override ILockFreeDoublyLinkedListNode RunOnLfdll(LfdllExecutionState state) 35 | { 36 | return state.AddingToKnownNodes(state.List.PushLeft(Value)); 37 | } 38 | 39 | public PushLeft(ObjectIdGenerator idGenerator, ListItemData value) 40 | : base(idGenerator, value) 41 | { 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/PushRight.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | using LockFreeDoublyLinkedLists; 22 | 23 | namespace Test.Tests.Test001_.Operations 24 | { 25 | internal class PushRight : NodeCreationOperation 26 | { 27 | public override LinkedListNode RunOnLinkedList( 28 | LinkedListExecutionState state) 29 | { 30 | LinkedListNode successor = state.List.Last; 31 | while (successor.Previous != state.List.First && 32 | successor.Previous.Value.Deleted) 33 | { 34 | successor = successor.Previous; 35 | } 36 | return state.AddingToKnownNodes(state.List.AddBefore(successor, new LinkedListItem(Value))); 37 | } 38 | 39 | public override ILockFreeDoublyLinkedListNode RunOnLfdll( 40 | LfdllExecutionState state) 41 | { 42 | return state.AddingToKnownNodes(state.List.PushRight(Value)); 43 | } 44 | 45 | public PushRight(ObjectIdGenerator idGenerator, ListItemData value) 46 | : base(idGenerator, value) 47 | { 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/Remove.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal class Remove : BoolReturningOperation 25 | { 26 | public override bool RunOnLinkedList( 27 | LinkedListExecutionState state) 28 | { 29 | if (state.Current == null 30 | || state.Current == state.List.First 31 | || state.Current == state.List.Last 32 | || state.Current.Value.Deleted) 33 | { 34 | return false; 35 | } 36 | state.Current.Value.Delete(); 37 | return true; 38 | } 39 | 40 | public override bool RunOnLfdll( 41 | LfdllExecutionState state) 42 | { 43 | if (state.Current == null 44 | || state.Current == state.List.Head 45 | || state.Current == state.List.Tail) 46 | return false; 47 | return state.Current.Remove(); 48 | } 49 | 50 | public Remove(ObjectIdGenerator idGenerator) 51 | : base(idGenerator) 52 | { 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/SelectRandomKnownNode.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal class SelectRandomKnownNode : VoidOperation 25 | { 26 | public override object RunOnLinkedList(LinkedListExecutionState state) 27 | { 28 | return run(state); 29 | } 30 | 31 | public override object RunOnLfdll(LfdllExecutionState state) 32 | { 33 | return run(state); 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return base.ToString() + ", seed: " + seed; 39 | } 40 | 41 | public SelectRandomKnownNode( 42 | ObjectIdGenerator idGenerator, int seed) 43 | : base(idGenerator) 44 | { 45 | this.seed = seed; 46 | } 47 | 48 | #region private 49 | private readonly int seed; 50 | 51 | private object run(IExecutionState state) 52 | { 53 | state.CurrentIndex = new Random(seed) 54 | .Next(state.KnownNodesCollection.Count); 55 | return null; 56 | } 57 | #endregion 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/SetValue.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal class SetValue : VoidOperation 25 | { 26 | public override object RunOnLinkedList(LinkedListExecutionState state) 27 | { 28 | LinkedListNode current = state.Current; 29 | if (current != null 30 | && current != state.List.First 31 | && current != state.List.Last) 32 | { 33 | current.Value = current.Value.NewWithData( 34 | current.Value.Data.NewWithValue(value)); 35 | } 36 | return null; 37 | } 38 | 39 | public override object RunOnLfdll(LfdllExecutionState state) 40 | { 41 | if (state.Current != null 42 | && state.Current != state.List.Head 43 | && state.Current != state.List.Tail) 44 | { 45 | long nodeId = state.Current.Value.NodeId; 46 | state.Current.Value = new ListItemData(nodeId, value); 47 | } 48 | return null; 49 | } 50 | 51 | public SetValue(ObjectIdGenerator idGenerator, int value) 52 | : base(idGenerator) 53 | { 54 | this.value = value; 55 | } 56 | 57 | #region private 58 | private readonly int value; 59 | #endregion 60 | } 61 | } -------------------------------------------------------------------------------- /test/Tests/Test001_/Operations/VoidOperation.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_.Operations 23 | { 24 | internal abstract class VoidOperation : Operation 25 | { 26 | public VoidOperation(ObjectIdGenerator idGenerator) 27 | : base(idGenerator) 28 | { 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/Tests/Test001_/TestIterationParameters.cs: -------------------------------------------------------------------------------- 1 | #region license 2 | // Copyright 2016 Christoph Müller 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | #endregion 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Threading.Tasks; 21 | 22 | namespace Test.Tests.Test001_ 23 | { 24 | internal class TestIterationParameters 25 | { 26 | public List OperationSequences 27 | { get; set; } 28 | 29 | public int InitialListLength { get; set; } 30 | public int InitializationSeed { get; set; } 31 | public int ExecutionSeed { get; set; } 32 | } 33 | 34 | internal class ExecutionSequenceParameters 35 | { 36 | public List Operations { get; set; } 37 | public int StartIndex { get; set; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Exe 7 | netcoreapp3.1 8 | false 9 | 10 | 11 | true 12 | full 13 | false 14 | DEBUG;TRACE 15 | 16 | 17 | pdbonly 18 | true 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------