master-degree-notes/Concurrent Systems/notes/4 - Semaphores.md

2.6 KiB

Object: entity with an implementation (hidden) and an interface (visible), made up of a set of operations and a specification of the behavior. Concurrent: if the object can be accessed by different processes.

Semaphore: is a shared counter S accessed via primitives up and down s.t.:

  • is initialized at s0 >= 0
  • it is alwayz >= 0
  • up atomically increases S
  • down atomically decreases S if it is not 0, otherwise the calling process is blocked and waits.

Main use: prevent busy waiting: suspend processes that cannot perform down.

Strong semaphore: if uses a FIFO policy for blocking/unblocking, otherwise it's weak. Binary semaphore: if it is at most 1 (also up are blocking).

A semaphore needs two underlying objects:

  • a counter initialized at s0 that can also become negative (see the implementation below to understand)
  • a data structure (typically a queue), initially empty, to store suspended processes.

Ideal implementation

S.down() :=
	S.counter--
	if S.counter < 0 then
		enter into S.queue
		SUSPEND
	return

S.up() :=
	S.counter++
	if S.counter <= 0 then
		activate a proc from S.queue
	return

[!note] note if S.counter ≥ 0, then this is the value of the semaphore; otherwise, S.counter tells you how many processes are suspended in S

all operations are in MUTEX

Actual implementation

Let t be a test&set register initialized at 0

S.down() :=
	Disable interrupts
	wait S.t.test&set() = 0
	S.counter--
	if S.counter < 0 then
		enter into S.queue
		S.t <- 0
		Enable interrupts
		SUSPEND
	else
		S.t <- 0
		Enable interrupts
	return

S.up() :=
	Disable interrupts
	wait S.t.test&set() = 0
	S.counter++
	if S.counter <= 0 then
		activate a proc from S.queue
	S.t <- 0
	Enable interrupts
	return

Same as before but we use test&set to actually ensure MUTEX (of course we could have used any other hardware MUTEX implementation seen so far).

(Single) Producer/Consumer

It's a shared FIFO buffer of size k.

  • BUF[0,…,k-1]: generic registers (not even safe) accessed in MUTEX
  • IN/OUT : two variables pointing to locations in BUF to (circularly) insert/remove items, both initialized at 0
  • FREE/BUSY: two semaphores that count thew number of free/busy cells of BUF, initialized at k and 0 respectively.
B.produce(v) :=
	FREE.down() # blocking if FREE becomes negative
	BUF[IN] <- v
	IN <- (IN+1) mod k # mod k since it is circular
	BUSY.up() # "there is something to consume"
	return

B.consume() :=
	BUSY.down() # it blocks if there is nothing to consume
	tmp <- BUF[OUT]
	OUT <- (OUT+1) mod k
	FREE.up()
	return tmp