@@ -75,6 +75,7 @@ is distributed under the [ISC license](LICENSE.md).
7575  -  [ Programming with transactional data structures] ( #programming-with-transactional-data-structures ) 
7676    -  [ The dining philosophers problem] ( #the-dining-philosophers-problem ) 
7777    -  [ A transactional LRU cache] ( #a-transactional-lru-cache ) 
78+     -  [ The sleeping barbers problem] ( #the-sleeping-barbers-problem ) 
7879  -  [ Programming with primitive operations] ( #programming-with-primitive-operations ) 
7980-  [ Designing lock-free algorithms with k-CAS] ( #designing-lock-free-algorithms-with-k-cas ) 
8081  -  [ Understand performance] ( #understand-performance ) 
@@ -1049,6 +1050,270 @@ val a_cache : (int, string) cache =
10491050As an exercise, implement an operation to ` remove `  associations from a cache and
10501051an operation to change the capacity of the cache.
10511052
1053+ #### The sleeping barbers problem  
1054+ 
1055+ The
1056+ [ sleeping barber problem] ( https://en.wikipedia.org/wiki/Sleeping_barber_problem ) 
1057+ is another classic communication and synchronization problem. Let's write a
1058+ solution using ** kcas** .
1059+ 
1060+ There are
1061+ [ many ways to solve the problem] ( https://en.wikipedia.org/wiki/Sleeping_barber_problem#Solutions ) 
1062+ and, in particular, there are concise and subtle implementations using
1063+ semaphores or mutexes. Instead of transliterating a solution using semaphores,
1064+ our approach uses queues and other concurrent data structures. We also solve the
1065+ generalized problem with multiple barbers and we also implement a mechanism to
1066+ close the barbershop. In addition, we abstract the concept of a barbershop,
1067+ where barbers and customers interact. All of this makes our solution longer than
1068+ the well known semaphore based solution. On the other hand, one might argue that
1069+ our solution is a more direct transliteration of the problem. Our solution also
1070+ avoids the starvation problem by using queues.
1071+ 
1072+ Let's begin by abstracting customer
1073+ 
1074+ ``` ocaml 
1075+ type customer = { 
1076+   notify_hair_has_been_cut : 'x.xt:'x Xt.t -> unit; 
1077+ } 
1078+ ``` 
1079+ 
1080+ and barber
1081+ 
1082+ ``` ocaml 
1083+ type barber = { 
1084+   wake_up : 'x.xt:'x Xt.t -> customer -> unit; 
1085+ } 
1086+ ``` 
1087+ 
1088+ actors. The idea is that barbers notify customers after finishing their haircut
1089+ and, adhering to the problem description, customers wake up sleeping barbers.
1090+ 
1091+ A barbershop consists of any number of barbers and waiting customers and can be
1092+ marked as closed:
1093+ 
1094+ ``` ocaml 
1095+ type barbershop = { 
1096+   sleeping_barbers : barber Queue.t; 
1097+   waiting_customers : customer Queue.t; 
1098+   is_closed : bool Loc.t; 
1099+ } 
1100+ ``` 
1101+ 
1102+ The barbershop constructor does not limit the number of barbers, which are
1103+ assumed to bring their own chairs, but does require a specification of the
1104+ number of waiting room chairs for customers:
1105+ 
1106+ ``` ocaml 
1107+ # let barbershop ~num_waiting_chairs = 
1108+     let sleeping_barbers = Queue.create () 
1109+     and waiting_customers = Queue.create ~capacity:num_waiting_chairs () 
1110+     and is_closed = Loc.make false in 
1111+     { sleeping_barbers; waiting_customers; is_closed } 
1112+ val barbershop : num_waiting_chairs:int -> barbershop = <fun> 
1113+ ``` 
1114+ 
1115+ Although the ` barbershop `  type is not abstract, we treat it as such, so we
1116+ provide a transactional predicate to check whether the barbershop is closed or
1117+ not:
1118+ 
1119+ ``` ocaml 
1120+ # let is_closed ~xt bs = Xt.get ~xt bs.is_closed 
1121+ val is_closed : xt:'a Xt.t -> barbershop -> bool = <fun> 
1122+ ``` 
1123+ 
1124+ To ` close `  a barbershop we set the ` is_closed `  location to ` true `  and clear both
1125+ the sleeping barbers and waiting customers queues:
1126+ 
1127+ ``` ocaml 
1128+ # let close ~xt bs = 
1129+     Xt.set ~xt bs.is_closed true; 
1130+     Queue.Xt.clear ~xt bs.sleeping_barbers; 
1131+     Queue.Xt.clear ~xt bs.waiting_customers 
1132+ val close : xt:'a Xt.t -> barbershop -> unit = <fun> 
1133+ ``` 
1134+ 
1135+ A barber can try to get a customer sitting on a waiting room chair:
1136+ 
1137+ ``` ocaml 
1138+ # let get_sitting_customer_opt ~xt bs = 
1139+     Queue.Xt.take_opt ~xt bs.waiting_customers 
1140+ val get_sitting_customer_opt : xt:'a Xt.t -> barbershop -> customer option = 
1141+   <fun> 
1142+ ``` 
1143+ 
1144+ Or may go to sleep on the barber's own chair:
1145+ 
1146+ ``` ocaml 
1147+ # let sleep ~xt bs barber = 
1148+     if not (is_closed ~xt bs) then 
1149+       Queue.Xt.add ~xt barber bs.sleeping_barbers 
1150+ val sleep : xt:'a Xt.t -> barbershop -> barber -> unit = <fun> 
1151+ ``` 
1152+ 
1153+ Note that the ` sleep `  transaction uses the ` is_closed `  predicate. Barbers, as
1154+ well as customers, must leave the shop in case it is closed.
1155+ 
1156+ A customer can try to find a sleeping barber:
1157+ 
1158+ ``` ocaml 
1159+ # let get_sleeping_barber_opt ~xt bs = 
1160+     Queue.Xt.take_opt ~xt bs.sleeping_barbers 
1161+ val get_sleeping_barber_opt : xt:'a Xt.t -> barbershop -> barber option = 
1162+   <fun> 
1163+ ``` 
1164+ 
1165+ Or sit on a waiting room chair:
1166+ 
1167+ ``` ocaml 
1168+ # let try_sitting ~xt bs customer = 
1169+     not (is_closed ~xt bs) && 
1170+     Queue.Xt.try_add ~xt customer bs.waiting_customers 
1171+ val try_sitting : xt:'a Xt.t -> barbershop -> customer -> bool = <fun> 
1172+ ``` 
1173+ 
1174+ The above ` try_sitting `  transaction is non-blocking. In case the
1175+ ` waiting_customers `  queue is full, it will return ` false ` . With the ` customer ` 
1176+ actor implementation we'll look at shortly this would mean that customers would
1177+ busy-wait, which works, but potentially wastes energy. Here is a blocking
1178+ version of ` try_sitting ` :
1179+ 
1180+ ``` ocaml 
1181+ # let try_sitting ~xt bs customer = 
1182+     not (is_closed ~xt bs) && 
1183+     begin 
1184+       Queue.Xt.add ~xt customer bs.waiting_customers; 
1185+       true 
1186+     end 
1187+ val try_sitting : xt:'a Xt.t -> barbershop -> customer -> bool = <fun> 
1188+ ``` 
1189+ 
1190+ Both of the above ` try_sitting `  transactions work with the ` customer `  actor
1191+ we'll see shortly, but with the latter blocking version we avoid busy-wait.
1192+ 
1193+ The above constitutes the barbershop abstraction which is a kind of passive
1194+ concurrent data structure. Let's then implement the active participants of the
1195+ problem.
1196+ 
1197+ A customer tries to get a haircut. When a customer enter the barbershop he first
1198+ tries to find a sleeping barber. If none is available, the customer then tries
1199+ to sit on a waiting room chair. If both fail, then the customer has no option
1200+ except to retry. Otherwise the customer waits to get a haircut. If the shop is
1201+ closed, the customer exits. Here is the ` customer `  actor:
1202+ 
1203+ ``` ocaml 
1204+ # let customer shop cuts = 
1205+     let clean = Mvar.create None in 
1206+     let self = { notify_hair_has_been_cut = Mvar.Xt.put clean true } in 
1207+     while not (Xt.commit { tx = is_closed shop }) do 
1208+       let get_barber_opt ~xt = 
1209+         match get_sleeping_barber_opt ~xt shop with 
1210+         | None -> 
1211+           try_sitting ~xt shop self 
1212+         | Some barber -> 
1213+           barber.wake_up ~xt self; 
1214+           true 
1215+       in 
1216+       if Xt.commit { tx = get_barber_opt } then 
1217+         let try_await_haircut ~xt = 
1218+           not (is_closed ~xt shop) && 
1219+           Mvar.Xt.take ~xt clean 
1220+         in 
1221+         if Xt.commit { tx = try_await_haircut } then 
1222+           Loc.incr cuts 
1223+     done 
1224+ val customer : barbershop -> int Loc.t -> unit = <fun> 
1225+ ``` 
1226+ 
1227+ A barber tries to get a customer to give a haircut. A barber first looks for a
1228+ customer from the waiting room. If none is available, the barber goes to sleep
1229+ waiting for a wakeup from a customer. After obtaining a customer in either way,
1230+ the barber gives a haircut to the customer. Otherwise the shop must be closed
1231+ and the barber exits. Here is the ` barber `  actor:
1232+ 
1233+ ``` ocaml 
1234+ # let barber shop cuts = 
1235+     let customer = Mvar.create None in 
1236+     let self = { wake_up = Mvar.Xt.put customer } in 
1237+     while not (Xt.commit { tx = is_closed shop }) do 
1238+       let cut customer = 
1239+         Xt.commit { tx = customer.notify_hair_has_been_cut }; 
1240+         Loc.incr cuts 
1241+       in 
1242+       let get_customer_opt ~xt = 
1243+         match get_sitting_customer_opt ~xt shop with 
1244+         | Some _ as some -> some 
1245+         | None -> 
1246+           sleep ~xt shop self; 
1247+           None 
1248+       in 
1249+       match Xt.commit { tx = get_customer_opt } with 
1250+       | Some customer -> cut customer 
1251+       | None -> 
1252+         let await_wakeup_opt ~xt = 
1253+           if is_closed ~xt shop then None 
1254+           else Some (Mvar.Xt.take ~xt customer) 
1255+         in 
1256+         match Xt.commit { tx = await_wakeup_opt } with 
1257+         | Some customer -> cut customer 
1258+         | None -> () 
1259+     done 
1260+ val barber : barbershop -> int Loc.t -> unit = <fun> 
1261+ ``` 
1262+ 
1263+ To run the problem, a barbershop is created with given number of waiting room
1264+ chairs, is populated by given number of barbers, and a given number of customers
1265+ are spawned. Once each barber has given and each customer has received a given
1266+ number of haircuts the shop is closed. This termination condition seeks to
1267+ demonstrate that no actor is starved. Here is the ` sleeping_barbers `  setup:
1268+ 
1269+ ``` ocaml 
1270+ # let sleeping_barbers ~barbers 
1271+                        ~num_waiting_chairs 
1272+                        ~customers 
1273+                        ~cuts_per_actor = 
1274+     assert (0 < barbers 
1275+          && 0 <= num_waiting_chairs 
1276+          && 0 <= customers 
1277+          && 0 <= cuts_per_actor); 
1278+     let shop = barbershop ~num_waiting_chairs in 
1279+     let barbers = Array.init barbers @@ fun _ -> 
1280+       let cuts = Loc.make 0 in 
1281+       (cuts, Domain.spawn @@ (fun () -> barber shop cuts)) 
1282+     and customers = Array.init customers @@ fun _ -> 
1283+       let cuts = Loc.make 0 in 
1284+       (cuts, Domain.spawn @@ (fun () -> customer shop cuts)) 
1285+     in 
1286+     let agents = Array.append barbers customers in 
1287+     while agents 
1288+           |> Array.map fst 
1289+           |> Array.exists @@ fun c -> 
1290+              Loc.get c < cuts_per_actor do 
1291+       Domain.cpu_relax () 
1292+     done; 
1293+     Xt.commit { tx = close shop }; 
1294+     agents 
1295+     |> Array.map snd 
1296+     |> Array.iter Domain.join 
1297+ val sleeping_barbers : 
1298+   barbers:int -> 
1299+   num_waiting_chairs:int -> customers:int -> cuts_per_actor:int -> unit = 
1300+   <fun> 
1301+ ``` 
1302+ 
1303+ Finally, let's try our solution:
1304+ 
1305+ ``` ocaml 
1306+ # sleeping_barbers ~barbers:2 
1307+                    ~num_waiting_chairs:1 
1308+                    ~customers:4 
1309+                    ~cuts_per_actor:10 
1310+ - : unit = () 
1311+ ``` 
1312+ 
1313+ Like mentioned in the beginning, this is not the most concise solution of the
1314+ sleeping barbers problem, but hopefully this solution can be understood
1315+ relatively easily with respect to the problem description.
1316+ 
10521317### Programming with primitive operations  
10531318
10541319In addition to the transactional interface, ** Kcas**  also provides the
0 commit comments