Concurrency and Synchronization - Operating Systems | CMSC 412, Study notes of Operating Systems

Material Type: Notes; Professor: Hicks; Class: Operating Systems; Subject: Computer Science; University: University of Maryland; Term: Spring 2007;

Typology: Study notes

Pre 2010

Uploaded on 07/30/2009

koofers-user-ifh
koofers-user-ifh 🇺🇸

5

(1)

10 documents

1 / 9

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
1
1
CMSC 412
Spring 2007
Concurrency and Synchronization
Announcements
Reading
Chapter 6
Project #1
Due Wed, 2/14
Systems = Objects + Activities
Safety is a property of objects, and groups of
objects, that participate across multiple activities.
Can be a concern at many different levels: objects,
composites, components, subsystems, hosts, …
Liveness is a property of activities, and groups of
activities, that span across multiple objects.
Levels: Messages, call chains, threads, sessions, scenarios,
scripts workflows, use cases, transactions, data flows,
mobile computations, …
Violating Safety
Data can be shared by threads
Scheduler can interleave or overlap
threads arbitrarily
Can lead to interference
Storage corruption (e.g. a data race/race
condition)
Violation of representation invariant
Violation of a protocol (e.g. A occurs before B)
How does this apply to OSs?
Any resource that is shared could be
accessed inappropriately
Shared memory
Kernel threads
Processes (shared memory set up by kernel)
Shared resources
Printer, Video screen, Network card, …
OS must protect shared resources
And provide processes a means to protect
their own abstractions
Data Race Example
static int cnt = 0;
t1.run() {
int y = cnt;
cnt = y + 1;
}
t2.run() {
int y = cnt;
cnt = y + 1;
}
cnt = 0
Start: both threads ready to
run. Each will increment the
global count.
Shared state
pf3
pf4
pf5
pf8
pf9

Partial preview of the text

Download Concurrency and Synchronization - Operating Systems | CMSC 412 and more Study notes Operating Systems in PDF only on Docsity!

CMSC 412

Spring 2007

Concurrency and Synchronization

Announcements

• Reading

  • Chapter 6

• Project

  • Due Wed, 2/

Systems = Objects + Activities

  • Safety is a property of objects, and groups of objects, that participate across multiple activities. - Can be a concern at many different levels: objects, composites, components, subsystems, hosts, …
  • Liveness is a property of activities, and groups of activities, that span across multiple objects. - Levels: Messages, call chains, threads, sessions, scenarios, scripts workflows, use cases, transactions, data flows, mobile computations, …

Violating Safety

• Data can be shared by threads

  • Scheduler can interleave or overlap

threads arbitrarily

  • Can lead to interference
    • Storage corruption (e.g. a data race / race condition)
    • Violation of representation invariant
    • Violation of a protocol (e.g. A occurs before B )

How does this apply to OSs?

• Any resource that is shared could be

accessed inappropriately

  • Shared memory
    • Kernel threads
    • Processes (shared memory set up by kernel)
  • Shared resources
    • Printer, Video screen, Network card, …

• OS must protect shared resources

  • And provide processes a means to protect

their own abstractions

Data Race Example

static int cnt = 0; t1.run() { int y = cnt; cnt = y + 1; } t2.run() { int y = cnt; cnt = y + 1; } cnt = 0 Start: both threads ready to run. Each will increment the global count. Shared state

Data Race Example

static int cnt = 0; t1.run() { int y = cnt; cnt = y + 1; } t2.run() { int y = cnt; cnt = y + 1; } cnt = 0 T1 executes, grabbing the global counter value into y. Shared state y = 0

Data Race Example

static int cnt = 0; t1.run() { int y = cnt; cnt = y + 1; } t2.run() { int y = cnt; cnt = y + 1; } cnt = 0 T1 is pre-empted. T executes, grabbing the global counter value into y. Shared state y = 0 y = 0

Data Race Example

static int cnt = 0; t1.run() { int y = cnt; cnt = y + 1; } t2.run() { int y = cnt; cnt = y + 1; } cnt = 1 T2 executes, storing the incremented cnt value. Shared state y = 0 y = 0

Data Race Example

static int cnt = 0; t1.run() { int y = cnt; cnt = y + 1; } t2.run() { int y = cnt; cnt = y + 1; } cnt = 1 T2 completes. T executes again, storing the old counter value (1) rather than the new one (2)! Shared state y = 0 y = 0

But When I Run it Again? Data Race Example

static int cnt = 0; t1.run() { int y = cnt; cnt = y + 1; } t2.run() { int y = cnt; cnt = y + 1; } cnt = 0 Start: both threads ready to run. Each will increment the global count. Shared state

Synchronization

static int cnt = 0; struct Mutex lock; Mutex_Init(&lock); void run() { Mutex_Lock (&lock); int y = cnt; cnt = y + 1; Mutex_Unlock (&lock); } Lock , for protecting The shared state Acquires the lock; Only succeeds if not held by another thread Releases the lock

Java-style synchronized block

static int cnt = 0; struct Mutex lock; Mutex_Init(&lock); void run() { synchronized (lock) { int y = cnt; cnt = y + 1; } } Lock , for protecting The shared state Acquires the lock; Only succeeds if not held by another thread Releases the lock

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 0 T1 acquires the lock

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 0 T1 reads cnt into y y = 0

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 0 T1 is pre-empted. T2 attempts to acquire the lock but fails because it’s held by T1, so it blocks y = 0

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 1 T1 runs, assigning to cnt y = 0

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 1 T1 releases the lock and terminates y = 0

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 1 T2 now can acquire the lock. y = 0

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 1 T2 reads cnt into y. y = 0 y = 1

Applying synchronization

int cnt = 0; t1.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } t2.run() { synchronized(lock) { int y = cnt; cnt = y + 1; } } Shared state cnt = 2 T2 assigns cnt, then releases the lock y = 0 y = 1

Mutexes (locks)

  • Only one thread can “acquire” a mutex
    • Other threads block until they can acquire it
    • Used for implementing critical sections
  • A critical section is a piece of code that

should not be interleaved with code from

another thread

  • Executed atomically
  • We’ll look at other ways to implement critical

sections later …

Mutex Policies

  • What if a thread already holds the mutex it’s

trying to acquire?

  • Re-entrant mutexes: The thread can reacquire the same lock many times. Lock is released when object unlocked the corresponding number of times - This is the case for Java
  • Non-reentrant: Deadlock! (defined soon.)
    • This is the case in GeekOS
  • What happens if a thread is killed while

holding a mutex? Or if it just forgets to

release it

  • Could lead to deadlock

Deadlock: Wait graphs

A T1 Thread T1 holds lock A T2 B Thread T2 attempting to acquire lock B Deadlock occurs when there is a cycle in the graph

Wait graph example

A T

T2 B

T1 holds lock on A T2 holds lock on B T1 is trying to acquire a lock on B T2 is trying to acquire a lock on A

Key Ideas

• Multiple threads can run simultaneously

  • Either truly in parallel on a multiprocessor
  • Or can be scheduled on a single processor
    • A running thread can be pre-empted at any time

• Threads can share data

  • Need to prevent interference
    • Synchronization is one way, but not the only way
  • Overuse use of synchronization can create

deadlock

  • Violation of liveness

Implementing Synchronization

• Next we’re going to revisit the issues

of synchronization for the

producer/consumer problem.

• Another way of looking at what we’ve

just gone over.

• Will prepare us to see possible

solutions next time.

Bounded-Buffer

• Shared data

#define BUFFER_SIZE 10 typedef struct {

... } item ; item buffer[BUFFER_SIZE]; int in = 0; int out = 0; int counter = 0;

Bounded-Buffer

  • Producer process item nextProduced; while (1) { while (counter == BUFFER_SIZE) ; / do nothing / buffer[in] = nextProduced; in = (in + 1) % BUFFER_SIZE; counter++; }

Bounded-Buffer

  • Consumer process item nextConsumed; while (1) { while (counter == 0) ; / do nothing / nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; counter--; }

Bounded Buffer

  • The statements

counter++;

counter--;

must be performed atomically.

  • Atomic operation means an operation that

completes in its entirety without

interruption.

Bounded Buffer

  • The statement “ count++ ” may be implemented in machine language as: register1 = counter register1 = register1 + 1 counter = register
  • The statement “ count-- ” may be implemented as: register2 = counter register2 = register2 – 1 counter = register

Bounded Buffer

• If both the producer and consumer

attempt to update the buffer

concurrently, the assembly language

statements may get interleaved.

• Interleaving depends upon how the

producer and consumer processes are

scheduled.

Bounded Buffer

  • Consider the three address code for the counter Counter Increment Counter Decrement reg 1 = counter reg 2 = counter reg 1 = reg 1 + 1 reg 2 = reg 2 - 1 counter = reg 1 counter = reg 2
  • Now consider an ordering of these instructions T 0 producer reg 1 = counter { reg 1 = 5 } T 1 producer reg 1 = reg 1 + 1 { reg 1 = 6 } T 2 consumer reg 2 = counter { reg 2 = 5 } T 3 consumer reg 2 = reg 2 - 1 { reg 2 = 4 } T 4 producer counter = reg 1 { counter = 6 } T 5 consumer counter = reg 2 { counter = 4 } (^) shouldThis be 5!

Race Condition

  • Race condition : The situation where several

processes access – and manipulate shared

data concurrently. The final value of the

shared data depends upon which process

finishes last.

  • To prevent race conditions, concurrent

processes must be synchronized.