Здравствуйте, Zhendos, Вы писали:
Z>Э, в C++ никаких гарантий нет даже если работать только через контейнеры/итераторы/умные указатели. Z>Например можно получить итератор для std::vector, потом вызвать push_back, а потом продолжать Z>пользоваться итератором. В Rust это ошибка компиляции, а в С++ это проблема программиста.
О, а вот это вообще отдельная тема, которую тут ещё не обсуждали. И это как раз хороший пример того, когда минусы намного перевешивают плюсы. Потому как на самом деле реализация этого механизма работает совсем не так, как ты описал. На практике его действие сводится к запрету всех модифицирующих операций внутри цикла. И в итоге весь SO забит вопросами типа "как переписать на Rust данный абсолютно корректный алгоритм на C++". И ответами типа "никак" (естественно без unsafe) — усложняйте логику.
Z>А можно пояснить почему это только "маленькая" проблема. Z>Допустим мы берем сторонее решение для С++ и Rust. Z>В Rust сторонняя библиотека "проанатирована" с помощью trait Send/Sync, Z>поэтому неправильно ее использовать нельзя, остается только проблема с багами Z>в самой сторонней библиотеке. В С++ же сразу обе проблемы налицо и сама библиотека и ее использование.
В данном случае подразумевалось решение проблемы разделяемой между потоками памятью с помощью библиотеки реализующей модель акторов — просто запрещаем такую память как таковую и всё. Это решает сразу множество проблем параллелизма, а не одну мелкую.
Здравствуйте, alex_public, Вы писали:
_>О, а вот это вообще отдельная тема, которую тут ещё не обсуждали. И это как раз хороший пример того, когда минусы намного перевешивают плюсы. Потому как на самом деле реализация этого механизма работает совсем не так, как ты описал. На практике его действие сводится к запрету всех модифицирующих операций внутри цикла.
Можно. Нужно только убрать все займы ссылок на элементы вектора. ЧСХ, такое же требование и для С++.
Здравствуйте, Cyberax, Вы писали:
_>>О, а вот это вообще отдельная тема, которую тут ещё не обсуждали. И это как раз хороший пример того, когда минусы намного перевешивают плюсы. Потому как на самом деле реализация этого механизма работает совсем не так, как ты описал. На практике его действие сводится к запрету всех модифицирующих операций внутри цикла. C>Можно. Нужно только убрать все займы ссылок на элементы вектора.
Каким образом ты уберёшь ссылки внутри цикла, если сам итератор работает за неё?
C>ЧСХ, такое же требование и для С++.
Нет, в C++ в каком-то смысле обратное требование (и соответствующее реальным внутренним процессам в контейнере): там нельзя использовать итератор после применения модифицирующей операции.
Здравствуйте, ·, Вы писали:
_>>Ну раз ты так утверждаешь, то тебе конечно же будет не сложно привести конкретный пример такой комбинации? ) ·>Ты прикалываешься что-ли? Как минимум я тебе в этом топике уже указывал на уплывание &-ссылок в массив лямбд. Ещё упоминали инвалидацию итераторов. Не говоря уж о тривиальном delete x; delete x;.
Т.е. привести конкретный пример, в котором комбинация безопасных конструкций приведёт к небезопасному коду, ты можешь, правильно? ) Понятно... )))
_>>Вообще то большинство необходимых элементов для данной формулировки уже озвучивались в данной темке. ·>Ну так и скажи, что не формулируется (балабольство не считается), зачем завираться-то?
В дискуссии с WolfHound было всё чётко рассказано. ) Или ты хочешь сказать, что не читал всю ветку? )))
_>>А ты забавный. ))) Люди, которые ежедневно профессионально пишут на Rust сами высказывали данной темке утверждение о том, что существует множество абсолютно корректных приложений, которые непропускает компилятор Rust (естественно без unsafe). Но при этом ты, который насколько я помню, специализируется на совсем других инструментах, пытаешься опровергать это утверждение. ·>Писали они несколько другое.
Писали как раз в точности это. Я прямо их формулировки повторял. Тебе дать прямые ссылки? )
_>>и при этом естественно не вызывает никаких утечек памяти. Так что, ты можешь указать какую строчку надо поставить на место знаков вопросов, чтобы в C++ появилась такая же утечка как в Rust'е? ·>Мде. Ты решил выиграть у себя конкурс на самый идиотский аргумент в споре?
Ну т.е. не можешь привести соответствующую строчку. Понятно, уже который слив в рамках одного сообщения...
·>Если ты и в правду такой, открой для себя, например, unique_ptr.release, практически аналог forget.
Функция unique_ptr.release не отключает работу RAII — ты получше разберись в теме, что ли.
_>>Циклические зависимости в shared_ptr решаются в C++ с помощью https://en.cppreference.com/w/cpp/memory/weak_ptr. В точности так же, как и в Rust'е https://doc.rust-lang.org/std/sync/struct.Weak.html. ·>Ты издеваешься что-ли? Я знаю как они решаются. Но ты тут заикнулся о гарантиях, а теперь внезапно "решается". Так какие _гарантии_ даёт c++ от утечек?
Ну раз знаешь что в C++ и в Rust используется одинаковый механизм, то к чему глупые вопросы про утечки?
_>>>>определённых условиях) приблизительно по той же схеме, что и с памятью: объявляем все межпоточные (но и только!) взаимодействия как unsafe и соответственно заставляем локализовать их в специальных типах, через которых происходит всё взаимодействие. Разница в ситуации с памятью в том, что таким образом решается только маленькая часть проблем параллелизма. В то время как некоторые сторонние библиотеки (и в C++ и в Rust), основанные на других подходах к проблеме параллелизма, предлагают решения с гораздо большими гарантиями. Ну а в тех редких случаях, когда эти библиотеки не подходят, обычно подходит только "матёрый unsafe" (что в C++, что в Rust). В итоге пользы от такой попытки особой нет (проще сразу взять сильное решение с настоящими гарантиями), а вот вред (в точности такой же, как и в ситуации с памятью) никуда не девается. _>>·>Это какие же _гарантии_? _>>Эээ что? ) ·>Ты обещал "сильное решение с настоящими гарантиями" — так какие же конкретно гарантии чего?
Я думаю ты прекрасно понял, что подразумевал под сильными решениями библиотеки типа модели акторов и т.п. И их плюсы мы тут уже подробно разбирали. Хочешь зайти на ещё один круг? В принципе мне не сложно. )))
_>>Ну тогда уж надо совсем до конца цитировать. Там сказано "целочисленый тип, обращения к которому атомарны, даже при обработке асинхронных сигналов". Надо пояснять разницу? ))) ·>Надо, и с учётом того, что этот тип появился фактически ещё до того как треды были придуманы в принципе.
Это значит что тип атомарный всегда, даже в таких сложных условиях, как асинхронные вызовы.
P.S. Мне вот интересно, ты до сих пор не сумел понять, что хотя для корректной многопоточности в общем случае недостаточно фактора атомарности, но в определённых частных случаях (в тех, когда можно применять atomic relaxed) этого более чем достаточно. Или же ты всё же уже давно сумел разобраться в теме и продолжаешь нести чушь исключительно для того, чтобы не признаваться в своей изначальной неправоте?
Здравствуйте, ·, Вы писали:
_>>·>Неужели ты предпочитаешь основывать рассуждения не о том, что прямо говорится в стандартне, а на том что я что-то не знаю? _>>Ладно бы только ты не знал. Так ведь и я тоже не знаю, а это совсем другое дело! ))) _>>Ну а если серьёзно, то ПО написанное на C++ всегда компилируется под конкретную архитектуру, так что подобные проблемы никогда не стоят. ·>Господин никогда не писал переиспользуемый портируемый код?
Ну так назови платформу, на которой данный код (предполагающий что sig_atomic==int) не будет работать без переделок.
_>>·>И что? Без borrow checker ты не сможешь гарантировать всё равно. _>>Ты бредишь? ))) Вот такой простейший код: ·>Ты меня решил взять на измор идиотскими примерами кода?
С учётом того, что ни на один прямой вопрос по конкретному приведённому коду ты не смог дать ответ, а только пытался отмазываться разными способами, похоже что это единственный способ общения с тобой. Все общетеоретические рассуждения ты профессионально забалтываешь. А вот от предложения продемонстрировать излагаемую тобой чушь на примерах конкретного кода тебя прямо корёжит. )))
_>>И ты хочешь сказать, что внутри process можно написать такой код, что он сможет каким-то образом обращаться непосредственно к переменной v? ) ·>Даже в таком тривиальном коде достаточно написать int process(int &p) и готово. А в реальном коде будет ещё 100500 способов нарваться на подобное.
О, снова меняем правила на ходу? ) Тебе было можно менять код внутри функции process, а не приведённый код её использования.
_>>Вред от оптимизации возможен в некорректном коде — в котором есть потребность в модификаторе volatile, но при этом его почему-то не поставили. Однако ни в одном из обсуждаем в данной темке примеров такого кода не было. ·> Это не вред от оптимизации. Это вред от некорректного кода.
Однако при отключённой оптимизации он вдруг резко станет корректным. Более того, в далёком прошлом (до возникновения оптимизаторов и volatile) код только так и писали.
_>>·>Вызов функции это про sequence points, что никакого отношения к многопоточке не имеет. Или я не понял что ты имеешь в виду. _>>А причём тут многопоточность или ещё что-то? Нам важно чтобы не были переставлены местам обращения к памяти и всё. Внутри тела одной функции ты можешь это отследить сам, а внутрь других функций (вызываемых из твоей) тебе лазить не надо, т.к. за этим следит компилятор. ·>И как seq-points от этого помогут? Оптимизатор, особенно в WPO, может такое наворотить.
Интересно конечно. Я тебе подробно объясняю как оно работает и ты прямо в ответ спрашиваешь "а как оно поможет?". Феерично просто. Разберись уже сам, что такое compiler barrier и что может служить причиной его появления (помимо atomic_signal_fence).
_>>Повторяю ещё раз, вроде как русским языком: причём тут вообще тема кэшей (которую ты зачем-то попытался приплести сюда) и т.п.? У тебя было конкретное утверждение о работе оптимизатора процессора. Что он может не только менять очерёдность исполнения инструкций, но и выкидывать некоторые из них (как это умеет делать компилятор, если ему не запретить через volatile). И мы все ждём подтверждения этого интересного утверждения. И кэши к подтверждению данного утверждения вообще никак не относятся. ·>Возможно ты "выкидывать инструкции" понял слишком буквально. Очевидно, CPU не может "выкидывать", т.к. не умеет править загруженные exe-шники. Он просто может не модифицировать память так, что другие ядра увидят модификации. Что по сути означает, что инструкция "ничего не делает".
И с чего это по твоему он будет не модифицировать память (под памятью в данном случае подразумевается естественно общий кэш, т.к. пересылка данных между ним и физической памятью уже не принципиальна для нашей дискуссии)? Какой конкретно процессор и в каких условиях так делает? Можно увидеть соответствующую документацию?
Здравствуйте, alex_public, Вы писали:
_>>>Ну раз ты так утверждаешь, то тебе конечно же будет не сложно привести конкретный пример такой комбинации? ) _>·>Ты прикалываешься что-ли? Как минимум я тебе в этом топике уже указывал на уплывание &-ссылок в массив лямбд. Ещё упоминали инвалидацию итераторов. Не говоря уж о тривиальном delete x; delete x;. _>Т.е. привести конкретный пример, в котором комбинация безопасных конструкций приведёт к небезопасному коду, ты можешь, правильно? ) Понятно... )))
Что тебе неконкретно в последних двух примерах выше? В первом примере я уже тоже конкретно говорил — убрать в том твоём примере t.join(). Хуже того... join может кинуть исключение. Рассказать что твой говнокод будет делать в таком случае?
_>>>Вообще то большинство необходимых элементов для данной формулировки уже озвучивались в данной темке. _>·>Ну так и скажи, что не формулируется (балабольство не считается), зачем завираться-то? _>В дискуссии с WolfHound было всё чётко рассказано. ) Или ты хочешь сказать, что не читал всю ветку? )))
Ссылку.
_>·>Писали они несколько другое. _>Писали как раз в точности это. Я прямо их формулировки повторял. Тебе дать прямые ссылки? )
Давай.
_>·>Если ты и в правду такой, открой для себя, например, unique_ptr.release, практически аналог forget. _>Функция unique_ptr.release не отключает работу RAII — ты получше разберись в теме, что ли.
Спасибо, я давно разобрался. Если у тебя возникли трудности, спрашивай.
_>>>Циклические зависимости в shared_ptr решаются в C++ с помощью https://en.cppreference.com/w/cpp/memory/weak_ptr. В точности так же, как и в Rust'е https://doc.rust-lang.org/std/sync/struct.Weak.html. _>·>Ты издеваешься что-ли? Я знаю как они решаются. Но ты тут заикнулся о гарантиях, а теперь внезапно "решается". Так какие _гарантии_ даёт c++ от утечек? _>Ну раз знаешь что в C++ и в Rust используется одинаковый механизм, то к чему глупые вопросы про утечки?
Так и скажи, что соврал, что опять юлишь? Вопросы про утечки ты первый начал, утверждая, что С++ даёт гарантии лучше, чем Rust.
_>>>·>Это какие же _гарантии_? _>>>Эээ что? ) _>·>Ты обещал "сильное решение с настоящими гарантиями" — так какие же конкретно гарантии чего? _>Я думаю ты прекрасно понял, что подразумевал под сильными решениями библиотеки типа модели акторов и т.п. И их плюсы мы тут уже подробно разбирали. Хочешь зайти на ещё один круг? В принципе мне не сложно. )))
И какие _гарантии_ дадут эти библиотеки? Что мне запретит вставить какую-нибудь глобальную переменную и мьютекс с тредом в программу, использующую такую библиотеку?
_>>>Ну тогда уж надо совсем до конца цитировать. Там сказано "целочисленый тип, обращения к которому атомарны, даже при обработке асинхронных сигналов". Надо пояснять разницу? ))) _>·>Надо, и с учётом того, что этот тип появился фактически ещё до того как треды были придуманы в принципе. _>Это значит что тип атомарный всегда, даже в таких сложных условиях, как асинхронные вызовы.
Если для тебя эти условия сложные, это не значит что это имеет какое-то отношение к семантике языка.
_>P.S. Мне вот интересно, ты до сих пор не сумел понять, что хотя для корректной многопоточности в общем случае недостаточно фактора атомарности, но в определённых частных случаях (в тех, когда можно применять atomic relaxed) этого более чем достаточно.
sig_atomic_t has no inter-thread properties. In fact, if an object of this type is accessed (modified) by more than one thread (as in your example code), it is technically undefined behavior (data race); Therefore, inter-thread memory ordering properties are not defined. (с) https://stackoverflow.com/questions/56598970
Ещё вопросы?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, alex_public, Вы писали:
_>>>И ты хочешь сказать, что внутри process можно написать такой код, что он сможет каким-то образом обращаться непосредственно к переменной v? ) _>·>Даже в таком тривиальном коде достаточно написать int process(int &p) и готово. А в реальном коде будет ещё 100500 способов нарваться на подобное. _>О, снова меняем правила на ходу? ) Тебе было можно менять код внутри функции process, а не приведённый код её использования.
Во-первых, int process(int &p) не код её использования, а код декларации функции. Ты опять соврал.
Во-вторых, правила на ходу меняешь ты, притом постоянно. Вот с чего началось:
_>Там нет кода, который изменил бы своё поведение, в случае "кэширования" значения переменной в регистре.
Это по коду не видно. Там вызывается некий метод process который может тоже делать хз что, а компилятор может видеть и этот код и принимать свои решения.
Ты начал юлить, что мол, переменная должна быть не глобальная, и функция должна быть что надо функция. Т.е. начал по ходу сочинять дополнительные ограничения и условия когда твой говнокод может хоть как-то вменяемо заработать.
В-третьих, мы обсуждаем гарантии. А следовательно все эти твои условия и ограничения должны быть чётко формализованы и надёжно проверены автоматическими средствами. Иначе это всё болтовня.
_>>>Вред от оптимизации возможен в некорректном коде — в котором есть потребность в модификаторе volatile, но при этом его почему-то не поставили. Однако ни в одном из обсуждаем в данной темке примеров такого кода не было. _>·> Это не вред от оптимизации. Это вред от некорректного кода. _>Однако при отключённой оптимизации он вдруг резко станет корректным.
Не станет, конечно.
_>Более того, в далёком прошлом (до возникновения оптимизаторов и volatile) код только так и писали.
Нет, писали используя api соответствующих библиотек и операционок. Ну или просто говнокодили как ты.
_>>>·>Вызов функции это про sequence points, что никакого отношения к многопоточке не имеет. Или я не понял что ты имеешь в виду. _>>>А причём тут многопоточность или ещё что-то? Нам важно чтобы не были переставлены местам обращения к памяти и всё. Внутри тела одной функции ты можешь это отследить сам, а внутрь других функций (вызываемых из твоей) тебе лазить не надо, т.к. за этим следит компилятор. _>·>И как seq-points от этого помогут? Оптимизатор, особенно в WPO, может такое наворотить. _>Интересно конечно. Я тебе подробно объясняю как оно работает и ты прямо в ответ спрашиваешь "а как оно поможет?". Феерично просто. Разберись уже сам, что такое compiler barrier и что может служить причиной его появления (помимо atomic_signal_fence).
Я давно разобрался. Помогу и тебе:
A compiler fence (by itself, without a CPU fence) is only useful in two situations:
* To enforce memory order constraints between a single thread and asynchronous interrupt handler bound to that same thread (such as a signal handler).
* To enforce memory order constraints between multiple threads when it is guaranteed that every thread will execute on the same CPU core.
In other words, the application will only run on single core systems, or the application takes special measures (through processor affinity) to ensure that every thread which shares the data is bound to the same core.
Теперь объясни причём тут это, когда мы обсуждаем многопоточку?
_>·>Возможно ты "выкидывать инструкции" понял слишком буквально. Очевидно, CPU не может "выкидывать", т.к. не умеет править загруженные exe-шники. Он просто может не модифицировать память так, что другие ядра увидят модификации. Что по сути означает, что инструкция "ничего не делает". _>И с чего это по твоему он будет не модифицировать память (под памятью в данном случае подразумевается естественно общий кэш
Во-первых, это никак не "естественно". volatile и т.п. на общесть кеша никак не влияет.
Во-вторых, не очень понятно что за общий кеш ты имеешь в виду? L1/L2/L3 — они все не общие между всеми ядрами. Который ты имеешь в виду?
_>, т.к. пересылка данных между ним и физической памятью уже не принципиальна для нашей дискуссии)? Какой конкретно процессор и в каких условиях так делает? Можно увидеть соответствующую документацию?
Я тебе это уже разжёвывал.
Во-первых, об этом явно говорится в стандарте языка, что когда и как должно происходить. И, очевидно, если не сказано, то может не происходить _в общем случае_. Тот факт, что современные железки работают определённым образом никакого отношения к яп не имеет.
Во-вторых, если тебе интересно, покопай сам в сторону Non-coherent Cache Architectures, пару имён я тут уже называл.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай