s10: threads required: lab 10a - threads recommended: multithreaded programming (posix pthreads...
TRANSCRIPT
S10: Threads
Required: Lab 10a - Threads
Recommended: Multithreaded Programming (POSIX pthreads Tutorial)
BYU CS 224 Threads 2
CS 224Chapter Lab Homework S00: Introduction
Unit 1: Digital Logic
S01: Data Types S02: Digital Logic
L01: Data TypesL02: FSM
HW01HW02
Unit 2: ISA
S03: ISA S04: Microarchitecture S05: Stacks / Interrupts S06: Assembly
L03: BlinkyL04: MicroarchL05b: Traffic LightL06a: Morse Code
HW03HW04HW05HW06
Unit 3: C
S07: C Language S08: Pointers S09: Structs S10: Threads S11: I/O
L07b: Morse IIL08a: LifeL09b: SnakeL10a: Threads
HW07HW08HW09HW10
Learning Objectives…
Learning Outcomes
After completing this section, you should be able to Explain the concept of a thread – a
fundamental unit of CPU utilization. Use POSIX Pthread APIs to
multithread a process. Use POSIX Pthread mutexes to
protect critical code. Use POSIX Pthread semaphores to
synchronize program events. Demonstrate the differences between
event driven models and multithreading programming solutions.
BYU CS 224 Threads 3
Topics Processes vs Threads CPU Context Thread Control Block Thread Management
Initialization Creation Termination Join
Scheduling Mutexes Thread-safe Semaphores
Threads 4BYU CS 224
Processes vs Threads
Traditionally, a process is considered an instance of a computer program that is being executed.
A process contains System resources: program code, user data, buffers, devices, I/O
channels, files. Current activity: CPU, registers, state, execution path, “On the
clock”, interleaved with other processes. Resources and CPU activity can be treated
independently: Unit of resource ownership process or task Unit of execution thread or lightweight process
A Thread is an independent program counter operating within a process.
Smallest unit of processing (context) that can be scheduled by an operating system.
Threads 5
Multi-threaded Process
BYU CS 224
Single threaded Process
code(.text)
dataglobal (.bss)
heap
systemfiles
MMU
contextPC, SR
registersstack
dataglobal (.bss)
heap
systemfiles
MMU
pthread 1PC, SR
registersstack
Multi-threaded Process
pthread 2PC, SR
registersstack
pthread 3PC, SR
registersstack
code(.text)
Threads 6
Concurrent Threads
BYU CS 224
pthread 1pthread 2pthread 3
Time
pthread 3
pthread 2
pthread 1
Threads only "appear" to be executing at the same time (unless running on multi-core hardware).
Unnecessary context switching between threads is pure wasted CPU usage (overhead).
In addition, resource sharing introduces concurrency problems of mutual exclusion, deadlock, and synchronization.
Because there are an infinite number of ways a multithreaded program can execute, often testing and debugging multithreaded programs are inherently more difficult than single-threaded applications.
Threads 7BYU CS 224
CPU Context
Processor state data are those pieces of information that define the status of a process when it's suspended, allowing the OS to restart it later and still execute correctly.
This always includes the content of the CPU program counter, general-purpose registers, the CPU process status word, stack and frame pointers etc.
During context switch, the running thread is stopped and another thread is given a chance to run.
The scheduler must stop the execution of the running thread, copy out the values in hardware registers to its Thread Control Block (TCB), and update the hardware registers with the values from the TCB of the new thread.
Threads 8BYU CS 224
Thread Control Block
A Thread Control Block (TCB) is a data structure that holds:
Thread identification data – unique identifier of thread (ctid). Thread state data - information that define the status of a thread
when it is not running. Thread program counter CPU general-purpose registers CPU process status register Thread stack pointer
Thread control data - information used to mange the thread. Thread scheduling state (“Running”, “Ready“, or “Blocked“) Scheduling information such as ID of event blocking the thread.
Other information such as priority, elapsed time, children id's, signals, usage, privileges,…
Threads 9
TCB States
BYU CS 224
R4R5R6R7R8R9
R10R11R12R13R14R15
R2 (SR)R0 (PC)&exit
tcbs[0].thread 0x0572.stack 0x05d4.block 0x0000.retval 0x0000
tcbs[1].thread 0x0202.stack 0x0254.block 0x0000.retval 0x0000
tcbs[2].thread 0x0282.stack 0x02d4.block 0x0422.retval 0x0000
tcbs[3].thread 0x0000.stack 0x0000.block 0x0000.retval 0x0000
Th
read
#2 S
tack
&exit
Th
read
#0 S
tack
Running
R4R5R6R7R8R9
R10R11R12R13R14R15
R2 (SR)R0 (PC)&exit
Th
read
#1 S
tack
Ready
Blocked
Thread #2 blockedon mutex/semaphore
0x0422
(Available)
Non-assignedThread
0x0000
ctid = 0(Available)
Thread #1Context
R4R5R6R7R8R9
R10R11R12R13R14R15
R2 (SR)R0 (PC)
Tcb tcbs[MAX_THREADS]; // thread control blocksvolatile pthread_t ctid; // current task id
Threads 10BYU CS 224
Thread Management
int pthread_init(pthread_attr_t*); Init TCB’s Start TimerA.
int pthread_create(pthread_t*, pthread_attr_t*, void* (*func)(void*), void*);
Malloc a new stack Add thread to ready queue (tcbs).
int pthread_join(pthread_t tid, void** return_value); Waits for thread to terminate.
void pthread_exit(void* return_value); Free thread stack Delete thread from ready queue (tcbs).
int pthread_yield(void); Forces rescheduling of threads.
Threads 11
Blockedse
m_w
ait
pth
read_m
ute
x_lo
ck
sem_sig
nal
pth
read_m
utex_u
nlo
ck
Scheduling
BYU CS 224
pthread_createNew Ready
pthread_exitExit
TA ISR
Time-outpthread_yiel
d
Running
The CPU scheduler (TA_ISR) decides which of the ready, in-memory threads is to be executed after a thread’s time quantum (time slice) is consumed or another form of signal from a thread or interrupt occurs.
Threads 12
pthreadsISR.asm
BYU CS 224
.cdecls C,"msp430.h" ; MSP430 .cdecls C,"pthreads.h" ; threads header
.def TA_isr .ref ctid,tcbs
tcb_thread .equ (tcbs + 0) ; beginning of thread stacktcb_stack .equ (tcbs + 2) ; ready/blocked stack pointertcb_block .equ (tcbs + 4) ; blocking event
; Timer A ISR ----------------------------------------------------------- .text ; beginning of executable codeTA_isr: bic.w #TAIFG|TAIE,&TACTL ; acknowledge & disable TA interrupt;; >>>>>> 1. Save current context on stack; >>>>>> 2. Save SP in task control block; >>>>>> 3. Find next non-blocked thread tcb; >>>>>> 4. If all threads blocked, set LPM0+GIE in SR and repeat step 3; >>>>>> 5. Set new SP; >>>>>> 6. Load context from stack; bis.w #TAIE,&TACTL ; enable TA interrupts reti
; Interrupt Vector ------------------------------------------------------ .sect ".int08" ; timer A section .word TA_isr ; timer A isr .end
Threads 13
pthreadsISR.c
BYU CS 224
#pragma vector = TIMERA1_VECTOR__interrupt void TIMERA1_ISR(void){ TACTL &= ~(TAIFG | TAIE); // acknowledge & disable TA interrupt saveCurrentState(); // save current thread state on stack tcbs[ctid].stack = (void*)__get_SP_register();
// find next non-blocked task in tcbs array register int count = 0; while (!tcbs[ctid = ++ctid & 0x0003].thread || tcbs[ctid].block) { if (!(count = ++count & 0x0003)) __bis_SR_register(LPM0 | GIE); }
__set_SP_register((unsigned short)tcbs[ctid].stack); restoreState(); // restore new thread state TACTL |= TAIE; // enable TA interrupts return;} // end TIMERA1_ISR
C Example of TimerA Scheduler:
Threads 14BYU CS 224
Reentrant vs Thread-safe
A reentrant function can be safely called from any thread at any time without crashing your program.
A thread-safe function guarantees the function is atomic and can complete without fear of corrupted results.
While POSIX stdio.h operations are generally thread-safe, our lcd functions are not.
The printf function is reentrant, but not thread-safe. You can not guarantee that during a lcd function (such as printf)
the output from one thread won't start half way through the output from another thread also using a lcd function.
Without some application-level locking, whatever is written by multiple threads could be interleaved.
Use a pthread mutex (lock, unlock) functions to achieve larger-than-single-function-call atomic segments of code.
Threads 15BYU CS 224
Mutexes
A mutex is locking mechanism used to synchronize access to a resource (such as code).
Only one task can acquire the mutex. It means there is ownership associated with mutex, and only the
owner can release the lock (mutex). A mutex is designed to protect critical data so that only
one thread can access it at the same time, such as a section of code or access to a variable.
Threads 16BYU CS 224
Mutex Example
void* thread1(void* arg){ int count = 0; while (1) { pthread_mutex_lock(lcd_mutex); lcd_cursor(20, 20); lcd_printf(“Thread1 %d", ++count); pthread_mutex_unlock(lcd_mutex); } return (void*)0;} // end thread1
pthread_mutex_t lcd_mutex;int main(){ pthread_init_mutex(lcd_mutex); pthread_create(NULL, NULL, thread1, NULL); pthread_create(NULL, NULL, thread2, NULL);}
void* thread2(void* arg){ int count = 0; while (1) { pthread_mutex_lock(lcd_mutex); lcd_cursor(20, 40); lcd_printf(“Thread2 %d", ++count); pthread_mutex_unlock(lcd_mutex); } return (void*)0;} // end thread2
Threads 17
Thread-safe Code
BYU CS 224
pthread_lock(m)
pthread_lock(m)pthread_lock(m)
pthread_unlock(m)
pthread_unlock(m)
pthread_unlock()
pthread 1pthread 2pthread 3
Critical Code
Exec
utio
n
TimeSlice
Context Switch
Critical Code
Thread 2 BlockedThread 3 Blocked
pthread_lock(m)
pthread_lock(m)pthread_lock(m)
Critical Code
Thread 1 Thread 2 Thread 3
Thread 2 BlockedThread 3 Blocked
Critical Code
Critical Code
pthread_unlock(m)
pthread_unlock(m)
pthread_unlock(m)
Threads 18BYU CS 224
Semaphores
A semaphore is a protected variable whose value is accessed and altered by the operations signal (produce) and wait (consume).
A semaphore is used for controlling access to a common resource in a concurrent system, such as multi-threading.
The value of a semaphore is the number of available resources and may be used to synchronize various task activities.
A useful way to think of a semaphore is as a record of how many units of a particular resource are available, coupled with operations to safely consume those units, and, if necessary, wait until a unit of the resource becomes available.
Threads 19BYU CS 224
Semaphore Example
//-- Watchdog Timer ISR#pragma vector = WDT_VECTOR__interrupt void WDT_ISR(void){ // 1 second counter if (--WDT_cps_cnt == 0) { LED_GREEN_TOGGLE; WDT_cps_cnt = WDT_CPS; seconds++; sem_signal(&sem_1sec); __ bic_SR_register_on_exit(LPM0_bits); } return;} // end WDT_ISR
//-- Timer Threadvoid* myThread(void* arg){ while (1) { sem_wait(&sem_1sec); pthread_mutex_lock(lcd_mutex); lcd_cursor(20, 40); lcd_printf(“Seconds = %d", seconds); pthread_mutex_unlock(lcd_mutex); } return (void*)0;} // end myThread
Producer Consumer
Threads 20BYU CS 224