Skip to content

Commit 6d7b516

Browse files
authored
Merge pull request #29 from rust-osdev/next
[v0.5] New design with two wrapper types: `VolatilePtr` and `VolatileRef`
2 parents 2a629b3 + 225ae4c commit 6d7b516

File tree

13 files changed

+1420
-877
lines changed

13 files changed

+1420
-877
lines changed

.github/workflows/build.yml

+20
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,26 @@ jobs:
7070
command: test
7171
args: --features unstable
7272

73+
very_unstable:
74+
name: Test Suite (very_unstable)
75+
runs-on: ubuntu-latest
76+
steps:
77+
- name: Checkout sources
78+
uses: actions/checkout@v2
79+
80+
- name: Install nightly toolchain
81+
uses: actions-rs/toolchain@v1
82+
with:
83+
profile: minimal
84+
toolchain: nightly
85+
override: true
86+
87+
- name: Run cargo test --features very_unstable
88+
uses: actions-rs/cargo@v1
89+
with:
90+
command: test
91+
args: --features very_unstable
92+
7393

7494
lints:
7595
name: Lints

Cargo.toml

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@ version = "0.4.6"
44
authors = ["Philipp Oppermann <[email protected]>"]
55
license = "MIT OR Apache-2.0"
66
keywords = ["volatile"]
7-
description = "A simple volatile wrapper type"
7+
description = "Volatile wrapper types for raw pointers"
88
documentation = "https://docs.rs/volatile"
99
repository = "https://github.com/rust-osdev/volatile"
10+
edition = "2021"
1011

1112
[dependencies]
1213

1314
[features]
1415
# Enable unstable features; requires Rust nightly; might break on compiler updates
1516
unstable = []
17+
# Enable unstable and experimental features; requires Rust nightly; might break on compiler updates
18+
very_unstable = ["unstable"]
19+
20+
[dev-dependencies]
21+
rand = "0.8.3"
1622

1723
[package.metadata.release]
18-
dev-version = false
1924
pre-release-replacements = [
20-
{ file="Changelog.md", search="# Unreleased", replace="# Unreleased\n\n# {{version}} – {{date}}", exactly=1 },
25+
{ file = "Changelog.md", search = "# Unreleased", replace = "# Unreleased\n\n# {{version}} – {{date}}", exactly = 1 },
2126
]
2227
pre-release-commit-message = "Release version {{version}}"
2328

Changelog.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Unreleased
22

3+
- **Breaking:** [New design based on raw pointers](https://github.com/rust-osdev/volatile/pull/29)
4+
- The previous reference-based design was [unsound](https://github.com/rust-osdev/volatile/pull/13#issuecomment-842455552) because it allowed the compiler to insert spurious reads.
5+
- The new design features two wrapper types for raw pointers: `VolatilePtr` and `VolatileRef`
6+
- `VolatilePtr` provides safe read and write access to volatile values. Like raw pointers, it implements `Copy` and is `!Sync`.
7+
- `VolatileRef` is a pointer type that respects Rust's aliasing rules. It doesn't implement `Copy`, requires a `&mut` reference for modification, and implements `Sync`. It can converted to temporary `VolatilePtr` instances through the `as_ptr`/`as_mut_ptr` methods.
8+
- We now provide methods for volatile slice operations and a `map!` macro for struct field projection. These advanced features are gated behind a cargo feature named _"unstable"_.
9+
310
# 0.4.6 – 2023-01-17
411

512
- Fix UB in slice methods when Deref returns different references ([#27](https://github.com/rust-osdev/volatile/pull/27))

README.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,27 @@
22

33
[![Build Status](https://github.com/rust-osdev/volatile/workflows/Build/badge.svg)](https://github.com/rust-osdev/volatile/actions?query=workflow%3ABuild) [![Docs.rs Badge](https://docs.rs/volatile/badge.svg)](https://docs.rs/volatile/)
44

5-
Provides the wrapper type `Volatile`, which wraps a reference to any copy-able type and allows for volatile memory access to wrapped value. Volatile memory accesses are never optimized away by the compiler, and are useful in many low-level systems programming and concurrent contexts.
5+
Provides volatile wrapper types for raw pointers.
66

7-
The wrapper types *do not* enforce any atomicity guarantees; to also get atomicity, consider looking at the `Atomic` wrapper types found in `libcore` or `libstd`.
7+
The volatile wrapper types in this crate wrap a pointer to any [`Copy`]-able type and provide volatile memory access to wrapped value.
8+
Volatile memory accesses are never optimized away by the compiler, and are useful in many low-level systems programming and concurrent contexts.
9+
10+
This crate provides two different wrapper types: [`VolatilePtr`] and [`VolatileRef`].
11+
The difference between the two types is that the former behaves like a raw pointer, while the latter behaves like a Rust reference type.
12+
For example, `VolatilePtr` can be freely copied, but not sent across threads because this could introduce mutable aliasing.
13+
The `VolatileRef` type, on the other hand, requires exclusive access for mutation, so that sharing it across thread boundaries is safe.
14+
15+
Both wrapper types *do not* enforce any atomicity guarantees; to also get atomicity, consider looking at the `Atomic` wrapper types found in `libcore` or `libstd`.
16+
17+
## Why is there no `VolatileCell`?
18+
19+
Many people expressed interest in a `VolatileCell` type, i.e. a transparent wrapper type that owns the wrapped value.
20+
Such a type would be similar to [`core::cell::Cell`], with the difference that all methods are volatile.
21+
Unfortunately, it is not sound to implement such a `VolatileCell` type in Rust.
22+
The reason is that Rust and LLVM consider `&` and `&mut` references as _dereferencable_.
23+
This means that the compiler is allowed to freely access the referenced value without any restrictions.
24+
So no matter how a `VolatileCell` type is implemented, the compiler is allowed to perform non-volatile read operations of the contained value, which can lead to unexpected (or even undefined?) behavior.
25+
For more details, see the discussion [in our repository](https://github.com/rust-osdev/volatile/issues/31) and [in the `unsafe-code-guidelines` repository](https://github.com/rust-lang/unsafe-code-guidelines/issues/411).
826

927
## License
1028

src/access.rs

+69-8
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,83 @@
1+
//! Marker types for limiting access.
2+
3+
/// Private trait that is implemented for the types in this module.
4+
pub trait Access: Copy + Default {
5+
/// Ensures that this trait cannot be implemented outside of this crate.
6+
#[doc(hidden)]
7+
fn _private() -> _Private {
8+
_Private
9+
}
10+
11+
/// Reduced access level to safely share the corresponding value.
12+
type RestrictShared: Access;
13+
}
14+
115
/// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`].
2-
pub trait Readable {}
16+
pub trait Readable: Copy + Default {
17+
/// Reduced access level to safely share the corresponding value.
18+
type RestrictShared: Readable + Access;
19+
20+
/// Ensures that this trait cannot be implemented outside of this crate.
21+
fn _private() -> _Private {
22+
_Private
23+
}
24+
}
325

426
/// Helper trait that is implemented by [`ReadWrite`] and [`WriteOnly`].
5-
pub trait Writable {}
27+
pub trait Writable: Access {
28+
/// Ensures that this trait cannot be implemented outside of this crate.
29+
fn _private() -> _Private {
30+
_Private
31+
}
32+
}
33+
34+
/// Implemented for access types that permit copying of `VolatileRef`.
35+
pub trait Copyable {
36+
/// Ensures that this trait cannot be implemented outside of this crate.
37+
fn _private() -> _Private {
38+
_Private
39+
}
40+
}
41+
42+
impl<T> Access for T
43+
where
44+
T: Readable + Default + Copy,
45+
{
46+
type RestrictShared = <T as Readable>::RestrictShared;
47+
}
648

749
/// Zero-sized marker type for allowing both read and write access.
8-
#[derive(Debug, Copy, Clone)]
50+
#[derive(Debug, Default, Copy, Clone)]
951
pub struct ReadWrite;
10-
impl Readable for ReadWrite {}
52+
impl Readable for ReadWrite {
53+
type RestrictShared = ReadOnly;
54+
}
1155
impl Writable for ReadWrite {}
1256

1357
/// Zero-sized marker type for allowing only read access.
14-
#[derive(Debug, Copy, Clone)]
58+
#[derive(Debug, Default, Copy, Clone)]
1559
pub struct ReadOnly;
16-
17-
impl Readable for ReadOnly {}
60+
impl Readable for ReadOnly {
61+
type RestrictShared = ReadOnly;
62+
}
63+
impl Copyable for ReadOnly {}
1864

1965
/// Zero-sized marker type for allowing only write access.
20-
#[derive(Debug, Copy, Clone)]
66+
#[derive(Debug, Default, Copy, Clone)]
2167
pub struct WriteOnly;
68+
impl Access for WriteOnly {
69+
type RestrictShared = NoAccess;
70+
}
2271
impl Writable for WriteOnly {}
72+
73+
/// Zero-sized marker type that grants no access.
74+
#[derive(Debug, Default, Copy, Clone)]
75+
pub struct NoAccess;
76+
impl Access for NoAccess {
77+
type RestrictShared = NoAccess;
78+
}
79+
impl Copyable for NoAccess {}
80+
81+
#[non_exhaustive]
82+
#[doc(hidden)]
83+
pub struct _Private;

0 commit comments

Comments
 (0)