-
1. Data: 2020-06-08 23:58:58
Temat: Integracja bibliotek event-based
Od: Maciej Sobczak <s...@g...com>
To jest wyciągnięty pod-wątek z wątku o serwerze HTTP.
Powiedzmy, że program używa 3 bibliotek event-based. Żeby nie trzeba było mieć wątków
i innych muteksów.
W jednej trzeba zrobić server->next(), w drugiej agent.do_some_work() a w trzeciej
worker::process(). Powiedzmy, że te funkcje od razu wychodzą jak nie mają nic do
roboty, a jak mają, to szybko robią (bo na nic nie czekają) i też wychodzą.
Co należy zrobić, żeby użyć ich razem w jednym programie?
Takie coś:
while (true)
{
server->next();
agent.do_some_work();
worker::process();
}
jest złe, bo kręci się w kółko i zużywa prąd nawet (zwłaszcza) wtedy, gdy nie ma nic
do zrobienia. Nie chcemy tak.
Jakie jest poprawne rozwiązanie dla integracji tych 3 bibliotek?
--
Maciej Sobczak * http://www.inspirel.com
-
2. Data: 2020-06-09 00:32:25
Temat: Re: Integracja bibliotek event-based
Od: heby <h...@p...onet.pl>
On 08/06/2020 23:58, Maciej Sobczak wrote:
> Jakie jest poprawne rozwiązanie dla integracji tych 3 bibliotek?
To zależy co robią.
Jeśli ich zadaniem jest coś policzyć i zniknąć to masz dobrze.
Jesli ich zadaniem jest coś policzyć tylko jak będzie potrzeba to od
tego masz wsparcie OSa i jego pętlę komunikatów. Być może też jakieś
wątki pod spodem, ale klient o nich nie wie, bibliteka często też nie
wie. Ogólnie cooperative multitasking bazuje na tym że robisz jak
przychodzi jakiś event. Systemowy zazwyczaj. Można poprosić aby
przychodził non-stop jeden za drugim. Każdemu w/g potrzeb.
Oba podejścia są od dziesiątek lat katowane choćby w Qt i masz tam jakiś
obraz tego jak zrobić 1) (onIdle()) i jak zrobić 2) (push event). Mogą z
sobą działać bardzo gładko i elegancko.
Mam duże (w sensie wielopoziomoe) algorytmy rozbite na pojednycze kroki
i popychane z onIdle z głownej pętli. Spadek wydajności jest, ale
pomijalny, choć oczywiscie wszystko zależy od tego jak realizowany jest
yeld(). To podejście jest nie dla sztuki, tylko warunki brzegowe
utrudniają użycie watków. Na szczęscie nie ma najmniejszego problemu, w
dodatku mogę takie coroutines wrzucić *dynamicznie* do wątku i z niego
wyjąć kiedy tylko chcę, na przykład w połowie pracy.
Teoretycznie każdy algorytm można przerobić na coroutine za pomocą
boost::context ale koszt może być wysoki, to wszak przełącza kontekst,
prawie jak wątek. W przypadku ręcznym najczęściej pasuje jakiś wzorzec
typu maszyn stanów. Do ogarnięcia w typowych przypadkach, rekurencja
jest bardziej bolesna, ale niewiele.
-
3. Data: 2020-06-09 00:38:22
Temat: Re: Integracja bibliotek event-based
Od: heby <h...@p...onet.pl>
On 09/06/2020 00:32, heby wrote:
> wie. Ogólnie cooperative multitasking bazuje na tym że robisz jak
> przychodzi jakiś event. Systemowy zazwyczaj. Można poprosić aby
> przychodził non-stop jeden za drugim. Każdemu w/g potrzeb.
z grubsza to jest tak:
std::vector< jobs > jobs;
Scheduler::onIdle()
{
if ( somethingToDo( jobs) )
{
sheduleNextOnIdleEvent();
doSomething( shuffle( jobs ) );
}
// stop onIdle callbacks by doing nothing
}
I to samoczynnie się zatrzyma jak coroutines zakończą pracę.
Mechanizm onIdle jest systemowy i jest drugorzędne jak działa.
-
4. Data: 2020-06-09 09:46:40
Temat: Re: Integracja bibliotek event-based
Od: Mateusz Viste <m...@x...invalid>
2020-06-08 o 14:58 -0700, Maciej Sobczak napisał:
> while (true)
> {
> server->next();
> agent.do_some_work();
> worker::process();
> }
>
> jest złe, bo kręci się w kółko i zużywa prąd nawet (zwłaszcza) wtedy,
> gdy nie ma nic do zrobienia. Nie chcemy tak.
Zdecydowanie nie chcemy. 30 lat temu na 386 to jeszcze uchodziło, bo
program był jeden na raz a i prądożernośćią CPU nikt się nie
przejmował. Dziś trochę wstyd, kiedy po uruchomieniu programu
wentylator w laptopie zaczyna się kręcić. :)
> Jakie jest poprawne rozwiązanie dla integracji tych 3 bibliotek?
To zależy co one mają robić. Skoro mowa o serwerze, to domniemywam że
punktem wejścia jest pojawienie się jakiegoś nowego stanu na
sockecie. Jeśli tak, to sprawa prosta:
for (;;) {
select(sockfd+1, &socksetr, &socksetw, &socksete, &timeout);
/* TODO obsługa SIGINT */
/* TODO obsługa timeout */
server->next();
agent.do_some_work();
worker::process();
}
Mateusz
-
5. Data: 2020-06-09 22:58:58
Temat: Re: Integracja bibliotek event-based
Od: Maciej Sobczak <s...@g...com>
W dniu wtorek, 9 czerwca 2020 09:46:44 UTC+2 użytkownik Mateusz Viste napisał:
> > Jakie jest poprawne rozwiązanie dla integracji tych 3 bibliotek?
>
> To zależy co one mają robić.
Każda robi coś innego.
> Skoro mowa o serwerze, to domniemywam że
> punktem wejścia jest pojawienie się jakiegoś nowego stanu na
> sockecie.
Ale my nie widzimy tych socketów, bo one są ukryte w tych bibliotekach. Jedna
obsługuje HTTP, druga asynchroniczną bazę danych a trzecia odbiera tweety z Twittera.
Każda z nich ma zapewne jakieś sockety, ale nie wiemy o tym.
> Jeśli tak, to sprawa prosta:
>
> for (;;) {
> select(sockfd+1, &socksetr, &socksetw, &socksete, &timeout);
No więc wcale nie jest prosta, bo nie możemy zrobić tego selecta.
Możemy próbować to, co pokazał heby z onIdle i podobnymi, ale co jak mamy nie jedną,
ale właśnie 3 biblioteki? Żadna z nich nie może zablokować żadnej z wywołanych
funkcji (to jest błąd u heby[*]), bo to zatrzyma pętlę, a być może pozostałe
biblioteki mają wtedy coś do zrobienia.
Więc gramy dalej. :-)
[*] Kurdę, łatwiej się odmienia "u Sebastiana", niż "u heby".
--
Maciej Sobczak * http://www.inspirel.com
-
6. Data: 2020-06-09 23:18:35
Temat: Re: Integracja bibliotek event-based
Od: heby <h...@p...onet.pl>
On 09/06/2020 22:58, Maciej Sobczak wrote:
> Żadna z nich nie może zablokować żadnej z wywołanych funkcji (to jest błąd u
heby[*])
To jest kontrakt. Jesli go złamiesz to masz problem. Więc to nie jest
błąd, bo jeśli tak to taki sam błąd możesz zrobić w preemptive blokując
muteks i wyrzucając klucz do kibla.
> [*] Kurdę, łatwiej się odmienia "u Sebastiana", niż "u heby".
To odmieniaj. Eternal Speptember randomicznie podpisuje mi maile, raz
heby z adresu raz pełnym podpisem. Ch... wie dlaczego. Pewno nie mają
unit testów ...
-
7. Data: 2020-06-10 10:56:25
Temat: Re: Integracja bibliotek event-based
Od: Mateusz Viste <m...@x...invalid>
2020-06-09 o 13:58 -0700, Maciej Sobczak napisał:
> Ale my nie widzimy tych socketów, bo one są ukryte w tych
> bibliotekach.
To jakieś strasznie opakowane te biblioteki, że nie pozwalają dobrać
się do źródłowego socketa.
> Jedna obsługuje HTTP, druga asynchroniczną bazę danych
> a trzecia odbiera tweety z Twittera. Każda z nich ma zapewne jakieś
> sockety, ale nie wiemy o tym.
A co mówi dokumentacja tych bibliotek? Chyba ich autorzy nie zakładają,
że ktoś będzie busy loop tworzył czekając na tweeta?
Jeśli chodzi o asynchroniczną bazę danych, to klient wysłał jakieś
polecenia i czeka na odpowiedź, jednocześnie robiąc coś innego. W
takiej sytuacji wystarczy odczekać chwilę (np. pustym selectem) i
sprawdzać jakimś db_isBusy() czy baza skończyła co miała robić. W
zależności od założeń projektowych tę samą metodę można zastosować dla
tweetów i "obsługi http" (cokolwiek by miało to znaczyć).
Słowem - to samo co sugerowałem wcześniej, tyle że z pustym sockset w
select() i sztywnym timeout, stosownie dobranym by niczego nie
zakorkować. Ale to tylko jeśli biblioteki faktycznie nie udostępniają
jakichkolwiek mechanizmów sygnalizacji swoich potrzeb.
Mateusz
-
8. Data: 2020-06-10 23:19:42
Temat: Re: Integracja bibliotek event-based
Od: Maciej Sobczak <s...@g...com>
> > Ale my nie widzimy tych socketów, bo one są ukryte w tych
> > bibliotekach.
>
> To jakieś strasznie opakowane te biblioteki, że nie pozwalają dobrać
> się do źródłowego socketa.
Jest gorzej. To nie muszą być sockety. Np. system QNX ma fajny mechanizm komunikacji
IPC, który istnieje poza przestrzenią deskryptorów plików. A ktoś inny będzie się
komunikował przez pamięć dzieloną. Itd. Więc nawet gdybyś miał dostęp do bebechów, to
i tak nie zapiąłbyś wszystkiego razem jednym selectem.
No i nie możemy zakładać, że socket jest jeden, albo że ciągle ten sam, itd. Straszne
komplikacje.
> A co mówi dokumentacja tych bibliotek?
To, co napisałem. Że wymienione funkcje robią robotę i wychodzą albo od razu
wychodzą.
> Chyba ich autorzy nie zakładają,
> że ktoś będzie busy loop tworzył czekając na tweeta?
Nie musi być busy loop. Bo możemy też założyć, że w każdej bibliotece jest dodatkowa
funkcja wait_for_work(). Albo że te funkcje mają dodatkowy argument, żeby czekać. Tak
jest w YAMI4. Wtedy pętla użytkownika nie musi być busy - po prostu blokuje się, jak
nie ma nic do roboty.
Ale mamy 3 takie funkcje. I co teraz?
> W
> takiej sytuacji wystarczy odczekać chwilę
Dalej kiepsko, bo chcemy bez opóźnień przetwarzać zadania, jeśli są dostępne, i nie
marnować prądu, jeśli nie są.
> Ale to tylko jeśli biblioteki faktycznie nie udostępniają
> jakichkolwiek mechanizmów sygnalizacji swoich potrzeb.
Dobry trop.
To w jaki sposób chciałbyś być poinformowany, że biblioteka ma potrzebę?
Przypominam, że mamy 3 biblioteki. Przyjmijmy wersję idealną, że wszystkie 3 będą
mieć ten mechanizm. Jaki?
--
Maciej Sobczak * http://www.inspirel.com
-
9. Data: 2020-06-11 10:37:32
Temat: Re: Integracja bibliotek event-based
Od: Mateusz Viste <m...@x...invalid>
2020-06-10 o 14:19 -0700, Maciej Sobczak napisał:
> Jest gorzej. To nie muszą być sockety. Np. system QNX ma fajny
> mechanizm komunikacji IPC, który istnieje poza przestrzenią
> deskryptorów plików. A ktoś inny będzie się komunikował przez pamięć
> dzieloną.
Trzymajmy się założeń: była mowa o bibliotece "do http", innej "do
tweetera" i trzeciej "baza danych". Każda z nich raczej (?) opiera się
na socketach.
> No i nie możemy zakładać, że socket jest jeden, albo że ciągle ten
> sam, itd. Straszne komplikacje.
Jeśli jest ten sam to komplikacji nie ma - jeśli się urywa to
biblioteka zwraca błąd i trzeba zainicjalizować na nowo. Jeśli
natomiast socket się tworzy nowy przy każdym zawołaniu (np. sprawdzanie
tweetów via nową sesję ssl za każdym razem), to każde sprawdzenia
powinno być regularnie wywoływane przez program.
> dodatkowy argument, żeby czekać. Tak jest w YAMI4. Wtedy pętla
> użytkownika nie musi być busy - po prostu blokuje się, jak nie ma nic
> do roboty.
Po bardzo pobieżnym przejrzeniu rozumiem to tak, że inspirel oczekuje
że stworzysz swój własny wątek specjalnie dla wołania ich biblioteki:
"All activity has to be driven by the application, which is supposed to
'lease' one of its threads for doing the I/O work on behalf of the
agent. This is achieved by calling the Do_Some_Work procedure in Ada
or the do_some_work function in C++.
The 'do some work' routine performs a single unit of work and
returns to the caller. In a typical application it will be called in
a tight loop."
W takiej sytuacji niewiele można zrobić. Patrz niżej.
> > Ale to tylko jeśli biblioteki faktycznie nie udostępniają
> > jakichkolwiek mechanizmów sygnalizacji swoich potrzeb.
>
> Dobry trop.
> To w jaki sposób chciałbyś być poinformowany, że biblioteka ma
> potrzebę?
>
> Przypominam, że mamy 3 biblioteki. Przyjmijmy wersję idealną, że
> wszystkie 3 będą mieć ten mechanizm. Jaki?
Idealnie? No to chcę sam obsługiwać kanał danych (socket, IPC,
whatever), a bibliotekę będę wołał tylko po to, żeby te dane
obrobić/przetłumaczyć. Alternatywnie - niech biblioteka da dostęp do
socketów, wówczas wiem, że jak coś się zmieni na kablu to powinien
zawołać kogoś do obsługi. Ostatnia opcja: niech biblioteka da jakiś
swój blokujący "wait_for_event()", sama niech blokuje w sposób
eko-mądry i zależny od implementacji, a ja sobie ją wywołam w osobnym
wątku. 3 biblioteki = 3 wątki, da się przeżyć.
Mateusz
-
10. Data: 2020-06-11 20:00:34
Temat: Re: Integracja bibliotek event-based
Od: Maciej Sobczak <s...@g...com>
> Trzymajmy się założeń: była mowa o bibliotece "do http", innej "do
> tweetera" i trzeciej "baza danych".
Nie. Była mowa o integracji kilku bibliotek event-based.
Przykłady dot. HTTP, bazy danych i Twittera to były *przykłady* do zobrazowania
problemu a nie konkretne zadania do rozwiązania.
Właściwym problemem jest integracja bibliotek narzucających jakiś idiom czy wzorzec
ich obsługi.
> Każda z nich raczej (?) opiera się
> na socketach.
Nie. W szczególności biblioteka do baz danych automatycznie dobierze metodę
komunikacji zależnie od tego, gdzie jest baza. W szczególności, jeśli baza jest na
tym samym komputerze, to istnieją sprawniejsze metody komunikacji, niż sockety.
Wyobraź sobie dla jeszcze szerszego spektrum, że jedna z tych bibliotek to zwykłe
GUI, gdzie "gotowość" to event w kolejce, bo użytkownik coś kliknął.
Czyli tu nie chodzi o sockety. Tu chodzi o integrację bibliotek.
> > No i nie możemy zakładać, że socket jest jeden, albo że ciągle ten
> > sam, itd. Straszne komplikacje.
>
> Jeśli jest ten sam to komplikacji nie ma - jeśli się urywa to
> biblioteka zwraca błąd i trzeba zainicjalizować na nowo. Jeśli
> natomiast socket się tworzy nowy przy każdym zawołaniu (np. sprawdzanie
> tweetów via nową sesję ssl za każdym razem), to każde sprawdzenia
> powinno być regularnie wywoływane przez program.
Nie na temat, ale dalej źle. Przykładowo, YAMI4 może mieć tysiące socketów. Pomijając
drobny fakt, że select() nie nadaje się do obsługi tysięcy socketów, biblioteka
stosuje algorytmy control-flow decydujące o tym, który kanał w ogóle albo w danym
momencie zasługuje na obsługę. Czyli z faktu, że wśród tysięcy socketów kilkaset ma
dane gotowe do odczytu wcale nie wynika, że to jest stan gotowości do wykonania
zadania, bo akurat te sockety z gotowymi danymi nie muszą mieć "zielonego światła" na
ruch komunikatów i może się okazać, że wtedy nadal jest "idle". O tym decyduje
biblioteka.
Dlatego nie możesz się nastawiać na to, że będziesz sobie sam obserwował te sockety,
bo i tak nie masz kontekstu do pełnej oceny sytuacji.
Co więcej, jest kompletnie bez sensu, żebyś duplikował ciężką pracę, którą biblioteka
i tak robi w środku.
> > Przypominam, że mamy 3 biblioteki. Przyjmijmy wersję idealną, że
> > wszystkie 3 będą mieć ten mechanizm. Jaki?
>
> Idealnie? No to chcę sam obsługiwać kanał danych (socket, IPC,
> whatever), a bibliotekę będę wołał tylko po to, żeby te dane
> obrobić/przetłumaczyć.
Już ustaliliśmy (powyżej), że nie.
> Ostatnia opcja: niech biblioteka da jakiś
> swój blokujący "wait_for_event()", sama niech blokuje w sposób
> eko-mądry i zależny od implementacji, a ja sobie ją wywołam w osobnym
> wątku. 3 biblioteki = 3 wątki, da się przeżyć.
Świetnie.
Doszedłeś w ten sposób do wniosku, że do obsługi *kilku* bibliotek event-based
potrzebne są wątki. Albo wyjdą z biblioteki albo do niej wejdą. Ale będą potrzebne.
Głębsza analiza wygląda tak:
Pojęcie "idle" lub "nie ma zadania", itp. to pojęcia *lokalne*, zrozumiałe tylko w
obrębie jednej biblioteki. Tzn. w bibliotece GUI to pojęcie oznacza, że user nic nie
kliknął, w bibliotece komunikacyjnej że nie ma komunikatu, w bibliotece do baz danych
że nie ma jeszcze odpowiedzi na wcześniejsze zapytanie, itd. To są stany *lokalne*.
Jeżeli program jest w całości oparty o rytm pracy *jednej* biblioteki (tak jak w
prostym GUI opartym o zdarzenia z okna), to ten lokalny koncept jest też globalny,
czyli dotyczy całego programu. Wtedy nie są potrzebne ani wątki, ani muteksy.
Ale jeśli mamy kilka takich bibliotek, każda ze swoim własnym konceptem "idle", to
ich integracja jest problemem, bo globalnie "idle" oznacza, że *nikt* nie ma nic do
roboty a tego stanu nie da się uwspólnić w jednym wątku, bez ciężkich kompromisów
typu kręcenie się w pętli z odpytywaniem (nawet z jakimś mniej lub bardziej
przemyślanym opóźnieniem).
Dlatego albo:
1. pozwolimy tym bibliotekom niezależnie informować o swoim bieżącym stanie przez
callbacki wychodzące z tych bibliotek (w takim callbacku można np. odetkać główną
pętlę czekającą na jakiejś fladze stopu) (co wymaga istnienia wątków w tych
bibliotekach), albo
2. zrobimy sobie osobne wątki do kręcenia blokującymi pętlami dla każdej biblioteki
osobno, albo
3. w ogóle schowamy ten koncept "idle" razem z pętlami zdarzeń i pozwolimy
bibliotekom kręcić swoimi zdarzeniami samodzielnie (z własnych wątków).
Tak czy inaczej mamy wątki. I ich konsekwencje.
Ktoś ma pomysł na rozwiązanie tego problemu bez wątków?
--
Maciej Sobczak * http://www.inspirel.com