Last active
April 26, 2017 23:54
-
-
Save mashingan/a91b928aae47e20149ef9024588149ec to your computer and use it in GitHub Desktop.
Barbershop problem implementation https://en.wikipedia.org/wiki/Sleeping_barber_problem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## 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