| 335 | |
| 336 | template <typename Key, class Comparator> |
| 337 | void SkipList<Key, Comparator>::Insert(const Key& key) { |
| 338 | // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual() |
| 339 | // here since Insert() is externally synchronized. |
| 340 | Node* prev[kMaxHeight]; |
| 341 | Node* x = FindGreaterOrEqual(key, prev); |
| 342 | |
| 343 | // Our data structure does not allow duplicate insertion |
| 344 | assert(x == nullptr || !Equal(key, x->key)); |
| 345 | |
| 346 | int height = RandomHeight(); |
| 347 | if (height > GetMaxHeight()) { |
| 348 | for (int i = GetMaxHeight(); i < height; i++) { |
| 349 | prev[i] = head_; |
| 350 | } |
| 351 | // It is ok to mutate max_height_ without any synchronization |
| 352 | // with concurrent readers. A concurrent reader that observes |
| 353 | // the new value of max_height_ will see either the old value of |
| 354 | // new level pointers from head_ (nullptr), or a new value set in |
| 355 | // the loop below. In the former case the reader will |
| 356 | // immediately drop to the next level since nullptr sorts after all |
| 357 | // keys. In the latter case the reader will use the new node. |
| 358 | max_height_.store(height, std::memory_order_relaxed); |
| 359 | } |
| 360 | |
| 361 | x = NewNode(key, height); |
| 362 | for (int i = 0; i < height; i++) { |
| 363 | // NoBarrier_SetNext() suffices since we will add a barrier when |
| 364 | // we publish a pointer to "x" in prev[i]. |
| 365 | x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i)); |
| 366 | prev[i]->SetNext(i, x); |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | template <typename Key, class Comparator> |
| 371 | bool SkipList<Key, Comparator>::Contains(const Key& key) const { |