package inferno

  1. Overview
  2. Docs

Source file TRef.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
(******************************************************************************)
(*                                                                            *)
(*                                  Inferno                                   *)
(*                                                                            *)
(*                       François Pottier, Inria Paris                        *)
(*                                                                            *)
(*  Copyright Inria. All rights reserved. This file is distributed under the  *)
(*  terms of the MIT License, as described in the file LICENSE.               *)
(*                                                                            *)
(******************************************************************************)

(* Every cell records both its current (possibly uncommitted) value
   and its last committed value. A cell is considered stable when
   these two values are (physically) equal, and unstable otherwise. *)

type 'a cell = {
  (* The current (possibly uncommitted) value. *)
  mutable current: 'a;
  (* The last committed value. *)
  mutable committed: 'a
}

(* A transaction contains a stack of all unstable cells (and possibly
   some stable cells too, although that is unlikely). *)

type 'a transaction =
    'a cell Stack.t

(* [create v] creates a new cell whose initial value is [v]. *)

let create v = {
  current = v;
  committed = v
}

(* [get cell] is the current value of the cell [cell]. *)

let get cell =
  cell.current

(* [set transaction cell v] sets the value of the cell [cell] to [v]. This is
   done as part of the transaction [transaction]. Thus, if the transaction is
   aborted, this update will be undone. If the transaction is committed, this
   update will be kept. *)

(* If this cell was created after the beginning of the transaction, then there
   is no need to insert it into the set of unstable cells, as it does not need
   to be rolled back. We do not implement this idea. In our intended application,
   cells are never created during a transaction. *)

let set transaction cell v =
  let current = cell.current in
  (* If the new value happens to be the current value, then there is nothing
     to do. *)
  if v == current then
    ()
  else begin
    (* If this cell was stable and now becomes unstable, then it must be
       inserted into the set of unstable cells, which is recorded as part
       of the transaction. *)
    if current == cell.committed then
      Stack.push cell transaction;
    (* The cell must then be updated. If [v] happens to be equal to
       [committed], this could make the cell stable again. We do not
       check for this unlikely situation. This means that the set of
       unstable cells could actually contain stable cells too. *)
    cell.current <- v
  end

(* [tentatively f] runs the function [f] within a new transaction. If [f]
   raises an exception, then the transaction is aborted, and any updates
   performed by [f] are rolled back. Otherwise, the updates performed by [f]
   are committed. *)

let commit cell =
  cell.committed <- cell.current

let rollback cell =
  cell.current <- cell.committed

let tentatively f =
  let transaction = Stack.create() in
  try
    let result = f transaction in
    (* Commit every unstable cell. *)
    Stack.iter commit transaction;
    result
  with e ->
    (* Roll back every unstable cell. *)
    Stack.iter rollback transaction;
    raise e

OCaml

Innovation. Community. Security.