OVH Cloud OVH Cloud

input non-bloquant

3 réponses
Avatar
Samuel Krempp
je viens de découvrir qu'il n'y a pas de moyen portable d'utiliser les
streams C++ avec un comportement non-bloquant garanti.
(c'est à dire leur demander des données si ils en ont, et ne rien faire si
ils ont rien de prêt)

Je comprends bien que la solution la plus complète est de faire un thread
qui gère le stream (ce thread passera la plupart de son temps à attendre
que des appels au stream se terminent) et d'accèder aux données du thread
(si il en a) depuis le thread principal.

Mais dans la mesure ou il y a bcp de streams qui sont en fait
intrinsèquement non-bloquant (sur les OS usuels, cin et cout donnent accès
à des buffers qui sont gèrés par l'os, pas par le programme, si je ne me
méprend pas, et c'est une situation de stream très fréquente),
j'aimerais autant manipuler ces streams directement de manière
non-bloquante, et éviter d'introduire un thread juste pour ça.

J'ai vu que c'est un problème connu, et c'est dommage qu'il n'y ait rien
pour utiliser proprement des streams non-bloquant en C++ portable.

Y-a-t-il des bidouilles portables pour faire ça à peu près convenablement
(ie du code correct, avec un risque éventuel de comportement bloquant selon
la palteforme..) ?

J'expérimente le comportement de streambuf::in_avail() pour voir si on peut
faire du non-bloquant avec, et chez moi (linux, gcc-3.3)
cin.rdbuf()->in_avail() est non bloquant et permet de bidouiller un peu,
par exple :

std::string s;
std::vector<char> v;
int navail;
size_t waits = 0;
while(std::cin && (s.size()==0 || s[s.size()-1]!='.') ) {
// s'arrete sur EOF, mais aussi sur '.' en fin de chaine
if((navail=cin.rdbuf()->in_avail() > 0 )) {
//s += "_"; // montre les bouts de in_avail
size_t sz = s.size();
v.resize(navail);
cin.read(& v[0], navail);
s.append(& v[0], navail);
}
else {
waits++;
sleep(1);
//std::cout<< "\n s==["<< s <<"]" << std::endl;
}
}
std::cout<< "\n--waits="<< waits<<" Final s==["<< s <<"]" << std::endl;

chez moi cet exemple montre que :
-le code est bien non-bloquant (pour un input donné, le programme s'éxécute
en 18.30s, et il y a eu 18 waits)
-in_avail() vaut 0 tant qu'il n'y a pas une ligne dans cin
-in_avail() vaut au plus 1
-décommenter la ligne avec cout fait tout cafouiller

Est-ce qu'on a aussi un in_avail() non-bloquant sur les autres plateformes
usuelles ? (windows, posix, avec les compilos habituels..)
Qu'est ce que cet exemple donne sur vos systemes ?

--
Sam

3 réponses

Avatar
Loïc Joly
Samuel Krempp wrote:

[...]

J'expérimente le comportement de streambuf::in_avail() pour voir si on peut
faire du non-bloquant avec, et chez moi (linux, gcc-3.3)
cin.rdbuf()->in_avail() est non bloquant et permet de bidouiller un peu,
par exple :

std::string s;
std::vector<char> v;
int navail;
size_t waits = 0;
while(std::cin && (s.size()==0 || s[s.size()-1]!='.') ) {
// s'arrete sur EOF, mais aussi sur '.' en fin de chaine
if((navail=cin.rdbuf()->in_avail() > 0 )) {
//s += "_"; // montre les bouts de in_avail
size_t sz = s.size();
v.resize(navail);
cin.read(& v[0], navail);
s.append(& v[0], navail);
}
else {
waits++;
sleep(1);
//std::cout<< "n s==["<< s <<"]" << std::endl;
}
}
std::cout<< "n--waits="<< waits<<" Final s==["<< s <<"]" << std::endl;


Sans aller plus loin, une solution à base de wait ne m'intéresse pas
dans la plupart de mes applications. C'est une solution soit trop
gourmande en ressources, soit pas assez réactive.

--
Loïc

Avatar
Samuel Krempp
le Thursday 15 January 2004 23:12, écrivit :

else {
waits++;
sleep(1);
//std::cout<< "n s==["<< s <<"]" << std::endl;
}
}
std::cout<< "n--waits="<< waits<<" Final s==["<< s <<"]" << std::endl;


Sans aller plus loin, une solution à base de wait ne m'intéresse pas
dans la plupart de mes applications. C'est une solution soit trop
gourmande en ressources, soit pas assez réactive.


hmm, j'imagine que ça dépend des applications. Pour du real-time ya de
fortes chances qu'il faudra de toute façon utiliser des features hyper
spécifiques au cas précis.
Je voudrais juste un moyen de faire que le programme fasse ce qu'il veut, et
regarde si il y a de l'input quand il le veut. Dans mon exemple j'ai rien à
faire, donc j'ai juste mis un sleep au lieu du corps du programme.
Le genre de truc que j'imagine, c'est un programme qui fait son truc, mais à
chaque itération de sa boucle jette un oeil sur un flux de contrôle. (pour
des messages, genre "stop", qui n'ont pas besoin d'être traité super
instantanément).

Mais en fait même si on veut une bonne réactivité, ce design peut très bien
fonctionner (ça sera au scheduler de l'os de bien faire son boulot). J'ai
un soft qui mets du son dans un buffer de 500ms, puis se mets en pause pour
70ms avant de recommencer à remplir tout ce qu'il peut, et je n'ai pas
constaté de problème dû à ce design processing/sleep/processing. La
réactivité est irréprochable, et la consommation de ressources négligeable.
Je pense que c'est parfaitement viable tant que le délai maximum toléré
reste assez loin de la granularité du scheduler.

Tes applications demandent une réactivité largement meilleure que cet ordre
de grandeur ?
et d'ailleurs tu fais comment, tu mets ton programme dans un sommeil spécial
qui s'interromp en cas d'input ? C'est le genre de truc qui est
particulièrement non-portable, ou bien l'existence de possibilités
similaires sur qques OS permet d'unifier ça dans une interface C++
commune ?


Enfin bref, de toute façon même si on ne cherche pas une bonne réactivité,
on peut avoir besoin d'utiliser cin en non-bloquant..

je vais voir du coté de ACE, il paraitrait que y ait des choses sur ce
thème.

--
Sam


Avatar
Loïc Joly
Samuel Krempp wrote:

le Thursday 15 January 2004 23:12, écrivit :


else {
waits++;
sleep(1);
//std::cout<< "n s==["<< s <<"]" << std::endl;
}
}
std::cout<< "n--waits="<< waits<<" Final s==["<< s <<"]" << std::endl;


Sans aller plus loin, une solution à base de wait ne m'intéresse pas
dans la plupart de mes applications. C'est une solution soit trop
gourmande en ressources, soit pas assez réactive.



hmm, j'imagine que ça dépend des applications. Pour du real-time ya de
fortes chances qu'il faudra de toute façon utiliser des features hyper
spécifiques au cas précis.
Je voudrais juste un moyen de faire que le programme fasse ce qu'il veut, et
regarde si il y a de l'input quand il le veut. Dans mon exemple j'ai rien à
faire, donc j'ai juste mis un sleep au lieu du corps du programme.


Ce cas me convient déjà plus qu'un sleep, même s'il a le problème que
potentiellement, quand une touche est frappée, le programme risque de
prendre trop de temps. Nous avons tenté une approche similaire sur une
de nos applications, et avons du revenir en arrière.

Le genre de truc que j'imagine, c'est un programme qui fait son truc, mais à
chaque itération de sa boucle jette un oeil sur un flux de contrôle. (pour
des messages, genre "stop", qui n'ont pas besoin d'être traité super
instantanément).

Mais en fait même si on veut une bonne réactivité, ce design peut très bien
fonctionner (ça sera au scheduler de l'os de bien faire son boulot). J'ai
un soft qui mets du son dans un buffer de 500ms, puis se mets en pause pour
70ms avant de recommencer à remplir tout ce qu'il peut, et je n'ai pas
constaté de problème dû à ce design processing/sleep/processing. La
réactivité est irréprochable, et la consommation de ressources négligeable.
Je pense que c'est parfaitement viable tant que le délai maximum toléré
reste assez loin de la granularité du scheduler.

Tes applications demandent une réactivité largement meilleure que cet ordre
de grandeur ?


Oui, hélàs. Je travaille sur des simulateurs de conduite, qui doivent
traiter des nouvelles données à chaque pas de temps, et, suivant les
parties, on peut vouloir des temps proches de la ms. On est en fait aux
limites de ce qu'on peut faire sans OS temps réel.

et d'ailleurs tu fais comment, tu mets ton programme dans un sommeil spécial
qui s'interromp en cas d'input ?


Généralement, les entrées/sorties sont effectuées dans un thread séparé,
de priorité moindre.

C'est le genre de truc qui est
particulièrement non-portable, ou bien l'existence de possibilités
similaires sur qques OS permet d'unifier ça dans une interface C++
commune ?


Les OS que nous visons sont Windows, Linux et Irix. Pour les threads,
quelquechose comme ce qu'il y a dans boost est suffisant, pour ce qui
est de cadencer un exécutable, un interface commune est facilement
envisageable (il est d'ailleurs amusant que je n'ai rien vu ou presque
en matière de bibliothèque portable visant à assurer qu'une tâche
s'exécutera à une fréquence spécifiée).

Enfin bref, de toute façon même si on ne cherche pas une bonne réactivité,
on peut avoir besoin d'utiliser cin en non-bloquant..


Oui, certes, j'ai juste voulu dans mon message réagir au slepp que j'ai
déjà vu dans la vraie vie employé à des endroits totalement inapropriés.

--
Loïc