@@ -31,21 +31,45 @@ public CancellableTasksTracker(T[] empty) {
31
31
}
32
32
33
33
private final Map <Long , T > byTaskId = ConcurrentCollections .newConcurrentMapWithAggressiveConcurrency ();
34
- private final Map <TaskId , T []> byParentTaskId = ConcurrentCollections .newConcurrentMapWithAggressiveConcurrency ();
34
+ private final Map <TaskId , Map <Long , T []>> byParentTaskId = ConcurrentCollections .newConcurrentMapWithAggressiveConcurrency ();
35
+
36
+ /**
37
+ * Gets the cancellable children of a parent task.
38
+ *
39
+ * Note: children of non-positive request IDs (e.g., -1) may be grouped together.
40
+ */
41
+ public Stream <T > getChildrenByRequestId (TaskId parentTaskId , long childRequestId ) {
42
+ Map <Long , T []> byRequestId = byParentTaskId .get (parentTaskId );
43
+ if (byRequestId != null ) {
44
+ T [] children = byRequestId .get (childRequestId );
45
+ if (children != null ) {
46
+ return Arrays .stream (children );
47
+ }
48
+ }
49
+ return Stream .empty ();
50
+ }
35
51
36
52
/**
37
53
* Add an item for the given task. Should only be called once for each task, and {@code item} must be unique per task too.
38
54
*/
39
- public void put (Task task , T item ) {
55
+ public void put (Task task , long requestId , T item ) {
40
56
final long taskId = task .getId ();
41
57
if (task .getParentTaskId ().isSet ()) {
42
- byParentTaskId .compute (task .getParentTaskId (), (ignored , oldValue ) -> {
43
- if (oldValue == null ) {
44
- oldValue = empty ;
58
+ byParentTaskId .compute (task .getParentTaskId (), (taskKey , oldRequestIdMap ) -> {
59
+ if (oldRequestIdMap == null ) {
60
+ oldRequestIdMap = ConcurrentCollections . newConcurrentMapWithAggressiveConcurrency () ;
45
61
}
46
- final T [] newValue = Arrays .copyOf (oldValue , oldValue .length + 1 );
47
- newValue [oldValue .length ] = item ;
48
- return newValue ;
62
+
63
+ oldRequestIdMap .compute (requestId , (requestIdKey , oldValue ) -> {
64
+ if (oldValue == null ) {
65
+ oldValue = empty ;
66
+ }
67
+ final T [] newValue = Arrays .copyOf (oldValue , oldValue .length + 1 );
68
+ newValue [oldValue .length ] = item ;
69
+ return newValue ;
70
+ });
71
+
72
+ return oldRequestIdMap ;
49
73
});
50
74
}
51
75
final T oldItem = byTaskId .put (taskId , item );
@@ -60,36 +84,50 @@ public T get(long id) {
60
84
}
61
85
62
86
/**
63
- * Remove (and return) the item that corresponds with the given task. Return {@code null} if not present. Safe to call multiple times
64
- * for each task. However, {@link #getByParent} may return this task even after a call to this method completes, if the removal is
65
- * actually being completed by a concurrent call that's still ongoing.
87
+ * Remove (and return) the item that corresponds with the given task and request ID . Return {@code null} if not present. Safe to call
88
+ * multiple times for each task. However, {@link #getByParent} may return this task even after a call to this method completes, if
89
+ * the removal is actually being completed by a concurrent call that's still ongoing.
66
90
*/
67
91
public T remove (Task task ) {
68
92
final long taskId = task .getId ();
69
93
final T oldItem = byTaskId .remove (taskId );
70
94
if (oldItem != null && task .getParentTaskId ().isSet ()) {
71
- byParentTaskId .compute (task .getParentTaskId (), (ignored , oldValue ) -> {
72
- if (oldValue == null ) {
95
+ byParentTaskId .compute (task .getParentTaskId (), (taskKey , oldRequestIdMap ) -> {
96
+ if (oldRequestIdMap == null ) {
73
97
return null ;
74
98
}
75
- if (oldValue .length == 1 ) {
76
- if (oldValue [0 ] == oldItem ) {
77
- return null ;
78
- } else {
99
+
100
+ for (Long requestId : oldRequestIdMap .keySet ()) {
101
+ oldRequestIdMap .compute (requestId , (requestIdKey , oldValue ) -> {
102
+ if (oldValue == null ) {
103
+ return null ;
104
+ }
105
+ if (oldValue .length == 1 ) {
106
+ if (oldValue [0 ] == oldItem ) {
107
+ return null ;
108
+ } else {
109
+ return oldValue ;
110
+ }
111
+ }
112
+ if (oldValue [0 ] == oldItem ) {
113
+ return Arrays .copyOfRange (oldValue , 1 , oldValue .length );
114
+ }
115
+ for (int i = 1 ; i < oldValue .length ; i ++) {
116
+ if (oldValue [i ] == oldItem ) {
117
+ final T [] newValue = Arrays .copyOf (oldValue , oldValue .length - 1 );
118
+ System .arraycopy (oldValue , i + 1 , newValue , i , oldValue .length - i - 1 );
119
+ return newValue ;
120
+ }
121
+ }
79
122
return oldValue ;
80
- }
81
- }
82
- if (oldValue [0 ] == oldItem ) {
83
- return Arrays .copyOfRange (oldValue , 1 , oldValue .length );
123
+ });
84
124
}
85
- for (int i = 1 ; i < oldValue .length ; i ++) {
86
- if (oldValue [i ] == oldItem ) {
87
- final T [] newValue = Arrays .copyOf (oldValue , oldValue .length - 1 );
88
- System .arraycopy (oldValue , i + 1 , newValue , i , oldValue .length - i - 1 );
89
- return newValue ;
90
- }
125
+
126
+ if (oldRequestIdMap .keySet ().isEmpty ()) {
127
+ return null ;
91
128
}
92
- return oldValue ;
129
+
130
+ return oldRequestIdMap ;
93
131
});
94
132
}
95
133
return oldItem ;
@@ -109,11 +147,11 @@ public Collection<T> values() {
109
147
* started before this method was called have not completed.
110
148
*/
111
149
public Stream <T > getByParent (TaskId parentTaskId ) {
112
- final T [] byParent = byParentTaskId .get (parentTaskId );
150
+ final Map < Long , T []> byParent = byParentTaskId .get (parentTaskId );
113
151
if (byParent == null ) {
114
152
return Stream .empty ();
115
153
}
116
- return Arrays . stream (byParent );
154
+ return byParent . values (). stream (). flatMap ( Stream :: of );
117
155
}
118
156
119
157
// assertion for tests, not an invariant but should eventually be true
@@ -123,12 +161,14 @@ boolean assertConsistent() {
123
161
124
162
// every by-parent value must be tracked by task too; the converse isn't true since we don't track values without a parent
125
163
final Set <T > byTaskValues = new HashSet <>(byTaskId .values ());
126
- for (T [] byParent : byParentTaskId .values ()) {
127
- assert byParent .length > 0 ;
128
- for (T t : byParent ) {
129
- assert byTaskValues .contains (t );
130
- }
131
- }
164
+ byParentTaskId .values ().forEach (byParentMap -> {
165
+ byParentMap .forEach ((requestId , byParentArray ) -> {
166
+ assert byParentArray .length > 0 ;
167
+ for (T t : byParentArray ) {
168
+ assert byTaskValues .contains (t );
169
+ }
170
+ });
171
+ });
132
172
133
173
return true ;
134
174
}
0 commit comments