(Machine m, boolean isNew)
| 237 | // simultaneous matches run using |this|. (The cache empties when |this| |
| 238 | // gets garbage collected or reset is called.) |
| 239 | @SuppressWarnings("MakeAlwaysEqual") // for ErrorProne, see below |
| 240 | void put(Machine m, boolean isNew) { |
| 241 | // To avoid allocation in the single-thread or uncontended case, reuse a node only if |
| 242 | // it was the only element in the stack when it was popped, and it's the only element |
| 243 | // in the stack when it's pushed back after use. |
| 244 | Machine head; |
| 245 | do { |
| 246 | head = pooled.get(); |
| 247 | if (!isNew && head != null) { |
| 248 | // If an element had a null next pointer and it was previously in the stack, another thread |
| 249 | // might be trying to pop it out right now, and if it sees the same node now in the |
| 250 | // stack the pop will succeed, but the new top of the stack will be the stale (null) value |
| 251 | // of next. Allocate a new Machine so that the CAS will not succeed if this node has been |
| 252 | // popped and re-pushed. |
| 253 | m = new Machine(m); |
| 254 | isNew = true; |
| 255 | } |
| 256 | |
| 257 | // Without this comparison, TSAN will complain about a race condition: |
| 258 | // Thread A, B, and C all attempt to do a match on the same pattern. |
| 259 | // |
| 260 | // A: Allocates Machine 1; executes match; put machine 1. State is now: |
| 261 | // |
| 262 | // pooled -> machine 1 -> null |
| 263 | // |
| 264 | // B reads pooled, sees machine 1 |
| 265 | // |
| 266 | // C reads pooled, sees machine 1 |
| 267 | // |
| 268 | // B successfully CASes pooled to null |
| 269 | // |
| 270 | // B executes match; put machine 1, which involves setting machine1.next to |
| 271 | // null (even though it's already null); preempted before CAS |
| 272 | // |
| 273 | // C resumes, and reads machine1.next in order to execute cas(head, head.next) |
| 274 | // |
| 275 | // There is no happens-before relationship between B's redundant null write |
| 276 | // and C's read, thus triggering TSAN. |
| 277 | // |
| 278 | // A future release of ErrorProne may want to make the assignment unconditionally. The |
| 279 | // @SuppressWarning("MakeAlwaysEqual") on this method is intended to prevent that from happening. |
| 280 | if (m.next != head) { |
| 281 | m.next = head; |
| 282 | } |
| 283 | } while (!pooled.compareAndSet(head, m)); |
| 284 | } |
| 285 | |
| 286 | @Override |
| 287 | public String toString() { |
no test coverage detected