aboutsummaryrefslogtreecommitdiff
path: root/book/ru/src/internals/late-resources.md
blob: 0fad0aecf8bae5036ce63b65482ce3519a8008f4 (plain)
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# Поздние ресурсы

Некоторые ресурсы инициализируются во время выполнения после завершения функции `init`.
Важно то, что ресурсы (статические переменные) полностью инициализируются
до того, как задачи смогут запуститься, вот почему они должны быть инициализированы
пока прерывания отключены.

Ниже показан пример кода, генерируемого фреймворком для инициализации позних ресурсов.

``` rust
#[rtic::app(device = ..)]
mod app {
    struct Resources {
        x: Thing,
    }

    #[init]
    fn init() -> init::LateResources {
        // ..

        init::LateResources {
            x: Thing::new(..),
        }
    }

    #[task(binds = UART0, resources = [x])]
    fn foo(c: foo::Context) {
        let x: &mut Thing = c.resources.x;

        x.frob();

        // ..
    }

    // ..
}
```

Код, генерируемы фреймворком выглядит примерно так:

``` rust
fn init(c: init::Context) -> init::LateResources {
    // .. пользовательский код ..
}

fn foo(c: foo::Context) {
    // .. пользовательский код ..
}

// Public API
pub mod init {
    pub struct LateResources {
        pub x: Thing,
    }

    // ..
}

pub mod foo {
    pub struct Resources<'a> {
        pub x: &'a mut Thing,
    }

    pub struct Context<'a> {
        pub resources: Resources<'a>,
        // ..
    }
}

/// Детали реализации
mod app {
    // неинициализированная статическая переменная
    static mut x: MaybeUninit<Thing> = MaybeUninit::uninit();

    #[no_mangle]
    unsafe fn main() -> ! {
        cortex_m::interrupt::disable();

        // ..

        let late = init(..);

        // инициализация поздних ресурсов
        x.as_mut_ptr().write(late.x);

        cortex_m::interrupt::enable(); //~ compiler fence

        // исключения, прерывания и задачи могут вытеснить `main` в этой точке

        idle(..)
    }

    #[no_mangle]
    unsafe fn UART0() {
        foo(foo::Context {
            resources: foo::Resources {
                // `x` уже инициализирована к этому моменту
                x: &mut *x.as_mut_ptr(),
            },
            // ..
        })
    }
}
```

Важная деталь здесь то, что `interrupt::enable` ведет себя как like a *compiler
fence*, которое не дает компилятору пореставить запись в `X` *после*
`interrupt::enable`. Если бы компилятор мог делать такие перестановки появились
бы гонки данных между этой записью и любой операцией `foo`, взаимодействующей с `X`.

Архитектурам с более сложным конвейером инструкций нужен барьер памяти
(`atomic::fence`) вместо compiler fence для полной очистки операции записи
перед включением прерываний. Архитектура ARM Cortex-M не нуждается в барьере памяти
в одноядерном контексте.