Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bad7b8f
Commiting leftover before changing tactic
lukecheeseman Dec 16, 2025
a4c8511
namespacing a few things
lukecheeseman Dec 17, 2025
02ac59c
adding linear and branching
lukecheeseman Dec 17, 2025
dfbcb5f
breaking a bunch of mixed up things into better abstraction to enable…
lukecheeseman Dec 18, 2025
97e6862
building infra for two sync protocols
lukecheeseman Dec 19, 2025
36fc7fa
simple spawn join works with linear
lukecheeseman Dec 19, 2025
ec6ebe6
pre clang format
lukecheeseman Dec 19, 2025
ac0099f
clang-format
lukecheeseman Dec 19, 2025
2bd041a
got the model checker running again, but it is not aware of the disti…
lukecheeseman Dec 19, 2025
d2f40a0
moving examples around and adding command line switch to pick between…
lukecheeseman Dec 23, 2025
8b1d272
tests use the branching switch
lukecheeseman Dec 23, 2025
1cef5c9
removing copy operator from thread context and moving child ctx init …
lukecheeseman Dec 23, 2025
38469b3
making it easier to identify failing tests
lukecheeseman Dec 23, 2025
764418f
working on fixing up linear tests
lukecheeseman Dec 23, 2025
ac7fbe3
adding printing and equality methods
lukecheeseman Dec 24, 2025
8ea2f42
some printing algorithms
lukecheeseman Dec 24, 2025
4804349
fix a priting issue, and fix expected error codes for rejects in pyth…
lukecheeseman Dec 24, 2025
6dc78e2
conflict on termination now result in an error
lukecheeseman Dec 24, 2025
482cf0c
tidying up run single thread
lukecheeseman Dec 25, 2025
94f10db
Building a event trace infrastructure
lukecheeseman Jan 2, 2026
da5cf83
sorting out the interpreter struct to be better encapsulated and cons…
lukecheeseman Jan 2, 2026
a1f0ef1
fix bug in accessing illegal threads, and switch threads vector to de…
lukecheeseman Jan 2, 2026
7da3b59
better termination encapsulation
lukecheeseman Jan 2, 2026
be8cd48
restoring some interactive commands
lukecheeseman Jan 2, 2026
a0a2408
trying to fix graphing but curently broken
lukecheeseman Jan 5, 2026
72a6142
separate protocols into different files
lukecheeseman Jan 5, 2026
52bf50a
adding the missing files for protocols
lukecheeseman Jan 5, 2026
df8a986
working on branching protocol
lukecheeseman Jan 6, 2026
7af4f62
trying to improve the DAG stuff by caching last writer
lukecheeseman Jan 6, 2026
e605777
a lot of graph traversal things to work out merge conflicts and value…
lukecheeseman Jan 7, 2026
9920dd2
build eager conflict detection with branching semantics
lukecheeseman Jan 7, 2026
b399b5b
moving protocol thread and lock sync state into the respective protoc…
lukecheeseman Jan 7, 2026
597d09a
adding missing sync state files
lukecheeseman Jan 7, 2026
60e753d
building initial plumbing to allow reads to fail lazily
lukecheeseman Jan 7, 2026
1cbd0f6
removing the unused global context in the sync protocols
lukecheeseman Jan 7, 2026
83ace8d
separating branching into lazy and eager conflict detection protocols…
lukecheeseman Jan 7, 2026
11399ff
making termination type richer and report more consitently
lukecheeseman Jan 8, 2026
7d2a63b
making read result restrucutre into the expected output type
lukecheeseman Jan 8, 2026
1d7dd34
preliminary version of lazy branching, i think the two failing tests …
lukecheeseman Jan 8, 2026
8851e63
threading through machinery to write the revision graph
lukecheeseman Jan 9, 2026
0d7bb0f
adding a print out for revision history in linear, and better using t…
lukecheeseman Jan 9, 2026
f6e9550
better graph for hierachical model
lukecheeseman Jan 9, 2026
8f8bc02
fixed the lazy test suites
lukecheeseman Jan 9, 2026
992696d
add printing of commit graphs on debug steps
lukecheeseman Jan 9, 2026
6415705
optional richer revisions graphs, graph printing seems to be working …
lukecheeseman Jan 11, 2026
15f63ea
making pending ends clearer
lukecheeseman Jan 11, 2026
ebbae5b
optional richer revisions graphs, graph printing seems to be working …
lukecheeseman Jan 11, 2026
fca0560
infrastructure to have protocol builders rather than synckind everywhere
lukecheeseman Jan 12, 2026
620ee30
removing builders for protocols, adding infra to later switch between…
lukecheeseman Jan 12, 2026
f94a680
allowing assert nodes in the graph to be passing or failing and only …
lukecheeseman Jan 12, 2026
e9c9c41
merge nodes are outside thread graphs, new test which is currently sh…
lukecheeseman Jan 12, 2026
3a28f26
fixed bug where commits couldn't be the result of find_lowest_lca
lukecheeseman Jan 12, 2026
5c7e2c9
Fixing up read from edges in linear protocol
lukecheeseman Jan 16, 2026
649a0ee
Fixing up read from edges in branching
lukecheeseman Jan 16, 2026
f6a5f19
pushing a few examples and change for more comprehensive error
lukecheeseman Feb 6, 2026
3775b3a
another example
lukecheeseman Feb 6, 2026
a608a38
another example
lukecheeseman Feb 6, 2026
471e74a
up-to-date README
lukecheeseman Feb 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,23 @@ FetchContent_MakeAvailable(trieste)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED True)

include_directories(src)

add_executable(gitmem
src/gitmem.cc
src/reader.cc
src/parser.cc
src/execution_state.cc
src/passes/expressions.cc
src/passes/statements.cc
src/passes/check_refs.cc
src/passes/branching.cc
src/linear/sync_protocol.cc
src/linear/version_store.cc
src/branching/base_sync_protocol.cc
src/branching/base_version_store.cc
src/branching/lazy/version_store.cc
src/branching/eager/version_store.cc
src/interpreter.cc
src/debugger.cc
src/model_checker.cc
Expand Down
235 changes: 209 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,231 @@
# gitmem

Experimental interpreter for creating execution diagrams for a new
concurrency model.
An experimental interpreter and model checker for exploring concurrent programs with Git-inspired memory semantics. Gitmem allows you to write multi-threaded programs and automatically explore all possible interleavings to detect data races, deadlocks, and assertion failures.

## Building and Running
## Overview

Gitmem is a research tool that models concurrent memory operations using version control semantics. It provides:

- **Multiple sync protocols**: Linear and branching (with eager/lazy variants) semantics for thread synchronization
- **Automatic model checking**: Explores all possible execution paths to find concurrency bugs
- **Interactive debugging**: Step through different thread schedules interactively
- **Execution visualization**: Generates GraphViz diagrams showing execution traces and revision graphs

## Language Features

To build you need CMake and Ninja. CMake will fetch any other dependencies.
The gitmem language supports:

The following commands should set you up:
- **Shared variables**: `x = value` (global shared state)
- **Thread-local registers**: `$r = value` (prefixed with `$`)
- **Thread operations**: `spawn { ... }`, `join $thread`
- **Synchronization**: `lock var`, `unlock var`
- **Control flow**: `if (condition) { ... } else { ... }`
- **Assertions**: `assert(condition)`
- **Operators**: `==`, `!=`, `+`

### Example Program

```gitmem
x = 0;
$t1 = spawn {
lock l;
x = x + 1;
unlock l;
};
$t2 = spawn {
lock l;
x = x + 1;
unlock l;
};
join $t1;
join $t2;
assert(x == 2);
```

## Building and Running

### Prerequisites

- CMake (3.14+)
- Ninja build system
- C++23 compatible compiler (Clang recommended)
- Python 3 (for tests)

### Build Instructions

```bash
mkdir build
cd build
cmake -G Ninja .. -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug
ninja
```

If you need, set the C standard with `-DCMAKE_CXX_STANDARD=20`.
If you are running a recent version of CMake, you may need
`-DCMAKE_POLICY_VERSION_MINIMUM=3.5`.
Optional CMake flags:
- `-DCMAKE_CXX_STANDARD=23` - Set C++ standard (if needed)
- `-DCMAKE_BUILD_TYPE=Release` - Build optimized version

You can test if the build was successful by running the following
command in the `build` directory:
### Quick Test

```
Test your build with:

```bash
./gitmem -e ../examples/race_condition.gm
```

The build script creates two executables:
## Usage

### Basic Execution

Run a single execution trace:

```bash
./gitmem examples/race_condition.gm
```

This generates a GraphViz `.dot` file showing the execution trace.

### Model Checking Mode

Explore all possible execution paths:

```bash
./gitmem -e examples/race_condition.gm
```

Model checking will:
- Try all possible thread interleavings
- Report data races, deadlocks, and assertion failures
- Generate execution diagrams for failing traces
- Exit with status 0 if all paths succeed, non-zero if any fail

### Interactive Mode

Step through executions manually:

```bash
./gitmem -i examples/singleton.gm
```

Commands in interactive mode:
- `?` - Show help
- `s` - Show current state
- `n` - Step one thread forward
- `r` - Run to next synchronization point

- `gitmem` parses source code and runs the interpreter in order to
create an execution diagram (work in progress). You can run the
interpreter interactively with the `-i` flag, and automatically
explore all possible traces with the `-e` flag (showing failing
runs).
- `gitmem_trieste` is the default
[Trieste](https://github.com/microsoft/Trieste) driver which can
be used to inspect the parsed source code and test the parser.
Running `gitmem_trieste build foo.gm` will create a file
`foo.trieste` with the parsed source code as an S-expression.
### Sync Protocols

Gitmem supports different memory models:

#### Linear (Default)
```bash
./gitmem --sync linear program.gm
```
Traditional sequential consistency model.

#### Branching (Eager)
```bash
./gitmem --sync branching --branching-mode eager program.gm
```
Git-like branching semantics where threads create branches that merge at synchronization points. Conflicts are detected eagerly.

#### Branching (Lazy)
```bash
./gitmem --sync branching --branching-mode lazy program.gm
```
Lazy conflict detection variant that defers checking until synchronization.

Additional flags:
- `--include-empty-commits` - Include empty commits in branching output
- `--raise-early-conflicts` - Raise conflicts before write suppression (lazy mode only)
- `-v, --verbose` - Enable verbose interpreter output
- `-o, --output <file>` - Specify output file path

## Testing

Run the test suite:

```bash
# From build directory
ninja run_gitmem_tests

# Or using CTest
ctest
```

The test suite includes:
- **Accept tests**: Programs that should execute successfully
- **Reject tests**: Programs with errors (deadlocks, races, assertion failures)
- Tests for both linear and branching semantics

## Project Structure

```
src/
├── gitmem.cc - Main entry point
├── lang.hh - Language token definitions
├── parser.cc - Parser implementation
├── interpreter.cc - Interpreter core
├── model_checker.cc - Model checking engine
├── debugger.cc - Interactive debugger
├── execution_state.hh - Thread and memory state
├── sync_protocol.hh - Sync protocol interface
├── linear/ - Linear sync protocol
└── branching/ - Branching sync protocols
├── base_sync_protocol.cc
├── eager/ - Eager conflict detection
└── lazy/ - Lazy conflict detection

examples/
├── accept/semantics/ - Valid programs
│ ├── linear/ - Linear semantics tests
│ └── branching/ - Branching semantics tests
└── reject/semantics/ - Programs with bugs
├── linear/ - Deadlocks, races for linear
└── branching/ - Bugs for branching semantics
```

## Executables

The build produces two binaries:

### `gitmem`
The main interpreter and model checker. Executes programs and generates execution diagrams.

### `gitmem_trieste`
Parser diagnostic tool built on [Trieste](https://github.com/microsoft/Trieste). Use it to inspect the AST:

```bash
./gitmem_trieste build program.gm
# Creates program.trieste with S-expression AST
```

## VSCode Extension

You should be able to use `Developer: Install Extension from
Location` in the VSCode command palette to install a rudimentary
extension in the `gitmem-extension` directory and get syntax
highlighting..
A syntax highlighting extension is available in `gitmem-extension/`.

Install via:
1. Open VSCode Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`)
2. Run: `Developer: Install Extension from Location`
3. Select the `gitmem-extension` directory

This provides syntax highlighting for `.gm` files.

## Output

Gitmem generates GraphViz `.dot` files visualizing:

- **Execution traces**: Thread operations and memory states
- **Revision graphs**: For branching semantics, shows branch/merge structure
- **Conflict detection**: Highlights data races and conflicts

View `.dot` files with GraphViz:

```bash
dot -Tpng output.dot -o output.png
```

## Exit Codes

- `0` - All execution paths succeeded
- `1` - Assertion failure, deadlock, data race, or error detected
- Other non-zero codes indicate internal errors
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ $t2 = spawn {
};
join $t1;
join $t2;
// FIXME in branching these assertions are always true, but not in linear
assert (x != 0);
assert (y != 0);
assert (x == y);
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ t = spawn {
};
assert(x == 2);
};
assert (x == 0);
join t;
assert (x == 2);
join t2;
Expand Down
11 changes: 11 additions & 0 deletions examples/accept/semantics/branching/lock_relock_as_sync.gm
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
x = 0;
$t = spawn {
lock l1;
x = 42;
unlock l1;
};
lock l1;
x = 2;
unlock l1;
lock l1;
unlock l1;
41 changes: 41 additions & 0 deletions examples/accept/semantics/branching/singleton.gm
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Construct the singleton pattern:
// A shared variable should be initialised only once by any thread
// Once initialised all threads should read the same value
// Perform the initialisation using double checked locking
// Each thread:
// 1. Checks the variable
// 1a. If it is uninitialised (here we use 0), then take the lock
// 1b. Taking the lock may pull in other thread updates, so check if the
// variable is still uninitialised
// 1bi. If the variable is still uninitialised then we know we need to
// initialise it. So do that and store the initialised value in a local
// var.
// 1bii. Otherwise the variable was initialised and we can read the value
// 2a. Otherwise the variable was initialised and we can read the value
// 3. Check that in all code paths we read the expected initialised value

instance = 0;

$t1 = spawn {
if (instance == 0) {
lock l;
if (instance == 0) {
instance = 100;
}
unlock l;
}
assert(instance == 100);
};


if (instance == 0) {
lock l;
if (instance == 0) {
instance = 100;
}
unlock l;
}
assert(instance == 100);

join $t1;
assert(instance == 100);
4 changes: 4 additions & 0 deletions examples/accept/semantics/linear/addition.gm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$r1 = 1;
x = 2;
y = $r1 + x;
assert(y + 1 != x + 1);
18 changes: 18 additions & 0 deletions examples/accept/semantics/linear/conditional_non_race.gm
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
x = 0;
y = 0;
$t1 = spawn {
if (x == 0) {
y = 1;
}
};
$t2 = spawn {
if (y == 0) {
x = 1;
}
};
join $t1;
join $t2;

// We don't know the interleaving of the two threads, but we know that they won't conflict,
// and that the reads are reading consistent values.
// We can get all of x = 0, y = 1; x = 1, y = 0; or x = 1, y = 1; but we won't get x = 0, y = 0.
5 changes: 5 additions & 0 deletions examples/accept/semantics/linear/globals.gm
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
nop;
x = 0;
x = 2;
y = 2;
assert(x == y);
13 changes: 13 additions & 0 deletions examples/accept/semantics/linear/if.gm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
$r = 0;
x = 0;
if ($r == 0) {
x = x + 1;
} else {
x = 0;
}
if ($r == 1) {
x = 0;
} else {
x = x + 1;
}
assert (x == 2);
Loading