Стандарт C ++ даже не требует, чтобы вызовы функций были реализованы с использованием стека (или чтобы у потоков был стек в этом смысле).
В текущем черновике C ++ говорится об перекрывающихся объектах :
Два объекта с перекрывающимися временами жизни, которые не являются битовыми полями, могут иметь один и тот же адрес, если один вложен в другой, или если хотя бы один является подобъектом нулевого размера, и они имеют разныетипы;в противном случае они имеют разные адреса и занимают непересекающиеся байты памяти.
И в (ненормативной) сноске:
В соответствии с правилом «как будто»Реализация позволяет хранить два объекта по одному и тому же машинному адресу или не сохранять объект вообще, если программа не может наблюдать разницу ([ intro.execution ]).
Inв вашем примере, я не думаю, что потоки синхронизируются должным образом, как, вероятно, предполагалось, поэтому время жизни объектов integer
не обязательно перекрывается, поэтому оба объекта могут быть помещены по одному адресу.
Если кодбыли исправлены для правильной синхронизации и foo
были вручную встроены в bar
, таким образом, что объект integer
все еще существует, когда его адрес печатается, тогда должно быть два объекта, выделенных по разным адресам, потому что различие является наблюдаемым.
Тем не менее, ничего из этого не говорит о том, можно ли реализовать в C ++ стековые сопрограммы безпомощь компилятора.Реальные компиляторы делают предположения о среде выполнения, которые не отражены в стандарте C ++ и подразумеваются только стандартами ABI.Особенно актуальным для сопрограмм с переключением стека является тот факт, что адрес дескриптора потока и локальных переменных потока не изменяется во время выполнения функции (потому что они могут быть дорогостоящими для вычисления, и компилятор выдает код для кэширования их в регистрах или настек).
Вот что может произойти:
Сопрограмма работает в потоке A и обращается к errno
.
Сопрограмма приостановлена из нити А.
Сопрограмма возобновляется в потоке B.
Доступ к сопрограммам errno
снова.
В этот момент поток B получит доступ к значению errno
потока A, который вполне может делать с ним что-то совершенно другое.
Этой проблемы можно избежать, если сопрограммавозобновляется только в том же потоке, в котором он был приостановлен, что является весьма ограничительным и, вероятно, не совсем тем, что имеет в виду большинство авторов сопрограммных библиотек.Хуже всего то, что возобновление в неправильном потоке, вероятно, работает, в большинстве случаев, потому что некоторые широко используемые локальные переменные потока (такие как errno
), которые не совсем локальны для потока, не сразу приводят к очевиднымглючные программы.