jcxz 184 13 июня, 2020 Опубликовано 13 июня, 2020 · Жалоба 1 час назад, amaora сказал: А чтобы заставить компилятор прочитать значение глобальной переменной снова, а не кэшировать в регистрах, достаточно вызвать функцию имеющую side effect. И как же узнать когда это сделать, если выполняющийся процесс не знает когда его прервали? Когда и как он будет вызывать эту функцию? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 20 13 июня, 2020 Опубликовано 13 июня, 2020 · Жалоба 9 hours ago, jcxz said: И как же узнать когда это сделать, если выполняющийся процесс не знает когда его прервали? Когда и как он будет вызывать эту функцию? Обычная синхронизация на флажках, все так же только не надо volatile. int ready_flag; // no volatile, only atomic read/write event_handler() { read(data); memcpy(buf, data, ...); fence(); ready_flag = 1; } main() { do { if (ready_flag == 1) { do_something(buf); ready_flag = 0; req_new_event(&event_handler); } // ... //fence(); os_wait(1); // it has memory fence inside } while (1); } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 3 часа назад, amaora сказал: Обычная синхронизация на флажках, все так же только не надо volatile. int ready_flag; // no volatile, only atomic read/write event_handler() { read(data); memcpy(buf, data, ...); fence(); ready_flag = 1; } main() { do { if (ready_flag == 1) { do_something(buf); ready_flag = 0; req_new_event(&event_handler); } // ... //fence(); os_wait(1); // it has memory fence inside } while (1); } Если у Вас fence() запрещает события, вызывающие event_handler(), а req_new_event() - снова их разрешает, то возможно так и будет работать. При ещё дополнительном условии: что компилятор считает req_new_event() как имеющую side effects (что совсем не обязательно). Но это упрощённый случай, когда нет одновременной работы двух процессов, а они работают поочередно. Да и то если компилятор посчитает req_new_event() имеющей side effects. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 20 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 9 hours ago, jcxz said: Если у Вас fence() запрещает события, вызывающие event_handler(), а req_new_event() - снова их разрешает, то возможно так и будет работать. При ещё дополнительном условии: что компилятор считает req_new_event() как имеющую side effects (что совсем не обязательно). Но это упрощённый случай, когда нет одновременной работы двух процессов, а они работают поочередно. Да и то если компилятор посчитает req_new_event() имеющей side effects. В простом случае, когда достаточно барьера компилятора (процессор не может менять порядок записи в память), fence() ничего не будет делать, ни одной инструкции не будет сгенерировано, это только указание компилятору. Хотя есть вариант и с пустой функцией из отдельного модуля компиляции, если LTO не включена. Событие подразумевалось разовое, повторный вызов обработчика будет только если снова настроить событие вызвав req_new_event(). Можно и FIFO сделать в конфигурации 1-читатель 1-писатель, для этого будет достаточно операции атомарной записи int и fence(). В SMP системе fence() будет содержать инструкции барьера, чтобы остальные процессоры увидели изменения в заданном порядке. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 22 минуты назад, amaora сказал: Событие подразумевалось разовое, повторный вызов обработчика будет только если снова настроить событие вызвав req_new_event(). Можно и FIFO сделать в конфигурации 1-читатель 1-писатель, для этого будет достаточно операции атомарной записи int и fence(). И что мешает оптимизатору перенести ready_flag в регистр внутри main()? И всё перестанет работать, хоть разовое событие хоть многоразовое. Никакие инструкции барьеров от этого не спасут. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 20 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 1 minute ago, jcxz said: И что мешает оптимизатору перенести ready_flag в регистр внутри main()? И всё перестанет работать, хоть разовое событие хоть многоразовое. Никакие инструкции барьеров от этого не спасут. В цикле в main есть fence() или, что более естественно там есть некий os_wait() которые даёт аналогичный результат. Для компилятора вызов os_wait() имеет побочный эффект, а значит от должен сделать все записи глобальных переменных до вызова и прочитать все снова после него. Всегда так делаю. Надо только помнить, что если при компиляции включена LTO, то компилятор может очень глубоко (до дна) заглянуть в os_wait() и сделать какие-то выводы. Лучше заранее убедиться, что там внутри есть аналог fence(), какой-то inline asm с указанием на изменение памяти, например. Либо это отдельный модуль компиляции. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexRayne 7 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба А почему Вам не нравится волатил? Это ж хорошее слово - оно привлекает внимание, заставляет програмиста напрячся, аккуратнее с пересенной обращаться. Оно гарантирует вам что вы запишете в переменную именно точто вычислили, именно столько раз сколько попросили. Это уже само по себе снимает кучу вопросов, если ращматывать сбой начнете. Если есть неуверенность в гарантиях кода, волатиль хоть чтото гарантирует. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 16 минут назад, amaora сказал: В цикле в main есть fence() или, что более естественно там есть некий os_wait() которые даёт аналогичный результат. Для компилятора вызов os_wait() имеет побочный эффект, а значит от должен сделать все записи глобальных переменных до вызова и прочитать все снова после него. Всегда так делаю. Ваш вариант работать будет только если в req_new_event() есть volatile-доступы к памяти. Иначе - компилятор запросто может ready_flag = 0 (саму запись в память) перенести в позицию после req_new_event() . Событие (и прерывание) будет возникать сразу после req_new_event() и последующее за ним ready_flag = 0 будет сносить нафиг флаг. И никакой fence() или os_wait() ему тут не помешает. Не в них дело. 9 минут назад, AlexRayne сказал: А почему Вам не нравится волатил? Это ж хорошее слово - оно привлекает внимание, заставляет програмиста напрячся, аккуратнее с пересенной обращаться. Оно гарантирует вам что вы запишете в переменную именно точто вычислили, именно столько раз сколько попросили. volatile там у amaora есть. Должен быть. По-крайней мере - для доступа к каким-то переменным внутри приведённых в коде функций (он же пишет про side effects - почитайте внимательнее). Иначе (если его вообще нигде нет), работать вообще не будет. Но вышеприведённый код будет работоспособным только если volatile (side effects) есть внутри req_new_event(). А это ничем не лучше, чем просто поставить модификатор volatile самой переменной ready_flag. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 20 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 4 minutes ago, jcxz said: Ваш вариант работать будет только если в req_new_event() есть volatile-доступы к памяти. Иначе - компилятор запросто может ready_flag = 0 (саму запись в память) перенести в позицию после req_new_event() . Событие (и прерывание) будет возникать сразу после req_new_event() и последующее за ним ready_flag = 0 будет сносить нафиг флаг. И никакой fence() или os_wait() ему тут не помешает. Не в них дело. Так они там и есть, это же работа с регистрами IO чтобы включить событие. А даже если нет, то ещё один fence() так же можно поставить (если им и без того не является сам req_new_event), чтобы запись 0 не перенеслась. 12 minutes ago, AlexRayne said: А почему Вам не нравится волатил? Это ж хорошее слово - оно привлекает внимание, заставляет програмиста напрячся, аккуратнее с пересенной обращаться. Оно гарантирует вам что вы запишете в переменную именно точто вычислили, именно столько раз сколько попросили. Это уже само по себе снимает кучу вопросов, если ращматывать сбой начнете. Если есть неуверенность в гарантиях кода, волатиль хоть чтото гарантирует. Это слово не вполне выражает то, что хотят им сказать в задачах синхронизации. В некоторых случаях порождает лишний код множественной записи-чтения там где этого не нужно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 18 минут назад, amaora сказал: Так они там и есть, это же работа с регистрами IO чтобы включить событие. А даже если нет, то ещё один fence() так же можно поставить (если им и без того не является сам req_new_event), чтобы запись 0 не перенеслась. Ну тогда Вы неявно сделали то же самое, что если просто поставить volatile для ready_flag - код точно такой же будет. Тогда непонятно - почему бы его просто не поставить и всё? 20 минут назад, amaora сказал: В некоторых случаях порождает лишний код множественной записи-чтения там где этого не нужно. В Вашем примере не будет никаких множественных записей чтений. А если ещё ready_flag = 0; поставить сразу после проверки if (ready_flag == 1), то это потенциально ещё уменьшит количество загрузок адреса (например для ARM-систем) на одну. Так как не надо будет дважды вычислять адрес ready_flag. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 20 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 11 minutes ago, jcxz said: Ну тогда Вы неявно сделали то же самое, что если просто поставить volatile для ready_flag - код точно такой же будет. Тогда непонятно - почему бы его просто не поставить и всё? В Вашем примере не будет никаких множественных записей чтений. А если ещё ready_flag = 0; поставить сразу после проверки if (ready_flag == 1), то это потенциально ещё уменьшит количество загрузок адреса (например для ARM-систем) на одну. Так как не надо будет дважды вычислять адрес ready_flag. ready_flag это на одном уровне кода (про который говорим), а работа с регистрами на другом (там volatile почти всегда к месту), не надо все в одно мешать. Сделаете тогда может быть все объявления типов с volatile? С флажком самый простой пример, чтобы показать что без и volatile можно сделать то же самое, причём часто все fence() уже есть в коде. Множественные ненужные чтения получаются когда volatile прилепляют ко всему, по методике "если доступ из разных потоков то надо volatile". Вот это я и считаю неверным, а не само использование volatile. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 184 14 июня, 2020 Опубликовано 14 июня, 2020 · Жалоба 5 часов назад, amaora сказал: ready_flag это на одном уровне кода (про который говорим), а работа с регистрами на другом (там volatile почти всегда к месту), не надо все в одно мешать. Сделаете тогда может быть все объявления типов с volatile? Не надо передёргивать! Я говорю лишь о том, что добавление volatile к ready_flag в вашем примере, никак не повлияет на код (не увеличит количество записей/чтений). Но при этом в то же время не нужно думать - а есть ли volatile внутри соседних функций или нет? Так что никакого смысла убирать volatile из ready_flag нет. Плюсов это не даёт никаких. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться