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