Hazard Pointers C++ Memory Ordering Issues

Slides:



Advertisements
Similar presentations
Chapter 7: Deadlocks.
Advertisements

Lists A list is a finite, ordered sequence of data items. Important concept: List elements have a position. Notation: What operations should we implement?
DATA STRUCTURES USING C++ Chapter 5
Linked Lists CSE 2451 Matt Boggus. Dynamic memory reminder Allocate memory during run-time malloc() and calloc() – return a void pointer to memory or.
Hazard Pointers: Safe Memory Reclamation of Lock-Free Objects
Hazard Pointers: Safe Memory Reclamation for Lock-Free Objects
Dynamic Memory Allocation (also see pointers lectures) -L. Grewe.
CS510 – Advanced Operating Systems 1 The Synergy Between Non-blocking Synchronization and Operating System Structure By Michael Greenwald and David Cheriton.
Maged M. Michael, “Hazard Pointers: Safe Memory Reclamation for Lock- Free Objects” Presentation Robert T. Bauer.
Lecture 10: Heap Management CS 540 GMU Spring 2009.
Wait-Free Reference Counting and Memory Management Håkan Sundell, Ph.D.
Locality-Conscious Lock-Free Linked Lists Anastasia Braginsky & Erez Petrank 1.
Introduction to Lock-free Data-structures and algorithms Micah J Best May 14/09.
1 Lock-Free Linked Lists Using Compare-and-Swap by John Valois Speaker’s Name: Talk Title: Larry Bush.
Relativistic Red Black Trees. Relativistic Programming Concurrent reading and writing improves performance and scalability – concurrent readers may disagree.
Chapter 1 Object Oriented Programming. OOP revolves around the concept of an objects. Objects are created using the class definition. Programming techniques.
Wait-Free Multi-Word Compare- And-Swap using Greedy Helping and Grabbing Håkan Sundell PDPTA 2009.
Practical concurrent algorithms Mihai Letia Concurrent Algorithms 2012 Distributed Programming Laboratory Slides by Aleksandar Dragojevic.
Hazard Pointers: Safe Memory Reclamation for Lock-Free Objects Maged M. Michael Presented by Abdulai Sei.
CS510 Concurrent Systems Jonathan Walpole. RCU Usage in Linux.
Linked lists. Data structures to store a collection of items Data structures to store a collection of items are commonly used Typical operations on such.
CPS110: Thread cooperation Landon Cox. Constraining concurrency  Synchronization  Controlling thread interleavings  Some events are independent  No.
Hazard Pointers: Safe Memory Reclamation for Lock-Free Objects MAGED M. MICHAEL PRESENTED BY NURIT MOSCOVICI ADVANCED TOPICS IN CONCURRENT PROGRAMMING,
Read-Log-Update A Lightweight Synchronization Mechanism for Concurrent Programming Alexander Matveev (MIT) Nir Shavit (MIT and TAU) Pascal Felber (UNINE)
Processes 2 Introduction to Operating Systems: Module 4.
CSCS-200 Data Structure and Algorithms Lecture
Single Linked Lists Objectives In this lesson, you will learn to: *Define single linked list *Identify the following types of linked lists: Single linked.
Read-Copy-Update Synchronization in the Linux Kernel 1 David Ferry, Chris Gill CSE 522S - Advanced Operating Systems Washington University in St. Louis.
Linked Lists Data Structures and Algorithms CS 244 Brent M. Dingle, Ph.D. Department of Mathematics, Statistics, and Computer Science University of Wisconsin.
LINKED LISTS.
December 1, 2006©2006 Craig Zilles1 Threads & Atomic Operations in Hardware  Previously, we introduced multi-core parallelism & cache coherence —Today.
Symmetric Multiprocessors: Synchronization and Sequential Consistency
Module 9: Memory and Resource Management
Lectures linked lists Chapter 6 of textbook
Data Structure and Algorithms
Håkan Sundell Philippas Tsigas
Lock-Free Linked Lists Using Compare-and-Swap
Department of Computer Science, University of Rochester
Atomic Operations in Hardware
Atomic Operations in Hardware
Extra: B+ Trees CS1: Java Programming Colorado State University
Concepts of programming languages
Expander: Lock-free Cache for a Concurrent Data Structure
Linked lists Motivation: we can make arrays, but their functionality is slightly limited and can be difficult to work with Biggest issue: size management.
CS510 Concurrent Systems Jonathan Walpole.
This pointer, Dynamic memory allocation, Constructors and Destructor
Practical Non-blocking Unordered Lists
LINKED LISTS CSCD Linked Lists.
Anders Gidenstam Håkan Sundell Philippas Tsigas
Prof. Neary Adapted from slides by Dr. Katherine Gibson
Non-blocking data structures and transactional memory
Circular Buffers, Linked Lists
Linked Lists.
Concurrent Data Structures Concurrent Algorithms 2016
CS510 Concurrent Systems Jonathan Walpole.
Lists List: finite sequence of data elements
CS510 - Portland State University
Linked Lists.
[Chapter 4; Chapter 6, pp ] CSC 143 Linked Lists [Chapter 4; Chapter 6, pp ]
Software Transactional Memory Should Not be Obstruction-Free
Multicore programming
CS510 Advanced Topics in Concurrency
C Programming Lecture-8 Pointers and Memory Management
Data Structures & Algorithms
CSE 153 Design of Operating Systems Winter 19
Lecture No.02 Data Structures Dr. Sohail Aslam
CS510 Concurrent Systems Jonathan Walpole.
EECE.3220 Data Structures Instructor: Dr. Michael Geiger Spring 2019
CPS110: Thread cooperation
Data Structures & Programming
Presentation transcript:

Hazard Pointers C++ Memory Ordering Issues Maged Michael Facebook NY Dagstuhl, 21-25 November 2016

Maged Michael - Hazard Pointers References Maged Michael, Hazard Pointers: Safe Memory Reclamation for Lock-Free Objects. IEEE Transactions on Parallel and Distributed Systems. 15 (8): 491–504, June 2004. [P0233R2] Latest version of the proposal to the C++ Standard Committee http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0233r2.pdf Prototype library under facebook/folly/experimental https://github.com/facebook/folly/tree/master/folly/experimental/hazptr Maged Michael - Hazard Pointers

Problems

Running Example: Wide CAS Wide CAS (compare and set) operates atomically on memory locations wider than the width of standard atomic primitives Wide CAS X atomically if (X == u) X = v return true else return false u A common solution: copy-on-write Place wide data in a dynamic block Updates replace the block P u v Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Wide CAS Class class WideCAS { class Node { T val_; ... }; atomic<Node*> s_ = {new Node()}; bool compareAndSet(T& u, T& v) { while (true) { Node* p = s_.load(); if (p->val_ != u) return false; Node* n = new Node(v); if (s_.compare_exchange_weak(p, n)) break; delete n; } delete p; return true; }; ABA Problem Unsafe Memory Reclamation Unsafe Memory Access incorrect Maged Michael - Hazard Pointers

Unsafe Memory Reclamation Example (Wide CAS) 1 Thread i reads pointer value A from s_ Node* p = s_.load(); if (p->val_ != u) ... Node* n = new Node(v); if (!s_.cas(p,n)) ... delete p; return true; 1 2 Thread j sets s_ to B and frees A to OS 3 3 Thread i accesses unmapped memory ACCESS VIOLATION s_ u A w B returned to OS Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers The ABA Problem Example 1 Thread i reads A from s_ 1 Node* p = s_.load(); if (p->val_ != u) ... Node* n = new Node(v); if (!s_.cas(p,n)) ... delete p; return true; 2 2 Thread i reads u from *A 6 3 Thread j sets s_ to B 7 4 Thread j reuses block A to hold value z s_ 5 Thread j sets s_ to A again u A w B 6 Thread i allocates block C to hold value v v C z A Thread i checks that s_ is equal to A CAS succeeds although s_->val_ == z != u 7 INCORRECT OUTCOME Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Non-Blocking Progress Guarantees Three levels of non-blocking progress: An operation is wait-free, if whenever a thread executing the operation takes a finite number of steps, the operation must have completed, regardless of the actions/inaction of other threads. An operation is lock-free, if whenever a thread executing the operation takes a finite number of steps, some operation must have completed, regardless of the actions/inaction of other threads. An operation is obstruction-free, if whenever a thread executing the operation takes a finite number of steps alone, the operation must have completed, regardless of where the other threads stopped. Maged Michael - Hazard Pointers

Hazard Pointers

Maged Michael - Hazard Pointers Features Lock-free progress end-to-end Bounded to-be-reclaimed objects No contention among readers Can reclaim cycles Unrestricted reclamation Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Protecting Objects A hazard pointer is a single-writer multi-reader pointer Each hazard pointer has one owner (that can write to it) By setting a hazard pointer to the address of an object, the owner is telling all threads: “if you remove this object after the last time I set this hazard pointer to this object don’t reclaim this object until the hazard pointer changes” Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Reclaiming Objects 1. Read active hazard pointers. Keep a private copy of non-null values Private copy can be arranged in an efficient search structure e.g., O(1) expected lookup time 2. For each removed object, do a lookup in the private structure Found? Keep the object for a future scan of hazard pointers Not found? Reclaim the object Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Wide CAS with Hazard Pointers 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class WideCAS { class Node : hazptr_obj_base <Node> { T val_: ... }; atomic<Node*> s_ = {new Node()}; ... bool compareAndSet(T& u, T& v) { hazptr_owner<Node> hptr; do { Node* p = p_.load(); hptr.set(p); if (s_.load() != p)) continue; if (p->val_ != u) return false; // access hazard Node* n = new Node(v); if (s_.compare_exchange_weak(p, n)) break; // aba hazard delete n; } while (true); hptr.clear(); delete p; p.retire(); // reclaim when safe return true; } }; Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Thread Roles User Owns hazard pointers Protects objects E.g., Traverses linked structures Remover Makes objects unreachable Prohibits the creation of new references to objects E.g., Removes objects from data structures Reclaimer Checks hazard pointers Reclaims objects that are not protected by hazard pointers Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Simplest Fully Concurrent Form of Hazard Pointers User Write hp := obj // no need for atomicity Read src == obj <safe use of obj> Write hp := !obj // no need for atomicity Remover / Reclaimer Write src := !obj Read hp != obj //no need for atomicity <reclaim obj> Maged Michael - Hazard Pointers

C++ Memory Ordering

Maged Michael - Hazard Pointers Three-Thread Pattern User hp.store(obj) src.load() == obj <unsafe use of obj> (prohibited) // application hp.store(nullptr) Remover src.store(other,maybe_weak) // application retired.insert(obj) Reclaimer retired.contains(obj) // bulk hp.load() != obj // bulk <reclaim obj> // application Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Three-Thread Pattern User hp.store(1) src.load() == 0 obj.load() == 1 (prohibited) // application hp.store(0) Remover src.store(1,maybe_weak) // application retired.store(1) Reclaimer retired.load() == 1 // bulk hp.load() == 0 // bulk obj.store(1,maybe_weak) // application Maged Michael - Hazard Pointers

is this all the needed ordering? Three-Thread Pattern Memory Order Using Herd User hp.store(1,release) fence(seq_cst) src.load(relaxed) == 0 obj.load() == 1 (prohibited) // application hp.store(0,release) Remover src.store(1, maybe_weak) // application fence(seq_cst) retired.store(1,relaxed) Reclaimer retired.load(relaxed) == 1 // bulk fence(seq_cst) hp.load(relaxed) == 0 // bulk fence(acquire) obj.store(1,maybe_weak) // application is this all the needed ordering? Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Four-Thread Pattern User hp.store(obj) src.load() == obj <unsafe use of obj> (prohibited) // application hp.store(nullptr) Remover src.store(other,maybe_weak) // application retired.insert(obj) Reclaimer retired.contains(obj) // bulk hp.load() != obj // bulk <reclaim obj> // application Reuser <reallocate obj> // application src.store(obj, release) // application Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Four-Thread Pattern User hp.store(1) src.load() == 0 obj.load() == 1 (prohibited) // application hp.store(0) Remover src.store(1,maybe_weak) // application retired.store(1) Reclaimer retired.load() == 1 // bulk hp.load() == 0 // bulk obj.store(1,maybe_weak) // application Reuser obj.load(maybe_weak) == 1 // application obj.store(0,maybe_weak) // application src.store(0, release) // application Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Four-Thread Pattern Memory Order Using Herd User hp.store(1,release) fence(seq_cst) src.load(acquire) == 0 obj.load() == 1 (prohibited) // application hp.store(0,release) Remover src.store(1,maybe_weak) // application fence(seq_cst) retired.store(1,relaxed) Reclaimer retired.load(,relaxed) == 1 // bulk fence(seq_cst) hp.load(relaxed) == 0 // bulk fence(acquire) obj.store(1,maybe_weak) // application Reuser obj.load(maybe_weak) == 1 // application obj.store(0,maybe_weak) // application src.store(0, release) // application Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Hazard Pointers Functions with Memory Order void set(T* ptr) { hp.store(release); fence(seq_cst); } bool try_protect(T* ptr,atomic<T*>& src) { set(ptr); return src.load(acquire) == ptr; void clear() { hp.store(nullptr,release); User Remover void retire() { fence(seq_cst); retired.insert(this); } Void bulkReclaim() { List objs = retired.extractAll(); // bulk fence(seq_cst); Set h = getHPVals(); // bulk fence(acquire) reclaimUnmatched(objs,hps); // bulk } Reclaimer Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Summary of Experience Support for RMW operations is important Automatic generation of valid memory order combinations would be convenient Maged Michael - Hazard Pointers

Thank You

Split Reference Counting atomic_shared_ptr RCU (Read-Copy-Update) Safe Reclamation Solutions Reference Counting shared_ptr Split Reference Counting atomic_shared_ptr Hazard Pointers RCU (Read-Copy-Update) Unreclaimed objects Bounded (+chains) Bounded Unbounded Non-blocking traversal Blocking (or lock-free w/ restrictions) lock-free Lock-free Wait-free Non-blocking reclamation Wait-free (lock-free with reclamation) Blocking Contention among readers Can be very high No contention Traversal speed Atomic updates (~2) Several Atomic updates (~6) Store-load fence No or low overhead Automatic reclamation Yes (restricted if lock-free) Yes No Cycle Reclamation Maged Michael - Hazard Pointers

C++ Interface

Maged Michael - Hazard Pointers Template Library Interface class hazptr_domain; template <typename T, template Deleter = std::default_delete<T>> hazptr_obj_base; template <typename T> hazptr_owner; Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers hazptr_domain class hazptr_domain { public: constexpr explicit hazptr_domain( std::pmr::memory_resource* /*C++17*/ = std::pmr::get_default_resource()) noexcept; ~hazptr_domain(); }; hazptr_domain& default_hazptr_domain(); Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers hazptr_obj_base template <typename T, template Deleter = std::default_delete<T>> hazptr_obj_base { public: void retire(hazptr_domain& domain = default_hazptr_domain(), Deleter reclaim = {}); }; Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers hazptr_owner template <typename T> class hazptr_owner { public: /* Automatically acquire a hazard pointer */ explicit hazptr_owner( hazptr_domain& domain = default_hazptr_domain()); /* Automatically clear and release the owned hazard pointer */ ~hazptr_owner(); /** Hazard pointer operations */ /* Return true if successful in protecting the object. * Otherwise set ptr to src */ bool try_protect(T*& ptr, const std::atomic<T*>& src) noexcept; /* Get a protected reference from a source */ T* get_protected(const std::atomic<T*>& src) noexcept; /* Set the hazard pointer to ptr */ void set(const T* ptr) noexcept; /* Clear the hazard pointer */ void clear() noexcept; Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers hazptr_owner continued /* Swap ownership of hazard pointers with another hazptr_owner. * The owned hazard pointers remain unmodified during the swap and * continue to protect the respective objects that they were * protecting before the swap, if any. */ void swap(hazptr_owner<T>&) noexcept; }; Template <typename T> void swap(hazptr_owner<T>&, hazptr_owner<T>&) noexcept; Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Wide CAS with Hazard Pointers Template Interface class WideCAS { class Node : hazptr_obj_base <Node> { T val_: ... }; atomic<Node*> s_ = {new Node()}; ... bool compareAndSet(T& u, T& v) { do { Node* p = s_.load(); hazptr_owner<Node> hptr; if (!hptr.try_protect(p, s_)) continue; if (p->val_ != u) return false; // access hazard Node* n = new Node(v); if (s_.compare_exchange_weak(p, n)) break; // aba hazard delete n; // Automatically clear and release the owned hazard pointer. } while (true); p.retire(); return true; } }; Maged Michael - Hazard Pointers

Maged Michael - Hazard Pointers Search Ordered Singly Linked List bool contains(T val) { // Acquire two hazard pointers for hand-over-hand traversal. hazptr_owner<Node> hptr_prev, hptr_curr; T elem; bool done = false; while (!done) { std::atomic<Node*>* prev = &head_; Node* curr = prev->load(); while (true) { if (!curr) { return false; } if (!hptr_curr.try_protect(curr, *prev)) break; Node* next = curr->next_.load(); // access hazard elem = curr->elem_; // access hazard if (prev->load() != curr) break; // aba hazard if (elem >= val) { done = true; break; } prev = &(curr->next_); curr = next; // hand-over-hand swap(hptr_curr, hptr_prev); } return elem == val; // The hazard pointers are released automatically. Maged Michael - Hazard Pointers