Атомные последовательности в промеле. Противоречивость в документации
Здесь, http://spinroot.com/spin/Man/Manual.html, написано, что:
В Promela есть также другой способ избежать теста и установить проблему: атомные последовательности. Префиксом последовательности выражений, заключенных в фигурные скобки, с ключевым словом atomic пользователь может указать, что последовательность должна быть выполнена как одна неделимая единица, не перемежаемая с любыми другими процессами. Это вызывает ошибку во время выполнения, если какой-либо оператор, кроме первого, блокирует атомарную последовательность. Вот как мы можем использовать атомарные последовательности для защиты одновременного доступа к состоянию глобальной переменной в предыдущем примере.
И здесь, http://spinroot.com/spin/Man/atomic.html, написано, что:
Если какой-либо оператор внутри атомарной последовательности блокируется, атомарность теряется. Если какой-либо оператор внутри атомарной последовательности блокируется, атомарность теряется, и тогда другим процессам разрешается начинать выполнение операторов. Когда заблокированный оператор снова становится исполняемым, выполнение атомарной последовательности может быть возобновлено в любое время, но не обязательно немедленно. Прежде чем процесс сможет возобновить атомарное выполнение оставшейся части последовательности, процесс должен сначала конкурировать со всеми другими активными процессами в системе, чтобы восстановить управление, то есть сначала он должен быть запланирован для выполнения.
Итак, что является правдой? Из первой цитаты мы можем узнать, что в атомарной блокировке запрещается (не первая инструкция)
Из второй цитаты мы узнаем, что в атомарном блоке все нормально. Вы просто теряете атомарность и все.
1 ответ
Противоречивая документация:
Я предполагаю, что противоречие является просто результатом того, что части документации не обновляются, чтобы отразить изменения, которые произошли в Spin за эти годы.
Фактически, в примечаниях к выпуску Spin v. 2.0 мы можем найти следующий фрагмент текста (выделение мое):
2.3.1 Блокировка атомных последовательностей
До сих пор считалось ошибкой, если атомарная последовательность содержала какое-либо утверждение, которое могло бы заблокировать выполнение последовательности. Это вызвало много путаницы и без необходимости усложняет моделирование. Начиная с версии Spin 2, атомарная последовательность может блокироваться. Если процесс внутри атомарной последовательности блокируется, управление недетерминированно переходит к другому процессу. Если позднее оператор становится исполняемым, управление может вернуться к процессу, и атомарное выполнение оставшейся части последовательности возобновляется.
Это изменение семантики позволяет относительно легко моделировать, например, сопрограммы, в которых управление не переходит от одного процесса к другому до тех пор, пока не будет запущен процесс.
Атомные заявления в Промеле:
В текущей версии Promela/Spin существует две атомные последовательности:
атомный: из документов, акцент мой:
ОПИСАНИЕ
Если последовательность операторов заключена в круглые скобки и имеет префикс с ключевым словом atomic, это указывает на то, что последовательность должна выполняться как одна неделимая единица, не чередующаяся с другими процессами. При чередовании выполнения процессов никакой другой процесс не может выполнять операторы с момента, когда первый оператор атомарной последовательности выполняется до последнего. Последовательность может содержать произвольные операторы Promela и может быть недетерминированной.
Если какой-либо оператор внутри атомарной последовательности блокируется, атомарность теряется, и тогда другим процессам разрешается начинать выполнение операторов. Когда заблокированный оператор снова становится исполняемым, выполнение атомарной последовательности может быть возобновлено в любое время, но не обязательно немедленно. Прежде чем процесс сможет возобновить атомарное выполнение оставшейся части последовательности, процесс должен сначала конкурировать со всеми другими активными процессами в системе, чтобы восстановить управление, то есть сначала он должен быть запланирован для выполнения.
[...]
d_step: из документов, акцент мой:
ОПИСАНИЕ
Последовательность d_step выполняется так, как если бы это был один неделимый оператор. Он сопоставим с атомарной последовательностью, но отличается от таких последовательностей следующими тремя точками:
- Перейти в или из последовательности d_step не допускается.
- Последовательность выполняется детерминистически. Если присутствует недетерминизм, он разрешается фиксированным и детерминированным способом, например, всегда выбирая первого истинного защитника в каждой структуре выбора и повторения.
- Ошибка, если выполнение какого-либо оператора внутри последовательности может блокироваться. Это означает, например, что в большинстве случаев операторы отправки и получения не могут использоваться внутри последовательностей d_step.
[...]
Конечно, это можно проверить на простом примере Promela.
ТЕСТ 1: потеря атомарности с atomic {}
Возьмите следующую модель Promela, в которой два процесса pippo
а также pluto
выполнить atomic {}
последовательность инструкций до 10
раз. Каждый процесс сохраняет свой уникальный _pid
внутри глобальной переменной p
когда он начинает выполнение атомарной последовательности, а затем проверяет flag
переменная:
- если
flag
являетсяtrue
,pippo
может выполнить, ноpluto
не может, поэтомуpluto
должен временно потерять атомарность (в некоторых трассировках выполнения) - если
flag
являетсяfalse
,pluto
может выполнить, ноpippo
не может, поэтомуpippo
должен временно потерять атомарность (в некоторых трассировках выполнения)
Мы проверяем последний случай, добавив assert(p == _pid)
инструкция в конце атомной последовательности в pippo
, Если условие не нарушено, то это означает, что нет выполнения, в котором pippo
теряет атомарность внутри атомной последовательности и pluto
начинает выполнять вместо В противном случае мы доказали, что описание в связанной документации для atomic {}
это точно.
bool flag;
pid p;
active proctype pippo ()
{
byte i;
do
:: i < 10 ->
atomic {
true ->
p = _pid;
flag; /* executable only if flag is true */
printf("pippo unblocked\n");
flag = !flag;
assert(p == _pid);
};
i++;
:: else -> break;
od;
};
active proctype pluto ()
{
byte i;
do
:: i < 10 ->
atomic {
true ->
p = _pid;
end:
!flag; /* executable only if flag is false */
printf("pluto unblocked\n");
flag = !flag;
};
i++;
:: else -> break;
od;
};
Если мы выполняем формальную проверку с помощью Spin, он находит трассировку выполнения, которая нарушает свойство:
~$ spin -search -bfs test.pml # -bfs: breadth-first-search, results in a
# shorter counter-example
pan:1: assertion violated (p==_pid) (at depth 6)
pan: wrote test.pml.trail
(Spin Version 6.4.3 -- 16 December 2014)
Warning: Search not completed
+ Breadth-First Search
+ Partial Order Reduction
Full statespace search for:
never claim - (none specified)
assertion violations +
cycle checks - (disabled by -DSAFETY)
invalid end states +
State-vector 20 byte, depth reached 6, errors: 1
15 states, stored
10 nominal states (stored-atomic)
0 states, matched
15 transitions (= stored+matched)
5 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.001 equivalent memory usage for states (stored*(State-vector + overhead))
0.290 actual memory usage for states
128.000 memory used for hash table (-w24)
128.195 total actual memory usage
pan: elapsed time 0 seconds
Утверждение нарушено, как указано в документации. Вы можете воспроизвести неверную трассировку выполнения следующим образом:
~$ spin -t -p -l -g test.pml
Двойная проверка. Теперь, если вы прокомментируете инструкцию flag:
внутри pippo
и вы повторите процедуру проверки, вы увидите, что не будет никаких следов выполнения, нарушающих утверждение. Это потому, что нет другой инструкции в атомарной последовательности pippo
может блокировать, и поэтому атомарность никогда не теряется.
ТЕСТ 2: ошибка блокировки с d_step {}
Теперь возьмите тот же пример кода и подставьте atomic
ключевое слово внутри pippo
с d_step
:
bool flag;
pid p;
active proctype pippo ()
{
byte i;
do
:: i < 10 ->
d_step {
true ->
p = _pid;
flag; /* executable only if flag is true */
printf("pippo unblocked\n");
flag = !flag;
assert(p == _pid);
};
i++;
:: else -> break;
od;
};
active proctype pluto ()
{
byte i;
do
:: i < 10 ->
atomic {
true ->
p = _pid;
end:
!flag; /* executable only if flag is false */
printf("pluto unblocked\n");
flag = !flag;
};
i++;
:: else -> break;
od;
};
Если вы формально подтвердите эту модель, вы увидите, что она все еще находит контрпример, но на этот раз с другой ошибкой:
~$ spin -search -bfs test.pml
pan:1: block in d_step seq (at depth 2)
pan: wrote test.pml.trail
(Spin Version 6.4.3 -- 16 December 2014)
Warning: Search not completed
+ Breadth-First Search
+ Partial Order Reduction
Full statespace search for:
never claim - (none specified)
assertion violations +
cycle checks - (disabled by -DSAFETY)
invalid end states +
State-vector 20 byte, depth reached 2, errors: 1
4 states, stored
4 nominal states (stored-atomic)
0 states, matched
4 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.290 actual memory usage for states
128.000 memory used for hash table (-w24)
128.195 total actual memory usage
pan: elapsed time 0 seconds
Что соответствует следующей трассировке выполнения:
~$ spin -t -p -l -g test.pml
using statement merging
1: proc 1 (pluto:1) test.pml:26 (state 1) [((i<10))]
2: proc 0 (pippo:1) test.pml:8 (state 1) [((i<10))]
3: proc 0 (pippo:1) test.pml:9 (state 8) [(1)]
3: proc 0 (pippo:1) test.pml:11 (state 3) [p = _pid]
spin: trail ends after 3 steps
#processes: 2
flag = 0
p = 0
3: proc 1 (pluto:1) test.pml:27 (state 7)
3: proc 0 (pippo:1)
2 processes created
Проблема в том, что pippo
блоки внутри d_step
последовательность, и это нарушает третье условие в кратком изложении d_step
документация, точно так, как описано.
Двойная проверка. Опять же, если вы прокомментируете flag;
инструкция вы увидите, что не будет никаких ошибок.