Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: __LDREX __STREX в STM32F407
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2, 3
jcxz
Цитата(Forger @ Jun 9 2017, 23:20) *
Вычитали один указатель (положили например в R1), произошло прерывание, изменило значение указателя.
Вернулись, присвоили другому указателю старое значение, которое положили до этого в R1.

Ничего не понял.... wacko.gif
Какие "один" и "другой" указатель? Что за "старое" значение? Какой R1?
Я же просил - покажите на приведённом мной примере где, в каком месте прерывание (или что там ещё) нарушит его работу???
Forger
Цитата(jcxz @ Jun 10 2017, 00:29) *
Ничего не понял.... wacko.gif
Вот именно! Один про Фому, а второй - про Ерёму ))

Цитата
Я же просил - покажите на приведённом мной примере где, в каком месте прерывание (или что там ещё) нарушит его работу???

Например, внутри flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью.
jcxz
Цитата(Forger @ Jun 9 2017, 23:27) *
Я ж говорю, ни при чем тут кольцо как таковое, речь про ситуации исчерпания и переполнения буфера, которые обращаются к обоим указателям.

Может хватит наконец-то пустословия? Покажите на моём примере ГДЕ ПРОБЛЕМА???

Цитата(Forger @ Jun 9 2017, 23:27) *
Очень убедительный довод. Оказывается, что кольцо еще и использовать нужно как-то по хитрому, чтобы оно правильно работало sm.gif
Нафик тогда нужно такое кольцо, вокруг которого нужно плясать с бубном?

Прочитайте выше ещё раз про отвёртку.
Или нафик такая отвёртка, которой ухо сломать можно? biggrin.gif biggrin.gif biggrin.gif biggrin.gif

Цитата(Forger @ Jun 9 2017, 23:32) *
Например, внутри flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью.

Произойдёт прерывание, которое внутри вызовет write()? И что? Это никак не нарушит кольцо. Подумайте. rolleyes.gif
AVI-crak
Цитата(Forger @ Jun 9 2017, 22:27) *
Поменять шило на мыло?
По вашей логике получится, что пока этот приоритетный SVC не отработает, ни одно более важное аппаратное прерывание не пройдет.
SVC с таким высоким приоритетом, вызываемые из других менее приоритетных прерываний по сути временно задирает их собственный приоритет, который был "ниже плинтуса", отбирая "право голоса" у более приоритетных прерываний.
На лицо - инверсия приоритетов. С чем боролись на то и напоролись. Ни чем не лучше глобальной критической секции (запрет/разрешение всех прерываний).

А должно быть совсем иначе - срочные и архиважные аппаратные прерывания должны отработаться предельно быстро, куда надо просемафорить и тут же освободить процессор.

Цель - сохранить стек без использования мази от геморроя.
Запреты прерываний - вот настоящий геморрой.

Насчёт кольцевого буфера (знатный срачь):
На сам буфер натравлено два указателя, или лучше два смещения. Один для приёмника другой для передатчика.
Всё что между приёмником и передатчиком - читает приёмник, как прочитает - переписывает приёмник.
Всё что перед передатчиком и позади приёмника - пишет передатчик, когда записал корректно - переписал передатчик.
Когда приёмник с передатчиком складывается - читать нечего.
Когда передатчик догоняет приёмник на расстояние одного символа - место для записи заканчивается. Это тупо означает что записывать в буфер нельзя, процесс за это отвечающий - должен ожидать.

Складывается ощущение, что не каждый понимает о чём тут разговор. Но читать приятно.
jcxz
Цитата(AVI-crak @ Jun 10 2017, 00:12) *
Насчёт кольцевого буфера (знатный срачь):
...
Складывается ощущение, что не каждый понимает о чём тут разговор. Но читать приятно.

Может быть Вы укажете, где именно проблема в приведённом мной примере реализации кольцевого буфера?
А то Forger видимо знает, но держит эту тайну при себе biggrin.gif
AVI-crak
Цитата(jcxz @ Jun 10 2017, 04:28) *
Может быть Вы укажете, где именно проблема в приведённом мной примере реализации кольцевого буфера?
А то Forger видимо знает, но держит эту тайну при себе biggrin.gif

Он считает что на буфер указывает всего один указатель, и при этом не совсем верно понимает слово "кольцевой".
Forger
Цитата(jcxz @ Jun 10 2017, 00:35) *
Произойдёт прерывание, которое внутри вызовет write()? И что? Это никак не нарушит кольцо. Подумайте. rolleyes.gif

Мля, да при чем тут некий write?
Еще раз - проблема в тех кусках кода, где производится обращение к обоим указателям, а не только к одному,
Обращение к обоим указателям должно быть атомарным. К одному - нет.
Если в функциях обращения к одному указателю производится обращение и ко второму (сравниваем оба указателя для контроля переполнение/исчерпание), то этот участок тоже должен быть атомарен.
Разумеется, в данном случае речь идет про атомарность в функциях, который вызываются внутри задач, а не прерываний.
Если же делать общее кольцо про все случаи жизни, то защищать приходится все места, где производится сравнение и тем более модификация сразу обоих указателей.

Например, ваш flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью.
Поэтому эта flush должна быть атомарна, т.к. работает с двумя указателями сразу.
Атомарна в данном случае означает, что тот, кто может прервать ее не должен обращаться к этим указателям, пока не отработает flush.
Вот тут про это есть.
см. USART_FlushTxBuf
Ну и в комментариях некто указал про то, что у тут пытаюсь донести до остальных.
Ищите по поиску на той же странице "Код содержит не меньше одной ошибки. Нет гарантии атомарности изменения переменых."

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


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

Цитата(jcxz)
А то Forger видимо знает, но держит эту тайну при себе

Поясняющие картинки рисовать уже не буду. И так уже по третьему кругу объясняю.
jcxz
Цитата(Forger @ Jun 10 2017, 08:27) *
Мля, да при чем тут некий write?

А что при чём?
Я Вас уже 100500-й раз тут прошу:
Приведите пример (кодом) ситуации, при которой по Вашему будет косяк.
Уже даже и не знаю как сказать... wacko.gif
Вот так вот прямо - возьмите мой код, скопипастите сюда, и вставьте в него код (с комментом) в какой-то точке, который вызовет сбой.
Вы вообще к программированию отношение имеете? Если имеете - должны уметь читать и понимать код. Как и все тут вроде-бы. Не надо этих пространных полотенец рассуждений - по ним совершенно не понятно, что именно Вас смущает. Я специально написал конкретный простейший код кольцевого буфера, чтобы, как мне казалось, любой человек знающий си и умеющий читать, мог понять его работу.
Проблему в кольцевых буферах здесь видите один только Вы.
Ещё раз подчеркну: Корректно реализованный кольцевой буфер, отвечающим условиям изложенным мной в том сообщении, где я привёл его код, не требует дополнительных блокировок, запретов прерываний и т.п. НИГДЕ!!!
Даже в том случае (а это самый сложный случай) если пишущий процесс выполняется на одном процессоре, а читающий - на другом. Единственное дополнительное условие при этом - объект Ring должен находиться в некешеруемом регионе ОЗУ (некешеруемом в обоих процессорах). В этом случае никакие блокировки одного процессора другим как правило и невозможны.

Цитата(Forger @ Jun 10 2017, 08:27) *
Еще раз - проблема в тех кусках кода, где производится обращение к обоим указателям, а не только к одному,

Ещё раз - какие "те"?
Я в классе Ring реализовал все методы необходимые для работы с кольцевым буфером - про какой из них Вы говорите??? Мы здесь не телепаты, не можем понять что такое "те" куски кода, а что такое "не те".

Цитата(Forger @ Jun 10 2017, 08:27) *
Обращение к обоим указателям должно быть атомарным. К одному - нет.

Я подозреваю Вы не понимаете понятие "атомарность".... laughing.gif

Цитата(Forger @ Jun 10 2017, 08:27) *
Если же делать общее кольцо про все случаи жизни, то защищать приходится все места, где производится сравнение и тем более модификация сразу обоих указателей.

Не надо ничего защищать. Всё уже защищено алгоритмом моего Ring. Да хоть на разных процах вызывайте write() и read() - всё будет ок, хоть эти процессоры выполняют свои команды совершенно асинхронно.

Цитата(Forger @ Jun 10 2017, 08:27) *
Например, ваш flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью.

Ещё 100500-й раз: Какой операции чтения-модификации-запись?
Вы понимаете смысл ключевого слова "class" языка си? А понятие прав "public", "private" класса Вам знакомо?
Ещё и ещё раз прочитайте мой код: я не зря их там поставил. Т.е. - для работы с объектом класса можно пользоваться только его методами. Среди методов есть модифицирующие, а есть readonly-методы.
Какой именно из них Вас не устраивает?
flush() туда тоже можно добавить и к нему будут относиться все ограничения указанные для других:
из пишущего процесса можно вызывать только функции, меняющие указатель wpos;
из читающего процесса можно вызывать только функции, меняющие указатель rpos.
flush() какой указатель меняет?
readonly-методы можно вызывать из любого процесса, читающего или пишущего, без ограничений.

Цитата(Forger @ Jun 10 2017, 08:27) *
см. USART_FlushTxBuf
Ну и в комментариях некто указал про то, что у тут пытаюсь донести до остальных.
Ищите по поиску на той же странице "Код содержит не меньше одной ошибки. Нет гарантии атомарности изменения переменых."

И при чём тут чей-то говнокод? Очевидно, что человек написавший его, совершенно не понимает принципов многозадачной работы. Это видно уже сразу в самом начале.
Я же привёл Вам свой пример кода, абсолютно корректный и не требующий блокировок.
Моя реализация не имеет с тем говнокодом абсолютно ничего общего. Если Вы конечно умеете читать и понимать си-код... laughing.gif
Если видите в моём коде проблему - укажите где? Кодом пожалуйста.
Forger
Цитата(jcxz)
...

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


зы. Ring::flush() { rpos = wpos; } неатомарна, после чтения wpos, может произойти прерывание, где может измениться wpos или rpos и вернуться назад.
По атомарностью в данном контексте я имею ввиду, непрерывность выполняемого кода, т.е. код, который не может прервать другой код, обращающийся к этим же данным (запись или чтение).

Цитата(jcxz)
Вы понимаете смысл ключевого слова "class" языка си? А понятие прав "public", "private" класса Вам знакомо?

Предлагаю избегать оскорблений подобного рода. Мы тут не впервые сремся общаемся.
jcxz
Цитата(Forger @ Jun 10 2017, 10:09) *
зы. Ring::flush() { rpos = wpos; } неатомарна, после чтения wpos, может произойти прерывание, где может измениться wpos или rpos и вернуться назад.

А Вы извините читать вообще умеете? Или "чукча не читатель"? biggrin.gif
Прочитайте это ещё раз и ещё и ещё много раз до полного просветления. Я это уже многократно повторил.
Цитата(jcxz @ Jun 10 2017, 10:07) *
из пишущего процесса можно вызывать только функции, меняющие указатель wpos;
из читающего процесса можно вызывать только функции, меняющие указатель rpos.

Ваша ситуация - невозможна.
Ибо ТОЛЬКО ОДИН процесс должен менять rpos. А Вы пишете про его модификацию В ДВУХ процессах.

Цитата(Forger @ Jun 10 2017, 10:09) *
Предлагаю избегать оскорблений подобного рода. Мы тут не впервые сремся общаемся.

Это не оскорбление, а констатация факта. Так как я явно в реализации класса указал, что с ним могут работать только его методы. А вы пишете про какие-то другие модификации-записи.
К его методам можно добавить и flush().
Forger
Цитата(jcxz @ Jun 10 2017, 11:15) *
Прочитайте это ещё раз и ещё и ещё много раз до полного просветления. Я это уже многократно повторил.

Аналогично адресую это Вам.
У меня класс Queue сделан совершенно аналогично вашему (у меня он вообще шаблон).
Раньше там не было ни каких критических секций.
Я напоролся на это в одном из проектов. Очень долго искал косяк. Проклял все на свете.
Но к счастью нашел. Исправил - прерывания запрещаются на время анализа исчерпания/переполнения и в этой самой flush.
Прерывания запрещаются лишь одно - то, которое обращается в этой очереди.
Проблема сразу ушла.

Вот под руками есть старая реализация этой Queue из одного из проектов, тут критические секции добавлены в лоб:
CODE

template <class Item, unsigned int SIZE>
class Queue
{
public:
Queue(void) : head(0), tail(0), freeSize(SIZE) { }

class CriticalSection // No nested, but very fast - 3 CPU cycles !!!
{
public:
inline CriticalSection() __attribute__((always_inline)) { __disable_irq(); }
inline ~CriticalSection() __attribute__((always_inline)) { __enable_irq(); }
};

void clear(void)
{
CriticalSection criticalSection;
head = 0;
tail = 0;
freeSize = SIZE;
}

bool isPutDone(const Item & item)
{
CriticalSection criticalSection;
if (freeSize == 0) return (false);
queue[head] = item;
head = head + 1;
if (head >= SIZE) head = 0;
freeSize = freeSize - 1;
return (true);
}

bool isGetDone(Item & item)
{
CriticalSection criticalSection;
if (freeSize >= SIZE) return (false);
item = queue[tail];
tail = tail + 1;
if (tail >= SIZE) tail = 0;
freeSize = freeSize + 1;
return (true);
}

void tryToGet(Item & item) { isGetDone(item); }

private:
Item queue[SIZE];
volatile unsigned int head;
volatile unsigned int tail;
volatile unsigned int freeSize;
};


Цитата(jcxz @ Jun 10 2017, 11:24) *
Ибо ТОЛЬКО ОДИН процесс должен менять rpos. А Вы пишете про его модификацию В ДВУХ процессах.

Тут не все так просто. Речь не просто про изменение (запись), а про обращение (чтение). Это очень важно!
Если бы было просто rpos = некая константа, то проблем нет, но у вас rpos=wpos, т.е. обращение происоходит к ОБОИМ индексам/указателям.
Этот процесс копирования нельзя прерывавать тем, кто может обращаться к этим же данным.
Это букварные истины ))
jcxz
Цитата(Forger @ Jun 10 2017, 10:29) *
Вот под руками есть старая реализация этой Queue из одного из проектов, тут критические секции добавлены в

Да, Ваша реализация требует критической секции. Потому что у Вас в методах изменяются сразу два члена Queue.
Моя реализация - не требует, так как в каждом методе изменяется только один член.
Странно что Вы не видите отличий в Вашей и моей реализации..... laughing.gif
Это ключевое различие. Если Вы его наконец-то поймёте, то сможете писать корректное многозадачное ПО.

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

PS: Признаю что погорячился насчёт вызова readonly-методов из третьего процесса - это в моей реализации недопустимо. Допустимы вызовы только из двух процессов: пишущего в Ring и читающего в Ring.
AVI-crak
Разве ерорная ситуация не должна обрабатываться системными вызовами? С закрытием потока и всех его хвостов... И кстати, получить сбой на кольцевом буфере - это очень сильно постараться нужно, прямо таки саботаж устроить.
Forger
Цитата(jcxz @ Jun 10 2017, 11:36) *
Признаю что погорячился насчёт вызова readonly-методов из третьего процесса - это в моей реализации недопустимо. Допустимы вызовы только из двух процессов: пишущего в Ring и читающего в Ring.

АЛЛИЛУЙЯ!!! Наконец-то мои слова начинают доходить!

Цитата
Да, Ваша реализация требует критической секции. Потому что у Вас в методах изменяются сразу два члена Queue.

Любая реализация требует подобных действий, если она должна контролировать исчерпание/переполнение буфера.
У вас free и cnt - отдельные функции, у меня - входят в состав соотв. методов записи/чтения очереди.
Т. к. контроль переполнения нужно делать перед тем, как что класть в очередь и соотв. контроль исчерпания перед тем, к брать данные из очереди.
То эти функции всегда нужно вызывать перед тем, как обращаться к очереди.
А функции должны быть атомарными, т. к. обращаются к обоим указателям.
Я привел старую реализацию, а в нынешней у меня защищены только части, где производится обращение (неважно что - запись или чтение) в ОБОИМ указателям.
Причем, запрещается прерывание от владельца этой очереди только там, где оно вызывается в фоне.

Цитата
Моя реализация - не требует, так как в каждом методе изменяется только один член.

Если никогда не вызывать cnt и free в коде, то это действительно так. С этим я и не спорил.

Цитата
Странно что Вы не видите отличий в Вашей и моей реализации..... laughing.gif Это ключевое различие.

Странно другое - вы упорно не слышите меня, но судя по всему, лед тронулся sm.gif


Цитата
Если Вы его наконец-то поймёте, то сможете писать корректное многозадачное ПО.

Мне не нравится ваша манера вести диалог подобным образом, поэтому настоятельно прошу избегать при общении со мной подобных выражений!

Цитата
И Вы похоже не поняли почему Ваша реализация требует критической секции.

Вот я как раз понимаю прекрасно.
В этой старой реализации это было сделано в лоб. Задача была решена.
В нынешней реализации это сделано менее "топорно".

Цитата
В моих методах в каждом изменяется не более одного члена-указателя, а значит их модификации атомарны.

Это неправда - в методах free и cnt обращение производится к обоим указателям. По сути оба этих метода должны целиком находится в критических секциях.
И я уже повторял, что не имеет значения - модификация (запись) или обращение (чтение).

Важно, чтобы этот процесс обращения (чтение или запись) к ОБОИМ указателям был неразрывен для прерывания, которое может обращаться к любому из этих указателей!
Все остальное - действительно не требует никаких критических секций и т.п.
jcxz
Цитата(Forger @ Jun 10 2017, 13:18) *
Если никогда не вызывать cnt и free в коде, то это действительно так. С этим я и не спорил.
...
Важно, чтобы этот процесс обращения (чтение или запись) к ОБОИМ указателям был неразрывен для прерывания, которое может обращаться к любому из этих указателей!

Нет и нет. Вы так ничего и не поняли. Живёте на какой-то своей волне. Все мои просьбы указать конкретное место где в моих функциях будет сбой и при каком конкретно событии - Вы проигнорировали. Другие аргументы - тоже игнорируете. И продолжаете нести бред...
Из чего я делаю вывод: Вы не способны понять работу кольцевого буфера. sad.gif
Любой, давший себе хоть немного труда подумать, уже всё понял. Но вы даже не пытаетесь понять, вот в чём беда...
Что-ж - может когда-нить до Вас дойдёт.
В данное время считаю дальнейшие попытки что-то Вам объяснить бесполезными - лепите свои критические секции хоть в каждой строке - Вы не способны обучаться... laughing.gif
Forger
Цитата(jcxz @ Jun 10 2017, 18:42) *
...продолжаете нести бред... Вы не способны понять ... может когда-нить до Вас дойдёт... Вы не способны обучаться...

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


Последняя попытка.
Если в кольцевом буфере никогда нет переполнений и исчерпаний или это вообще не контроллируется и не влияет на логику выполнения кода,
то можно всем этим не заморачиваться, т. е. никакие критические участки кода уже ни к чему.

Но как только в коде производится вычисление разницы между значениями ОБОИХ указателей и этот результат влияет на ход работы программы, а не только пишет логи,
то этот участок кода, где производится чтение ОБОИХ указателей должен быть атомарным для того, кто может обращаться к одному из этих указателей и тем более его изменить.
Это касается не только кольцевых буферов, а вообще любых асинхронных действий.



AHTOXA
Цитата(Forger @ Jun 10 2017, 20:48) *
участок кода, где производится чтение ОБОИХ указателей должен быть атомарным для того, кто может обращаться к одному из этих указателей и тем более его изменить.


Попробую и я объяснить вамsm.gif

1. Есть два указателя - put и get. При их равенстве - буфер пуст. При put+1 == get - буфер полон.
2. Запись производится только в одном потоке, чтение - только в другом.
3. Записывающий поток перед записью проверяет наличие свободного места. Для этого он считывает в промежуточные переменные оба указателя, затем производит сравнение. Если свободное место есть, то всё в порядке - поток производит запись, увеличивает промежуточную переменную, содержащую указатель на позицию записи, и записывает её в put. Если записывающий поток в любом месте этой процедуры будет прерван читающим потоком, то это не собьёт алгоритм. Если он будет прерван до считывания значения get в промежуточную переменную, то поток будет иметь дело с уже обновлённой переменной, если нет - то с предыдущим её значением. В любом случае запись отработает корректно.
4. Читающий поток поступает так же - перед считыванием копирует put и get во временные переменные, производит проверки, если буфер не пуст - считывает элемент, модифицирует временную переменную, содержащую указатель на чтение и записывает её в get. Если в любом месте этой процедуры читающий поток будет прерван записывающим потоком, то это не нарушит логики операции. Возможны два варианта - во временную переменную, содержащую позицию записи, будет считано либо значение put, либо put+1. В обоих случаях чтение пройдёт корректно, будет считан нужный элемент (или не считан, если буфер пуст).

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

Надеюсь, я смог объяснить.
Forger
Цитата(AHTOXA @ Jun 10 2017, 21:39) *
Для этого он считывает в промежуточные переменные оба указателя,...
Если он будет прерван до считывания значения get в промежуточную переменную, то поток будет иметь дело с уже обновлённой переменной, если нет - то с предыдущим её значением.

Есть еще третий вариант - прерывание возникнет между чтениями этих двух указателей.
Ведь читаются они не одновременно, а поочередно.

Для примера, имеем 16-битный указатель на 8-битном проце (некий МК типа PIC или STM8).
Если прерывание, где используется и тем более изменяется значение этого указателя, возникнет между записями старшего и младшего байта этого 16-битного указателя в основном кода, то будет беда.
Это - очень простой пример, но он наглядно демонстрирует что я имею ввиду.

Цитата
Надеюсь, я смог объяснить.

Это все понятно, и спору нет. Об этом выше и сказал.
Я же говорю о ситуациях, когда прерывание возникает при чтении ОБОИХ указателей МЕЖДУ этими обращениями. Ведь это происходит не атомарно за один такт, а поочередно.
Однажды, я наступил на эти грабли и решил задачу в лоб - критическими секциями. Это, конечно, помогло, но потом я исправил и сделал это более аккуратно и деликатно.

В данном случае с кольцевым буфером (речь про 32-битные ARMы), можно избежать критических секций вообще, если оба указателя являются 16-битными индексами массива кольцевого буфера, но находятся в памяти рядом.
Т.е. можно их вычитать или даже записать одновременно, т. е. атомарно как целое 32-битное слово.
Фактически, атомарность доступа реализуется средствами самого ядра.
Вряд ли где-то на обычном МК понадобится буфер более чем на 65536 элементов, поэтому будет вполне достаточно 16-битных индексов вместо 32-битных указателей.

Простая задачка: на 8-битном МК имеем кольцевой буфер с 16-битными указателями (или индексами).
1) прямо внутри flush (пока изменяются указатели) возникнет прерывание и дописывает в буфер новые данные
2) процесс анализа оставшего места в буфере в основном коде прерван соотв. прерыванием, где в буфер докидываются новые данные.
В каких случаях следует беспокоится об атомарности доступа к этим указателям? И следует ли это делать вообще?
AVI-crak
Может на Си показать кусок кода?


CODE
#define buf_zize (127)
uint8_t _std_out_buffer[buf_zize];
uint8_t _std_in_buffer[buf_zize];
uint8_t m_mk_buf[buf_zize + 1];
#pragma pack(push, 4)
struct _stdout
{
const uint16_t length;
const volatile uint16_t tail;
volatile uint16_t head;
uint16_t mode;
volatile uint8_t *ptr;
}_eb_monitor_stdout ={buf_zize,0,0,1,&_std_out_buffer[0]};

struct _stdin
{
const uint16_t length;
volatile uint16_t tail;
const volatile uint16_t head;
uint16_t mode;
const volatile uint8_t *ptr;
}_eb_monitor_stdin ={buf_zize,0,0,1,&_std_in_buffer[0]};
#pragma pack(pop)

void monitor_fining (void)
{
uint16_t tmp = 0;
do
{
m_mk_buf[tmp++] = 0x20;
}while (tmp != buf_zize);
};


/// печать строки
void monitor_print (uint8_t* text)
{
uint16_t temp_t = 0;
if (text[temp_t] == '\0') return;
uint16_t temp_h = _eb_monitor_stdout.head;
uint16_t temp_l;
do
{
temp_l = temp_h; temp_h++;
if (temp_h == buf_zize) temp_h = 0;
if (_eb_monitor_stdout.tail != temp_h ) _eb_monitor_stdout.ptr[temp_h] = text[temp_t++];
else
{
_eb_monitor_stdout.head = temp_l;
while (_eb_monitor_stdout.tail != temp_l ) sTask_skip();// Delay(1000);// там занято
_eb_monitor_stdout.ptr[temp_h] = text[temp_t++];
_eb_monitor_stdout.head = temp_h;
}
}while (text[temp_t] != '\0');
_eb_monitor_stdout.head = temp_h; __DSB();
};

uint8_t *monitor_scan (uint8_t *text)
{
uint16_t temp_h = _eb_monitor_stdin.head;
uint16_t temp_l = _eb_monitor_stdin.tail;
uint16_t temp_x = 0;
if(temp_l == temp_h) { text[0] = 0; return text;};
while (temp_l != temp_h)
{
temp_l++; if (temp_l == buf_zize) temp_l = 0;
text[temp_x++] = _eb_monitor_stdin.ptr[temp_l];
}
_eb_monitor_stdin.tail = temp_l;
text[temp_x] = 0;
return text;
}


Это функции для печати в терминал через программатор. Глюков и зависаний не обнаружено, печатает отдельный процесс, в случае переполнения - остаётся в вечном ожидании, с последующим вытеснением. То-есть программа на мк не падает в случае автономного исполнения.
Оба указателя читаются, и участвуют в управлении - но писать можно только один. Второй указатель переписывает отладчик. То самое в структуре под "const volatile" - меняется сторонним процессом.
Инициализация один раз, при старте.
Есть варианты с динамическим запуском в произвольном месте, но для терминала через программатор - получается слишком сложно для примера.
AHTOXA
Цитата(Forger @ Jun 11 2017, 00:19) *
Есть еще третий вариант - прерывание возникнет между чтениями этих двух указателей.
Ведь читаются они не одновременно, а поочередно.

Это без разницы. Потому что в этом прерывании возможны только транзакции пусто-не пусто для записывающего потока и не пусто-пусто - для читающего. А другой поток делает лишь проверку на пусто/не пусто (которая выливается в проверку на равенство).

Цитата(Forger @ Jun 11 2017, 00:19) *
Для примера, имеем 16-битный указатель на 8-битном проце (некий МК типа PIC или STM8).
Если прерывание, где используется и тем более изменяется значение этого указателя, возникнет между записями старшего и младшего байта этого 16-битного указателя в основном кода, то будет беда.

Да нет же, всё будет нормально.

Для записывающего потока: нет равенства [put+1 == get] - пишем. (Тут нас может прервать читающий поток и он может изменить ситуацию только в одном направлении - буфер полон -> буфер не полон. То есть [put+1 == get] => [put+1 != get]). Если буфер был не полон - то он и останется не полон, независимо от того, прервали нас или нет. Если буфер был полон, то тут зависит от того, успели мы считать указатель get целиком или нет. Если успели - мы посчитаем, что буфер полон, и записи не будет. Это корректное поведение. Если читающий поток успел модифицировать хотя бы один байт из get, то буфер перестанет быть полон, и мы с полным правом запишем туда байт. И это тоже корректно.

Для читающего потока: нет равенства put == get - читаем. Соответственно, тут наоборот, прервавший нас записывающий поток может изменить ситуацию только в направлении буфер пуст => буфер не пуст. То есть, [put == get] => [put != get]. Все рассуждения полностью аналогичны предыдущему случаю.

И совершенно неважно, какими порциями записываются эти put и get.

Вообще я удивлён. Вы вроде такой опытный по вашим словам разработчик, и не знаете этот широко известный приём. Он применялся ещё на MCS51, причём очень широко.
Forger
Цитата(AHTOXA @ Jun 10 2017, 22:54) *
Да нет же, всё будет нормально.
Вашими устами да мед пить sm.gif

AHTOXA
Цитата(Forger @ Jun 11 2017, 01:01) *
Вашими устами да мед пить sm.gif

Не буду оригинален: слив засчитан. Вы меня разочаровали.
Forger
Цитата(AHTOXA @ Jun 10 2017, 23:32) *
Не буду оригинален ...


Ваш пример в прошлом посте будет работать, пока в очереди всегда будет меньше чем 255 не отправленных элементов.

Предположим, что в очереди уже есть 255 еще не отправленных элементов (набивали очередь перед отправкой), т.е. put = 0x00FF, а в это время get = 0x0000
Мы собираемся класть туда 256-й элемент, т.е. указатель изменит сразу 9 бит из 16, изменятся оба байта в указателе, put должен стать 0x0100, но по-байтно

После изменения младшего байта put будет 0x0000 и тут возникло прерывание, мы видим, что [get == put], логично предполагаем, что буфер пуст, останавливаем передачу.
Выходим из прерывания.
Тут указатель put сразу же стал 0x0100 (записали старшее слово), мы наивно набиваем очередь дальше, думая, что передача продолжается.

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

Именно по этой причине для реализации длинных аппаратных таймеров (например, 16-битный на 8-битном камне) используются теневые регистры,
а обновление финального регистра таймера производится аппаратно только при записи лишь одного из байтов теневого регистра.
AHTOXA
Цитата(Forger @ Jun 11 2017, 11:06) *
...

Ну наконец-то, я смог вытащить из вас примерsm.gif
Да, вы правы, указанный кольцевой буфер работает лишь при условии атомарности изменений указателей на засовывание/выборку. То есть, на 8-битниках индекс до 255. Для 32-битниов ограничения нет.
Но это требование гораздо слабее того, что вы изначально утверждали:
Цитата(Forger @ Jun 10 2017, 11:27) *
Обращение к обоим указателям должно быть атомарным.

К каждому из указателей - да, атомарность нужна. К обоим - нет.
ViKo
Известен способ правильного чтения длинных счетчиков в малоразрядных процессорах - читаем старший байт, затем младший, затем опять старший, и если он не изменился - у нас правильное слово. Если старший байт изменился, значит, попали в момент переполнения младшего байта. Нужно провести все чтение еще раз.
Forger
Цитата(ViKo @ Jun 11 2017, 10:06) *
Нужно провести все чтение еще раз.
В итоге можно плотно зависнуть в таком цикле sm.gif


AHTOXA
Цитата(ViKo @ Jun 11 2017, 12:06) *
Известен способ правильного чтения длинных счетчиков в малоразрядных процессорах - читаем старший байт, затем младший, затем опять старший

Здесь это не поможет - здесь как раз чтение происходит в прерывании, и два чтения подряд дадут одинаковое значение.
Forger
Цитата(AHTOXA @ Jun 11 2017, 09:48) *
Но это требование гораздо слабее того, что вы изначально утверждали:

Это требование относилось конкретно к вашему примеру.

Цитата
К каждому из указателей - да,

Если указатель длиннее разрядности проца, то да. Иначе - нет.

Цитата
атомарность нужна. К обоим - нет.

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

Вот пример:

Пусть put = 10, get = 9, т.е. в очереди остался всего один еще не отправленный элемент.
Перед отправкой каждого элемента производится расчет свободного места в очереди посредством сравнения двух указателей и выполнения соотв. действий.
Предположим, произошло прерывание в отправляющей задаче сразу после копирования put и get в некие временные переменные, но проанализировать и принять решение еще не успели.

В прерывании смотрим наличие данных в буфере, сравнивая put и get, тут они не равны,
берем данные из буфера, изменяем указатель get (= 10), отправляем данные, выходим из прерывания
по некоторой причине управление долго не передавалось отправляющей задаче (работали другие более приоритетные задачи) и вновь возникло прерывание от передатчика,
в этом прерывании смотрим, что get == put и прекращаем передачу (прерываний от передатчика не будет, пока заново их не запустим) выходим из прерывания.
Наконец-то попадаем в отправляющую задачу.

В данный момент w = 10, get = 9. Т. е. буфер как бы непустой, смело докладываем новый элемент.
А так, как буфер был непустой, то запуск передачи не делаем (не формируем соотв. прерывание от передатчика).
В итоге кладем в буфер данные, надеясь, что они отправляются, а это не так.
По-хорошему, решение о запуске прерываний нужно делать повторно вычитывая put и get.
Но кто даст гарантию, что прерывание не возникнет после сравнения put/get но перед принятием решения и запуске прерываний от передатчика?

Т. е. проблема тут не в самих указателях, а в действиях, предпринимаемых для запуска/останова аппаратного передатчика.
Фактически, нужно делать атомарным (для прерывания от передатчика): чтение указателей, сравнение, анализ результата и управление передатчиком.



ViKo
Так запретите прерывание чтения, когда модифицируете счетчик записи. Проблема, не стоящая ломаного яйца.
AHTOXA
Цитата(Forger @ Jun 11 2017, 13:04) *
Это требование относилось конкретно к вашему примеру.

Это вы не со мной спорили. Я же специально сделал цитату со ссылкой - ткните, посмотрите контекст. Причём на тот момент восьмибитность ещё не упоминалась, это вы её уже потом за уши притянули, что само по себе доставляет: в разделе ARM, в теме про LDREX/STREX (!).

Цитата(Forger @ Jun 11 2017, 13:04) *
В данный момент w = 10, get = 9. Т. е. буфер как бы непустой, смело докладываем новый элемент.
А так, как буфер был непустой, то запуск передачи не делаем (не формируем соотв. прерывание от передатчика).
В итоге кладем в буфер данные, надеясь, что они отправляются, а это не так.

А при чём здесь буфер? Буфер здесь не при делах, его дело - быть буфером. Принять элемент, и выдать его потом. В порядке очереди. И описанный буфер это делает, чётко, без сбоев.
А описанная вами проблема ортогональна кольцевому буферу. Решать можно кучей способов. Например, разрешая прерывания без всяких проверок - раз мы положили элемент в буфер, то безусловно включаем прерывание, потому что в буфере гарантированно имеются данные. Или, как предложил ViKo - запрещая прерывания передатчика в момент засовывания элемента в буфер.
AVI-crak
Цитата(Forger @ Jun 11 2017, 14:04) *
Вот пример:

Пусть put = 10, get = 9, т.е. в очереди остался всего один еще не отправленный элемент.

Конкретный пример кольцевого буфера на Си.
Сначала проверяется возможность записи данных, и только потом пишется, и только после этого указатель переписывается.
Сначала проверяется возможность чтения, потом читается, и только после этого - переписывается указатель.
Кстати, лично у меня использование глобальных переменных в функции в прямом виде - приводит к их использованию в виде локальных переменных. В результате любое изменение сразу записывается в память (это грёбаное ускорение от GCC). Чтобы этого не происходило - нужно глобальные перемененные переписывать локальные принудительно, и уже с ними работать. Этот глюк появился в GCC с переходом от 4,2 до 4,8 версии.
Кроме всего прочего, частое переписывание указателей (по чайной ложке) - это банальная нагрузка на память.

Буфер всегда должен иметь хотя-бы один пустой элемент, это есть защита от глюка.
Одинаковые указатели - признак пустого буфера.

Forger
Цитата(AHTOXA @ Jun 11 2017, 12:16) *
Решать можно кучей способов. Например, разрешая прерывания без всяких проверок - раз мы положили элемент в буфер, то безусловно включаем прерывание,

Именно! А речь ведь была про то, что запреты/разрешения прерываний тут не нужны.
Я же пытаюсь донести, что не все тут так просто.

Я делаю так.
В передающей задаче при передаче данных вот что делается.
В методе отправке элемента данных: если буфер пустой, просто взводится флажок прерывания от передатчика о том, что он пустой (регистр передатчика пуст), т. е. принудительно запускаем передачу.
Если это делать во время работы передатчика, то будет глюк, т.к. передатчик в этом момент все еще работает и может нарушить последовательность логику работы с указателями (описано выше).
Поэтому перед запуском передачи следует просто временно запретить прерывание от передатчика (только от него).
Если же буфер в данный момент пустой, то нужно взвести флажок прерывания о том, что аппаратный буфер передатчика пустой, чтобы вызвать по выходу соотв. прерывание.
Если же буфер не пустой, и есть в нем место, то просто положить туда данные и только после этого разрешить прерывание от передатчика.
Также прерывание от передатчика нужно запрещать перед входом во flush, т. к. там модифицируется оба указателя. Более того нужно принудительно очищать весь аппаратный буфер передатчика.
Никакие другие прерывания запрещать, конечно же, не нужно.
В особых случаях можно принудительно заново проинициализировать весь передатчик. Особенно, если допускается на ходу менять скорость обмена.

В прерывании передатчика использую еще один флажок прерывания - передача окончена, это когда опустел и аппаратный буфер передатчика и опустел выходной сдвиговый регистра передатчика (речь про USART).
Тут у меня к задачам семафорится факт того, что все передачи завершены.
Это очень удобно для построения протокола в стиле запрос-ответ, особенно по каналу RS-485, где нужно управлять направлением работы внешнего трансивера.
На всякий случай в этом обработчике можно запретить прерывания от передатчика, хотя это и не обязательно.

По аналогии работаю с приемником, но чуть иначе: запрещать прерывания от приемника нужно лишь перед вызовом flush(), после сразу же разрешить.
По аналогии с передатчиком нужно принудительно очищать весь аппаратный буфер приемника.
Если этого не делать, то указатель приема может отличаться от указателя передачи по выходу из flush. Это при условии, что flush вызывается в фоне задачи.
В принимающей задаче если используется счетный семафор RTOS, запрещать прерывания от приемника уже не нужно: по сути защиту от исчерпания буфера обеспечивает семафор.

Вот именно об этих критических секциях я и говорил.

Без них код тоже будет работать, но пока не дойдет до граничных условий, одно из которых я описал выше.
Однажды я столкнулся с подобным поведение кода, больше не хочу.
AVI-crak
Цитата(Forger @ Jun 11 2017, 16:22) *
Я же пытаюсь донести, что не все тут так просто.

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

А для USART кольцевой буфер мало подходит, для него необходимо иметь два линейных!!! Переписывать данные в ручном режиме, дабы дма могло нормально работать.
(я охреневаю от всплывающих подробностей twak.gif )
Forger
Цитата(AVI-crak @ Jun 11 2017, 13:47) *
Сначала проверяем свободное место, потом пишем данные, потом переписываем указатель.
Сначала проверяем наличие данных, потом их читаем, потом переписываем указатель.

Я именно так и делаю, но прерывания временно запрещаю, т. к. между проверкой и изменением указателя проходит время, в которое может влезть прерывание, которое обращается к тем же указателям.
Без эти временных запретов в одном из моих старых проектов вылезала редкая и потому очень трудноуловимая ошибка (((

Цитата
А для USART кольцевой буфер мало подходит,
У меня два кольцевых буфера - один на прием, другой на передачу.
Каждый буфер работает независимо от другого. По сути они ничего не знают друг о друге. Временно запрещают прерывания только от своих источников.
Более того, обе задачи, которые разгребают эти буферы, тоже ничего не знают друг о друге.
Приемная задача разгребает свою очередь, проверяя контрольные суммы, заголовки и т.п.
Передающая задача наоборот, все это упаковывает и отправляет на передачу.
Задачи выше работают с этими задачами на уровне логических пакетов, ни как не связанных со средой передачи.
Разумеется, реализованы механизмы таймаута как между пакетами, а так и между байтами. Контроль битых пакетов. Перезапуск разорванной и восстановленной связи.
В итоге любые сбои в канале связи никогда не вызывают опасных непредсказуемых глюков в приложении.


AHTOXA
Цитата(Forger @ Jun 11 2017, 15:22) *
Именно!

Вы поразительный человек! Вроде как уже всё объяснено, разжёвано до мельчайших подробностей, и тут вы с репликой "Именно!" начинаете всю канитель по-новой. Я так общаться не могу, чесслово.
AVI-crak
Цитата(Forger @ Jun 11 2017, 17:02) *
Я именно так и делаю, но прерывания временно запрещаю, т. к. между проверкой и изменением указателя проходит время, в которое может влезть прерывание, которое обращается к тем же указателям.
Без эти временных запретов в одном из моих старых проектов вылезала редкая и потому очень трудноуловимая ошибка (((


Я не понимаю, как читающий может переписать чужой указатель???
Ну не успели выбрать все данные, или наоборот - положить всё что нужное в буфер. Конец света от этого не наступает, успеете сделать когда будет такая возможность. Не понимаю, зачем жопу рвать на британский флаг???

По поводу неуловимых глюков.
Я уже писал насчёт проблемы использования внешних глобальных переменных с атрибутом volatile.
Прямое использование в функции под GCC4,8 и выше - будет давать ошибку. Например в простом выражении где А - внешняя глобальная volatile переменная.
А= (А+ В +С +Е)/2
переменная А будет физически переписана 4 раза!!! Потому что она volatile и внешняя.
Чтобы избавиться от подобного - необходимо объявить локальную переменную в пределах функции, и работать уже с ней, и уже готовый результат переписать в внешнюю А.

По этой причине большая часть кода написанного под GCC4.2 - глючит. Когда стало широко применяться внеочередное исполнение инструкций в коде ARM процессоров (GCC4.8) - вздрогнули все аурдинщики, без исключения.
Это отдельная тема, но вы должны быть в курсе.
Forger
Цитата(AVI-crak @ Jun 11 2017, 15:08) *
Я не понимаю, как читающий может переписать чужой указатель???

Может перезаписать свой и исходя из этого изменить логику работы очереди, в мое случае - остановить передачу тогда, когда это делать рано.
Сама очередь ничего не потеряла, но таймингки протокола на приемной стороне (внешнее устройство) были нарушены, что вызывало сбои в обмене.
Тут уже озвучивал более подробно. Понимаю, ситуация крайне редкая, но мне она попила кровушки в свое время (((
Если нет желания вникать так глубоко, то я предлагаю закончить на этом обсуждение кольцевых буферов sm.gif
В любом случае из этой темы я уже узнал все, что нужно по этим буферам. Есть некоторые новые вещи, о которых не задумывался ранее. Пригодятся, чтобы причесать свой код.

Цитата
Я уже писал насчёт проблемы использования внешних глобальных переменных с атрибутом volatile.
Я нигде не использую глобальные переменные. Ни одной.

Цитата
По этой причине большая часть кода написанного под GCC4.2 - глючит. ... Это отдельная тема, но вы должны быть в курсе.

Я уже давно не пользую GCC, поэтому вообще не в курсе его костылей ((
А ардуино использую лишь для примитивных разовых поделок, скажем, проверить по-быстрому какой-нить новый компонент. Как макетку с мозгами.

ps А вообще при чем тут gcc?
AVI-crak
Цитата(Forger @ Jun 11 2017, 18:19) *
Может перезаписать свой и исходя из этого изменить логику работы очереди, в мое случае - остановить передачу тогда, когда это делать рано.
Сама очередь ничего не потеряла, но таймингки протокола на приемной стороне (внешнее устройство) были нарушены, что вызывало сбои в обмене.

Да ёёёё.
Кто-ж использует кольцевой буфер для физических устройств?????
Для физики все (без исключения) применяют линейный буфер: одинарный или двойной - для одного!!! направления.
Спокойно создаём сообщение, дожидаемся готовности физики, и пинаем дма.
Всё, тайминги в порядке, процессорное время свободно, мозг никто не клюёт.
Forger
Цитата(AVI-crak @ Jun 11 2017, 16:07) *
Кто-ж использует кольцевой буфер для физических устройств?????

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

Цитата
Для физики все (без исключения) применяют линейный буфер: одинарный или двойной - для одного!!! направления.

И я использую такую штуку в небольших проектах, правда, у меня это называется несколько иначе, но сути не меняет wink.gif
AHTOXA
Цитата(Forger @ Jun 11 2017, 18:19) *
Но сам кольцевой буфер у меня одинаковый для всех проектов без привязки к физике, вообще ни к чему не привязан.

Вот тут не верю. Вы только что нам тут расписывали, как вы атомарно вычитываете два указателя, вычисляете их разницу, и тут же запрещаете/разрешаете прерывания. Это невозможно выполнить с отдельным буфером. Такая функциональность может быть только встроенной в буфер. Ну либо вы в прерывании напрямую модифицируете переменные структуры буфера, но тогда это нельзя назвать "буфер вообще ни к чему не привязан".
Forger
Цитата(AHTOXA @ Jun 11 2017, 16:42) *
Вот тут не верю. Вы только что нам тут расписывали, как вы атомарно вычитываете два указателя, вычисляете их разницу, и тут же запрещаете/разрешаете прерывания. Это невозможно выполнить с отдельным буфером.

Возможно, если делать это снаружи соотв. методов, а не внутри, как у меня было раньше.
Сначала запрещаются соотв. прерывания, которые могут нарушить логику работы, и уже потом оперируем данными.
Но можно реализовывать критические секции и внутри методов очереди (см. ниже enterCritical / exitCritical ).

Цитата
Такая функциональность может быть только встроенной в буфер. Ну либо вы в прерывании напрямую модифицируете переменные структуры буфера,

Все поля класса Queue - закрытые, private. Вообще, во всех классах поля закрытые, только часть методов доступна владельцу объекта.
Равно как и класс имеет право обращаться лишь к некоторым методам его владельца.
Поэтому нет никакого прямого доступа к полям Queue, впрочем, как и к любому другому классу.

Цитата
Это невозможно выполнить с отдельным буфером.

Все возможно, если писать под с++ используя и шаблоны )))
У меня используется шаблон очереди, а у него впрочем как у каждого объекта есть поле владельца (owner).
При создании и инициализации объекта очереди ей передается ссылка на ее владельца, уже который имеет обязательные методы enterCritical и exitCritical, доступные этому экземпляру Queue
их и вызывают соотв. методы очереди. Пока что до данного момента было так ...

зы. Предлагаю уходить в другую тему, иначе погонят не спрашивая... sm.gif
jcxz
Цитата(AHTOXA @ Jun 11 2017, 13:06) *
Вы поразительный человек! Вроде как уже всё объяснено, разжёвано до мельчайших подробностей, и тут вы с репликой "Именно!" начинаете всю канитель по-новой. Я так общаться не могу, чесслово.

Пациент неоперабелен. Только в морг...
Жизни не смыслит без критических секций, а ему запрещают. Беда... crying.gif
KnightIgor
Цитата(jcxz @ Jun 11 2017, 18:13) *
Пациент неоперабелен. Только в морг...
Жизни не смыслит без критических секций, а ему запрещают. Беда... crying.gif

ОТ: непристало форумеру со статусом "гуру" использовать такую терминологию по отношению к коллегам. Тот случай, когда медные трубы непроходимы?
AHTOXA
Цитата(Forger @ Jun 11 2017, 18:59) *
Возможно, если делать это снаружи соотв. методов, а не внутри, как у меня было раньше.
Сначала запрещаются соотв. прерывания, которые могут нарушить логику работы, и уже потом оперируем данными.

Ага, то есть, в очереди таки ничего запрещать не надо? Ну надо же! sm.gif

Цитата(Forger @ Jun 11 2017, 18:59) *
Но можно реализовывать критические секции и внутри методов очереди (см. ниже enterCritical / exitCritical ).

В любом случае, очередь не в курсе про прерывания от, например, UART. А полное запрещение прерываний не всегда желательно.
Вы, конечно, можете сказать, что owner может предоставить методы enterCritical / exitCritical, которые как раз запрещают/разрешают прерывания от UART. Но ведь вам не всегда надо разрешать, а только когда очередь не пуста. А такую функциональность в очередь уже не встроить.
Цитата(Forger @ Jun 11 2017, 18:59) *
зы. Предлагаю уходить в другую тему, иначе погонят не спрашивая... sm.gif

Давайте уже закругляться, новых идей нет, а по пятому кругу обсуждать одно и то же смысла особого нет.
Forger
Цитата(AHTOXA @ Jun 12 2017, 12:12) *
Вы, конечно, можете сказать, что owner может предоставить методы enterCritical / exitCritical, которые как раз запрещают/разрешают прерывания от UART.

Именно так я и скажу sm.gif

Цитата
Но ведь вам не всегда надо разрешать, а только когда очередь не пуста. А такую функциональность в очередь уже не встроить.

Ничего страшного в запрете каждый раз нет, главное - запрещать только от того, кто может прервать, а не от всех источников прерываний.
Но ни что не мешает вынести запреты наружу и защищать лишь критичные куски кода в фоне задач и то только там, где это действительно нужно.

зы Если есть еще какие вопросы, предлагаю перейти в другую тему или в личку ))
AVI-crak
Цитата(Forger @ Jun 12 2017, 15:29) *
Именно так я и скажу sm.gif

Где именно вы умудряетесь нарушить алгоритм?
1 прочитать в ЛОКАЛЬНЫЕ переменные указатели кольцевого буфера
2 обработать данные (записать или прочитать), если таковые имеются
3 переписать ОДИН указатель по ЗАВЕРШЕНИЮ работы с данными.
Прошу внимательно прочитать, и дать простой ответ под номером строки 1-2-3.

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

Прервать алгоритм можно в любом месте, например прерыванием или переключением задач. Главное корректно его восстанавливать.
Forger
Цитата(AVI-crak @ Jun 12 2017, 13:12) *
Где именно вы умудряетесь нарушить алгоритм?

Друзья, все, что я хотел сказать по этой теме, тут уже прозвучало, повторяться не хочу.
Если не согласны со моими выкладками, дело - ваше, переубеждать боле не стану, не вижу смысла.
И давайте уже заканчивать эту канитель ))


jcxz
Цитата(AVI-crak @ Jun 12 2017, 12:12) *
Прошу внимательно прочитать, и дать простой ответ под номером строки 1-2-3.

Я именно то же самое просил у товарища несколько раз. Специально для него написал простейшую реализацию классического кольца. Просил указать в какой конкретно строке и что именно нужно сделать (согласно условиям использования изложенным мной там же) чтобы сломать. На что не получил никакого ответа laughing.gif
AHTOXA
Цитата(AVI-crak @ Jun 12 2017, 15:12) *
Где именно вы умудряетесь нарушить алгоритм?
1 прочитать в ЛОКАЛЬНЫЕ переменные указатели кольцевого буфера
2 обработать данные (записать или прочитать), если таковые имеются
3 переписать ОДИН указатель по ЗАВЕРШЕНИЮ работы с данными.
Прошу внимательно прочитать, и дать простой ответ под номером строки 1-2-3.

Я отвечу за Forger-а, а то он как-то невнятно объясняет.
Он описал такой случай: изначально буфер не пуст. Мы собираемся добавить туда элемент. Между 1 и 2 произойдёт прерывание, забирающее данные из буфера, опустошит буфер, и отключит прерывания на передачу, а мы потом (после пункта 2) не включим прерывания передатчика, потому что судя по нашим локальным копиям указателей буфер был не пуст.

Я лично считаю, что это не дело буфера - включать прерывания от передатчика, и такой глюк не является глюком буфера. Кроме того, в последующих сообщениях выяснилось, что и сам Forger использует реализацию буфера, которая не умеет включать прерывания передатчика. Но чего не скажешь в пылу полемикиsm.gif
LightElf
Цитата(jcxz @ Jun 8 2017, 23:32) *
Вы это расскажите NXP с их линейкой LPC43xx. Про свои определения. cool.gif

Определения читайте у ARM. [CENSORED] ничего не меняют.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.