0% found this document useful (0 votes)
14 views53 pages

Lock

The document discusses concurrency and lock-based concurrent data structures. It introduces thread safety and how adding locks to a data structure can make it usable by multiple threads concurrently while maintaining correctness and performance. It provides examples of concurrent counters, linked lists, queues, and hash tables. It explains issues that can arise with locks and strategies for resolving them, such as using approximate counters to reduce lock overhead.

Uploaded by

razi haider
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views53 pages

Lock

The document discusses concurrency and lock-based concurrent data structures. It introduces thread safety and how adding locks to a data structure can make it usable by multiple threads concurrently while maintaining correctness and performance. It provides examples of concurrent counters, linked lists, queues, and hash tables. It explains issues that can arise with locks and strategies for resolving them, such as using approximate counters to reduce lock overhead.

Uploaded by

razi haider
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 53

Operating System (OS)

CS232
Concurrency: Lock-based Concurrent Data Structures

Dr. Muhammad Mobeen Movania


Dr Muhammad Saeed
Outlines
• What is thread safety of a data structure?
• Concurrent Counter
• Issues and resolution of Concurrent Counter
• Concurrent Linked Lists
• Concurrent Queues
• Concurrent Hash Tables
• Summary
What is thread safety?
• Adding locks to a data structure to make it
usable by multiple threads concurrently
makes the data structure thread safe
• How locks are added determines correctness
and performance of the data structure
• Many different data structures exist, simplest
is a concurrent counter
Concurrent Counter
• Simple counter without locks is not thread
safe
Concurrent Counter (2)
• Counter with lock is thread safe
Concurrent Counter (3)
• Performance of
concurrent counter
scales poorly
– Locks add additional
overhead which
reduces performance
• Solution
– Use approximate
counters which update
counter value after
some threshold S so the
lock is accessed S times
Approximate Counter
• Performance of approximate counter with
varying values of S
Concurrent Linked List
• Per-list lock which is acquired when needed
Concurrent Linked List (2)
• Insert Function: Malloc call could be moved
out of lock to avoid branching
Concurrent Queues
• Implemented using two locks: head and tail
locks (to provide concurrency in enqueue and
dequeuer operations)
Concurrent Queues (2)
• Enqueue and Dequeue operations
Concurrent Hash Table
• Made using concurrent linked list
• Uses one lock per hash bucket each
represented by a list
Summary
• We introduced a sampling of concurrent data
structures, from counters, to lists and queues,
and finally to the ubiquitous and heavily used
hash table
• We must be careful with acquisition/release of
locks around conditional control flow
• Enabling more concurrency does not
necessarily increase performance
Operating System (OS)
CS232
Concurrency: Mutual Exclusion, Locks
Dr. Muhammad Mobeen Movania
Dr Muhammad Saeed
Outlines
• Concurrency & Race Condition (recap)
• Producer Consumer Problem
– Mutual exclusion
– Synchronization
• Semaphore
– Counting and binary
• Mutual Exclusion
– Mutex
– Race Condition – solution
• Lock based Concurrent Data Structures
Concurrency & Race Condition
Concurrency & Race Condition
Larger Context
– Client/Server Applications
– Peer to Peer Applications
– Producer / Consumer Problem
– Readers / Writers Problem
Locks/Mutex/Semaphore
• Lock
– allows only one thread to enter critical section
– is not shared with any other processes
• Mutex (binary semaphore)
– same as a lock but it can be shared by multiple
processes
• Semaphore (counting semaphore)
– does the same as a mutex but allows x number of
threads to enter
– can be used to limit the number of CPU, I/O or ram
intensive tasks running at the same time.
Produce/ Consumer Problem

N = 0,1,2 . . . . .

P0

P1 C

P2

Pn
Solution
• Requirements
– Mutual Exclusion
– Synchronization
• Semaphore
• Programming Techniques
– Mutex & Condition Variables
– Semaphore
Semaphore (counting)
Semaphore (binary)
Producer / Consumer - Solution
Producer / Consumer - Solution
Mutual Exclusion
• To protect shared resources from race condition and
data inconsistency. (Balance = Balance +/- Amount)

Thread 1 Thread 2 Shared Data X


A=X 100
B=X 100
B = B + 300 100
A = A + 200 100
X=A 300
X=B 400
Thread - Issues
• When we have a number of threads accessing a shared
resource
– Multiple threads can enter critical section simultaneously
– Critical section may be interrupted in the middle

• How can we make the critical section mutually exclusive!


– i.e. only one thread can enter a critical section at any given
instant
– This would have the effect of the critical section being atomic!

• Solution
– Locks
Mutex
• Library - #include <pthread.h>

• Structure
– pthread_mutex_t

• Fuctions
– pthread_mutex_init (mutex , attr)
– pthread_mutex_destroy (mutex)

– pthread_mutexattr_init (attr)
– pthread_mutexattr_destroy (attr)
Locking and Unlocking Mutex
• Functions
– pthread_mutex_lock (mutex)
– pthread_mutex_unlock (mutex)
– pthread_mutex_trylock (mutex)
Race Condition - Solution
Reading Assignment
• Types of locks and locking strategies
• Their pros and cons
• Two phase and Three phase locking
Locks
• Locks are constructs provided by the OS (or libraries)
• Locks can be acquired and released
• With OS (and hardware) support, it is ensured that
only one thread can acquire a given lock at any given
time!
• We use locks to protect our critical sections in
multiple threads
Locks (2)
• A lock is a variable which can be in any of two states:
– Available (or free, or unlocked)
– Acquired (or held, or locked)

• When a thread calls lock() on a particular lock variable


– if the lock is in free state, it acquires the lock, and lock()
function returns
– If the lock is in held state, it will block and the lock()
function will not return until it has acquired the lock

• A thread can free an acquired lock by calling unlock()


on it.
Lock (3)
• Two approaches to locking
– Coarse-grained locking: Having one big lock used
when any critical section is accessed
– Fine-grained locking: Having different data and
data structures with different locks
• Pthread locks (mutexes)
Evaluating Locks
• Goals:
– Correctness - Achieve mutual exclusion
– Fairness – Give chance to each waiting thread
– Performance - Be efficient
• Single thread single processor
• Multiple threads single processor
• Multiple threads multiple processors
Lock Implementation-Disable Interrupt
• First solution
– Disable interrupts
– CPU has special instructions for this
– Works for single processor systems
• Issues
– Turning off interrupts is a privileged operation
– If interrupts are off, useful interrupts can be lost
Lock Implementation – Use Flag
• Second solution
– Use a variable (flag) to communicate b/w threads
Lock Implementation – Use Flag
• Second Solution
– Issues
• Performance (spin waiting)
• Correctness? No Mutual Exclusion
Lock Implementation -
(Use H/W Support)
• Accessing a shared flag might be interrupted
hence mutual exclusion may not be possible
• Third solution – Use hardware support
– test-and-set (atomic exchange) instruction
– It provide an atomic instruction
Lock Implementation -
(Use H/W Support)
• Test-and-set spin lock

• Issues
– Needs a preemptive scheduler on a single processor
otherwise a thread may never relinquish the CPU
Evaluating spin locks
• Correctness
– Yes it provides mutual exclusion
• Fairness
– No, spin locks don’t provide fairness guarantee (a
thread spinning may lock the CPU causing waiting
threads to starve
• Performance
– Worse performance if the thread holding the lock is
preempted within a critical section
• All other threads will wait spin wasting CPU cycles
Lock Implementation –
(Use H/W Support)
• compare-and-swap instruction
Lock Implementation –
(Use H/W Support)
• load-linked and store-conditional instruction
• Both instructions operate in tandem
Lock Implementation –
(Use H/W Support)
• Store-conditional succeeds only if no
intervening store has happened to that
address since the last load-linked!!
Lock Implementation –
(Use H/W Support)
• fetch-and-add instruction
• Used to implement ticket lock
Lock Implementation –
(Use H/W Support)
• All lock implementations using hardware
support spin wait wasting CPU cycles
• We can avoid spin wait by process calling yield
function to voluntarily give the CPU time to
other waiting thread
• Requires support of the operating system,
yield is a system call
Lock Implementation – Using yield
Locks Implementation – Use Queues
• Use Queues
• Sleep instead of spinning, if lock is held.
• Park() and unpark() support provided by Solaris
for sleep

• guard is a spin lock around flag and wait queues


• some spinning is done but only for the time we
access the flag & queue

• Flag is not set to 0 on unpark()!


Futexes
Futexes
• MSB is the status
• All other bits keep count
Two-phased locks
• If the lock his held, spin for a while to see if it’s
acquirable in near future.
• If not, then go to sleep.
Summary
• We saw what are the different mechanisms of
locks and how are they implemented
• We saw the role that hardware support provides
in lock implementation
• We saw the different metrics on which a lock
implementation is evaluated (correctness,
fairness and performance)
• We also saw how spin locks waste CPU cycles and
so alternate OS supported functions like yield and
sleep are used to improved performance

You might also like