@@ -74,6 +74,7 @@ is distributed under the [ISC license](LICENSE.md).
7474  -  [ Programming with transactional data structures] ( #programming-with-transactional-data-structures ) 
7575    -  [ The dining philosophers problem] ( #the-dining-philosophers-problem ) 
7676    -  [ A transactional LRU cache] ( #a-transactional-lru-cache ) 
77+     -  [ The sleeping barbers problem] ( #the-sleeping-barbers-problem ) 
7778  -  [ Programming with primitive operations] ( #programming-with-primitive-operations ) 
7879-  [ Designing lock-free algorithms with k-CAS] ( #designing-lock-free-algorithms-with-k-cas ) 
7980  -  [ Understand performance] ( #understand-performance ) 
@@ -1046,6 +1047,164 @@ val a_cache : (int, string) cache =
10461047As an exercise, implement an operation to ` remove `  associations from a cache and
10471048an operation to change the capacity of the cache.
10481049
1050+ #### The sleeping barbers problem  
1051+ 
1052+ The
1053+ [ sleeping barber problem] ( https://en.wikipedia.org/wiki/Sleeping_barber_problem ) 
1054+ is another classic communication and synchronization problem. Let's write a
1055+ solution using ** kcas** .
1056+ 
1057+ ``` ocaml 
1058+ module Barbershop : sig 
1059+   type ('barber, 'customer) t 
1060+   val create : int -> ('b, 'c) t 
1061+   val get_barber_opt : xt:'x Xt.t -> ('b, 'c) t -> 'b option 
1062+   val try_enqueue : xt:'x Xt.t -> ('b, 'c) t -> 'c -> bool 
1063+   val get_customer_opt : xt:'x Xt.t -> ('b, 'c) t -> 'c option 
1064+   val sleep : xt:'x Xt.t -> ('b, 'c) t -> 'b -> unit 
1065+   val is_closed : xt:'x Xt.t -> ('b, 'c) t -> bool 
1066+   val close : xt:'x Xt.t -> ('b, 'c) t -> unit 
1067+ end = struct 
1068+   type ('barber, 'customer) t = { 
1069+     sleeping_barbers : 'barber Queue.t; 
1070+     waiting_customers : 'customer Queue.t; 
1071+     is_closed : bool Loc.t; 
1072+   } 
1073+ 
1074+   let create capacity = 
1075+     let sleeping_barbers = Queue.create () 
1076+     and waiting_customers = Queue.create ~capacity () 
1077+     and is_closed = Loc.make false in 
1078+     { sleeping_barbers; waiting_customers; is_closed } 
1079+ 
1080+   let get_barber_opt ~xt bs = 
1081+     Queue.Xt.take_opt ~xt bs.sleeping_barbers 
1082+ 
1083+   let try_enqueue ~xt bs customer = 
1084+     not (Xt.get ~xt bs.is_closed) && 
1085+     Queue.Xt.try_add ~xt customer bs.waiting_customers 
1086+ 
1087+   let get_customer_opt ~xt bs = 
1088+     Queue.Xt.take_opt ~xt bs.waiting_customers 
1089+ 
1090+   let sleep ~xt bs barber = 
1091+     if not (Xt.get ~xt bs.is_closed) 
1092+     then Queue.Xt.add ~xt barber bs.sleeping_barbers 
1093+ 
1094+   let is_closed ~xt bs = Xt.get ~xt bs.is_closed 
1095+ 
1096+   let close ~xt bs = 
1097+     Xt.set ~xt bs.is_closed true; 
1098+     Queue.Xt.clear ~xt bs.sleeping_barbers; 
1099+     Queue.Xt.clear ~xt bs.waiting_customers 
1100+ end 
1101+ ``` 
1102+ 
1103+ ``` ocaml 
1104+ type customer = { 
1105+   cut_hair : 'x.xt:'x Xt.t -> unit; 
1106+ } 
1107+ 
1108+ type barber = { 
1109+   wake_up : 'x.xt:'x Xt.t -> customer -> unit; 
1110+ } 
1111+ ``` 
1112+ 
1113+ ``` ocaml 
1114+ # let customer shop cuts = 
1115+     let clean = Mvar.create None in 
1116+     let self = { cut_hair = Mvar.Xt.put clean true } in 
1117+     while not (Xt.commit { tx = Barbershop.is_closed shop }) do 
1118+       let try_get_barber ~xt = 
1119+         match Barbershop.get_barber_opt ~xt shop with 
1120+         | None -> 
1121+           Barbershop.try_enqueue ~xt shop self 
1122+         | Some barber -> 
1123+           barber.wake_up ~xt self; 
1124+           true 
1125+       in 
1126+       if Xt.commit { tx = try_get_barber } then 
1127+         let try_get_haircut ~xt = 
1128+           not (Barbershop.is_closed ~xt shop) && 
1129+           Mvar.Xt.take ~xt clean 
1130+         in 
1131+         if Xt.commit { tx = try_get_haircut } then 
1132+           Loc.incr cuts 
1133+     done 
1134+ val customer : (barber, customer) Barbershop.t -> int Loc.t -> unit = <fun> 
1135+ ``` 
1136+ 
1137+ ``` ocaml 
1138+ # let barber shop cuts = 
1139+     let customer = Mvar.create None in 
1140+     let self = { wake_up = Mvar.Xt.put customer } in 
1141+     while not (Xt.commit { tx = Barbershop.is_closed shop }) do 
1142+       let cut customer = 
1143+         Xt.commit { tx = customer.cut_hair }; 
1144+         Loc.incr cuts 
1145+       in 
1146+       let try_get_customer ~xt = 
1147+         match Barbershop.get_customer_opt ~xt shop with 
1148+         | Some _ as some -> some 
1149+         | None -> 
1150+           Barbershop.sleep ~xt shop self; 
1151+           None 
1152+       in 
1153+       match Xt.commit { tx = try_get_customer } with 
1154+       | Some customer -> cut customer 
1155+       | None -> 
1156+         let sleeping ~xt = 
1157+           if Barbershop.is_closed ~xt shop then None 
1158+           else Some (Mvar.Xt.take ~xt customer) 
1159+         in 
1160+         match Xt.commit { tx = sleeping } with 
1161+         | Some customer -> cut customer 
1162+         | None -> () 
1163+     done 
1164+ val barber : (barber, customer) Barbershop.t -> int Loc.t -> unit = <fun> 
1165+ ``` 
1166+ 
1167+ ``` ocaml 
1168+ # let sleeping_barbers ~barbers 
1169+                        ~queue_max 
1170+                        ~customers 
1171+                        ~cuts_per_agent = 
1172+     assert (0 < barbers 
1173+          && 0 <= queue_max 
1174+          && 0 <= customers 
1175+          && 0 <= cuts_per_agent); 
1176+     let shop = Barbershop.create queue_max in 
1177+     let barbers = Array.init barbers @@ fun _ -> 
1178+       let cuts = Loc.make 0 in 
1179+       (cuts, Domain.spawn (fun () -> barber shop cuts)) 
1180+     and customers = Array.init customers @@ fun _ -> 
1181+       let cuts = Loc.make 0 in 
1182+       (cuts, Domain.spawn (fun () -> customer shop cuts)) 
1183+     in 
1184+     let agents = Array.append barbers customers in 
1185+     while agents 
1186+           |> Array.map fst 
1187+           |> Array.exists @@ fun c -> 
1188+              Loc.get c < cuts_per_agent do 
1189+       Domain.cpu_relax () 
1190+     done; 
1191+     Xt.commit { tx = Barbershop.close shop }; 
1192+     agents 
1193+     |> Array.map snd 
1194+     |> Array.iter Domain.join 
1195+ val sleeping_barbers : 
1196+   barbers:int -> queue_max:int -> customers:int -> cuts_per_agent:int -> unit = 
1197+   <fun> 
1198+ ``` 
1199+ 
1200+ ``` ocaml 
1201+ # sleeping_barbers ~barbers:2 
1202+                    ~queue_max:1 
1203+                    ~customers:4 
1204+                    ~cuts_per_agent:10 
1205+ - : unit = () 
1206+ ``` 
1207+ 
10491208### Programming with primitive operations  
10501209
10511210In addition to the transactional interface, ** kcas**  also provides the
0 commit comments