SlideShare a Scribd company logo
Lab 4: If you liked it, then you should have put a
“lock” on it
Advanced Operating Systems

Zubair Nabi
zubair.nabi@itu.edu.pk

February 20, 2013
Concurrency within the OS

1

Multiple CPUs share kernel data structures
• Can interfere with each other leading to inconsistencies

2

Even on uniprocessors, interrupt handlers can interfere with
non-interrupt code

3

xv6 uses locks for both situations
Concurrency within the OS

1

Multiple CPUs share kernel data structures
• Can interfere with each other leading to inconsistencies

2

Even on uniprocessors, interrupt handlers can interfere with
non-interrupt code

3

xv6 uses locks for both situations
Concurrency within the OS

1

Multiple CPUs share kernel data structures
• Can interfere with each other leading to inconsistencies

2

Even on uniprocessors, interrupt handlers can interfere with
non-interrupt code

3

xv6 uses locks for both situations
Race conditions: Example

• Several processors share a single disk
• Disk driver has a single linked list idequeue of outstanding disk
requests
• Processors concurrently make a call to iderw which adds a
request to the list
Race conditions: Example

• Several processors share a single disk
• Disk driver has a single linked list idequeue of outstanding disk
requests
• Processors concurrently make a call to iderw which adds a
request to the list
Race conditions: Example

• Several processors share a single disk
• Disk driver has a single linked list idequeue of outstanding disk
requests
• Processors concurrently make a call to iderw which adds a
request to the list
Race conditions (2): Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

struct list {
int data;
struct list ∗ next;
};
struct list ∗ list = 0;
void insert (int data)
{
struct list ∗ l;
l = malloc ( sizeof ∗ l);
l −>data = data;
l −>next = list;
list = l;
}
Race conditions (3): Problem

• Possible race condition on line 13 and 14
• In case of two concurrent insertions, the first one will be lost
Race conditions (3): Problem

• Possible race condition on line 13 and 14
• In case of two concurrent insertions, the first one will be lost
Race conditions (4): Solution
1
2
3
4
5
6
7
8
9
10
11
12
13

struct list ∗ list = 0;
struct lock listlock ;
void insert (int data)
{
struct list ∗ l;
acquire (& listlock );
l = malloc ( sizeof ∗ l);
l −>data = data;
l −>next = list;
list = l;
release (& listlock );
}
Lock representation

1
2
3

struct spinlock {
uint locked ;
};
Acquire lock

1
2
3
4
5
6
7
8
9

void acquire ( struct spinlock ∗ lk)
{
for (;;) {
if(!lk −>locked ) {
lk −>locked = 1;
break ;
}
}
}
Acquiring lock (2): Problem

• Possible race condition on line 4 and 5
• So the solution itself causes a race condition!
Acquiring lock (2): Problem

• Possible race condition on line 4 and 5
• So the solution itself causes a race condition!
Hardware support

1
2
3
4
5
6
7
8
9
10

static inline uint
xchg( volatile uint ∗ addr , uint newval )
{
uint result ;
asm volatile ("lock; xchgl %0, %1" :
"+m" ( ∗ addr), "=a" ( result ) :
"1" ( newval ) :
"cc");
}
Atomic acquire lock

1
2
3
4
5
6
7
8
9
10

void acquire ( struct spinlock ∗ lk)
{
pushcli (); // disable interrupts .
if( holding (lk))
panic (" acquire ");
while (xchg (&lk −>locked , 1) != 0)
;
}
Atomic release lock

1
2
3
4
5
6
7
8
9

void release ( struct spinlock ∗ lk)
{
if(! holding (lk))
panic (" release ");
xchg (&lk −>locked , 0);
popcli ();
}
Recursive

• What happens if a callee tries to acquire a lock held by its caller?
• Solution: recursive locks
• But they do not ensure mutual exclusion between the caller and
the callee
• Pass the buck to the programmer
Recursive

• What happens if a callee tries to acquire a lock held by its caller?
• Solution: recursive locks
• But they do not ensure mutual exclusion between the caller and
the callee
• Pass the buck to the programmer
Recursive

• What happens if a callee tries to acquire a lock held by its caller?
• Solution: recursive locks
• But they do not ensure mutual exclusion between the caller and
the callee
• Pass the buck to the programmer
Recursive

• What happens if a callee tries to acquire a lock held by its caller?
• Solution: recursive locks
• But they do not ensure mutual exclusion between the caller and
the callee
• Pass the buck to the programmer
Lock usage example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void iderw ( struct buf ∗ b)
{
struct buf ∗ ∗ pp;
acquire (& idelock );
b −>qnext = 0;
for(pp =& idequeue ; ∗ pp; pp =&( ∗ pp)−>qnext)
;
∗ pp = b;
// Wait for request to finish .
while ((b −>flags & ( B_VALID | B_DIRTY ))
!= B_VALID ){
sleep (b, & idelock );
}
release (& idelock );
}
When to use locks

• To use:
1
2

A variable can concurrently be written to by multiple CPUs
An invariant spans multiple data structures

• Possible to protect the kernel through a “giant kernel lock”
• Problem(s)?
• Reminiscent of the GIL in Python

• Possible to have fine-grained locks
When to use locks

• To use:
1
2

A variable can concurrently be written to by multiple CPUs
An invariant spans multiple data structures

• Possible to protect the kernel through a “giant kernel lock”
• Problem(s)?
• Reminiscent of the GIL in Python

• Possible to have fine-grained locks
When to use locks

• To use:
1
2

A variable can concurrently be written to by multiple CPUs
An invariant spans multiple data structures

• Possible to protect the kernel through a “giant kernel lock”
• Problem(s)?
• Reminiscent of the GIL in Python

• Possible to have fine-grained locks
When to use locks

• To use:
1
2

A variable can concurrently be written to by multiple CPUs
An invariant spans multiple data structures

• Possible to protect the kernel through a “giant kernel lock”
• Problem(s)?
• Reminiscent of the GIL in Python

• Possible to have fine-grained locks
When to use locks

• To use:
1
2

A variable can concurrently be written to by multiple CPUs
An invariant spans multiple data structures

• Possible to protect the kernel through a “giant kernel lock”
• Problem(s)?
• Reminiscent of the GIL in Python

• Possible to have fine-grained locks
When to use locks

• To use:
1
2

A variable can concurrently be written to by multiple CPUs
An invariant spans multiple data structures

• Possible to protect the kernel through a “giant kernel lock”
• Problem(s)?
• Reminiscent of the GIL in Python

• Possible to have fine-grained locks
When to use locks

• To use:
1
2

A variable can concurrently be written to by multiple CPUs
An invariant spans multiple data structures

• Possible to protect the kernel through a “giant kernel lock”
• Problem(s)?
• Reminiscent of the GIL in Python

• Possible to have fine-grained locks
Lock ordering

• If code path x needs locks in the order A and B while y needs
them in order B and A, could there be a problem?
• Need to ensure that all code paths acquire locks in the same
order
Lock ordering

• If code path x needs locks in the order A and B while y needs
them in order B and A, could there be a problem?
• Need to ensure that all code paths acquire locks in the same
order
Interrupt handlers

• Locks are also used to synchronize access between interrupt
handlers and non-interrupt code
Interrupt handlers (2): Example

1
2
3
4
5
6
7
8
9

T_IRQ0 + IRQ_TIMER :
if(cpu −>id == 0){
acquire (& tickslock );
ticks ++;
wakeup (& ticks );
release (& tickslock );
}
lapiceoi ();
break ;
Interrupt handlers (3): Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void sys_sleep (void ){
int n;
uint ticks0 ;
if( argint (0, &n) < 0)
return −1;
acquire (& tickslock );
ticks0 = ticks;
while ( ticks − ticks0 < n){
if(proc −>killed ){
release (& tickslock );
return −1;
}
sleep (& ticks , & tickslock );
}
release (& tickslock );
Interrupt handlers

• Interrupts lead to concurrency problems on uniprocessors too
• Disable interrupts before acquiring a lock: pushcli()
• Re-enable on release: popcli()
• Avoids recursive locks
Interrupt handlers

• Interrupts lead to concurrency problems on uniprocessors too
• Disable interrupts before acquiring a lock: pushcli()
• Re-enable on release: popcli()
• Avoids recursive locks
Interrupt handlers

• Interrupts lead to concurrency problems on uniprocessors too
• Disable interrupts before acquiring a lock: pushcli()
• Re-enable on release: popcli()
• Avoids recursive locks
Interrupt handlers

• Interrupts lead to concurrency problems on uniprocessors too
• Disable interrupts before acquiring a lock: pushcli()
• Re-enable on release: popcli()
• Avoids recursive locks
Today’s Task
• xv6 processes only have a single thread so processes do not
share any memory
• But it is definitely possible to add multithreading
• Imagine that you have been provided a library with methods to
create (xv6_thread_create()) and block
(xv6_thread_join()) threads
• xv6 already provides you with a spinlock with methods to
acquire and release that lock.
• In addition, it also has methods to:
1 Put a thread to sleep (sleep(void *chan, struct
spinlock *lk)) on a variable (*chan) (it releases lk before
2

sleeping and then reacquires it after waking up)
Wake up (wakeup(void *chan)) threads sleeping on a
variable (*chan)
Today’s Task
• xv6 processes only have a single thread so processes do not
share any memory
• But it is definitely possible to add multithreading
• Imagine that you have been provided a library with methods to
create (xv6_thread_create()) and block
(xv6_thread_join()) threads
• xv6 already provides you with a spinlock with methods to
acquire and release that lock.
• In addition, it also has methods to:
1 Put a thread to sleep (sleep(void *chan, struct
spinlock *lk)) on a variable (*chan) (it releases lk before
2

sleeping and then reacquires it after waking up)
Wake up (wakeup(void *chan)) threads sleeping on a
variable (*chan)
Today’s Task
• xv6 processes only have a single thread so processes do not
share any memory
• But it is definitely possible to add multithreading
• Imagine that you have been provided a library with methods to
create (xv6_thread_create()) and block
(xv6_thread_join()) threads
• xv6 already provides you with a spinlock with methods to
acquire and release that lock.
• In addition, it also has methods to:
1 Put a thread to sleep (sleep(void *chan, struct
spinlock *lk)) on a variable (*chan) (it releases lk before
2

sleeping and then reacquires it after waking up)
Wake up (wakeup(void *chan)) threads sleeping on a
variable (*chan)
Today’s Task
• xv6 processes only have a single thread so processes do not
share any memory
• But it is definitely possible to add multithreading
• Imagine that you have been provided a library with methods to
create (xv6_thread_create()) and block
(xv6_thread_join()) threads
• xv6 already provides you with a spinlock with methods to
acquire and release that lock.
• In addition, it also has methods to:
1 Put a thread to sleep (sleep(void *chan, struct
spinlock *lk)) on a variable (*chan) (it releases lk before
2

sleeping and then reacquires it after waking up)
Wake up (wakeup(void *chan)) threads sleeping on a
variable (*chan)
Today’s Task
• xv6 processes only have a single thread so processes do not
share any memory
• But it is definitely possible to add multithreading
• Imagine that you have been provided a library with methods to
create (xv6_thread_create()) and block
(xv6_thread_join()) threads
• xv6 already provides you with a spinlock with methods to
acquire and release that lock.
• In addition, it also has methods to:
1 Put a thread to sleep (sleep(void *chan, struct
spinlock *lk)) on a variable (*chan) (it releases lk before
2

sleeping and then reacquires it after waking up)
Wake up (wakeup(void *chan)) threads sleeping on a
variable (*chan)
Today’s Task
• xv6 processes only have a single thread so processes do not
share any memory
• But it is definitely possible to add multithreading
• Imagine that you have been provided a library with methods to
create (xv6_thread_create()) and block
(xv6_thread_join()) threads
• xv6 already provides you with a spinlock with methods to
acquire and release that lock.
• In addition, it also has methods to:
1 Put a thread to sleep (sleep(void *chan, struct
spinlock *lk)) on a variable (*chan) (it releases lk before
2

sleeping and then reacquires it after waking up)
Wake up (wakeup(void *chan)) threads sleeping on a
variable (*chan)
Today’s Task
• xv6 processes only have a single thread so processes do not
share any memory
• But it is definitely possible to add multithreading
• Imagine that you have been provided a library with methods to
create (xv6_thread_create()) and block
(xv6_thread_join()) threads
• xv6 already provides you with a spinlock with methods to
acquire and release that lock.
• In addition, it also has methods to:
1 Put a thread to sleep (sleep(void *chan, struct
spinlock *lk)) on a variable (*chan) (it releases lk before
2

sleeping and then reacquires it after waking up)
Wake up (wakeup(void *chan)) threads sleeping on a
variable (*chan)
Today’s Task (2)

• Design a thread-safe library (in pseudo code) for xv6 that uses
these existing primitives, to implement semaphores (binary and
counting) and conditional variables and their various methods
• Also, add commenting to describe why your solution is
thread-safe
Reading

• Chapter 4 from “xv6: a simple, Unix-like teaching operating
system”
• Timothy L. Harris. 2001. A Pragmatic Implementation of
Non-blocking Linked-Lists. In Proceedings of the 15th
International Conference on Distributed Computing (DISC ’01),
Jennifer L. Welch (Ed.). Springer-Verlag, London, UK, 300-314.
Online: http:

//www.timharris.co.uk/papers/2001-disc.pdf

More Related Content

PDF
AOS Lab 11: Virtualization
PDF
AOS Lab 6: Scheduling
PDF
AOS Lab 8: Interrupts and Device Drivers
PDF
AOS Lab 1: Hello, Linux!
PDF
AOS Lab 5: System calls
PDF
Kernel Recipes 2015: Solving the Linux storage scalability bottlenecks
PPTX
Modern Linux Tracing Landscape
PDF
Kernel Recipes 2015: Kernel packet capture technologies
AOS Lab 11: Virtualization
AOS Lab 6: Scheduling
AOS Lab 8: Interrupts and Device Drivers
AOS Lab 1: Hello, Linux!
AOS Lab 5: System calls
Kernel Recipes 2015: Solving the Linux storage scalability bottlenecks
Modern Linux Tracing Landscape
Kernel Recipes 2015: Kernel packet capture technologies

What's hot (20)

PDF
Kernel Recipes 2015 - So you want to write a Linux driver framework
PPTX
Linux Interrupts
PPTX
SecureCore RTAS2013
PPTX
The Silence of the Canaries
PPTX
Windows Internals for Linux Kernel Developers
PDF
Stateless Hypervisors at Scale
PDF
High Performance Storage Devices in the Linux Kernel
PDF
LISA2010 visualizations
PDF
Making Linux do Hard Real-time
PPTX
Broken Linux Performance Tools 2016
PDF
Embedded linux network device driver development
PDF
LinuxCon_2013_NA_Eckermann_Filesystems_btrfs.pdf
PDF
DTrace Topics: Introduction
PDF
BPF: Tracing and more
PDF
What Linux can learn from Solaris performance and vice-versa
PPTX
OMFW 2012: Analyzing Linux Kernel Rootkits with Volatlity
ODP
Speeding up ps and top
PDF
Kernel Recipes 2015 - Porting Linux to a new processor architecture
PDF
Block I/O Layer Tracing: blktrace
PDF
Linux BPF Superpowers
Kernel Recipes 2015 - So you want to write a Linux driver framework
Linux Interrupts
SecureCore RTAS2013
The Silence of the Canaries
Windows Internals for Linux Kernel Developers
Stateless Hypervisors at Scale
High Performance Storage Devices in the Linux Kernel
LISA2010 visualizations
Making Linux do Hard Real-time
Broken Linux Performance Tools 2016
Embedded linux network device driver development
LinuxCon_2013_NA_Eckermann_Filesystems_btrfs.pdf
DTrace Topics: Introduction
BPF: Tracing and more
What Linux can learn from Solaris performance and vice-versa
OMFW 2012: Analyzing Linux Kernel Rootkits with Volatlity
Speeding up ps and top
Kernel Recipes 2015 - Porting Linux to a new processor architecture
Block I/O Layer Tracing: blktrace
Linux BPF Superpowers
Ad

Viewers also liked (14)

PDF
AOS Lab 10: File system -- Inodes and beyond
PDF
AOS Lab 7: Page tables
PDF
Topic 13: Cloud Stacks
PDF
AOS Lab 9: File system -- Of buffers, logs, and blocks
PDF
AOS Lab 2: Hello, xv6!
PDF
Topic 14: Operating Systems and Virtualization
PDF
AOS Lab 12: Network Communication
PDF
Topic 15: Datacenter Design and Networking
PDF
AOS Lab 1: Hello, Linux!
PDF
MapReduce and DBMS Hybrids
PDF
Raabta: Low-cost Video Conferencing for the Developing World
PDF
The Anatomy of Web Censorship in Pakistan
PDF
MapReduce Application Scripting
PPTX
The Big Data Stack
AOS Lab 10: File system -- Inodes and beyond
AOS Lab 7: Page tables
Topic 13: Cloud Stacks
AOS Lab 9: File system -- Of buffers, logs, and blocks
AOS Lab 2: Hello, xv6!
Topic 14: Operating Systems and Virtualization
AOS Lab 12: Network Communication
Topic 15: Datacenter Design and Networking
AOS Lab 1: Hello, Linux!
MapReduce and DBMS Hybrids
Raabta: Low-cost Video Conferencing for the Developing World
The Anatomy of Web Censorship in Pakistan
MapReduce Application Scripting
The Big Data Stack
Ad

Similar to AOS Lab 4: If you liked it, then you should have put a “lock” on it (20)

PPTX
Computer Operating Systems Concurrency Slide
PDF
Preparation for mit ose lab4
PDF
Describe synchronization techniques used by programmers who develop .pdf
PDF
Linux kernel development_ch9-10_20120410
PDF
Linux kernel development chapter 10
PDF
Kernel locking
PPTX
9-Operating Systems -Synchronization, interprocess communication, deadlock.pptx
PDF
Operating Systems - Advanced Synchronization
PPT
Processes and Thread OS_Tanenbaum_3e
PPT
Synchronization linux
PPTX
Synchronization problem with threads
PPTX
Memory model
PDF
Linux Locking Mechanisms
PDF
spinlock.pdf
PPTX
Computer architecture related concepts, process
PDF
Lect04
PPT
Os4
PPT
10 Multicore 07
PDF
Locks (Concurrency)
Computer Operating Systems Concurrency Slide
Preparation for mit ose lab4
Describe synchronization techniques used by programmers who develop .pdf
Linux kernel development_ch9-10_20120410
Linux kernel development chapter 10
Kernel locking
9-Operating Systems -Synchronization, interprocess communication, deadlock.pptx
Operating Systems - Advanced Synchronization
Processes and Thread OS_Tanenbaum_3e
Synchronization linux
Synchronization problem with threads
Memory model
Linux Locking Mechanisms
spinlock.pdf
Computer architecture related concepts, process
Lect04
Os4
10 Multicore 07
Locks (Concurrency)

More from Zubair Nabi (11)

PDF
Lab 5: Interconnecting a Datacenter using Mininet
PDF
Topic 12: NoSQL in Action
PDF
Lab 4: Interfacing with Cassandra
PDF
Topic 10: Taxonomy of Data and Storage
PDF
Topic 11: Google Filesystem
PDF
Lab 3: Writing a Naiad Application
PDF
Topic 9: MR+
PDF
Topic 8: Enhancements and Alternative Architectures
PDF
Topic 7: Shortcomings in the MapReduce Paradigm
PDF
Lab 1: Introduction to Amazon EC2 and MPI
PDF
Topic 6: MapReduce Applications
Lab 5: Interconnecting a Datacenter using Mininet
Topic 12: NoSQL in Action
Lab 4: Interfacing with Cassandra
Topic 10: Taxonomy of Data and Storage
Topic 11: Google Filesystem
Lab 3: Writing a Naiad Application
Topic 9: MR+
Topic 8: Enhancements and Alternative Architectures
Topic 7: Shortcomings in the MapReduce Paradigm
Lab 1: Introduction to Amazon EC2 and MPI
Topic 6: MapReduce Applications

Recently uploaded (20)

PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
KodekX | Application Modernization Development
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Approach and Philosophy of On baking technology
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Encapsulation theory and applications.pdf
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Network Security Unit 5.pdf for BCA BBA.
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Unlocking AI with Model Context Protocol (MCP)
KodekX | Application Modernization Development
20250228 LYD VKU AI Blended-Learning.pptx
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Chapter 3 Spatial Domain Image Processing.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
“AI and Expert System Decision Support & Business Intelligence Systems”
Approach and Philosophy of On baking technology
Understanding_Digital_Forensics_Presentation.pptx
Encapsulation theory and applications.pdf
Advanced methodologies resolving dimensionality complications for autism neur...
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Building Integrated photovoltaic BIPV_UPV.pdf
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Encapsulation_ Review paper, used for researhc scholars
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Network Security Unit 5.pdf for BCA BBA.
Digital-Transformation-Roadmap-for-Companies.pptx
Build a system with the filesystem maintained by OSTree @ COSCUP 2025

AOS Lab 4: If you liked it, then you should have put a “lock” on it

  • 1. Lab 4: If you liked it, then you should have put a “lock” on it Advanced Operating Systems Zubair Nabi zubair.nabi@itu.edu.pk February 20, 2013
  • 2. Concurrency within the OS 1 Multiple CPUs share kernel data structures • Can interfere with each other leading to inconsistencies 2 Even on uniprocessors, interrupt handlers can interfere with non-interrupt code 3 xv6 uses locks for both situations
  • 3. Concurrency within the OS 1 Multiple CPUs share kernel data structures • Can interfere with each other leading to inconsistencies 2 Even on uniprocessors, interrupt handlers can interfere with non-interrupt code 3 xv6 uses locks for both situations
  • 4. Concurrency within the OS 1 Multiple CPUs share kernel data structures • Can interfere with each other leading to inconsistencies 2 Even on uniprocessors, interrupt handlers can interfere with non-interrupt code 3 xv6 uses locks for both situations
  • 5. Race conditions: Example • Several processors share a single disk • Disk driver has a single linked list idequeue of outstanding disk requests • Processors concurrently make a call to iderw which adds a request to the list
  • 6. Race conditions: Example • Several processors share a single disk • Disk driver has a single linked list idequeue of outstanding disk requests • Processors concurrently make a call to iderw which adds a request to the list
  • 7. Race conditions: Example • Several processors share a single disk • Disk driver has a single linked list idequeue of outstanding disk requests • Processors concurrently make a call to iderw which adds a request to the list
  • 8. Race conditions (2): Example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct list { int data; struct list ∗ next; }; struct list ∗ list = 0; void insert (int data) { struct list ∗ l; l = malloc ( sizeof ∗ l); l −>data = data; l −>next = list; list = l; }
  • 9. Race conditions (3): Problem • Possible race condition on line 13 and 14 • In case of two concurrent insertions, the first one will be lost
  • 10. Race conditions (3): Problem • Possible race condition on line 13 and 14 • In case of two concurrent insertions, the first one will be lost
  • 11. Race conditions (4): Solution 1 2 3 4 5 6 7 8 9 10 11 12 13 struct list ∗ list = 0; struct lock listlock ; void insert (int data) { struct list ∗ l; acquire (& listlock ); l = malloc ( sizeof ∗ l); l −>data = data; l −>next = list; list = l; release (& listlock ); }
  • 13. Acquire lock 1 2 3 4 5 6 7 8 9 void acquire ( struct spinlock ∗ lk) { for (;;) { if(!lk −>locked ) { lk −>locked = 1; break ; } } }
  • 14. Acquiring lock (2): Problem • Possible race condition on line 4 and 5 • So the solution itself causes a race condition!
  • 15. Acquiring lock (2): Problem • Possible race condition on line 4 and 5 • So the solution itself causes a race condition!
  • 16. Hardware support 1 2 3 4 5 6 7 8 9 10 static inline uint xchg( volatile uint ∗ addr , uint newval ) { uint result ; asm volatile ("lock; xchgl %0, %1" : "+m" ( ∗ addr), "=a" ( result ) : "1" ( newval ) : "cc"); }
  • 17. Atomic acquire lock 1 2 3 4 5 6 7 8 9 10 void acquire ( struct spinlock ∗ lk) { pushcli (); // disable interrupts . if( holding (lk)) panic (" acquire "); while (xchg (&lk −>locked , 1) != 0) ; }
  • 18. Atomic release lock 1 2 3 4 5 6 7 8 9 void release ( struct spinlock ∗ lk) { if(! holding (lk)) panic (" release "); xchg (&lk −>locked , 0); popcli (); }
  • 19. Recursive • What happens if a callee tries to acquire a lock held by its caller? • Solution: recursive locks • But they do not ensure mutual exclusion between the caller and the callee • Pass the buck to the programmer
  • 20. Recursive • What happens if a callee tries to acquire a lock held by its caller? • Solution: recursive locks • But they do not ensure mutual exclusion between the caller and the callee • Pass the buck to the programmer
  • 21. Recursive • What happens if a callee tries to acquire a lock held by its caller? • Solution: recursive locks • But they do not ensure mutual exclusion between the caller and the callee • Pass the buck to the programmer
  • 22. Recursive • What happens if a callee tries to acquire a lock held by its caller? • Solution: recursive locks • But they do not ensure mutual exclusion between the caller and the callee • Pass the buck to the programmer
  • 23. Lock usage example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void iderw ( struct buf ∗ b) { struct buf ∗ ∗ pp; acquire (& idelock ); b −>qnext = 0; for(pp =& idequeue ; ∗ pp; pp =&( ∗ pp)−>qnext) ; ∗ pp = b; // Wait for request to finish . while ((b −>flags & ( B_VALID | B_DIRTY )) != B_VALID ){ sleep (b, & idelock ); } release (& idelock ); }
  • 24. When to use locks • To use: 1 2 A variable can concurrently be written to by multiple CPUs An invariant spans multiple data structures • Possible to protect the kernel through a “giant kernel lock” • Problem(s)? • Reminiscent of the GIL in Python • Possible to have fine-grained locks
  • 25. When to use locks • To use: 1 2 A variable can concurrently be written to by multiple CPUs An invariant spans multiple data structures • Possible to protect the kernel through a “giant kernel lock” • Problem(s)? • Reminiscent of the GIL in Python • Possible to have fine-grained locks
  • 26. When to use locks • To use: 1 2 A variable can concurrently be written to by multiple CPUs An invariant spans multiple data structures • Possible to protect the kernel through a “giant kernel lock” • Problem(s)? • Reminiscent of the GIL in Python • Possible to have fine-grained locks
  • 27. When to use locks • To use: 1 2 A variable can concurrently be written to by multiple CPUs An invariant spans multiple data structures • Possible to protect the kernel through a “giant kernel lock” • Problem(s)? • Reminiscent of the GIL in Python • Possible to have fine-grained locks
  • 28. When to use locks • To use: 1 2 A variable can concurrently be written to by multiple CPUs An invariant spans multiple data structures • Possible to protect the kernel through a “giant kernel lock” • Problem(s)? • Reminiscent of the GIL in Python • Possible to have fine-grained locks
  • 29. When to use locks • To use: 1 2 A variable can concurrently be written to by multiple CPUs An invariant spans multiple data structures • Possible to protect the kernel through a “giant kernel lock” • Problem(s)? • Reminiscent of the GIL in Python • Possible to have fine-grained locks
  • 30. When to use locks • To use: 1 2 A variable can concurrently be written to by multiple CPUs An invariant spans multiple data structures • Possible to protect the kernel through a “giant kernel lock” • Problem(s)? • Reminiscent of the GIL in Python • Possible to have fine-grained locks
  • 31. Lock ordering • If code path x needs locks in the order A and B while y needs them in order B and A, could there be a problem? • Need to ensure that all code paths acquire locks in the same order
  • 32. Lock ordering • If code path x needs locks in the order A and B while y needs them in order B and A, could there be a problem? • Need to ensure that all code paths acquire locks in the same order
  • 33. Interrupt handlers • Locks are also used to synchronize access between interrupt handlers and non-interrupt code
  • 34. Interrupt handlers (2): Example 1 2 3 4 5 6 7 8 9 T_IRQ0 + IRQ_TIMER : if(cpu −>id == 0){ acquire (& tickslock ); ticks ++; wakeup (& ticks ); release (& tickslock ); } lapiceoi (); break ;
  • 35. Interrupt handlers (3): Example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void sys_sleep (void ){ int n; uint ticks0 ; if( argint (0, &n) < 0) return −1; acquire (& tickslock ); ticks0 = ticks; while ( ticks − ticks0 < n){ if(proc −>killed ){ release (& tickslock ); return −1; } sleep (& ticks , & tickslock ); } release (& tickslock );
  • 36. Interrupt handlers • Interrupts lead to concurrency problems on uniprocessors too • Disable interrupts before acquiring a lock: pushcli() • Re-enable on release: popcli() • Avoids recursive locks
  • 37. Interrupt handlers • Interrupts lead to concurrency problems on uniprocessors too • Disable interrupts before acquiring a lock: pushcli() • Re-enable on release: popcli() • Avoids recursive locks
  • 38. Interrupt handlers • Interrupts lead to concurrency problems on uniprocessors too • Disable interrupts before acquiring a lock: pushcli() • Re-enable on release: popcli() • Avoids recursive locks
  • 39. Interrupt handlers • Interrupts lead to concurrency problems on uniprocessors too • Disable interrupts before acquiring a lock: pushcli() • Re-enable on release: popcli() • Avoids recursive locks
  • 40. Today’s Task • xv6 processes only have a single thread so processes do not share any memory • But it is definitely possible to add multithreading • Imagine that you have been provided a library with methods to create (xv6_thread_create()) and block (xv6_thread_join()) threads • xv6 already provides you with a spinlock with methods to acquire and release that lock. • In addition, it also has methods to: 1 Put a thread to sleep (sleep(void *chan, struct spinlock *lk)) on a variable (*chan) (it releases lk before 2 sleeping and then reacquires it after waking up) Wake up (wakeup(void *chan)) threads sleeping on a variable (*chan)
  • 41. Today’s Task • xv6 processes only have a single thread so processes do not share any memory • But it is definitely possible to add multithreading • Imagine that you have been provided a library with methods to create (xv6_thread_create()) and block (xv6_thread_join()) threads • xv6 already provides you with a spinlock with methods to acquire and release that lock. • In addition, it also has methods to: 1 Put a thread to sleep (sleep(void *chan, struct spinlock *lk)) on a variable (*chan) (it releases lk before 2 sleeping and then reacquires it after waking up) Wake up (wakeup(void *chan)) threads sleeping on a variable (*chan)
  • 42. Today’s Task • xv6 processes only have a single thread so processes do not share any memory • But it is definitely possible to add multithreading • Imagine that you have been provided a library with methods to create (xv6_thread_create()) and block (xv6_thread_join()) threads • xv6 already provides you with a spinlock with methods to acquire and release that lock. • In addition, it also has methods to: 1 Put a thread to sleep (sleep(void *chan, struct spinlock *lk)) on a variable (*chan) (it releases lk before 2 sleeping and then reacquires it after waking up) Wake up (wakeup(void *chan)) threads sleeping on a variable (*chan)
  • 43. Today’s Task • xv6 processes only have a single thread so processes do not share any memory • But it is definitely possible to add multithreading • Imagine that you have been provided a library with methods to create (xv6_thread_create()) and block (xv6_thread_join()) threads • xv6 already provides you with a spinlock with methods to acquire and release that lock. • In addition, it also has methods to: 1 Put a thread to sleep (sleep(void *chan, struct spinlock *lk)) on a variable (*chan) (it releases lk before 2 sleeping and then reacquires it after waking up) Wake up (wakeup(void *chan)) threads sleeping on a variable (*chan)
  • 44. Today’s Task • xv6 processes only have a single thread so processes do not share any memory • But it is definitely possible to add multithreading • Imagine that you have been provided a library with methods to create (xv6_thread_create()) and block (xv6_thread_join()) threads • xv6 already provides you with a spinlock with methods to acquire and release that lock. • In addition, it also has methods to: 1 Put a thread to sleep (sleep(void *chan, struct spinlock *lk)) on a variable (*chan) (it releases lk before 2 sleeping and then reacquires it after waking up) Wake up (wakeup(void *chan)) threads sleeping on a variable (*chan)
  • 45. Today’s Task • xv6 processes only have a single thread so processes do not share any memory • But it is definitely possible to add multithreading • Imagine that you have been provided a library with methods to create (xv6_thread_create()) and block (xv6_thread_join()) threads • xv6 already provides you with a spinlock with methods to acquire and release that lock. • In addition, it also has methods to: 1 Put a thread to sleep (sleep(void *chan, struct spinlock *lk)) on a variable (*chan) (it releases lk before 2 sleeping and then reacquires it after waking up) Wake up (wakeup(void *chan)) threads sleeping on a variable (*chan)
  • 46. Today’s Task • xv6 processes only have a single thread so processes do not share any memory • But it is definitely possible to add multithreading • Imagine that you have been provided a library with methods to create (xv6_thread_create()) and block (xv6_thread_join()) threads • xv6 already provides you with a spinlock with methods to acquire and release that lock. • In addition, it also has methods to: 1 Put a thread to sleep (sleep(void *chan, struct spinlock *lk)) on a variable (*chan) (it releases lk before 2 sleeping and then reacquires it after waking up) Wake up (wakeup(void *chan)) threads sleeping on a variable (*chan)
  • 47. Today’s Task (2) • Design a thread-safe library (in pseudo code) for xv6 that uses these existing primitives, to implement semaphores (binary and counting) and conditional variables and their various methods • Also, add commenting to describe why your solution is thread-safe
  • 48. Reading • Chapter 4 from “xv6: a simple, Unix-like teaching operating system” • Timothy L. Harris. 2001. A Pragmatic Implementation of Non-blocking Linked-Lists. In Proceedings of the 15th International Conference on Distributed Computing (DISC ’01), Jennifer L. Welch (Ed.). Springer-Verlag, London, UK, 300-314. Online: http: //www.timharris.co.uk/papers/2001-disc.pdf