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
无锁编程
PPTX
Jvm memory model
PPTX
Synchronization problem with threads
PDF
Hoard_2022AIM1001.pptx.pdf
KEY
Verification with LoLA: 4 Using LoLA
PDF
Blocks & GCD
PDF
Search at Twitter: Presented by Michael Busch, Twitter
PDF
LXC Containers and AUFs
PPTX
Non blocking programming and waiting
PDF
Who go Types in my Systems Programing!
PDF
Wait for your fortune without Blocking!
PDF
APEX Connect 2019 - array/bulk processing in PLSQL
PDF
Multithreading and Parallelism on iOS [MobOS 2013]
PDF
spinlock.pdf
PDF
CNIT 127 14: Protection Mechanisms
PPTX
Memory model
PPTX
Computer architecture related concepts, process
KEY
JavaOne 2012 - JVM JIT for Dummies
Computer Operating Systems Concurrency Slide
无锁编程
Jvm memory model
Synchronization problem with threads
Hoard_2022AIM1001.pptx.pdf
Verification with LoLA: 4 Using LoLA
Blocks & GCD
Search at Twitter: Presented by Michael Busch, Twitter
LXC Containers and AUFs
Non blocking programming and waiting
Who go Types in my Systems Programing!
Wait for your fortune without Blocking!
APEX Connect 2019 - array/bulk processing in PLSQL
Multithreading and Parallelism on iOS [MobOS 2013]
spinlock.pdf
CNIT 127 14: Protection Mechanisms
Memory model
Computer architecture related concepts, process
JavaOne 2012 - JVM JIT for Dummies

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)

PPTX
Internet of Everything -Basic concepts details
PPTX
AI IN MARKETING- PRESENTED BY ANWAR KABIR 1st June 2025.pptx
PDF
Credit Without Borders: AI and Financial Inclusion in Bangladesh
PPTX
Training Program for knowledge in solar cell and solar industry
PDF
4 layer Arch & Reference Arch of IoT.pdf
PDF
Consumable AI The What, Why & How for Small Teams.pdf
PDF
giants, standing on the shoulders of - by Daniel Stenberg
PDF
Flame analysis and combustion estimation using large language and vision assi...
DOCX
Basics of Cloud Computing - Cloud Ecosystem
PDF
CloudStack 4.21: First Look Webinar slides
PDF
sbt 2.0: go big (Scala Days 2025 edition)
PDF
Accessing-Finance-in-Jordan-MENA 2024 2025.pdf
PDF
5-Ways-AI-is-Revolutionizing-Telecom-Quality-Engineering.pdf
PDF
Transform-Your-Factory-with-AI-Driven-Quality-Engineering.pdf
PPTX
Microsoft Excel 365/2024 Beginner's training
PDF
Transform-Your-Streaming-Platform-with-AI-Driven-Quality-Engineering.pdf
PPTX
Build Your First AI Agent with UiPath.pptx
PDF
Comparative analysis of machine learning models for fake news detection in so...
PDF
“A New Era of 3D Sensing: Transforming Industries and Creating Opportunities,...
PDF
Transform-Your-Supply-Chain-with-AI-Driven-Quality-Engineering.pdf
Internet of Everything -Basic concepts details
AI IN MARKETING- PRESENTED BY ANWAR KABIR 1st June 2025.pptx
Credit Without Borders: AI and Financial Inclusion in Bangladesh
Training Program for knowledge in solar cell and solar industry
4 layer Arch & Reference Arch of IoT.pdf
Consumable AI The What, Why & How for Small Teams.pdf
giants, standing on the shoulders of - by Daniel Stenberg
Flame analysis and combustion estimation using large language and vision assi...
Basics of Cloud Computing - Cloud Ecosystem
CloudStack 4.21: First Look Webinar slides
sbt 2.0: go big (Scala Days 2025 edition)
Accessing-Finance-in-Jordan-MENA 2024 2025.pdf
5-Ways-AI-is-Revolutionizing-Telecom-Quality-Engineering.pdf
Transform-Your-Factory-with-AI-Driven-Quality-Engineering.pdf
Microsoft Excel 365/2024 Beginner's training
Transform-Your-Streaming-Platform-with-AI-Driven-Quality-Engineering.pdf
Build Your First AI Agent with UiPath.pptx
Comparative analysis of machine learning models for fake news detection in so...
“A New Era of 3D Sensing: Transforming Industries and Creating Opportunities,...
Transform-Your-Supply-Chain-with-AI-Driven-Quality-Engineering.pdf

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 [email protected] 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