lab 2 revised
pthreads: detached vs. joinable threads
shorter recap (by popular demand)
mmap
interprocess communication
low-level interfaces: void pointer (typeless) & size
sizeof
operator to get size (#bytes) of objectmessage passing is safer (easier to reason about, hence less error-prone) than shared memory
networking: preferred IPC mechanism because you can put processes on different machines (or not)
threads vs. processes
thread implementation
distributed convex hull: more logically implemented using threads
Posix Threads:
types: compile-time concept
C was designed as a high-level assembler
struct: lay out in memory may include padding
Observations:
struct S1 {
char c1;
char c2;
int i;
};
struct S2 {
char c1;
int i;
char c2;
};
Note:
sizeof(S1) == 8
but sizeof(S2) == 12
array: `a[i] == (&(a[0]) + i * sizeof(a))
a == &(a[0])
p + i
is same as (char *) p + i * sizof(*p)
a[i] == *(a + i) == *(i + a) = i[a]
casting: lets programmer assert (to compiler) that block of memory corresponds to a type
void *
: untyped address
char *
for this purpose
char *
because
byte is smallest addressable unit of memory
(sizeof(char) == 1
)pointer to function: use address of function entry point
struct Animal{void (*speak)();};
dog.speak()
--> woofcat.speak()
--> meowcritical section: region of code which only one process at a time may execute
may be a performance bottleneck
requirements:
canonical example:
// process/thread 1:
current_balance = account.get_balance()
new_balance = current_balance + paycheck
account.update(new_balance)
// process/thread 2:
current_balance = account.get_balance()
new_balance = current_balance - withdrawl
account.update(new_balance)
sequential execution ok: either transaction may come first
concurent execution: problematic
TODO: diagram
Decker's algorithm
naive approaches fail:
reasoning about concurrent processes is hard
solution: combine both approaches
TODO: diagram & pseudocode
```
```
atomic operations
e.g.: test & set
for most practical purposes, a mutex is a binary semaphore
for full capability of semaphore, need to combine mutex (exclusive access) with condition variable (signaling)
example: protecting a dictionary vs process that requires exclusive access to ditionary
Dijkstra (Dutch Computer Scientist, you may have heard of him) ~1962/63
OS-level service: "convenient" interface
Semaphore S
P()
, V()
P()
: Proben
wait()
or down()
V()
: Verhogen
signal()
or up()
mutex: more-or-less binary semaphore
#include <pthread.h>
int pthread_mutex_init(
pthread_mutex_t *mutex,
const pthread_mutex_attr_t *attr
);
// if attr is NULL, use default values
pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_trylock
example: producer-consumer pair: producer and consumer must have exclusive access (mutual exclusion) to buffer, but consumer must also wait until the buffer is not empty to proceed
polling:
while !done
get mutex
if condition
process()
done = true
release mutex
mutex guarantees exclusive access, but polling for condition is expensive
pthread condition variable is used to tell process to wake up and check to see if condition holds (e.g. is buffer non-empty)
condition varible:
while !done
get mutex lock
while !condition
wait on condition variable
if condition
process()
done = true
release lock
wait
signal
signal
signal(2)
broadcast
int pthread_cond_init(
pthread_cond_t *cond,
const phtread_condattr_t *attr // NULL for default settings
)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_wait(
pthread_cond_t *cond,
pthread_mutex_t *mutex
);
wait with timeout: pthread_cond_timedwait
all waiters must use same mutex
pthread_cond_wait
when holding the mutex
possible to have spurious returns from pthread_cond_wait()
language-level implementation, e.g
protected {
// code
}
only one thread at a time may enter protected (monitor) block
Java: synchronized
blocks
can implement monitor in C++ using RAII (Resource Allocation Is Initiation):
{
// unprotected code
{
// more unprotected code (optional)
Monitor m(); // constructor acquires lock
// protected code
m.wait_for(signal_var) // optional
// more protected code
} // destructor releases lock, called automagically on object m when leaving scope
// even more unprotected code
}