Re[42]: Безопасность Rust
От: vdimas Россия  
Дата: 03.06.19 14:29
Оценка:
Здравствуйте, ·, Вы писали:

V>>·>Ты заявил "многопоточность в языке появилось давно"

V>>Дословно было сказано:
·>Дословно было сказано:
·>

Понятие многопоточности в языке появилось давно, одновременно с ключевым словом volatile.


Эта цитата тоже не совпадает с тем, что приписываешь мне ты.
Разве "многопоточность в языке" не означает, что ср-вами языка можно порождать потоки?

И разве из той же подветки:

Согласно дизайну, многопоточность реализована в виде внешнего механизма.

такого пояснения было недостаточно?


V>>·>но своё заявление ничем не подтвердил.

V>>Пока мест это просто исторический факт, который не требуется подтверждать.
·>Требуется, конечно.

Факт разработки языка Си для целей написания UNIX подтверждённый.


·>Неподтверждённые факты — это фантазии.


Я бы даже усилил — в отсутствии эрудиции и присутствии необоснованного упрямства — натуральная проблема.


V>>Цитата там такова:

V>>sig_atomic_t — an integer type which can be accessed as an atomic entity even in the presence of asynchronous interrupts made by signals.
·>И где тут про многопоточность?

Конкретно про многопоточность с вытеснением здесь:
http://www.rsdn.org/forum/flame.comp/7460101.1

Если тебя интересует другая модель многопоточности, то просьба уточнить, какая именно.


·>Т.к. здесь behaviour этого типа в условиях многопоточности undefined, то использовать его в условиях многопоточности — Undefined Behaviour.


Какой именно модели многопоточности?
И как до стандарта C++11 работали операционки?
Как работает Linux, которая писана и вовсе на голом С?


V>>Until C++11, which introduced std::atomic and std::atomic_signal_fence, about the only thing a strictly conforming program could do in a signal handler was to assign a value to a volatile static std::sig_atomic_t variable and promptly return.

·>Ну да. И если ты volatile используешь не для signal handler, а для многопоточности, то у тебя не будет "strictly conforming program".

Если использую для вытесняющей многопоточности, реализованной через механизм прерываний, и меня интересует только relaxed memory order, то всё будет ОК.
Собсно, я всё это уже перечислял тут многократно, тебе стоило спорить с моими утверждениями целиком.


·>Да пофиг. UB от этого не перестаёт быть UB. В этом его и суть. Ты лишь пытаешься доказать, что в некоторых условиях UB может работать ожидаемо. Это очевидно. И что?

·>Да, если механизм потоков реализован неким конкретным способом, если у нас определённая архитекутура железа и софта, то да, возможно мы можем использовать int/volatile/sig_atomic для взаимодействия потоков. Но это всё равно будет UB, по стандарту.

а-а-а
(терпение заканчивается)

По стандарту UB будет только при доступе к нескольким переменным.
Т.е. если в некоем алгоритме участвует более одной расшаренной переменной и алгоритму важен взаимный порядок изменения их значений.
Проблески понимания еще не появились? ))

Если переменная только одна в алгоритме, т.е. если никакой взаимный порядок обращения к ней приплести не получается, то никакого UB прямо по стандарту.
Из стандарта на русском:

std::memory_order (упорядочение доступа к памяти) определяет, как обычный, неатомарный доступ к памяти, упорядочивается вокруг атомарных операций. При отсутствии каких-либо ограничений, на многоядерных системах, когда множество потоков одновременно читает и пишет в несколько переменных, один поток может наблюдать изменение значений переменных в порядке, отличающемся от того, в котором другой поток записывает их.



V>>>>Если алгоритм не требует какой-либо сильной модели памяти, т.е. достаточно модели "relaxed", то достаточно простой атомарности операций чтения/записи переменной.

V>>·>Заблуждение.
V>>Дык, вперёд! Раскрой суть заблуждения!
·>Заблуждение в том что "атомарность" в контексте асинхронных сигналов это та же атомарость, что и в случае многопоточности.

Как смело ты проигнорил условие под "если". ))
И это при том, что тебе уже цитировали стандарт тут:
http://www.rsdn.org/forum/flame.comp/7460432.1

Атомарные операции, отмеченные как std::memory_order_relaxed, не являются синхронизирующими операциями, они не упорядочивают память. Они гарантируют только атомарность и согласованность порядка модификации.



V>>Какого-нить алгоритма, для которого достаточно relaxed-модели памяти?

·>Мы говорим не о алгоритмах, а о многопоточности.

Мы говорим пока о твоём непонимании связи конкретных сценариев (т.е. алгоритмов) со стандартом.
Я показал тебе алгоритм, который абсолютно корректен с т.з. стандартов.
Hint: в алгоритме использовалась всего одна расшаренная переменная с атомарным доступом к ней, т.е. упорядочивание памяти не требуется, т.е. не будет никакого UB при доступе к переменной из нескольких потоков.
Re[18]: Безопасность Rust
От: vdimas Россия  
Дата: 03.06.19 14:44
Оценка:
Здравствуйте, alex_public, Вы писали:

WH>>А по факту их всегда используют на область данных.

WH>>Исключений я не видел.
_>Ну да, т.е. вся индустрия идиоты и проектируют свои интерфейсы для одного, а используют их совсем для другого. Конечно, конечно. ))) Кстати, помнится я уже слышал от тебя аналогичные утверждения, по какому-то совсем другому поводу.

Так он же и критиковал одно время тот факт, что мьютексы де-факто защищают код, а не данные. ))
Re[29]: Безопасность Rust
От: alex_public  
Дата: 03.06.19 14:46
Оценка:
Здравствуйте, ·, Вы писали:

_>>https://en.cppreference.com/w/c/program/sig_atomic_t — там в разделе References можешь их найти. )

·>Мде. Во-первых, ты писал int, а не это. Во-вторых, sig_atomic_t — это atomic в контексте ВНЕЗАПНО signal handler и к многопоточности отношения не имеет. Тут подробности.
·>И поэтому — твой код некорректный и типичный UB, даже твоя сильная вера не поможет. Чисто по Стандарту.

Ты это, дурачком то не прикидывайся. Уже видимо давно понял что сказал чушь со своим утверждением, но продолжаешь до конца защищать её. Ну если так хочешь, то разберём её до конца.

Мы говорили об атомарности не в каких-то там абстрактных пониманиях, а во вполне конкретном. Что при одновременной записи из двух потоков в одну ячейку памяти в ней будет точно или одно значение или второе, а не какое-то третье случайное значение. Ты это всё очень хорошо и в деталях описал здесь http://rsdn.org/forum/flame.comp/7456435.1
Автор: ·
Дата: 28.05.19
и я даже со всем этим согласен, за исключением того, что для int'а не будет атомарности. Потому как любой тип данных (ну со всякими там оговорками на выравнивание, копирование целиком, а не кусками и т.п.), умещающийся в регистр целевой архитектуры обладает данным свойством. Тип sig_atomic_t введён исключительно для того, чтобы не проверять многочисленными ifdef'ами, что на целевой платформе размер подходящий (ну и кстати я не знаю платформы, где он не равен int, но это не суть). А так, подходит любой тип и естественно в том числе и int, про который ты сказал, что он не обладает свойством атомарности (в значение выше) на каких-то платформах. Так вот мы все до сих пор ждём, когда озвучишь эти самые платформы, или это будет однозначный слив..

А отдельно от всего выше описанного, существует тип std::atomic, который помимо гарантий атомарности в значение выше, даёт ещё ряд дополнительных удобств. А именно, он даёт гарантии атомарности некоторых операций вида "чтение-изменение-запись", операции CAS, и опционально гарантии сохранения порядка операций чтения/записи вокруг (сущность самих операций от этого никак не меняется). Так вот операции чтения/записи это класса реализованы в точности теми же самыми ассемблерными инструкциями, что и обычного int'а. В случае включения опции сохранения порядка к этим инструкциям добавляются специальные указания процессору не выполнять переупорядочивание в конвейере для операций запись/чтение. Ну и атомарные инструкции "чтение-изменение-запись" тоже реализуются через отдельные инструкции процессора (хотя например у x86 это просто префикс, блокирующий шину) и поэтому доступны не на всех платформах.

Теперь тебе понятна реальная ситуация в данной области? Да, для каких-то задач будет подходить только std::atomic, но не потому что запись в банальный int не атомарна, а потому что требуются какие-то дополнительные гарантии, типа того же сохранения порядка операций (что кстати можно сделать в C++ в произвольном месте, с любыми типами и без std::atomic, просто с помощью вызова функции atomic_thread_fence).

_>>>>5. Ну и наконец тот самый главный тезис: как ни называй описанную проблему, Rust никак от неё не защищает.

_>>·>Rust защищает от data race.
_>>Вне зависимости от верности или не верности этого твоего утверждения, совершенно непонятно какое оно имеет отношение к моему тезису об отсутствие какой-то помощи в решение проблем сопрограмм и т.п. гонок в рамках одного потока. У тебя снова включился режим "несу чушь монологом, не слушая ответов собеседника"?
·>data race в одном потоке быть не может. По определению. Я тебе его уже давал.
·>Гонки в пределах одного потока это вообще не про то. Того же уровня как гонки в цикле for — логические ошибки, а не проблемы возникающие на уровне модели ЯП.

Кстати, в нормальных реализациях приостановленную сопрограмму можно передать на исполнение в любой другой поток. Причём стратегия поведения (всегда в одном потоке или берём свободный из пула) может управляться одной мелкой галочкой где-то в другом конце приложения. Интересно как это всё можно формализовать на Rust? Подозреваю что никак и будет один гигантский unsafe.

_>>>>Ну если ты знаешь CPU, в котором перемещение регистра в ячейку памяти является не атомарным, то с удовольствием послушаю про такой. )))

_>>·>Да, CPU сохраняет регистр атомарно, но это не "равносильно оператору равенства в твоём языке программирования", это вообще лажа. Или пункт Стандарта в студию.
_>>Это равносильно для выровненного (оно так и есть по умолчанию) int'a. Про стандарт уже было выше.
·>Как выяснилось, это очередное твоё заблуждение.

Ну если заблуждение, то ты конечно же назовёшь платформу, где это не так? Или просто честно признаешься что ошибался? Это как бы поприличнее, чем пытаться отмазываться и вести всё к явному сливу.

_>>>>А причём ты тут приплёл барьеры вообще не понятно — они работают только с переупорядочиванием инструкций, никак не влияя на их свойства.

_>>·>Не только. Но и этого достаточно. Без барьеров у тебя этот global_var может так и остаться жить в регистре, например.
_>>Даа, ну и каша у тебя в голове. Барьеры всего лишь управляют переупорядочиванием инструкций (т.е. если инструкция записи в ячейку памяти была, то она всё равно будет исполнена в какой-то момент), причём действует это как на компилятор, так и на конвейер процессора (т.е. это специальные ассемблерные инструкции).
·>Инструкции могут не только переупорядочиваться, но и выкидываться. Если оптимизатор решит, что переменная не меняется в данном участке кода, то она может так и жить в регистре или вообще выоптимизировать её в ноль. Или несмотря на то, что в исходном коде перемення изменяется несколько раз, он может выкинуть промежуточные инструкции сохранения в память.
·>Барьеры предотвращают и это.

Глупости говоришь. Даже если компилятор в коде без барьеров сведёт две операции записи в одну, то это:
— никак не повлияет на факт выделения участка памяти для неё
— никак не повлияет на атомарность оставшейся операции записи.

Так что тема барьеров к атомарности операций чтения/записи отношения не имеет.

_>>А та оптимизация, на которую ты намекал, не имеет с этим ничего общего — это исключительно игры компилятора.

·>Переупорядочивание инструкций это и есть один из видов оптимизации.

Конечно. Только во-первых оно может происходить и в компиляторе и в процессоре (для управления чем есть специальные инструкции). И процессор у нас точно не умеет заменять переменные регистрами. )))

_>>И отключается она для данной переменной не с помощью барьеров, а помощью ключевого слова volatile.

·> А ты почитай Cтандарт про volatile:
·>

This makes volatile objects suitable for communication with a signal handler, but not with another thread of execution, see std::memory_order

·>И не неси эту чушь больше.

И где в твоей цитате слова об отключение оптимизации переменная->регистр? ) Какое она вообще имеет отношение к моему утверждению об отключение этой оптимизации с помощью volatile? )

_>>>>На самом деле на многих ещё актуальных аппаратных платформах просто нет специальных атомарных инструкций, как ты думаешь, как там выживают? )))

_>>·>lock-инструкциями, например. Читай std::atomic::is_lock_free() и для чего нужен std::atomic_flag. А шо делать?!..
_>>Какими ещё lock инструкциями, если наш процессор их не умеет? )))
·>Это какой процессор не умеет? cli/sti — умеют как минимум все. А если мы говорим о многопроцессорных системах...

Эээ что? Ну ты прямо развеселил... Весь даже если бы тебе дали доступ к управлению прерываниями ради такой ерунды как инкремент int'a, это всё равно действовало бы только в рамках одного ядра, а другим ядрам было бы всё равно.

·>>>>>Там про race condition.

_>>>>И? О того что ты назовёшь это по другому, у тебя некорректный код вдруг заработает правильно? )))
_>>·>Опятьдвадцатьпять. _Некоторый_ некорректный код, является ошибкой компиляции в rust, когда как в С++ — UB. В этом принципиальное отличие.
_>>Так race condition у нас теперь оказывается даёт ошибку компиляции в Rust? Какие интересные новости... )))
·>Нет. Ты читать умеешь что тебе пишут? "Некоторый" != "race condition".

Аааа, т.е. это ты просто очередной раз написал свой бред не в тему (сам же перед этим писал "Там про race condition."), а просто потому что надо написать что-то, но по теме сказать нечего? ) Ну понятно, понятно... )))
Re[33]: Безопасность Rust
От: alex_public  
Дата: 03.06.19 14:58
Оценка: +1
Здравствуйте, ·, Вы писали:

_>>Ха. Вообще то для чтения/записи atomic переменной используется в точности такая же инструкция, как и для обычного int'a. Можешь посмотреть на реальном коде https://godbolt.org/z/I3lOwe, если так до сих пор сам не понял и мне не веришь. Банально потому, что это эти инструкции атомарные от природы!

·>
·>Ну давай посмотрим вместе:
·>atomic:
·>
·>        mov     DWORD PTR v[rip], 1
·>        mfence
·>

·>non atomic:
·>
·>        mov     DWORD PTR v1[rip], 1
·>

·>Действительно... одной инструкцией меньше, одной больше... Кака разница? В главном-то ты прав!

Ох, ну что за детский сад. Ты реально не в курсе что такое mfence? Ну мне даже лень объяснять, так что поясню проще: https://godbolt.org/z/7zWFvr — с точки зрения вопросов атомарности это в точности тот же самых код с вполне корректным std::atomic, что и раньше, только переписанный чуть под другому. Дальше сам додумаешь? )))

Да, а ещё можешь там вообще убрать упоминания про std::atomic (оставить только голый int), но добавить вызов "atomic_thread_fence(std::memory_order_seq_cst);" и насладиться результатом.

И да, можешь ещё попереключать другие компиляторы/архитектуры с кодом по ссылке выше — может тогда поймёшь, что сохранение порядка записи/чтения на разных процессора задаётся по разному (не только отдельной инструкций, как в x86, но и например модифицированными операциями записи типа str/stlr), но к вопросу атомарности записи это отношения не имеет.
Re[40]: Безопасность Rust
От: · Великобритания  
Дата: 03.06.19 15:43
Оценка: -1 :)
Здравствуйте, vdimas, Вы писали:

V>И, чтобы подтвердить свои слова насчёт UB, тебе надо показать, что C/C++ компилятор для чтения/записи переменной:

V>
V>static volatile sig_atomic_t someFlag;
V>

V>(где sig_atomic_t — это простой typedef на некий целочисленный тип прямо по стандарту)
V>в некоей архитектуре породит код, который не будет атомарно писать и читать эту переменную.
V>Смогёшь?
Я ж уже тебе показывал, но чукча не читатель: https://godbolt.org/z/LLNDkD
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[43]: Безопасность Rust
От: · Великобритания  
Дата: 03.06.19 16:24
Оценка: :)
Здравствуйте, vdimas, Вы писали:

V>>>·>Ты заявил "многопоточность в языке появилось давно"

V>>>Дословно было сказано:
V>·>Дословно было сказано:
V>·>

Понятие многопоточности в языке появилось давно, одновременно с ключевым словом volatile.

V>Эта цитата тоже не совпадает с тем, что приписываешь мне ты.
V>Разве "многопоточность в языке" не означает, что ср-вами языка можно порождать потоки?
"многопоточность в языке" означает, что язык описывает поведение языковых конструкций в контексте многих потоков.
volatile же описывает поведение языка в контексте асинхронных сигналов.

V>И разве из той же подветки:

V>

V>Согласно дизайну, многопоточность реализована в виде внешнего механизма.

V>такого пояснения было недостаточно?
То что можно дёрнуть syscall для создания потоков это ещё не значит, что язык умеет потоки.

V>>>·>но своё заявление ничем не подтвердил.

V>>>Пока мест это просто исторический факт, который не требуется подтверждать.
V>·>Требуется, конечно.
V>Факт разработки языка Си для целей написания UNIX подтверждённый.
Интересует взаимосвязь этого факта с многопоточностью.

V>·>Неподтверждённые факты — это фантазии.

V>Я бы даже усилил — в отсутствии эрудиции и присутствии необоснованного упрямства — натуральная проблема.
"А в стандарте C11 в язык добавили реализацию потоков и поддержку атомарных типов" И объясни причём тут sig_atomic_t.

V>>>Цитата там такова:

V>>>sig_atomic_t — an integer type which can be accessed as an atomic entity even in the presence of asynchronous interrupts made by signals.
V>·>И где тут про многопоточность?
V>Конкретно про многопоточность с вытеснением здесь:
V>http://www.rsdn.org/forum/flame.comp/7460101.1
V>Если тебя интересует другая модель многопоточности, то просьба уточнить, какая именно.
Перечисли модели многопоточности какие есть в языке С/С++.

V>·>Т.к. здесь behaviour этого типа в условиях многопоточности undefined, то использовать его в условиях многопоточности — Undefined Behaviour.

V>Какой именно модели многопоточности?
Это тебя надо спросить, почему ты до сих пор не прочитал о memory model.

V>И как до стандарта C++11 работали операционки?

Мы обсуждаем язык, а не операционки.

V>Как работает Linux, которая писана и вовсе на голом С?

Linux не написана целиком на С, а вот такие критические платформенно-зависимые вещи типа атомарности, тредов и прочего частенько пишется на соответствующем ассемблере.

V>>>Until C++11, which introduced std::atomic and std::atomic_signal_fence, about the only thing a strictly conforming program could do in a signal handler was to assign a value to a volatile static std::sig_atomic_t variable and promptly return.

V>·>Ну да. И если ты volatile используешь не для signal handler, а для многопоточности, то у тебя не будет "strictly conforming program".
V>Если использую для вытесняющей многопоточности, реализованной через механизм прерываний, и меня интересует только relaxed memory order, то всё будет ОК.
V>Собсно, я всё это уже перечислял тут многократно, тебе стоило спорить с моими утверждениями целиком.
Возможно будет, но этого нет в стандарте языка, а значит UB.

V>По стандарту UB будет только при доступе к нескольким переменным.

Хватит бла-бла. Приведи ссылку на стандарт.

V>>>Какого-нить алгоритма, для которого достаточно relaxed-модели памяти?

V>·>Мы говорим не о алгоритмах, а о многопоточности.
V>Мы говорим пока о твоём непонимании связи конкретных сценариев (т.е. алгоритмов) со стандартом.
V>Я показал тебе алгоритм, который абсолютно корректен с т.з. стандартов.
Ты показал сигналы, а не многопоточность. И да, тот код корректен, но к многопоточности отношения не имеет.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[35]: Безопасность Rust
От: alex_public  
Дата: 03.06.19 16:35
Оценка: +1
Здравствуйте, Cyberax, Вы писали:

_>>Вообще то это очевидно просто из факта атомарности процессорной команды записи регистра в память. Т.е. тебе просто нужны гарантии того, что оператор присвоения переменной компилировался ровно в одну инструкцию перемещения. А для гарантий этого тебе достаточно чтобы размер данных помещался в регистр и ячейка памяти была выровнена (это и так по умолчанию).

C>Это неверно примерно на половине архитектур, x86 имеет необычно сильную модель.

Ну если это так, то тебе конечно же не составит труда назвать хотя бы несколько из этой "половины архитектур"?

C>Но даже если запись слова атомарна, то нет никаких гарантий "happens after".

C>Т.е. код типа:
C>
C>int valA = 0, valB = 0;

C>void thread1() {
C>   valA = 11;
C>   valB = 12;
C>}

C>void thread2() {
C>   while(true) {
C>      if (valB == 12) {
C>         assert(valA == 11);
C>      }
C>   }
C>}
C>

C>Будет периодически падать с assert'ами, так как thread2 вполне может увидеть значение valB раньше, чем valA.

А вот здесь всё абсолютно верно. Только вот к факту атомарности это не имеет никакого отношения — в данных переменных будут вполне однозначные значения. А то, что возможно изменение очерёдности операций чтения/записи — это вообще отдельная тема, регулируемая с помощью барьеров.
Re[31]: Безопасность Rust
От: alex_public  
Дата: 03.06.19 16:53
Оценка: +1
Здравствуйте, Cyberax, Вы писали:

_>>На всех платформах будет абсолютно одинаковое поведение: атомарные операции записи и чтения регистра в память. И я всё ещё жду от тебя пример платформы, на которой это не так (ведь это было твоё однозначное утверждение).

C>Отвечу за WolfHound'а.
C>См. реализацию методов Load64. На многих архитектурах требуются барьеры/блокировки.

Требуются для чего, для атомарности или же для сохранения порядка выполнения? )

C>PPC64: https://github.com/golang/go/blob/4a4e05b0b166ef17d62789d7ca6d58aeb846c5d1/src/runtime/internal/atomic/atomic_ppc64x.s#L32

C>MIPS64: https://github.com/golang/go/blob/4a4e05b0b166ef17d62789d7ca6d58aeb846c5d1/src/runtime/internal/atomic/atomic_mips64x.s#L30

Видимо у атомиков из go (в отличие от того же C++) нет возможности выбрать включать ли гарантию сохранения очерёдности соответствующих операций или нет. Поэтому там всегда используется код с SYNC.

C>Для сравнения, на x86 этот метод выглядит так: https://github.com/golang/go/blob/4a4e05b0b166ef17d62789d7ca6d58aeb846c5d1/src/runtime/internal/atomic/atomic_amd64x.go


Да, потому что на чтение последовательность сохраняется на x86 даже без специальных инструкций. А вот если бы ты привёл код на запись, то обнаружил бы там соответствующую команду (mfence в данном случае) и для x86.

Можно посмотреть список соответствующих гарантий для разных архитектур например здесь: https://www.linux-mips.org/wiki/Memory_consistency/. Только опять же непонятно зачем ты вообще притащил вопрос переупорядочивания в обсуждение темы атомарности операций чтения/записи.
Re[30]: Безопасность Rust
От: · Великобритания  
Дата: 03.06.19 17:00
Оценка:
Здравствуйте, alex_public, Вы писали:

_>·>И поэтому — твой код некорректный и типичный UB, даже твоя сильная вера не поможет. Чисто по Стандарту.


_>Ты это, дурачком то не прикидывайся. Уже видимо давно понял что сказал чушь со своим утверждением, но продолжаешь до конца защищать её. Ну если так хочешь, то разберём её до конца.


_>Мы говорили об атомарности не в каких-то там абстрактных пониманиях, а во вполне конкретном. Что при одновременной записи из двух потоков в одну ячейку памяти в ней будет точно или одно значение или второе, а не какое-то третье случайное значение.

У тебя проблемы с пониманием прочитанного и логикой. Я сказал "в переменной может оказаться всё что угодно, никаких гарантий нет". Из этого никак не следует, что "там будет какое-то третье случайное значение". Это просто значит, что гарантии поведения у данного участка кода отсутствуют. И даже если всё остальное (операционка, железо, компилятор, етс) будет идеально безбажным и работающим точно по всем спекам — результат работы такого кода неопределён, т.е. код некорректен и его надо переписать правильно.

_>Ты это всё очень хорошо и в деталях описал здесь http://rsdn.org/forum/flame.comp/7456435.1
Автор: ·
Дата: 28.05.19
и я даже со всем этим согласен, за исключением того, что для int'а не будет атомарности. Потому как любой тип данных (ну со всякими там оговорками на выравнивание, копирование целиком, а не кусками и т.п.), умещающийся в регистр целевой архитектуры обладает данным свойством. Тип sig_atomic_t введён исключительно для того, чтобы не проверять многочисленными ifdef'ами, что на целевой платформе размер подходящий (ну и кстати я не знаю платформы, где он не равен int, но это не суть). А так, подходит любой тип и естественно в том числе и int, про который ты сказал, что он не обладает свойством атомарности (в значение выше) на каких-то платформах. Так вот мы все до сих пор ждём, когда озвучишь эти самые платформы, или это будет однозначный слив..

Ты видимо что-то не понимаешь, что значит "гарантии". То что в современных платформах это обычно так, это не значит, что это чем-то гарантированно. Я завтра могу спаять новую платформу где это будет не так и твой говнокод поломается и тебе придётся править баги.
Кстати, такое было в 8088 — инструкции 16-битные, а шина 8-битная. Т.е. запись регистра 16 бит полезет в ячейки памяти дважды. Правда многопоточноть никто на этих чипах не реализовывал.

_>·>data race в одном потоке быть не может. По определению. Я тебе его уже давал.

_>·>Гонки в пределах одного потока это вообще не про то. Того же уровня как гонки в цикле for — логические ошибки, а не проблемы возникающие на уровне модели ЯП.
_>Кстати, в нормальных реализациях приостановленную сопрограмму можно передать на исполнение в любой другой поток. Причём стратегия поведения (всегда в одном потоке или берём свободный из пула) может управляться одной мелкой галочкой где-то в другом конце приложения. Интересно как это всё можно формализовать на Rust? Подозреваю что никак и будет один гигантский unsafe.
Обернуть конструкцию передачи в другой поток в какой-нибудь там mfence — и вуаля — гарантии. Rust поможет "ломать компиляцию" если в твоём коде ты "забудешь" делать это правильно.

_>>>Это равносильно для выровненного (оно так и есть по умолчанию) int'a. Про стандарт уже было выше.

_>·>Как выяснилось, это очередное твоё заблуждение.
_>Ну если заблуждение, то ты конечно же назовёшь платформу, где это не так? Или просто честно признаешься что ошибался? Это как бы поприличнее, чем пытаться отмазываться и вести всё к явному сливу.
Гарантии не определяются наличием или отсутствием конкретных платформ, а прописаны в стандарте языка.

_>·>Инструкции могут не только переупорядочиваться, но и выкидываться. Если оптимизатор решит, что переменная не меняется в данном участке кода, то она может так и жить в регистре или вообще выоптимизировать её в ноль. Или несмотря на то, что в исходном коде перемення изменяется несколько раз, он может выкинуть промежуточные инструкции сохранения в память.

_>·>Барьеры предотвращают и это.
_>Глупости говоришь. Даже если компилятор в коде без барьеров сведёт две операции записи в одну, то это:
_>- никак не повлияет на факт выделения участка памяти для неё
_>- никак не повлияет на атомарность оставшейся операции записи.
Атомарность вообще-то подразумевает ещё и видимость значений. Если в коде значение переменной присваивается, то вполне ожидаемо, что значение таки изменится. Отсутствие барьеров может нарушить эту гарантию.

_>>>И отключается она для данной переменной не с помощью барьеров, а помощью ключевого слова volatile.

_>·> А ты почитай Cтандарт про volatile:
_>·>

This makes volatile objects suitable for communication with a signal handler, but not with another thread of execution, see std::memory_order

_>·>И не неси эту чушь больше.
_>И где в твоей цитате слова об отключение оптимизации переменная->регистр? ) Какое она вообще имеет отношение к моему утверждению об отключение этой оптимизации с помощью volatile? )
В цитате явно говорится, что volatile это про сигналы, а не треды. Ты делаешь бессмысленные высказывания, основанные на ложной посылке.

_>>>Какими ещё lock инструкциями, если наш процессор их не умеет? )))

_>·>Это какой процессор не умеет? cli/sti — умеют как минимум все. А если мы говорим о многопроцессорных системах...
_>Эээ что? Ну ты прямо развеселил... Весь даже если бы тебе дали доступ к управлению прерываниями ради такой ерунды как инкремент int'a, это всё равно действовало бы только в рамках одного ядра, а другим ядрам было бы всё равно.
Управлением прерываниями занимается ось, притом конкретный механизм управления зависит от конкретного железа. И она же даёт мне доступ к этой магии через сисколы.

_>·>Нет. Ты читать умеешь что тебе пишут? "Некоторый" != "race condition".

_>Аааа, т.е. это ты просто очередной раз написал свой бред не в тему (сам же перед этим писал "Там про race condition."), а просто потому что надо написать что-то, но по теме сказать нечего? ) Ну понятно, понятно... )))
Я просто повторил свой тезис, в ответ на твой бред.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[34]: Безопасность Rust
От: · Великобритания  
Дата: 03.06.19 19:14
Оценка:
Здравствуйте, alex_public, Вы писали:

_>·>Действительно... одной инструкцией меньше, одной больше... Кака разница? В главном-то ты прав!

_>Ох, ну что за детский сад. Ты реально не в курсе что такое mfence? Ну мне даже лень объяснять, так что поясню проще: https://godbolt.org/z/7zWFvr — с точки зрения вопросов атомарности это в точности тот же самых код с вполне корректным std::atomic, что и раньше, только переписанный чуть под другому. Дальше сам додумаешь? )))
А как это объяснишь? https://godbolt.org/z/LLNDkD

_>Да, а ещё можешь там вообще убрать упоминания про std::atomic (оставить только голый int), но добавить вызов "atomic_thread_fence(std::memory_order_seq_cst);" и насладиться результатом.

Ну логично. Это собственно что .store по дефолту делает. И?

_>И да, можешь ещё попереключать другие компиляторы/архитектуры с кодом по ссылке выше — может тогда поймёшь, что сохранение порядка записи/чтения на разных процессора задаётся по разному (не только отдельной инструкций, как в x86, но и например модифицированными операциями записи типа str/stlr), но к вопросу атомарности записи это отношения не имеет.

Вот и попереключай.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[44]: Безопасность Rust
От: vdimas Россия  
Дата: 03.06.19 19:45
Оценка:
Здравствуйте, ·, Вы писали:

·>"многопоточность в языке" означает, что язык описывает поведение языковых конструкций в контексте многих потоков.

·>volatile же описывает поведение языка в контексте асинхронных сигналов.

Я уже отвечал на это — где появился volatile, там многопоточность работала поверх одного процессора.


V>>такого пояснения было недостаточно?

·>То что можно дёрнуть syscall для создания потоков это ещё не значит, что язык умеет потоки.

Ес-но.
Поэтому я и не утверждал, что "в языке есть потоки", я утверждал, что в языке есть "понятие о многопоточности".
Явным образом оперирования потоками (создание, ожидание, доступ к информации о потоке) и т.д. в языке Си нет.


V>>Факт разработки языка Си для целей написания UNIX подтверждённый.

·>Интересует взаимосвязь этого факта с многопоточностью.

UNIX многозадачна by design.


V>>Я бы даже усилил — в отсутствии эрудиции и присутствии необоснованного упрямства — натуральная проблема.

·>"А в стандарте C11 в язык добавили реализацию потоков и поддержку атомарных типов"

До С++11 жизни не было, что ле?


V>>Если тебя интересует другая модель многопоточности, то просьба уточнить, какая именно.

·>Перечисли модели многопоточности какие есть в языке С/С++.

В языке Си нет никаких моделей многопоточности.
Модель многопоточности — это абстракция, от языка зависит мало.

Конкретно в Си есть предположение о возможном изменении переменной "кем-то еще", помимо текущего потока исполнения, плюс гарантия существования целочисленного типа, изменяемого атомарно в условиях работы механизма прерываний.

В стандарт С++11 добавили поддержку конкретно SMP-многопоточности.
Заметь, не в конструкции языка, а в стандартизированную часть его библиотек.

Еще пример — для кооперативной многозадачности никаких изменений в языке не требуется.


V>>·>Т.к. здесь behaviour этого типа в условиях многопоточности undefined, то использовать его в условиях многопоточности — Undefined Behaviour.

V>>Какой именно модели многопоточности?
·>Это тебя надо спросить, почему ты до сих пор не прочитал о memory model.

ЧТД. ))


V>>И как до стандарта C++11 работали операционки?

·>Мы обсуждаем язык, а не операционки.

Ты обсуждаешь удобное тебе, а не объективную реальность.
Причём, твои мотивы странные — разбираться ты не хочешь, тебе надо непременно что-то эдакое "доказать", о чём ты, что характерно, не упел составить никакого представления, бо родом из другой эпохи.


V>>Как работает Linux, которая писана и вовсе на голом С?

·>Linux не написана целиком на С, а вот такие критические платформенно-зависимые вещи типа атомарности, тредов и прочего частенько пишется на соответствующем ассемблере.

Это верно для той эпохи, когда появились interlocked-операции, но компиляторы их еще не поддерживали.
Я и сам выдирал из исходников линухов ассемблерные вставки в 2000-х для реализации cas в отстутствии соотвeтствующих built-in's в gcc.

Но простое переключение потоков исполнения, достаточное для реализации многозадачности поверх однопроцессорности (одноядерности) несложно делается через long jump.
Доступ к аппаратуре — через inp/outp.
Так же из Си возможно было обращаться напрямую к регистрам — они в ту пору вводились как некие предопределённые глобальные переменные.
Не вижу никаких сложностей.
И разработчики Си тоже не видели, бо язык должен был позволить описать работу операционки UNIX.


V>>Собсно, я всё это уже перечислял тут многократно, тебе стоило спорить с моими утверждениями целиком.

·>Возможно будет, но этого нет в стандарте языка, а значит UB.

Тут работает более одного пункта стандарта одновременно.
К уже процитированному добавляется:

6.8.2.1.20
Two actions are potentially concurrent if
(20.1) — they are performed by different threads, or
(20.2) — they are unsequenced, at least one is performed by a signal handler, and they are not both performed
by the same signal handler invocation.
...
The execution of a program contains a data race if it contains two potentially concurrent conflicting actions,
at least one of which is not atomic...
Any such data race results in undefined behavior.



V>>По стандарту UB будет только при доступе к нескольким переменным.

·>Хватит бла-бла. Приведи ссылку на стандарт.

Тебе приводились цитаты из cppreference.
Ссылка на то же самое на английском:
https://en.cppreference.com/w/cpp/atomic/memory_order

when multiple threads simultaneously read and write to several variables, one thread can observe the values change in an order different from the order another thread wrote them.


Как стандарте если — там тебе было бы еще сложнее:

The enumeration memory_order specifies the detailed regular (non-atomic) memory synchronization order as defined in 6.8.2 and may provide for operation ordering.


Собсно, до прошлого сообщения я и сам не понимал, что тебе может быть непонятного в самоописываемом идентификаторе memory_order?
Мне понадобилось несколько раундов, чтобы сообразить, что для тебя выделенное — пустой звук.
В том сообщении, на которое я отвечаю, ты и вовсе спросил меня прямо: "почему ты до сих пор не прочитал о memory model?"
Бгг, вот так ослиные уши песочницы ака "Джава" гордо воссияли в топике С++. ))

В C++ с этим более чем просто:

A program that has two conflicting evaluations has a data race unless:
— both evaluations execute on the same thread or in the same signal handler, or
— both conflicting evaluations are atomic operations (see std::atomic), or
- one of the conflicting evaluations happens-before another (see std::memory_order)


Ключевое выделил.
Теперь вернись на прошлое моё сообщение и медитируй до просветления.


V>>Я показал тебе алгоритм, который абсолютно корректен с т.з. стандартов.

·>Ты показал сигналы, а не многопоточность. И да, тот код корректен, но к многопоточности отношения не имеет.

Мякотка в том, что тебе никто не запрещает вызывать тот хенлдер сигнала ручками из другого потока.

Дабы исключить лишний пинг-понг, на этот счёт тоже есть оговорки в стандарте:

6.8.2.1.23
Transformations that introduce a speculative read of a potentially shared memory location may not
preserve the semantics of the C++ program as defined in this document, since they potentially introduce a
data race. However, they are typically valid in the context of an optimizing compiler that targets a specific
machine with well-defined semantics for data races.

Отредактировано 03.06.2019 19:47 vdimas . Предыдущая версия .
Re[31]: Безопасность Rust
От: alex_public  
Дата: 03.06.19 20:27
Оценка:
Здравствуйте, ·, Вы писали:

_>>Ты это, дурачком то не прикидывайся. Уже видимо давно понял что сказал чушь со своим утверждением, но продолжаешь до конца защищать её. Ну если так хочешь, то разберём её до конца.

_>>Мы говорили об атомарности не в каких-то там абстрактных пониманиях, а во вполне конкретном. Что при одновременной записи из двух потоков в одну ячейку памяти в ней будет точно или одно значение или второе, а не какое-то третье случайное значение.
·>У тебя проблемы с пониманием прочитанного и логикой. Я сказал "в переменной может оказаться всё что угодно, никаких гарантий нет". Из этого никак не следует, что "там будет какое-то третье случайное значение". Это просто значит, что гарантии поведения у данного участка кода отсутствуют. И даже если всё остальное (операционка, железо, компилятор, етс) будет идеально безбажным и работающим точно по всем спекам — результат работы такого кода неопределён, т.е. код некорректен и его надо переписать правильно.

Что-то ты похоже заболтался. Есть простое определение атомарности и оно как раз и означает, что если операция записи атомарная, то будет точно или одно значение или другое. Всё, точка. Если операция удовлетворяет этому условию, то значит она атомарная. Если не удовлетворяет (например там возникает половинка от одного значения, а половинка от другого), то значит не атомарная. Куда проще то? )

_>>Ты это всё очень хорошо и в деталях описал здесь http://rsdn.org/forum/flame.comp/7456435.1
Автор: ·
Дата: 28.05.19
и я даже со всем этим согласен, за исключением того, что для int'а не будет атомарности. Потому как любой тип данных (ну со всякими там оговорками на выравнивание, копирование целиком, а не кусками и т.п.), умещающийся в регистр целевой архитектуры обладает данным свойством. Тип sig_atomic_t введён исключительно для того, чтобы не проверять многочисленными ifdef'ами, что на целевой платформе размер подходящий (ну и кстати я не знаю платформы, где он не равен int, но это не суть). А так, подходит любой тип и естественно в том числе и int, про который ты сказал, что он не обладает свойством атомарности (в значение выше) на каких-то платформах. Так вот мы все до сих пор ждём, когда озвучишь эти самые платформы, или это будет однозначный слив..

·>Ты видимо что-то не понимаешь, что значит "гарантии". То что в современных платформах это обычно так, это не значит, что это чем-то гарантированно. Я завтра могу спаять новую платформу где это будет не так и твой говнокод поломается и тебе придётся править баги.

Я правильно понимаю это как признание, что на самом деле таких заявленных платформ ты не знаешь? )

·>Кстати, такое было в 8088 — инструкции 16-битные, а шина 8-битная. Т.е. запись регистра 16 бит полезет в ячейки памяти дважды. Правда многопоточноть никто на этих чипах не реализовывал.


Абсолютно нормально там работала бы многопоточность (в рамках одного ядра), т.к, инструкция то одна была. А вот реализовывать многоядерным такого уродца точно никто бы не стал. )))

_>>Кстати, в нормальных реализациях приостановленную сопрограмму можно передать на исполнение в любой другой поток. Причём стратегия поведения (всегда в одном потоке или берём свободный из пула) может управляться одной мелкой галочкой где-то в другом конце приложения. Интересно как это всё можно формализовать на Rust? Подозреваю что никак и будет один гигантский unsafe.

·>Обернуть конструкцию передачи в другой поток в какой-нибудь там mfence — и вуаля — гарантии. Rust поможет "ломать компиляцию" если в твоём коде ты "забудешь" делать это правильно.

Точка передачи (планировщик) там в любом случае будет unsafe, просто потому, что его по другому не сделать. И мы будем ему доверять на слово, так же как другим подобным инструментам (типа Mutex, Atomic и т.п.). Вопрос в коде внутри самих сопрограмм: он получается может быть валидным или нет в зависимости от некой программной опции — сомневаюсь что такое возможно разумно задать с помощью системы типов. )

_>>·>Инструкции могут не только переупорядочиваться, но и выкидываться. Если оптимизатор решит, что переменная не меняется в данном участке кода, то она может так и жить в регистре или вообще выоптимизировать её в ноль. Или несмотря на то, что в исходном коде перемення изменяется несколько раз, он может выкинуть промежуточные инструкции сохранения в память.

_>>·>Барьеры предотвращают и это.
_>>Глупости говоришь. Даже если компилятор в коде без барьеров сведёт две операции записи в одну, то это:
_>>- никак не повлияет на факт выделения участка памяти для неё
_>>- никак не повлияет на атомарность оставшейся операции записи.
·>Атомарность вообще-то подразумевает ещё и видимость значений. Если в коде значение переменной присваивается, то вполне ожидаемо, что значение таки изменится. Отсутствие барьеров может нарушить эту гарантию.

Так последнее значение в любом случае присвоится, даже если компилятор выкинет промежуточные присвоения — всё в рамках ожидания.

_>>>>И отключается она для данной переменной не с помощью барьеров, а помощью ключевого слова volatile.

_>>·> А ты почитай Cтандарт про volatile:
_>>·>

This makes volatile objects suitable for communication with a signal handler, but not with another thread of execution, see std::memory_order

_>>·>И не неси эту чушь больше.
_>>И где в твоей цитате слова об отключение оптимизации переменная->регистр? ) Какое она вообще имеет отношение к моему утверждению об отключение этой оптимизации с помощью volatile? )
·>В цитате явно говорится, что volatile это про сигналы, а не треды. Ты делаешь бессмысленные высказывания, основанные на ложной посылке.

Volatile — это не про сигналы и не про потоки. Это на самом деле очень простая вещь: указание компилятору на то, что к данной переменной может иметь доступ какой-то посторонний код (который компилятору не виден). Это резко сужает набор оптимизаций (в том числе уход в регистр), которые можно применять к этой переменной. Всё, точка. Атомарность, многопоточность, сигналы, упорядоченность записи/чтения и т.п. — это всё уже другие темы, не относящиеся к volatile.

_>>>>Какими ещё lock инструкциями, если наш процессор их не умеет? )))

_>>·>Это какой процессор не умеет? cli/sti — умеют как минимум все. А если мы говорим о многопроцессорных системах...
_>>Эээ что? Ну ты прямо развеселил... Весь даже если бы тебе дали доступ к управлению прерываниями ради такой ерунды как инкремент int'a, это всё равно действовало бы только в рамках одного ядра, а другим ядрам было бы всё равно.
·>Управлением прерываниями занимается ось, притом конкретный механизм управления зависит от конкретного железа. И она же даёт мне доступ к этой магии через сисколы.

Не очень понял эту твою фразу. Ты тут хотел сказать, что на каком-то железе команда запрета прерываний подействует сразу на все ядра или что? )))

_>>Аааа, т.е. это ты просто очередной раз написал свой бред не в тему (сам же перед этим писал "Там про race condition."), а просто потому что надо написать что-то, но по теме сказать нечего? ) Ну понятно, понятно... )))

·>Я просто повторил свой тезис, в ответ на твой бред.

Давай ка я кратко перескажу весь наш диалог, а ты попробуешь взглянуть на него со стороны и оценить, можно ли твоё общение считать хотя немного адекватным. И так:

Идёт обсуждение каких-то проблем многопоточности.
Ты: так там же race condition!
Я: от факта такого названия оно не перестаёт быть проблемой.
Ты: для некоторых проблем Rust выдаст ошибку компиляции!
Я: так Rust умеет выдывать ошибку компиляции для некоторых случаев race condition?
Ты: нет конечно, ты что, не умеешь читать? "Некоторый" != "race condition".
Я: ???? .... ... .... ...
Re[41]: Безопасность Rust
От: vdimas Россия  
Дата: 03.06.19 20:31
Оценка:
Здравствуйте, ·, Вы писали:

V>>в некоей архитектуре породит код, который не будет атомарно писать и читать эту переменную.

V>>Смогёшь?
·>Я ж уже тебе показывал, но чукча не читатель: https://godbolt.org/z/LLNDkD

Показать и доказать — разные вещи. ))

Упрощу:
https://godbolt.org/z/vNstSQ

static volatile sig_atomic_t v;

void s(sig_atomic_t num) {
    v=num;
}
======
1 s(int):
2       lui     a5,%hi(v)
3       sw      a0,%lo(v)(a5)
4       ret


Насколько я понимаю, в строке 2 в a5 записывается старшая половина статического адреса v, а в строке 3 значение a0 (параметр num) записывается по адресу low(v) + hi(v) из a5.
Тебе необходимо показать, что в строке 3 может произойти неатомарная запись.

На всяк случай — ты привёл абстрактный RISC, который именно разрабатывается как абстрактный.
В его доке есть примечания:

The atomic memory operation (AMO) instructions perform read-modify-write operations for multiprocessor synchronization
...
The AMOs were designed to implement the C11 and C++11 memory models efficiently.
...
implementations might also implement AMOs at memory controllers, and can optimize away fetching the original value when the destination is x0.


У тебя по ссылке swap, но чтение содержимого переменной не требовалось, там безусловная запись, поэтому, скорее всего, будет работать "can optimize away fetching the original value".

Итого, когда биты aq=0 и rl=0 у этих операций и чтение значения не требуется (см в твоём примере amoswap.w zero, ...), семантика происходящего не будет отличаться от простого sw.

Вопрос: а почему же всё-таки AMO, а не sw?
Ответ там же в доке по AMO:

If the address is not naturally aligned, a misaligned address exception will be generated.

Но на этот счёт я уже делал комментарии ранее:
http://www.rsdn.org/forum/flame.comp/7458035.1
Отредактировано 03.06.2019 20:38 vdimas . Предыдущая версия . Еще …
Отредактировано 03.06.2019 20:37 vdimas . Предыдущая версия .
Отредактировано 03.06.2019 20:34 vdimas . Предыдущая версия .
Re[35]: Безопасность Rust
От: vdimas Россия  
Дата: 03.06.19 20:37
Оценка:
Здравствуйте, ·, Вы писали:

·>А как это объяснишь? https://godbolt.org/z/LLNDkD


Элементарно — ты не понимаешь кода по собственной ссылке.
RTFM!

Объяснение:
http://www.rsdn.org/forum/flame.comp/7460823.1
Re[42]: Безопасность Rust
От: · Великобритания  
Дата: 03.06.19 22:38
Оценка:
Здравствуйте, vdimas, Вы писали:

V>>>Смогёшь?

V>·>Я ж уже тебе показывал, но чукча не читатель: https://godbolt.org/z/LLNDkD
V>Показать и доказать — разные вещи. ))
После очередной подмены тезис стал заключаться в том, что atomic<int>+relaxed и volatile sig_atomic_t — оба атомарны с т.з. многопоточности т.к. генерят одинаковый код. Я показал, что иногда генерится разный код. ЧТД.

Но ты снова подменяешь тезис какими-то конкретными имплементациями:

V>У тебя по ссылке swap, но чтение содержимого переменной не требовалось, там безусловная запись, поэтому, скорее всего, будет работать "can optimize away fetching the original value".

И что, что скорее всего? А иногда и нет. Типичный UB.

V>Итого, когда биты aq=0 и rl=0 у этих операций и чтение значения не требуется (см в твоём примере amoswap.w zero, ...), семантика происходящего не будет отличаться от простого sw.

Если implement AMOs at memory controllers. А если нет?

V>Вопрос: а почему же всё-таки AMO, а не sw?

V>Ответ там же в доке по AMO:

V>If the address is not naturally aligned, a misaligned address exception will be generated.

Ты хочешь сказать, что это единственная причина? Почему тогда сгенерился такой код? Ведь компилятор явно знает, что оно aligned.

В любом случае, даже если risc и все известные архитектуры будут атомик по самые гланды, это не меняет того факта, что по стандарту _языка_ ни int, ни даже volatile int не являются atomic.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[36]: Безопасность Rust
От: · Великобритания  
Дата: 03.06.19 22:44
Оценка: :)
Здравствуйте, vdimas, Вы писали:

V>·>А как это объяснишь? https://godbolt.org/z/LLNDkD

V>Элементарно — ты не понимаешь кода по собственной ссылке.
Я как минимум понимаю, что код разный. Этого достаточно, чтобы доказать ложность утверждения: "с точки зрения вопросов атомарности это в точности тот же самых код с вполне корректным std::atomic"
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[32]: Безопасность Rust
От: · Великобритания  
Дата: 03.06.19 23:55
Оценка:
Здравствуйте, alex_public, Вы писали:

_>>>Мы говорили об атомарности не в каких-то там абстрактных пониманиях, а во вполне конкретном. Что при одновременной записи из двух потоков в одну ячейку памяти в ней будет точно или одно значение или второе, а не какое-то третье случайное значение.

_>·>У тебя проблемы с пониманием прочитанного и логикой. Я сказал "в переменной может оказаться всё что угодно, никаких гарантий нет". Из этого никак не следует, что "там будет какое-то третье случайное значение". Это просто значит, что гарантии поведения у данного участка кода отсутствуют. И даже если всё остальное (операционка, железо, компилятор, етс) будет идеально безбажным и работающим точно по всем спекам — результат работы такого кода неопределён, т.е. код некорректен и его надо переписать правильно.
_>Что-то ты похоже заболтался. Есть простое определение атомарности и оно как раз и означает, что если операция записи атомарная, то будет точно или одно значение или другое.
Или, например, исключение (ну там какой-нибудь способ придумают детектить говнокод).

_>Всё, точка. Если операция удовлетворяет этому условию, то значит она атомарная.

Второе условие — ты таки увидишь значение "после".

_>·>Ты видимо что-то не понимаешь, что значит "гарантии". То что в современных платформах это обычно так, это не значит, что это чем-то гарантированно. Я завтра могу спаять новую платформу где это будет не так и твой говнокод поломается и тебе придётся править баги.

_>Я правильно понимаю это как признание, что на самом деле таких заявленных платформ ты не знаешь? )
Нет, не знаю. И что? Из моего незнания будешь делать выводы?

_>·>Кстати, такое было в 8088 — инструкции 16-битные, а шина 8-битная. Т.е. запись регистра 16 бит полезет в ячейки памяти дважды. Правда многопоточноть никто на этих чипах не реализовывал.

_>Абсолютно нормально там работала бы многопоточность (в рамках одного ядра), т.к, инструкция то одна была. А вот реализовывать многоядерным такого уродца точно никто бы не стал. )))
А ты клянёшься, что никто никода не станет?

_>>>Кстати, в нормальных реализациях приостановленную сопрограмму можно передать на исполнение в любой другой поток. Причём стратегия поведения (всегда в одном потоке или берём свободный из пула) может управляться одной мелкой галочкой где-то в другом конце приложения. Интересно как это всё можно формализовать на Rust? Подозреваю что никак и будет один гигантский unsafe.

_>·>Обернуть конструкцию передачи в другой поток в какой-нибудь там mfence — и вуаля — гарантии. Rust поможет "ломать компиляцию" если в твоём коде ты "забудешь" делать это правильно.
_>Точка передачи (планировщик) там в любом случае будет unsafe, просто потому, что его по другому не сделать. И мы будем ему доверять на слово, так же как другим подобным инструментам (типа Mutex, Atomic и т.п.). Вопрос в коде внутри самих сопрограмм: он получается может быть валидным или нет в зависимости от некой программной опции — сомневаюсь что такое возможно разумно задать с помощью системы типов. )
Эээ? А что там такого невозможного? По большому счёту это просто синт-сахар. На гарантии корректности тут мало что влияет.

_>·>Атомарность вообще-то подразумевает ещё и видимость значений. Если в коде значение переменной присваивается, то вполне ожидаемо, что значение таки изменится. Отсутствие барьеров может нарушить эту гарантию.

_>Так последнее значение в любом случае присвоится, даже если компилятор выкинет промежуточные присвоения — всё в рамках ожидания.
Но не факт, например, что компилятор не выкинет чтение из памяти.

_>>>И где в твоей цитате слова об отключение оптимизации переменная->регистр? ) Какое она вообще имеет отношение к моему утверждению об отключение этой оптимизации с помощью volatile? )

_>·>В цитате явно говорится, что volatile это про сигналы, а не треды. Ты делаешь бессмысленные высказывания, основанные на ложной посылке.
_>Volatile — это не про сигналы и не про потоки. Это на самом деле очень простая вещь: указание компилятору на то, что к данной переменной может иметь доступ какой-то посторонний код (который компилятору не виден). Это резко сужает набор оптимизаций (в том числе уход в регистр), которые можно применять к этой переменной. Всё, точка. Атомарность, многопоточность, сигналы, упорядоченность записи/чтения и т.п. — это всё уже другие темы, не относящиеся к volatile.
В смысле ты считаешь, что в цитате не говорится про сигналы и потоки? Или что? Каким образом твои слова согласуются с цитатой? Или ты цитату считаешь неверной?

_>>>·>Это какой процессор не умеет? cli/sti — умеют как минимум все. А если мы говорим о многопроцессорных системах...

_>>>Эээ что? Ну ты прямо развеселил... Весь даже если бы тебе дали доступ к управлению прерываниями ради такой ерунды как инкремент int'a, это всё равно действовало бы только в рамках одного ядра, а другим ядрам было бы всё равно.
_>·>Управлением прерываниями занимается ось, притом конкретный механизм управления зависит от конкретного железа. И она же даёт мне доступ к этой магии через сисколы.
_>Не очень понял эту твою фразу. Ты тут хотел сказать, что на каком-то железе команда запрета прерываний подействует сразу на все ядра или что? )))
Ты спросил выше "Какими ещё lock инструкциями, если наш процессор их не умеет" — и внезапно "все ядра". Бывают многоядерные системы, без инструкций синхронизации?

_>>>Аааа, т.е. это ты просто очередной раз написал свой бред не в тему (сам же перед этим писал "Там про race condition."), а просто потому что надо написать что-то, но по теме сказать нечего? ) Ну понятно, понятно... )))

_>·>Я просто повторил свой тезис, в ответ на твой бред.
_>Давай ка я кратко перескажу весь наш диалог, а ты попробуешь взглянуть на него со стороны и оценить, можно ли твоё общение считать хотя немного адекватным. И так:
А давай я кратко перескажу диалог как я вижу:

Я: "safe rust даёт гарантии от data race"
Ты: "rust не решает проблемы многопоточности"
Я: "вот ссылка о том, что safe rust даёт гарантии от data race"
Ты: "а вот там написано, что rust гарантий не даёт"
Я: "там это написано о race condition, а rust даёт гарантии для другой проблемы"
Ты: "нет, rust не даёт гарантии от race condition"
Я: "Никто и не обещал такие гарантии. safe rust даёт гарантии от data race"
Ты: "ты просто очередной раз написал свой бред не в тему"

Давай так. по каждому пункту. Ответь — "согласен/не согласен". Если "не согласен", то приводи ссылку на спеку.
1. Safe rust даёт гарантии от data race.
2. Доступ через volatile не является atomic.
3. Конкурентный доступ из разных потоков к volatile является data race.
4. data race является undefined behavriour.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[35]: Безопасность Rust
От: alex_public  
Дата: 04.06.19 00:02
Оценка: 2 (1)
Здравствуйте, ·, Вы писали:

_>>·>Действительно... одной инструкцией меньше, одной больше... Кака разница? В главном-то ты прав!

_>>Ох, ну что за детский сад. Ты реально не в курсе что такое mfence? Ну мне даже лень объяснять, так что поясню проще: https://godbolt.org/z/7zWFvr — с точки зрения вопросов атомарности это в точности тот же самых код с вполне корректным std::atomic, что и раньше, только переписанный чуть под другому. Дальше сам додумаешь? )))
·>А как это объяснишь? https://godbolt.org/z/LLNDkD

Это кривая реализация встроенной функции gcc __atomic_store_n для данной маргинальной платформы. Собственно тут (https://github.com/michaeljclark/riscv-atomics) можно увидеть две реализации и та, что на сайте godbolt, использует именно кривой вариант через встроенную функцию gcc, а не через ассемблер. Нормальную реализацию можно увидеть например здесь https://github.com/michaeljclark/riscv-atomics/blob/master/src/stdatomic_asm.h#L95. И подробное обсуждение всей этой кривизны можно увидеть здесь https://github.com/michaeljclark/riscv-atomics/tree/master/results.
Re[43]: Безопасность Rust
От: vdimas Россия  
Дата: 04.06.19 07:04
Оценка:
Здравствуйте, ·, Вы писали:

·>После очередной подмены тезис стал заключаться в том, что atomic<int>+relaxed и volatile sig_atomic_t — оба атомарны с т.з. многопоточности т.к. генерят одинаковый код. Я показал, что иногда генерится разный код. ЧТД.


Не-не-не, Дэвид Блэйн, вы показывали Алексу, что в одном случае атомарность, а в другом нет.
Тем самым ты и Сайберикс показали, что не понимаете, что есть атомарность, путаете её с упорядочиванием/синхронизацией.

А я лишь утверждал о требовании записи значения в память в одну инструкцию из-за работы в условиях прерываний.
Для x86 таких инструкций несколько, кстате.


·>Но ты снова подменяешь тезис какими-то конкретными имплементациями:


Я показал тебе, что не понимаешь ассемблерного кода, который сам же привёл.


V>>У тебя по ссылке swap, но чтение содержимого переменной не требовалось, там безусловная запись, поэтому, скорее всего, будет работать "can optimize away fetching the original value".

·>И что, что скорее всего? А иногда и нет. Типичный UB.

В голос уже ))
1. "Скорее всего" относится не к описанному поведению, а к конкректной реализации проца под этот ассемблер;
2. Даже если в какой-то из реализаци в этой команде будет происходить не store, а swap — во втором случае всё-равно никакого UB.


V>>Итого, когда биты aq=0 и rl=0 у этих операций и чтение значения не требуется (см в твоём примере amoswap.w zero, ...), семантика происходящего не будет отличаться от простого sw.

·>Если implement AMOs at memory controllers. А если нет?

То на уровне проца, вестимо.
А у тебя какие варианты?


V>>If the address is not naturally aligned, a misaligned address exception will be generated.

·>Ты хочешь сказать, что это единственная причина?

Да.
Это слишком веская причина.
Лично я бы предпочел иметь в x86/x86_x64 инструкции, которые генерят аналогичное прерывание в случае нарушения выравнивания.
Что характерно, что в x86/x86_x64 если невыровненное значение попадай на край линейки кеша, то прерыванеи происходит ВСЕГДА, но это прерывание перехвачено операционкой и операционка сама "ручками" разруливает такую ситуацию, копируя это значение побайтно. Т.е. и эффективность падает и атомарности никакой, а программист и не в курсе. Хотелось бы иметь возможность каким-либо образом указывать в некоторых случаях на то, что ничего разруливать не надо, надо явно сигнализировать об ошибке.


·>Почему тогда сгенерился такой код? Ведь компилятор явно знает, что оно aligned.


Ты так говоришь, будто современные компиляторы — верх разума.
Это шаблон генерации, скорее всего, который работает для разных случаев адресации в том числе.
Я дважды находил ошибки в gcc в разное время, а тут и ошибки-то нет, придраться не к чему.

Могу показать выход работы, например, JIT дотнета, где происходят явные глупости, которые хотелось бы исправить.
Т.е. не боги горшки обжигают, а обычные люди, не большей квалификации, чем в среднем участники этого форума.


·>В любом случае, даже если risc и все известные архитектуры будут атомик по самые гланды, это не меняет того факта, что по стандарту _языка_ ни int, ни даже volatile int не являются atomic.


По стандарту sig_atomic_t в любом случае будет atomic.

Помнишь из этой ветки:
http://www.rsdn.org/forum/flame.comp/7460799.1

Мякотка в том, что тебе никто не запрещает вызывать тот хенлдер сигнала ручками из другого потока.

Симметричная мякотка в том, что в многопроцессорной системе обработчик прерывания может быть вызыван из другого потока (из основного), а не из того, в котором ты осуществляешь оперироавние с переменной типа sig_atomic_t.

А будет ли этот typedef на short, int, long — лично мне до фени.

Но если уже спорить ради спора, я бы запросто поставил ящик коньяка на то, что в 99,9% всех реализаций это будет int. ))
Отредактировано 04.06.2019 7:10 vdimas . Предыдущая версия . Еще …
Отредактировано 04.06.2019 7:05 vdimas . Предыдущая версия .
Отредактировано 04.06.2019 7:04 vdimas . Предыдущая версия .
Re[43]: Безопасность Rust
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.06.19 07:21
Оценка: :)
Здравствуйте, vdimas, Вы писали:
V>Факт разработки языка Си для целей написания UNIX подтверждённый.
Всегда восторгаюсь способности нести чушь с умным видом. Ничего, что слово volatile появилось в С через 15 лет после того, как переписывание ядра UNIX на него закончилось?
Ну, и мелочи вроде путания Linux и UNIX, то есть дедушки с внуком, они как бы тоже прекрасны в рамках данной дискуссии с претензией на понимание не только фактов, но и причинно-следственных связей.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.