@@ -46,8 +46,8 @@ Features and properties:
46
46
read-only compare (CMP) operations that can be performed on overlapping
47
47
locations in parallel without interference.
48
48
49
- - ** _ Blocking await_ ** : The algorithm supports timeouts and awaiting for changes
50
- to any number of shared memory locations.
49
+ - ** _ Blocking await_ ** : The algorithm supports cancelation and awaiting for
50
+ changes to any number of shared memory locations.
51
51
52
52
- ** _ Composable_ ** : Independently developed transactions can be composed with
53
53
ease sequentially, conjunctively, conditionally, and disjunctively.
@@ -75,7 +75,7 @@ is distributed under the [ISC license](LICENSE.md).
75
75
- [ A transactional lock-free queue] ( #a-transactional-lock-free-queue )
76
76
- [ Composing transactions] ( #composing-transactions )
77
77
- [ Blocking transactions] ( #blocking-transactions )
78
- - [ Timeouts] ( #timeouts )
78
+ - [ Cancelation and Timeouts] ( #cancelation-and- timeouts )
79
79
- [ A transactional lock-free leftist heap] ( #a-transactional-lock-free-leftist-heap )
80
80
- [ Programming with transactional data structures] ( #programming-with-transactional-data-structures )
81
81
- [ The dining philosophers problem] ( #the-dining-philosophers-problem )
@@ -100,14 +100,7 @@ is distributed under the [ISC license](LICENSE.md).
100
100
101
101
To use the library
102
102
103
- <!--
104
103
``` ocaml
105
- # #thread
106
- ```
107
- -->
108
-
109
- ``` ocaml
110
- # #require "kcas"
111
104
# open Kcas
112
105
```
113
106
@@ -142,6 +135,7 @@ Block waiting for changes to locations:
142
135
143
136
``` ocaml
144
137
# let a_domain = Domain.spawn @@ fun () ->
138
+ Scheduler.run @@ fun () ->
145
139
let x = Loc.get_as (fun x -> Retry.unless (x <> 0); x) x in
146
140
Printf.sprintf "The answer is %d!" x
147
141
val a_domain : string Domain.t = <abstr>
@@ -550,13 +544,28 @@ and then spawn a domain that tries to atomically both pop and dequeue:
550
544
551
545
``` ocaml
552
546
# let a_domain = Domain.spawn @@ fun () ->
547
+ Scheduler.run @@ fun () ->
553
548
let tx ~xt = (pop ~xt a_stack, dequeue ~xt a_queue) in
554
549
let (popped, dequeued) = Xt.commit { tx } in
555
550
Printf.sprintf "I popped %d and dequeued %d!"
556
551
popped dequeued
557
552
val a_domain : string Domain.t = <abstr>
558
553
```
559
554
555
+ ** Kcas** uses the [ Picos] ( https://github.com/ocaml-multicore/picos/ ) interface
556
+ to implement blocking. Above ` Scheduler.run ` starts an effects based
557
+ [ Picos compatible] ( https://ocaml-multicore.github.io/picos/doc/picos/index.html#interoperability )
558
+ scheduler, which allows ** Kcas** to block in a scheduler friendly manner.
559
+
560
+ > ** _ Note_ ** : Typically your entire program would run inside a scheduler and you
561
+ > should
562
+ > [ fork fibers] ( https://ocaml-multicore.github.io/picos/doc/picos_mux/index.html#examples )
563
+ > rather than spawn domains and start schedulers. The
564
+ > [ MDX] ( https://github.com/realworldocaml/mdx ) tool used for checking this
565
+ > document does not allow one to start a scheduler once and run individual code
566
+ > snippets within the scheduler, which is why individual examples spawn domains
567
+ > and start schedulers.
568
+
560
569
The domain is now blocked waiting for changes to the stack and the queue. As
561
570
long as we don't populate both at the same time
562
571
@@ -584,7 +593,7 @@ The retry mechanism essentially allows a transaction to wait for an arbitrary
584
593
condition and can function as a fairly expressive communication and
585
594
synchronization mechanism.
586
595
587
- #### Timeouts
596
+ #### Cancelation and Timeouts
588
597
589
598
> If you block, will they come?
590
599
@@ -604,43 +613,30 @@ val pop_or_raise_if :
604
613
xt:'a Xt.t -> bool Loc.t -> 'b list Loc.t -> xt:'c Xt.t -> 'b = <fun>
605
614
```
606
615
607
- This works, but creating, checking, and canceling timeouts properly can be a lot
608
- of work. Therefore ** Kcas** also directly supports an optional ` timeoutf `
609
- argument for potentially blocking operations. For example, to perform a blocking
610
- pop with a timeout, one can simply explicitly pass the desired timeout in
611
- seconds:
612
-
613
- ``` ocaml
614
- # let an_empty_stack = stack () in
615
- Xt.commit ~timeoutf:0.1 { tx = pop an_empty_stack }
616
- Exception: Failure "Domain_local_timeout.set_timeoutf not implemented".
617
- ```
618
-
619
- Oops! What happened above is that the
620
- [ _ domain local timeout_ ] ( https://github.com/ocaml-multicore/domain-local-timeout )
621
- mechanism used by ** Kcas** was not implemented on the current domain. The idea
622
- is that, in the future, concurrent schedulers provide the mechanism out of the
623
- box, but there is also a default implementation using the Stdlib ` Thread ` and
624
- ` Unix ` modules that works on most platforms. However, to avoid direct
625
- dependencies to ` Thread ` and ` Unix ` , we need to explicitly tell the library that
626
- it can use those modules:
616
+ This works, but creating, checking, and canceling timeouts properly in this
617
+ manner can be a lot of work. Therefore ** Kcas** also directly supports
618
+ [ cancelation] ( https://ocaml-multicore.github.io/picos/doc/picos_std/Picos_std_structured/index.html#understanding-cancelation )
619
+ through the [ Picos] ( https://github.com/ocaml-multicore/picos/ ) interface. This
620
+ both allows ** Kcas** transactions to be cleanly terminated in case the program
621
+ has encountered an error and also allows one to simply use a timeout mechanism
622
+ provided by the scheduler. For example, the sample
623
+ [ structured concurrency library] ( https://ocaml-multicore.github.io/picos/doc/picos_std/Picos_std_structured/index.html )
627
624
628
625
``` ocaml
629
- # Domain_local_timeout.set_system (module Thread) (module Unix)
630
- - : unit = ()
626
+ # open Picos_std_structured
631
627
```
632
628
633
- This initialization, if needed, should be done by application code rather than
634
- by libraries.
635
-
636
- If we now retry the previous example we will get a
637
- [ ` Timeout ` ] ( https://ocaml-multicore.github.io/kcas/doc/kcas/Kcas/Timeout/index.html#exception-Timeout )
638
- exception as expected:
629
+ for Picos provides the
630
+ [ ` Control.terminate_after ` ] ( https://ocaml-multicore.github.io/picos/doc/picos_std/Picos_std_structured/Control/index.html#val-terminate_after )
631
+ operation, which allows one to easily run an operation with a timeout on the
632
+ current fiber:
639
633
640
634
``` ocaml
641
635
# let an_empty_stack = stack () in
642
- Xt.commit ~timeoutf:0.1 { tx = pop an_empty_stack }
643
- Exception: Kcas.Timeout.Timeout.
636
+ Scheduler.run @@ fun () ->
637
+ Control.terminate_after ~seconds:0.1 @@ fun () ->
638
+ Xt.commit { tx = pop an_empty_stack }
639
+ Exception: Picos_std_structured__Control.Terminate.
644
640
```
645
641
646
642
Besides
@@ -650,7 +646,7 @@ potentially blocking single location operations such as
650
646
[ ` update ` ] ( https://ocaml-multicore.github.io/kcas/doc/kcas/Kcas/Loc/index.html#val-update ) ,
651
647
and
652
648
[ ` modify ` ] ( https://ocaml-multicore.github.io/kcas/doc/kcas/Kcas/Loc/index.html#val-modify )
653
- support the optional ` timeoutf ` argument .
649
+ support cancelation .
654
650
655
651
#### A transactional lock-free leftist heap
656
652
@@ -838,10 +834,9 @@ structures.
838
834
One source of ready-made data structures is
839
835
[ ** Kcas_data** ] ( https://ocaml-multicore.github.io/kcas/doc/kcas_data/Kcas_data/index.html ) .
840
836
Let's explore how we can leverage those data structures. Of course, first we
841
- need to ` #require ` the package and we'll also open it for convenience:
837
+ open ` Kcas_data ` for convenience:
842
838
843
839
``` ocaml
844
- # #require "kcas_data"
845
840
# open Kcas_data
846
841
```
847
842
@@ -914,6 +909,7 @@ the philosophers:
914
909
in
915
910
Array.iter Domain.join @@ Array.init philosophers @@ fun i ->
916
911
Domain.spawn @@ fun () ->
912
+ Scheduler.run @@ fun () ->
917
913
let fork_lhs = forks.(i)
918
914
and fork_rhs = forks.((i + 1) mod philosophers)
919
915
and eaten = eaten.(i) in
0 commit comments