Skip to content

Instantly share code, notes, and snippets.

@mashingan
Last active April 26, 2017 23:54
Show Gist options
  • Save mashingan/a91b928aae47e20149ef9024588149ec to your computer and use it in GitHub Desktop.
Save mashingan/a91b928aae47e20149ef9024588149ec to your computer and use it in GitHub Desktop.
Barbershop problem implementation https://en.wikipedia.org/wiki/Sleeping_barber_problem
## The implementation of Sleeping Barber Problem
## https://en.wikipedia.org/wiki/Sleeping_barber_problem
## The `seatAvailable` is guarded with `queue` Lock while the barber
## explicitly acquired and released
## Using pointer math as written in this post
## https://forum.nim-lang.org/t/1188#7366
## compile and run: $ nim c -r --threads:on --threadAnalysis:off barbershop.nim
from os import sleep
from random import random, randomize
import locks
type
Customer = ref object
id: int
inQueue, isDone: bool
Coll[T] = object
coll: ptr T
size: int
PColl[T] = ptr Coll[T]
const
totalCustomer = 20
seat = 3
cuttingTime = 1000 # in ms
var
lock, queue: Lock
servedCustomers: int
customers: array[totalCustomer, Thread[Customer]]
proc newColl[T](): PColl[T] =
result = create(Coll[T], 1)
result.coll = createShared(T, 1)
result.size = 0
proc newColl[T](size = 0, init: T): PColl[T] =
var newsize: int
if size == 0:
newsize = 1
result = create(Coll[T], newsize)
result.coll = createShared(T, newsize)
result.coll[] = init
result.size = newsize
var seatAvailable{.guard: queue.} = newColl[int]()
proc freeColl(p: PColl){.discardable.} =
discard p.coll.resizeShared 0
discard p.resize 0
proc `$`(p: PColl): string =
result = "["
for i in 0..<p.size:
result &= $p.coll[i]
if i != p.size - 1:
result &= ", "
result &= "]"
proc `[]`[T](p: PColl[T], off: int): T =
p.coll[off]
proc `[]=`[T](p: var PColl[T], off: int, val: T) =
p.coll[off] = val
proc notInQueue(c: Customer): bool =
not c.inQueue
template servingAction(num: int): typed =
echo "(b) serving customer ", num
sleep random(cuttingTime)
inc servedCustomers
echo "(c) customer ", num, " finish cutting hair"
template `+`[T](p: ptr T, off: int): ptr T =
cast[ptr p[].type](cast[ByteAddress](p) +% off * p[].sizeof)
template `[]`[T](p: ptr T, off: int): T =
(p+off)[]
template `[]=`[T](p: ptr T, off: int, val: T) =
(p+off)[] = val
proc len(p: PColl): int =
p.size
proc inc[T](p: var ptr T) {.discardable.} =
p = p + 1
proc contains[T](p: ptr T, x: T): bool =
var temp = p
for i in 0..<p.len:
if x == temp[]:
return true
inc temp
false
proc contains[T](p: PColl[T], val: T): bool =
for i in 0..<p.size:
if val == p.coll[i]:
return true
false
proc delete(p: var PColl, idx: int){.discardable.} =
if idx > p.size:
return
var temp = p.coll + idx + 1
p.coll[idx] = temp[]
inc temp
for i in idx+1..<p.size:
if temp.isNil:
break
p.coll[i] = temp[]
inc temp
dec p.size
p.coll = resizeShared(p.coll, p.size)
proc add[T](p: var PColl, val: T) {.discardable.} =
p.coll = resizeShared(p.coll, p.size+1)
if p.size == 0:
p[0] = val
else:
p[p.size] = val
inc p.size
template illegableToWait(c: Customer): untyped =
{.locks: [queue].}:
seatAvailable.len >= 0 and seatAvailable.len < seat and
c.id notin seatAvailable and c.notInQueue
template tobeTurnedAway(c: Customer): untyped =
{.locks: [queue].}:
c.id notin seatAvailable
proc serving (cust: Customer) {.thread.} =
echo "(c) customer ", cust.id, " entering shop"
{.locks: [queue].}:
echo "(s) current queuing: ", seatAvailable
while true:
let barberFree = tryAcquire lock
if barberFree:
{.locks: [queue].}:
var turn: int
if seatAvailable.len <= seat and seatAvailable.len > 0:
turn = seatAvailable[0]
seatAvailable.delete 0
else:
turn = cust.id
echo "now seatAvailable before serving: ", seatAvailable
servingAction turn
release lock
break
elif cust.inQueue:
continue
elif cust.illegableToWait:
{.locks: [queue].}:
echo "(s) seatAvailable: ", seatAvailable
echo "(c) customer ", cust.id, " waiting in queue"
seatAvailable.add cust.id
echo "(c) seatAvailable after queuing: ", seatAvailable
cust.inQueue = true
elif cust.tobeTurnedAway:
{.locks: [queue].}:
echo "seatAvailable: ", seatAvailable
echo "(c) turn away customer ", cust.id
break
proc newCustomer(id: int): Customer =
new result
result.id = id
result.inQueue = false
result.isDone = false
proc main =
randomize()
initLock lock
initLock queue
echo "Total customer today will be: ", totalCustomer
for i in 1..totalCustomer:
echo "loop in: ", i
customers[i-1].createThread serving, newCustomer(i)
# to make it the customer come at random time when barber working
sleep random(cuttingTime div 2)
joinThreads(customers)
echo "Total served customers is ", servedCustomers
{.locks: [queue].}:
seatAvailable.freeColl
when isMainModule:
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment