Проверка в транзакциях STM, вложенных с orElse

Эта страница с комментариями описывает множество мелких деталей STM в GHC, но я хотел бы прояснить пару моментов.

Во-первых, становится ли вложенная транзакция недействительной при изменении переменных, доступ к которым осуществляется в родительском объекте?

Например, у нас есть в потоке A:

takeTMVar a `orElse` takeTMVar b `orElse` takeTMVar c

Скажем, пока A выполняет вложенную транзакцию takeTMVar b, другой поток B выполняет putTMVar a (); может ли поток A успешно завершить свою вложенную транзакцию или он признан недействительным (это может показаться мне неправильным)?

Второй момент, который, я думаю, я понимаю, но не возражал бы против подтверждения: в случае, когда вся транзакция верхнего уровня, описанная выше для A, повторяется и, наконец, блокируется, правильно ли, что A будет просыпаться при изменении любого из a, b или c?

Наконец, в качестве бонуса, изменится ли семантика приведенной выше транзакции, если мы (или авторы библиотеки) изменим orElse на infixr?


person jberryman    schedule 17.01.2014    source источник


Ответы (1)


Я не думаю, что "вложенный" - правильный термин для описания этого. Это три альтернативные транзакции; ни один не вложен в другой. В частности, произойдет и будет совершено ровно одно из трех событий, но какое из них произойдет, не является детерминированным. Этого одного предложения должно быть достаточно, чтобы ответить на все три вопроса, но чтобы быть уверенным, давайте осторожно скажем для каждого:

  1. Нет никакой гарантии. Возможно, takeTMVar b завершится и зафиксируется; или, может быть, он будет упрежден, и takeTMVar a будет разбужен и завершен. Но они не будут оба завершены, это точно.

  2. Да, правильно: все три TMVar могут разбудить этот поток.

  3. Семантика не меняется: всякий раз, когда несколько из них могут зафиксироваться, зафиксирует крайний левый. (В частности, в документе, описывающем STM, говорится: «Функция orElse подчиняется полезным законам: она ассоциативна и имеет единицу измерения retry».)

  4. (из вашего вопроса в комментариях) Семантика STM на странице 8 связанной статьи действительно гарантирует, что самая левая успешная транзакция является успешной. Итак: если поток A выполняет takeTMVar b (но еще не зафиксирован), а поток B выполняет и фиксирует запись в a, и после этого ничего не происходит, вы можете быть уверены, что поток A будет перезапущен и вернуть вновь записанное значение из a. Часть «больше ничего не происходит после этого» важна: семантика дает обещание о том, что произойдет, но не о том, как реализация этого достигает; так что если, скажем, из a тут же взялся другой поток (так что takeTMvar a все равно идет в retry), достаточно умная реализация позволяет заметить это и не перезапускать поток A с начала транзакции.

person Daniel Wagner    schedule 18.01.2014
comment
Я могу не понять ваш пункт 1; чтобы было ясно, что мое использование TMVar было только первым примером, который я мог придумать, это была простая транзакция с retry (т.е. конкретное взаимодействие взятий и путов и того, какие вызовы повторяются, не важно). На самом деле я пытаюсь понять в своем первом вопросе, существует ли конфликт между подтранзакциями в разных потоках, работающих на разных TVars. Если это не имеет смысла, я попытаюсь уточнить после того, как что-нибудь съем - person jberryman; 18.01.2014
comment
также вложенным является язык, используемый в связанном комментарии - person jberryman; 18.01.2014
comment
О, я понимаю, что вы имеете в виду! Я так понимаю, что только одна из ветвей orElse будет успешной; Я хочу знать, может ли в A, после перехода от первой ветки, другой поток (B) сделать недействительной транзакцию As, прикоснувшись к TVar, который мы используем в первой ветке - person jberryman; 18.01.2014
comment
@jberryman Я добавил текст. Надеюсь, на этот раз я понял ваш вопрос. - person Daniel Wagner; 18.01.2014
comment
Большое спасибо, Даниэль. Что касается последнего пункта, мне пришлось написать мучительную тестовую программу, чтобы убедиться в этом, но это кажется правильным. Я все еще пытаюсь понять это, но это действительно кажется мне неправильным поведением: что хорошего в том, чтобы и левая, и правая ветви orElse имели одинаковый взгляд на мир, поскольку альтернативы полностью изолированы друг от друга? Думаю, гораздо более полезным свойством было бы наличие отдельных атомарных ветвей. Так как становится очень легко получить голод, составив несколько orElse. Мысли? - person jberryman; 18.01.2014
comment
@jberryman Я думаю, причина, по которой это сделано таким образом, заключается в том, что другой тип orElse можно запрограммировать, просто имея два потока. Пусть они напишут в общие TVar и retry, если обнаружат, что другой написал первым. Поток, проигравший гонку, получит сборщик мусора. - person Daniel Wagner; 19.01.2014