Страниц: [1] 2 3 ... 5
  Печать  
Автор Тема: Работа через USB с FlirOne Gen 2  (Прочитано 4995 раз)
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« : Марта 15, 2017, 07:24:20 pm »

Купил я недавно такую игрушку, как Flir One Gen 2. Это тепловизионная приставка к смартфону. Смартфонов у меня не водилось и не планируется, покупал чтобы достать датчик Lepton 3 и подключить к микроконтроллеру. Но, как оказалось, это устройство можно подключить к PC по USB собрав кабель micro USB (мама)->USB (папа), что я, собственно, и сделал. На форуме ( www.eevblog.com/forum/thermal-imaging/question-about-flir-one-for-android/?all ) ребята разобрались с протоколом обмена и привели программу для libusb. За это им огромное спасибо (особенно, tomas123). Я на их базе создал своё приложение под Windows. Оказалось, что под Windows ещё ни у кого такая игрушка не завелась - libusb выдаёт ошибку "неверный параметр". Ну и фиг с этой Windows. Под Linux (Mageia 5) всё заработало отлично. Под Raspbian на Raspberry Pi тоже всё работает (и даже количество кадров в секунду выше). Под XUbuntu, правда, libusb подглючивает и часто пишет error pipe, но в целом, данные приходят и картинка собирается. И вот захотелось мне запустить этот тепловизор под QNX.
И вот тут есть нюансы: я с USB знаком буквально неделю и представление имею недостаточное (да, я читал о стеке и как всё это устроено, но полной ясности для меня пока ещё нет). Smiley С помощью разных книжек, гугла, его переводчика (я слаб в английском) и этого форума, я сегодня сделал обвязку для работы через USB из-под QNX 6.3 SP3. И даже приходят данные. Только вот приходят они как-то странно - в картинку не собираются, но строки штатного ответа тепловизора (там есть текстовые строчки) я там вижу. Тепловизор этот имеет 3 интерфейса и bulk- и control-конечные точки. Изохронных точек нет (а я думал, что есть Smiley ). Гонял на Celeron 2.4 с 1 Гб ОЗУ - что на работе было для QNX, то и взял.

Я сделал так:

1) Определяем функции подключения и отключения устройства.
2) Делаем usbd_connect.
3) В функции подключения устройства делаем:
3.1) usbd_attach
3.2) usbd_interface_descriptor
3.3) Бегаем по всем конечным точкам интерфейса с помощью usbd_parse_descriptors и для нужных открываем каналы с помощью usbd_open_pipe.
3.4) Для каждого канала выделяем URB с помощью usbd_alloc_urb(NULL);
4) Когда все каналы готовы и все интерфейсы обработаны, начинаем работу с устройством:
4.1) С помощью usbd_alloc выделяем память для буфера данных (у меня один буфер на все команды - или нужен каждой по буферу? Насколько команды usbd_setup_bulk пересекаются?)
4.2) С помощью usbd_setup_vendor готовим команду управления (я так понял, usbd_setup_vendor эквивалента usbd_setup_control, которой уже нет в .h файле). И посылаем её с помощью usbd_io.
4.3) С помощью usbd_setup_bulk готовим обмен с устройством. И запускаем обмен с помощью usbd_io.
4.4) С помощью usbd_urb_status определяем, чем закончился обмен и сколько байт было принятно/передано.
4.5) Повторяем с 4.3 (после команды тепловизор только передаёт нам данные - передавать ему что-либо уже не обязательно).
5) В функции отключения устройства закрываем каналы ( usbd_close_pipe) и отключаем интерфейс ( usbd_detach ). После отключения всех интерфейсов отключаемся от стека USB ( usbd_disconnect ) и освобождаем буфер ( usbd_free ). (Кстати, не обратил внимания, нужно ли после usbd_alloc_urb этот самый URB удались какой-либо командой.  Undecided )

И вот какие вопросы возникли (может быть, кто-нибудь подскажет, а то информации практически ноль):

1) Если функции подключения и отключения асинхронны по отношению к основной программе, то во время работы программы указатели на всякие usbd_device_instance_t мгновенно станут недостоверными при отключении устройства? Как в таком случае быть? Причём, так же легко я потеряю и каналы и URB ( я ведь их освобождаю в функции отключения устройства). Или в функциях подключения/отключения просто вести подсчёт подключённых интерфейсов и заниматься освобождением ресурсов только когда все отключены? Как обычно делают?
2) usbd_setup_vendor имеет в конце указатель на буфер и длину. Длину чего? В libusb подобная функция имеет длину принимаемых данных от устройства в ответ на управляющую команду. В QNX, судя по потому, что если записать в длину 0 ничего не работает, это количество передаваемых байт (не дополнительно, а вообще?). Но зачем эта длина управляющей команде? Я уже определил эту длину, задав тип запроса, индекс и тому подобное.  Undecided Ну ладно, я задал длину равной 8 (вроде как управляющая команда столько и занимает). Ну а на самом деле что она значит и чему должна быть равна?
3) usbd_setup_bulk использует буфер. Какого максимального размера должен быть этот буфер? У меня кроме как 512 хорошо числа больше или меньше не работают. Была надежда на пару мегабайт - так вот, не работает. Или буфер должен быть не более, чем прописано в параметрах конечной точки?
4) Моя программа убивает нафиг io-usb через некоторое время. При этом загрузка процессора 100%. С буфером больше 512 всё сдыхает сразу же. С буфером меньшего размера ещё несколько секунд что-то поработает, но данные не принимаются. С буфером 512 данные принимаются как минимум минуту, но явно что-то по дороге теряется нафиг - картинка не собирается. В чём же тут дело-то?

Я приложил полный проект "драйвера". В нём много отладочной фигни и повторов таких вот отладочных блоков. Я храню все интерфейсы в списке list_SDevice; для каждого из устройств я открываю все каналы и храню там же в SDevice собственным вектором.

Собственно, может быть, кто-нибудь подскажет, как это всё сделать правильно? Спасибо. Smiley

P.S. А картинки должны получаться вот такие, как ниже. Smiley Модуль сборки картинки в этом проекте отсутствует - тут я просто сохраняю данные в файл, а потом его расшифровываю отдельной программой.
« Последнее редактирование: Марта 16, 2017, 07:09:42 am от da-nie » Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #1 : Марта 16, 2017, 08:08:48 pm »

Интересно получается! Если в функции "устройство подключено" ( void InsertionFlirOne(usbd_connection *connect,usbd_device_instance_t *instance) ) просто собирать список из этих самых *instance, а потом, когда все устройства будут подключены их сделать usbd_attach, то происходят странные вещи. Интерфейсы добавлялись последовательно от 0 до 3. А при usbd_attach по списку они идут в обратном порядке (я это вижу по номерам интерфейсов и номерам конечных точек; однако, в список они добавлены в верном порядке).  Cool Да ещё и последний не может сделать usbd_attach! Указатели что ли изменяются при добавлении нового устройства?
А ещё я нашёл, что нужно сделать usbd_select_config(). Этого я не сделал. Sad Завтра проверю, поможет это или нет.

В общем, в документации где-то было:

Цитировать
How a class driver works

   A class driver typically performs the following operations:

1.   Connect to the USB stack (usbd_connect()) and provide two callbacks:
   one for insertion and one for removal.
2.   In the insertion callback:
   1.Connect to the USB device (usbd_attach()).
   2.Get descriptors (usbd_descriptor()).
   3.Select the configuration (usbd_select_config()) and interface (
      usbd_select_interface()).
   4.Set up communications pipes to the appropriate endpoint (
      usbd_open_pipe()).
3.   In the removal callback, detach from the USB device (usbd_detach()).
4.   Set up all data communications (e.g. reading and writing data, sending
   and receiving control information, etc.) via the usbd_setup_*()
    functions (usbd_setup_bulk(), usbd_setup_interrupt(), etc.).
5.   Initiate data transfer using the usbd_io() function (with completion
   callbacks if required).



Ну и ещё у меня новый вопрос: Зачем нужна память, выделенная usbd_device_extra для usbd_attach? Я так понял (читая старые темы этого форума), это блок пользовательской памяти и её использовать можно как угодно. Можно и не выделять её вовсе (я так и сделал). Верно ли это или эту память нужно выделять обязательно и она участвует в обмене с устройством?
« Последнее редактирование: Марта 16, 2017, 08:20:54 pm от da-nie » Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #2 : Марта 17, 2017, 09:41:25 am »

Кстати, никто не подскажет, почему я не могу использовать volatile для вектора и как это победить? Никогда не использовал контейнеры stl с volatile.  Cool А тут захотелось в контейнер собирать подключённые устройства.

Код:
volatile vector<SDevice> vector_SDevice;
long size=vector_SDevice.size(); - ошибка "passing `volatile std::vector<SDevice,std::allocator<SDevice> >' as `this' argument of `size_t std::vector<SDevice,std::allocator<SDevice> >::size() const' discards qualifiers"

Я так понял, для классов volatile неприменим? У меня VC6 такое не принимает:

Код:
class CTest
{
 unsigned long Variables;
 public:
  long size(void){return(0);}
};

volatile CTest cTest;

long size=cTest.size(); - ошибка.

Интересно, почему так? Я так понимаю, судя по сообщению компилятора, функция класса определена для обычного объекта без модификатора volatile и не может работать с valatile объектом.

Со структурами тоже весело:

Код:
struct SData
{
 long Data;
};

volatile SData sData_Array[100];

SData sData;
sData.Data=0;
sData_Array[0]=sData; - ошибка, не может выполнить =.

Понимаю, что оператор присваивания вызывался, но по факту-то я просто хочу заместить блок памяти. Что же, через memcpy делать это что ли? Или делать так ((SData*)sData_Array)[0]=sData;

Update.

Вот получилась программа, которая даёт 2-3 кадра в секунду. Остальные кадры она просто пропускает - не успевает всё принять. Если играться с временами ожидания или размером буфера, то io-usb быстро дохнет. Ещё я заметил, что поставив в цикл приёма данных delay(1) работать стало стабильнее - меньше вылетаний io-usb. Эта программа уже пишет tga-файлы в каталог. Всего-то выставил в usbd_io таймаут в ноль. Smiley И сразу стало принимать существенно быстрее. И буфер можно задать в 16384. С таким буфером работает. С большим в 2 раза уже нет.

« Последнее редактирование: Марта 17, 2017, 02:53:20 pm от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #3 : Марта 17, 2017, 06:42:39 pm »

Кстати, никто не подскажет, почему я не могу использовать volatile для вектора и как это победить? Никогда не использовал контейнеры stl с volatile.  Cool А тут захотелось в контейнер собирать подключённые устройства...

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

Цитировать
Я так понял, для классов volatile неприменим? У меня VC6 такое не принимает:

Для объектов классов применим. Но обращатся через волатильный объект можно только к тем полям (переменным/методам и тд) которые объявлены с модификатором volatile.
Почему так? Примерно по той же причине что и ограничения накладываемые на объекты с модификатором const, (но ограничений чуть больше).
Конечно, всегда можно убрать "вызванные" ограничения компилятора через  const_cast, но опять возникает вопрос - зачем тогда вам волатильность?
« Последнее редактирование: Марта 17, 2017, 06:50:58 pm от lastcross » Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #4 : Марта 17, 2017, 06:53:44 pm »

Я создал структуру подключённых устройств и планировал хранить список таких устройств в контейнере stl. Удобно просто. Но, так как функция подключения USB-устройства асинхронна по отношению к основной программе, нужно компилятору указать, чтобы он читал код "как есть" и не пытался оптимизировать доступ к контейнеру и кэшировать значения. Вот для этого-то я и хотел объявить контейнер volatile.

Цитировать
Для объектов классов применим. Но обращатся через объект можно только к тем полям (переменным/методам и тд) которые объявлены с модификатором volatile. Близкая аналогия с модификатором const, но со доп ограничениями.

Очень плохо. И очень неудобно. Вот есть у меня чей-то класс и я не смогу сделать его с volatile. Собственно, контейнеры это и показали.

Цитировать
Конечно, всегда можно убрать "ограничение вызова" через  const_cast, но опять возникает вопрос - зачем тогда вам волатильность?

Но сам-то объект остаётся при этом volatile. Только вот, интересно что меняется для компилятора в случае такого вот изменения типа непосредственно при обращении к volatile-объекту?
Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #5 : Марта 17, 2017, 07:51:18 pm »

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

В целях "обезопасить себя от гонок" многопоточности? Если только ради этого - то volatile это не инструмент, это иллюзия на инструмент. И чем сложнее пользовательский тип к которому он применяется тем страшнее иллюзия.

Цитировать
Очень плохо. И очень неудобно. Вот есть у меня чей-то класс и я не смогу сделать его с volatile. Собственно, контейнеры это и показали.

Не согласен с Вами и почему - я уже дал ответ выше. Если Вы хотите использовать volatile для объекта какого-то класса, значит Вы точно знаете для чего отключаете оптимизацию и как именно этот класс должен себя вести при этом. В противовес Вашим классам - разработчики библиотек дают общее решение и не могут знать заранее как именно вы собираетесь использовать их классы с волатильностью. Вы рассчитывали что объявив волатильный объект какого-то класса тем самым укажите компилятору применить волатильность ко всем полям/методам данного объекта? Тогда скажите как бы компилятор понял что волатильный и не волатильный объект принадлежит одному типу (допустил бы такое и скомпилировал удачно). Типы бы этих объектов были бы разные так как они бы содержали разный код их реализующий.
Допускаю, что это одна из причин, почему решено было сделать так - из волатильных объектов можно сослатся только на волатильность (либо принудительно const_cast и тогда компилятор умывает руки - вы сами себе доктор)
Я не зря сослался на аналогию с const модификатором - так как оба этих модификатора влияют также на сигнатуру методов.

Цитировать
Но сам-то объект остаётся при этом volatile. Только вот, интересно что меняется для компилятора в случае такого вот изменения типа непосредственно при обращении к volatile-объекту?

const_cast - в большинстве случаев первый звонок к тому что Вы делаете что-то не так и как правило заканчивается UB. Все что делает const_cast - это говорит от Вашего имени компилятору: "я лучше знаю, что это - поэтому я сам справлюсь, даже если прострелю себе ногу"
« Последнее редактирование: Марта 17, 2017, 07:56:53 pm от lastcross » Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #6 : Марта 17, 2017, 08:17:04 pm »

Цитировать
В целях "обезопасить себя от гонок" многопоточности? Если только ради этого - то volatile это не инструмент, это иллюзия на инструмент. И чем сложнее пользовательский тип к которому он применяется тем страшнее иллюзия.

Да всё это понятно, но при любом раскладе (хоть с мютексом, хоть нет), необходимо объявить общие переменные с модификатором volatile. Мютекс, я, кстати, ещё не ставил - есть у меня подозрение, что эта асинхронная функция останавливает основной поток (я без понятия, как её вызывает QNX). Это я сперва проверю. А то с мютексом можно в неё зайти и больше не выйти.

Цитировать
Не согласен с Вами и почему - я уже дал ответ выше.

Сейчас поясню, что мне не нравится. Smiley Все эти const нужны для того, чтобы страховать от ошибок программиста. А volatile нужен всего лишь, чтобы программист точно знал, что вот как он написал, так и должен получится код и никакой самодеятельности компилятора не должно быть. И ровно в такой же последовательности этот код  должен выполниться, если volatile используется как барьер. Отсюда выглядит достаточно уродливым ограничение компилятора на контроль сложных типов. Нет, формально всё это логично - объект ведь иного типа. Но вот с точки зрения управляемости объектом выходит ерунда. Для структур и классов компилятор вызывает операцию присваивания и отказывает в компиляции, а для простых типов копирование идёт за милую душу. При этом для меня, создавшего структуру с volatile, это ограничение выглядит уродливым.
А причина уродства в том, что создав объект класса, я жду от компилятора всего того же, что он умеет делать с объектом, плюс отсутствие самодеятельности. То есть, у меня Миша и Коля объекты класса Человек, и я про Колю сказал компилятору, что Коля может сам по себе внезапно менять своё состояние, поэтому компилятору ни к коем случае не нужно что-то про Колю помнить, не спрашивая каждый раз об этом что-то самого Колю. Мне откровенно всё равно, что у компилятора одна функция-метод на все объекты и она - вот незадача - не умеет работать с Колей, за которым нужно следить. Конечно, она, эта функция, внутри оптимизирована без учёта слежения за таким, как Коля. Ну так пусть компилятор создаст вторую функцию, специально для таких вот Коль. Только пусть он от меня эту реализацию скроет.
Впрочем, хрен с ним. Smiley

Цитировать
const_cast - в большинстве случаев первый звонок к тому что Вы делаете что-то не так и как правило заканчивается UB

В большинстве, но не во всех же. Smiley Меня больше интересует, что для компиляции означает запись ((SData*)sData_Array)[0]=sData; где sData_Array объявлен volatile. Насколько это соответствует обычному копированию.

Цитировать
Все что делает const_cast - это говорит от Вашего имени компилятору: "я лучше знаю, что это - поэтому я сам справлюсь, даже если прострелю себе ногу"

Так ведь так и должно быть - вы должны лучше компилятора понимать, что вы пишете и как это работает. Smiley У меня тут полно странных товарищей, которые впишут пустые циклы для микроконтроллера, откомпилируют на -o3 и удивляются странным результатам - "компилятор хреновый, код не работает с оптимизацией, в асмовской трансляции команды отсутствуют". Они лучше компилятора знают, что хотят получить, но не прочли, как это объяснить компилятору и даже не догадываются, что этот компилятор такой вот умный.
« Последнее редактирование: Марта 18, 2017, 07:57:05 am от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #7 : Марта 18, 2017, 07:59:27 pm »

Цитировать
Да всё это понятно, но при любом раскладе (хоть с мютексом, хоть нет), необходимо объявить общие переменные с модификатором volatile.
Совершенно не обязательно, и более того - для многопоточности совершенно не при любом раскладе. Еще раз, volatile в задачах многопоточности в большинстве случаев иллюзия. Хотите общие переменные между потоками - используйте atomic или другие механизмы синхронизации. При желании найдете точное описание стандарта, но навскидку - вот вам ссылка , в которой вполне себе ясно сказано, что волатильный объект не годится для "общего объекта" между потоками. В теории он бы может и был бы полезен в случае с несколькими потоками, но только если компилятор гарантирует атомарность операций с этим объектом - что в большинстве случаев не так, в добавок и не переносимо. Я уж молчу про сложные типы. В дополнение почитайте что пишут другие

Цитировать
Сейчас поясню, что мне не нравится. Smiley Все эти const нужны для того, чтобы страховать от ошибок программиста.

Не только от ошибок. Эти модификаторы влияют на сигнатуру функции. Функция с модификатором и без на конце - это разные методы.
Они составляют так же часть типа для переменных (c volatile в отличии от const есть в этом случае свои ньюансы). Это не просто страховка - это гарантия вызова конкретных методов, способа хранения (разное алацирование в памяти) и т.д. и т.п.

Может узнаете что-то новое.

Цитировать
Для структур и классов компилятор вызывает операцию присваивания и отказывает в компиляции

Только для тех типов которые не определили внутри себя методы работы с волатильностью или соответствующие волатильне публичные переменные.


Цитировать
Меня больше интересует, что для компиляции означает запись ((SData*)sData_Array)[0]=sData; где sData_Array объявлен volatile.
sData_Array - у Вас волатильный указатель (на начало массива) и только, о каком волатильном копировании идет речь мне трудно понять.


Цитировать
Так ведь так и должно быть - вы должны лучше компилятора понимать, что вы пишете и как это работает.
Компилятор вправе лучше знать как это будет работать на той платформе под которую он собирает код. Иначе возвращайтесь кодированию в машинных кодах и реализуйте все то что "должно быть" непосредственно в них.
Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #8 : Марта 18, 2017, 09:30:09 pm »

Цитировать
Совершенно не обязательно, и более того - для многопоточности совершенно не при любом раскладе.

При любом, за исключением всяких новомодных прибамбасов. И практика работы такого ПО до появления С++ 11 это подтверждает. Особенно, на микроконтроллерах, которые этот самый С++ 11 никогда и не видели. Volatile - необходимое, но не достаточное условие работы в многопоточных системах. Атомарность же обеспечивается объектами синхронизации отдельно - их для этого и придумали. Но без volatile одних объектов синхронизации мало. Вместе же они составляют необходимый и достаточный набор элементов для работы с общими переменными для многопоточности.
Что же касается atomic:

Цитировать
Директива atomic работает быстрее, чем критические секции, поскольку некоторые атомарные операции могут быть напрямую заменены командами процессора. Следовательно, эту директиву желательно применять везде, где требуется защита общей памяти при элементарных операциях. К таким операциям, согласно спецификации OpenMP, относятся операции следующего вида:

x binop= expr
x++
++x
x--
--x

Здесь х - скалярная переменная, expr - выражение со скалярными типами, в котором не присутствует переменная х, binop - не перегруженный оператор +, *, -, /, &, ^, |, <<, или >>. Во всех остальных случаях применять директиву atomic нельзя (это проверяется компилятором).

Между тем, volatile+мютекс охватывают вообще всё, что только можно придумать. Smiley

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

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

Цитировать
sData_Array - у Вас волатильный указатель (на начало массива) и только, о каком волатильном копировании идет речь мне трудно понять.

Нет, не volatile-указатель - volatile распространяется на элементы массива. Если в тексте я сделаю преобразование такого массива к обычному (был указатель на volatile-массив, а стал на обычный массив), то при записи по такому указателю не сделает ли компилятор какую-нибудь фишку, решив, что раз массив уже не volatile, то возможно всё. Smiley Впрочем, думаю, что не сделает - не вижу, что он тут способен испортить.  Cool

Цитировать
Компилятор вправе лучше знать как это будет работать на той платформе под которую он собирает код.

Я не об этом (впрочем, асм я оставил без сожаления лет так 20 назад). Как автор программы, не компилятор, а я должен точно знать, как она должна выполняться. Где я могу отдать программу компилятору, а где его нужно направить, объяснить, что вот это понимать следует буквально. И полагаю, вы точно также знаете, как работает ваша программа и знаете это лучше компилятора. Заметьте, не как её оптимизировать, а как она должна выполняться.
« Последнее редактирование: Марта 18, 2017, 10:04:55 pm от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #9 : Марта 18, 2017, 11:08:49 pm »

Цитировать
При любом, за исключением ..

Так как речь идет об ключевых словах C++ - дайте ссылку на стандарт, который бы вам гарантировал что-либо в отношении volatile при multithreding. Ну или требования к переменным, что они должны быть минимум volatile )

Цитировать
Что же касается atomic ... Между тем, volatile+мютекс охватывают вообще всё, что только можно придумать.

Это абсолютно разные подходы - atomic и mutex. Атомарные операции я предложил как вариант, альтернативу volatile переменным, в отличии от которых, атомики честно решают (без блокировок) "разделение" переменной между потоками (и не требуют никаких volatile, да и начиная ++11 входят в состав языка). Но речь даже не в этом - я до сих пор не пойму, чем обычный мютекс с обычной переменной хуже того же мютекса но с volatile переменной (при условии что работа с переменной гарантирована мютексом)? ))

Если не сложно, опишите вообще - что Вы ожидаете от volatile переменной? Если только для того, чтобы она не оптимизировалась/отбрасывалась компилятором - то ок. А если надежда на что-то большее, приведите пример, упрощенный.

Цитировать
В исполняемом файле никаких таких вещей нет. Все эти модификаторы живут только внутри компилятора. И нужны они только ему (это не более, чем ключи управления компиляцией)
В исполняемом файле нет много чего: ни операторов присваивания, конструкторов, деструкторов, наследования, пользовательских типов  и бог весть чего еще нет. Между тем язык заставляет разработчика постоянно следить например - от имени какого типа производится вызов, что будет вызвано - присвоение или конструирование, будет ли умолчательное преобразование и т.д. Тем не менее вы используете язык С++, "играете" по его правилам. Модификатор - языковый а не компилятора, и требование относительно модификатора - это требования языка, а не компилятора. Так что не апеллируйте к исполняемому файлу. Язык - это абстракция, volatile - часть этой абстракции, исполняемый файл - реализация. Я вам уже давал ссылку выше на пример в котором "одинаковые" функции из-за модификаторов "одинаковыми" не являются. Это не просто ключи компилятора (или какой-то там inline) - это средство языка, которое влияет на подход к разработке кода (независимо от конкретного компилятора языка С++)

Цитировать
С моей же стороны нет никакого интереса задумываться, почему компилятор не желает сам следить за тем, какой код ему нужно сделать, а заставляет меня отдельно описывать функцию класса для volatile-элементов

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

Цитировать
Нет, не volatile-указатель - volatile распространяется на элементы массива
Ссылку пожалуйста  на стандарт (или на что либо авторитетное) на основании которой Вы сделали такой вывод, и пример кода. В свою очередь я уверен, что у Вас только указатель волатильный. Вот Вам пример, который как бы намекает на то что, для присвоения волатильного объекта нужно нечто больше нежели оператор присваивания по-умолчанию http://cpp.sh/5hmkk . Кроме того, как только Вы попытаетесь реализовать тривиальный оператор присваивания, скорее всего компилятор намекнет что он не совсем-то ему и подходит и волатильность он отбросит
« Последнее редактирование: Марта 19, 2017, 01:19:09 am от lastcross » Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #10 : Марта 19, 2017, 07:38:57 am »

Цитировать
дайте ссылку на стандарт, который бы вам гарантировал что-либо в отношении volatile при multithreding. Ну или требования к переменным, что они должны быть минимум volatile )

А что он должен гарантировать? Volatile гарантирует отсутствие кэширования переменной, что, собственно, и требуется при мультипоточности.

Цитировать
при условии что работа с переменной гарантирована мютексом

А, вы про то, что мютекс является барьером памяти? Ну да, он синхронизирует переменную при захвате и освобождении. Впрочем, я это совсем упустил из вида - мютекс и мютекс, приелось как-то (вчера задумался, а почему я не описал volatile во всех своих многопоточных проектах переменные и почему это работает до сих пор). Roll Eyes Да, одного мютекса будет достаточно. Это для микроконтроллеров, где их нет, нужен volatile и всякие аналоги sei() и cli() для запрета и разрешения прерываний.  
Впрочем, интересно, везде ли это работает - обычно, про это мало где написано и вот так специально не освещается (защитите переменную мютексом - и никаких проблем. А вот почему автоматически обеспечивается аналог volatile на границах мютекса почти никогда не упоминается). Тут компилятор был для TMS320 (1967ВЦ1Т), так он даже long a[3]={1,2,3} не выполнял, хотя ошибки компиляции не было. А что в стандарте написано? Всегда гарантируется барьер памяти на объектах синхронизации или варьируется от компилятора к компилятору?

Цитировать
Язык - это абстракция, volatile - часть этой абстракции,

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

Цитировать
Ссылку пожалуйста  на стандарт (или на что либо авторитетное) на основании которой Вы сделали такой вывод, и пример кода.

Я не знаю, что для вас авторитетное, но, например, вот тут так и пишут: http://valera.asf.ru/cpp/book/c03_13.html И я других мнений про volatile для массива не встречал. Что касается ссылки на стандарт, я без понятия, где этот стандарт вообще ищется (и есть ли на русском он вообще).
В примере я нифига не понял, что я должен увидеть. Или его надо откомпилировать и прочитать, что напишет компилятор? А что он напишет? Что volatile_b = volatile_a; не может сделать, потому что оператор "=" не задан для volatile-объектов?

А вот про usb, я так полагаю, никто не сможет ничего подсказать? Жалко. Я до сих пор не понимаю, почему io-usb так легко завершается аварийно таким простым "драйвером".
« Последнее редактирование: Марта 19, 2017, 08:24:37 am от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #11 : Марта 19, 2017, 12:46:36 pm »

Цитировать
.. что, собственно, и требуется при мультипоточности.
и этого НЕДОСТАТОЧНО для мультипоточности. А механизмы которые полностью удовлетворяют мультипоточности - не требуют volatile. Такие механизмы стали частью языка с ++11, а до этого реализовывались средствами конкретной платформы и для них volatile фактически не требуется. Или, если требуется - то это специфическая платформа.

Цитировать
А, вы про то, что мютекс является барьером памяти?
Я про то что мьютекс - является объектом ядра. Операции над ним - "атомарны" и гарантировано это самим ядром (как правило ядро - это ОС). Про контроллеры я вам ничего не скажу.


Цитировать
А что в стандарте написано? Всегда гарантируется барьер памяти на объектах синхронизации или варьируется от компилятора к компилятору?
В стандарте до ++11 и его использованием std::thread ничего не сказано ни про какие объекты синхронизации, он о них не знает. И потому тот стандарт ничего вам не гарантирует и не требует. Гарантии на работу с такими объектами дает система (библиотека которая обобщает и обворачивает эти вызовы), к которой указанные объекты принадлежат. Начиная с ++11 в стандарте прописана работа с потоками и механизмами синхронизации (std::thread), там же и указаны гарантии/требования к объектам типы которых входят в это пространство имен.

Цитировать
Вот. Абстракция. Следовательно, это нужно только чтобы программист управлял компиляцией и сам себя страховал (отсюда всякие explicit, const и прочие)
Нет, абстракция нужна для того чтобы задача была разделена на определенные уровни облегчающие ее понимание и упрощающие поиск решения задачи. Объявив метод как константный, я не просто прошу компилятор позаботится что бы кто-то случайно не обратился к this как к обычному указателю. Я так же сообщаю пользователям этого метода - что у него есть контракт, который гарантирует возможность вызова этого метода для константных объектов. Вот что я имел ввиду упоминая "абстракцию". Точно так же и с volatile

Цитировать
Я не знаю, что для вас авторитетное, но, например, вот тут так и пишут: http://valera.asf.ru/cpp/book/c03_13.html
Не знаю что за ресурс, но даже там ничего не сказано про массивы (и Ваше ожидание к ним) и не сказано про многопоточность (и Ваше ожидание к ним) - откуда у вас такие ожидания тогда?)

Цитировать
Что касается ссылки на стандарт, я без понятия, где этот стандарт вообще ищется (и есть ли на русском он вообще).
Можно так например https://google.gik-team.com/?q=c%2B%2B+standard+pdf

Цитировать
В примере я нифига не понял, что я должен увидеть. Или его надо откомпилировать и прочитать, что напишет компилятор? А что он напишет? Что volatile_b = volatile_a; не может сделать, потому что оператор "=" не задан для volatile-объектов?
Да, нужно запустить (это возможность я оставил для Вас Smiley ).
Да именно так, не может присвоить объекты если они были объявлены как волфтильные. Даже если при этом типы простые структуры у которых компилятор встраивает оператор присваивания с побитовым копированием. Другими словами не волатильные объекты присвоить компилятор даст, а волатильные - требует наличие принудительно написанного специального оператора присваивания.
А теперь возвращаясь к Вашим вопросам - у вас волатильный указатель на массив. Почему я так решил? Да потому что иначе Вам бы пришлось для структуры, типы которых вы пытаетесь "скопировать", написать руками "специальный" оператор. Написав такой оператор Вы бы не утверждали что с пользовательскими типами волатильность не работает. Делаю вывод что такого оператора в структуре/типе нет. А раз нет, компилятор ничего не сказал - значит волатильный только указатель. Если компилятор что-либо сказал (подобное что в моей ссылке) и вы делаете преобразование принудительно - то опять же, теряете волатильность для операции присвоения (оставляя для указателя на массив).
Как именно конкретно у Вас объявлен sData_Array - вы не предоставили, поэтому я взял смелость сделать такой вывод (можно как массив C, а можно как указатель, а можно же как std::vector а может и как std::array, а может вообще это тип со своим перегруженным оператором индекса)

Мне кажется мы и так затянули про volatile - так что если возникли вопросы, пишите лично. Более мне тут сказать нечего)
« Последнее редактирование: Марта 19, 2017, 01:26:03 pm от lastcross » Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #12 : Марта 19, 2017, 01:24:21 pm »

Цитировать
и этого НЕДОСТАТОЧНО для мультипоточности.

Ещё раз. Никто и не писал, что этого ДОСТАТОЧНО. Wink

Цитировать
А механизмы которые полностью удовлетворяют мультипоточности - не требуют volatile

На микроконтроллерах - требуют. Вы ограничиваетесь исключительно теми платформами, которые ваш любимый Си++ 11 поддерживают и отчего-то полагаете, что так везде? Ну так нет.

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

А как по-вашему компилятор соображает, что переменная внутри Lock() - Unlock() мютекса и внешняя переменная не volatile типа могут быть связаны? Сам мютекс, как объект, ничего не знает о защищаемых переменных.  
Пример:
Код:
bool Done=false;

pthread_mutex_t mutex_ID;

//первый поток
void Thread_1(void)
{
 sleep(10);
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=true;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

//второй поток
void Thread_2(void)
{
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=false;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 sleep(1);
 while(1)
 {
  pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
  if (Done==true) break;
  pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 }
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

В этом примере if (Done==true) break; компилятор, основываясь только на мютексе, запросто мог бы выкинуть эту строчку. А что? Он ничего не знает о том, что Done способно измениться внезапно. Так мютекс не просто объект ядра, мютекс реализует вот какой механизм:

acquire_barrier(); - барьер захвата
if (Done==true) break;
release_barrier(); - барьер освобождения

Подробнее тут: http://scrutator.me/post/2015/05/16/memory_barriers.aspx
И вот именно поэтому и не нужен volatile внутри мютекса. И компилятор (а не только ядро) всё это знают.
Но когда вы начнёте писать для микроконтроллеров, там не будет барьеров и всё это вы будете делать сами с помощью этого самого volatile. Поясню примером.
Допустим, вы в прерывании меняете значение Done. А атомарность обеспечите sei и cli. Всё, что между ними - атомарно (прерывание запрещено). Но компилятор похерит if (Done==true) break; с вероятностью 100%.

Цитировать
Я так же сообщаю пользователям этого метода

Чем же это противоречит тому, что я писал? Все эти модификаторы нужно программисту и только ему.

Цитировать
Не знаю что за ресурс, но даже там ничего не сказано про массивы

Как это? "ixa – неустойчивый массив целых, причем каждый элемент такого массива считается неустойчивым. "

Цитировать
Да именно так, не может присвоить объекты если они были объявлены как волфтильные.

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

Цитировать
Мне кажется мы и так затянули про volatile - так что если возникли вопросы, пишите лично. Более мне тут сказать нечего)

А вот вы зря. Тема эта интересная. И очень полезная. Потому как многопоточность много где применяется


Кстати, я ведь написал, как и что объявлено:

Код:
struct SData
{
 long Data;
};

volatile SData sData_Array[100];

SData sData;
sData.Data=0;
sData_Array[0]=sData; - ошибка, не может выполнить =.

Понимаю, что оператор присваивания вызывался, но по факту-то я просто хочу заместить блок памяти. Что же, через memcpy делать это что ли? Или делать так ((SData*)sData_Array)[0]=sData;

И вот так работает: ((SData*)sData_Array)[0]=sData;

« Последнее редактирование: Марта 19, 2017, 01:32:43 pm от da-nie » Записан

И день и ночь в пути
darkelf
QOR.Moderator
*****
Offline Offline

Сообщений: 256


Просмотр профиля
« Ответ #13 : Марта 20, 2017, 12:15:24 pm »

Цитировать
А механизмы которые полностью удовлетворяют мультипоточности - не требуют volatile
На микроконтроллерах - требуют. Вы ограничиваетесь исключительно теми платформами, которые ваш любимый Си++ 11 поддерживают и отчего-то полагаете, что так везде? Ну так нет.
Сорри, что вмешиваюсь - но микроконтроллеры это своя, очень специфичная область, в которой очень много ньюансов. По моему мнению, volatile, по крайней мере на этапе изобретения, был придуман вовсе не для многопоточности и синхронизации доступа, а для обращения к устройствам, отображенным на память. Для этого он вполне себе подходит, для других вещей, имхо - не очень, если не сказать хуже.

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

А как по-вашему компилятор соображает, что переменная внутри Lock() - Unlock() мютекса и внешняя переменная не volatile типа могут быть связаны? Сам мютекс, как объект, ничего не знает о защищаемых переменных.  
Пример:
Код: (C++)
bool Done=false;

pthread_mutex_t mutex_ID;

//первый поток
void Thread_1(void)
{
 sleep(10);
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=true;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

//второй поток
void Thread_2(void)
{
 pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
 Done=false;
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 sleep(1);
 while(1)
 {
  pthread_mutex_lock(&mutex_ID);//блокируем мьютекс
  if (Done==true) break;
  pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
 }
 pthread_mutex_unlock(&mutex_ID);//разблокируем мьютекс
}

В этом примере if (Done==true) break; компилятор, основываясь только на мютексе, запросто мог бы выкинуть эту строчку. А что? Он ничего не знает о том, что Done способно измениться внезапно. Так мютекс не просто объект ядра, мютекс реализует вот какой механизм:

acquire_barrier(); - барьер захвата
if (Done==true) break;
release_barrier(); - барьер освобождения

Подробнее тут: http://scrutator.me/post/2015/05/16/memory_barriers.aspx
И вот именно поэтому и не нужен volatile внутри мютекса. И компилятор (а не только ядро) всё это знают.
Сорри, не совсем понял, что должен был выкинуть компилятор?

Ну и кроме того - при обсуждении необходимо смотреть стандарты. Функции семейства pthread* покрывает не стандарт C++, и даже не стандарт C, а POSIX - видимо туда и надо смотреть.

Но когда вы начнёте писать для микроконтроллеров, там не будет барьеров и всё это вы будете делать сами с помощью этого самого volatile. Поясню примером.
Допустим, вы в прерывании меняете значение Done. А атомарность обеспечите sei и cli. Всё, что между ними - атомарно (прерывание запрещено). Но компилятор похерит if (Done==true) break; с вероятностью 100%.
По-моему тут есть объяснение почему в обвёртке pthread_mutex_*() всё работает, а в sie/cli - нет. "Any function whose definition is not available in this translation unit (and is not intrinsic) is a compiler memory barrier. pthread_mutex_lock, pthread_mutex_unlock, pthread_create also issue a hardware memory barrier to prevent the CPU from reordering reads and writes." sie/cli - скорее всего реализованы или через inline-ассемблер, или те самые intrinsic-и, а pthread_mutex_lock()/pthread_mutex_unlock() - через внешние, для данного модуля, библиотеки.
« Последнее редактирование: Марта 20, 2017, 12:22:47 pm от darkelf » Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #14 : Марта 20, 2017, 02:42:46 pm »

Цитировать
Сорри, не совсем понял, что должен был выкинуть компилятор?

Да очень простую вещь:

bool done=false;

while(1)
{
 if (done==true) break;
}

Компилятор выбросит if (done==true) break; так как он видит, что done не меняется. При этом если done будет volatile, то компилятор эту строчку не выбросит. Либо если этот контроль будет внутри заблокированного мютекса.

Цитировать
По-моему тут есть объяснение почему в обвёртке

Вот, я и написал, что мютекс - это барьер памяти. Это совершенно особая вещь для компилятора.

И всё же, хоть надежды и нет (эти люди были тут лет 12 назад), может быть, кто-нибудь знакомый с программированием USB драйверов для QNX сможет мне ответить на мои вопросы? Smiley У меня работает, но с "нюансами" и вылетами io-usb.
Записан

И день и ночь в пути
Страниц: [1] 2 3 ... 5
  Печать  
 
Перейти в: