-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlist_delete.go
129 lines (114 loc) · 4.49 KB
/
list_delete.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package linkedlist
// Delete searches given node from the specified position in the list and
// removes it. In case if deletition is sucessful removed node retuns. If
// node wasn't found in the list function reports nil as return value
//
// Function returns two boolean flags:
// - node has been deleted
// - node has been deleted by the current call
//
// Latest flag allows to determinate winner in case if two thread removes same
// node. Only one of a such threads will get true in latest boolean result
func Delete(start, delNode Node) (Node, bool, bool) {
update := &State{}
left, right := start, LoadState(start).Next
for {
// Looking for the node to remove. Do not pay attention
// onto possible node states during the scan, all combinations
// will be handled by WeakDelete
for right != delNode {
left, right = right, LoadState(right).Next
// Not found
if right == nil {
return nil, false, false
}
}
// Delete
p, suc, byme := WeakDelete(left, right, update)
if suc {
return p, suc, byme
}
// Failed to delete, so start now points to the new predecessor
left, right = p, LoadState(p).Next
}
}
// WeakDelete trys to delete right node from the given pair. Operation might
// fails due to concurrent structural modifications of the list:
// - new node inserted in between of left and right
// - left node has been deleted
// - right node has been deleted
// In all that cases WeakDelete reports a fail.
//
// Function returns leftmost alive node from the {left, right} pair. In case if
// left node has been deleted by the concurrent thread righmost alive predecessor
// of it will be returned
//
// Along with node, function returns two boolean flags:
// - is right node has been deleted
// - is right node has been deleted by the current thread
func WeakDelete(left, right Node, update *State) (Node, bool, bool) {
update.Next = right
update.Back = nil
update.Flags = FREEZE
// Try to mark left node as freezed. That is the only sync point where
// delete operation could say that the current thread start deletition of
// the node
//
// Once node marked as freezed it successor will be removed during any list
// operation (delete, insert or next). So report node deleted and deleted by
// the current thread
if UpdateState(left, right, NONE, update) {
// fmt.Printf("Freezed: %s -> %s\n", prevNode, delNode)
CompleteDelete(left, right)
return left, true, true
}
// We have failed to mark node as removed. It could be 3 different reasons
prev := LoadState(left)
// 1. It might be that del node is not a successor of prev node, in that case
// freeze can't be done. Do not try to recover from structural modification
if prev.Next != right {
return left, false, false
}
// 2. Update might fails because concurrent thread already
// freezed node, so just help some other thread to complete
// removal
if prev.IsFreezed() {
CompleteDelete(left, right)
return left, true, false
}
// 3. Concurrent thread might already delete node, in that
// case we need to step back and find a new delNode predessor
for prev.IsRemoved() {
left, prev = prev.Back, LoadState(prev.Back)
}
// 3. It could be that delNode is not a successor of prevNode because
// has been removed or because prevNode has been deleted and we walked
// too far by backlinks. In any way we need to search for the key again
return left, false, false
}
// CompleteDelete helps to complete removal of a node just after predecessor has been freezed.
//
// Prev node is freezed and it means that del node should be removed in a few
// steps: setup backlink, mark as removed and delete it phisically
func CompleteDelete(prev, del Node) {
// First of all mark DEL node as removed
expected, new := LoadState(del), &State{Next: nil, Back: prev, Flags: DELETE}
for !expected.IsRemoved() {
// Calculate new desired state and try to setup
new.Next = expected.Next
// If del node freezed then it successor should be removed before
// remove del node itself
if expected.IsFreezed() {
CompleteDelete(del, expected.Next)
} else if UpdateState(del, expected.Next, NONE, new) {
// fmt.Printf("Marked: %s -> %s\n", prev, del)
break
}
// setup failed, means expected state is outdated - read it again and try
// one more time. Note that as prev node has been freezed del node must
// endup in DELETE state no matter what, so that cycle will always ends
expected = LoadState(del)
}
// Unlink node, consider two options:
UpdateState(prev, del, FREEZE, &State{Next: expected.Next, Back: nil, Flags: NONE})
}