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