[ leka @ 15.01.2002. 20:00 ] @
Dakle evo ukratko o reentrantnim funkcijama... Sam taj pojam vezan je
za problem sa kojim su se susretali programeri koji su pisali programe
koji su trebali da rade u multitaskingu.
Zamislimo ovakav slucaj - imamo vunkciju proveri_vreme() koja je recimo
iz neke nase biblioteke, koja nam vraca trenutno vreme, recimo kao
timestamp, a koja normalno radi super. Hajde da malo dodatno
zakomplikujemo stvari. Recimo da je je u jednom trenutku vremena nasa
rutina pocela da ispisuje trenutno vreme - "Fri, Sep 6, 1991 at 12:03:44 PM by DL".
U jednom trenutku (sto se uvek desava!) rutinu je prekinuo sistem, jer
je vreme da neki drugi proces nastavi sa svojim radom (tako to ide u
multitaskingu...). Znaci rutina je ispisivala "F", pa "r", pa "i" i
PAF!- prekinuo je sistem! Recimo da je sistem "predao stafetu" drugom
procesukoji takodje koristi istu ovu rutinu! To se zove "system
REENTERS again the same routine" na anglijskom - sve Vam je jasno sada
zasto se citava stvar zove reentrantne funkcije i slicno... Dakle drugi
proces je pozvao proveri_vreme() a posto nasa jadna funkcija nije
reentrantna, ona iz pocetka krece da ispisuje trenutno vreme! :( Na
kraju imamo rezultat da na izlazu vidimo rezultat u obliku "FriFri, Sep
6, Se,...", kao sto vidite, rezultati su se pomesali!
Ovaj primer je najlaksi, i jedan od najlaksih primera pomocu kojih se
mogu objasniti slicne pojave, na ovome se ne zavrsavaju problemi
pisanja aplikacija za multitasking, to je samo pocetak nazalost.
Srecom, danas ima vise kvalitetnih biblioteka koje u sebi sadrze sve
potrebno za pisanje kvalitetnog softvera u multitaskingu.
[ Dragi Tata @ 16.01.2002. 02:06 ] @
Aha! Počeli smo sa konkurentnim programiranjem, znači. Može, može...

Problem koji si opisao ću da ilustrujem na jednom prostom primeru:

Code:


int temp; /*globalna promenljiva */

void swap (int* a, int* b)
{
    temp = *a;
    *a = *b;
    *b = temp;
}



Šta fali gornjoj funkciji? Ako je program sa jednom niti (singlethreaded), ništa,
osim što je glupo koristiti globalne promenljive u ovakvom kontekstu, ali program će lepo da radi. Problemi nastaju ako imamo više niti i iz njih pozivamo ovu funkciju. Tako jedna nit može da postavi vrednost za temp, a da joj druga nit promeni tu vrednost, pre nego što je b dobilo odgovarajuću vrednost, i onda smo u g... do guše.

Rešenje je da se gornji primer malo izmeni, da bude:

Code:

void swap (int* a, int* b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}



Ovde je temp definisana kao lokalna promenljiva na steku,
i svaka nit pri pozivu ove funkcije kreira svoju verziju temp.

Primer koji sam naveo je malo "nategnut", ali u praksi sam imao silne glavobolje rešavajući ovakve probleme. Čovek napiše kod koji radi bez greške i prođe unit - testing, a kad ga ja ugradim u višenitnu aplikaciju, puca k'o da je od stakla.

Posebna je priča što su originalne C biblioteke pisane bez razmišljanja o ovakvim situacijama (u to vreme Unix nije imao podršku za višenitno programiranje), pa su i pojedine C funkcije (malloc, free, strok, itd) umele da se "ponašaju" u višenitnom okruženju, ali je to u međuvremenu uglavnom zakrpljeno.

Što se Microsoft-a tiče, oni daju posebnu verziju CRT-a za jednonitne, a posebnu za višenitne aplikacije, tako da ako linkuješ program sa pravom verzijom, ovakvih problema nema.

Tema je interesantna, i stvarno može mnogo da se priča o tome. Međutim, konkurentno programiranje nije direktno podržano u standardnom C-u, a ni C++ u (mada postoji biblioteka za ovo u okviru Boost projekta), pa bi priča lako mogla da sklizne u mutne vode operativnih sistema i njihovih specifičnosti na tom planu. U principu, nemam ništa protiv, ako se moderatori slože. elitno :) triote, a drzavu ze
[ leka @ 16.01.2002. 20:14 ] @
Heh, tako je zapravo i resen problem u bibliotekama za "niti" - pokazivacima.

Inace, primer je odlican, dobio si peticu ;)


[ leka @ 16.01.2002. 20:16 ] @
GNU kolekcija kompajlera (novije verzije) imaju potpunu podrsku za konkurentno programiranje...
[ anatogen @ 16.01.2002. 23:51 ] @
Citat:
leka:
Znaci rutina je ispisivala "F", pa "r", pa "i" i
PAF!- prekinuo je sistem! Recimo da je sistem "predao stafetu" drugom
procesukoji takodje koristi istu ovu rutinu! To se zove "system
REENTERS again the same routine" na anglijskom - sve Vam je jasno sada
zasto se citava stvar zove reentrantne funkcije i slicno...


Ako je ta funkcija sto ispisuje vreme na primer kernel funkcija valjda se na taj deo koda lupi semafor i ne ulazi se ponovo u tu funkciju dok proces koji je prvo usao u nju ne zavrsi i inkrementira smafor...
[ Dragi Tata @ 17.01.2002. 17:03 ] @
Citat:
anatogen:
Ako je ta funkcija sto ispisuje vreme na primer kernel funkcija valjda se na taj deo koda lupi semafor i ne ulazi se ponovo u tu funkciju dok proces koji je prvo usao u nju ne zavrsi i inkrementira smafor...


Uopšte nije problem u kernel funkcijama. Ako kernel već podržava rad sa više niti, onda je dovoljno pametan da ne pravi switch u toku izvršavanja kernel funkcija. Međutim, CRT funkcije su već nešto drugo. Ako pod Windows-om linkuješ program sa jednonitnom verzijom CRT-a, a onda ipak napraviš višenitnu aplikaciju, desiće ti se baš ono što je Leka opisao.
[ anatogen @ 18.01.2002. 01:02 ] @
Ma znam da nije rec o kernel funkcijam nego cisto pomenuh kako se u kernelu ceka na kod, pa mozda i kod multithread programiranja postoje slicne fore kao sto su semafori itd...
mislim planiram u dogledno vreme polako da se upoznajem sa tim pa bi bilo lepo da se ovde vidi jedna dobra diskusija na tu temu...
[ ned @ 18.01.2002. 09:01 ] @
Ovaj primer nije bas najbolji posto timestamp moze da bude potpuno
dobro sagradjena, tj. "reentrant" je u tome smislu da jedan primer
egsekucije formira svoju poruku bez ikakve moguce smetjne od strane
neke druge egsekucie (rada?) od nekog drugog procesa ili nita.
Problem je sto izlazna funkcija negde u biblioteci ima svojih problema.
Taj tekst "Fri, Sep 6, " ce se stampati najcesce sa funkcijom
printf() ili puts() a ta funkcije, iako imaju definitan posao da
otstampaju seriju slova, mozda ne stite tu operaciju.
Moje iskustvo sa 'mutithreaded" programima u MS je da printf()
ce retko pomesati tekst is raznih niti ali i nema nikakve zastite.
Ako se upotrebi C++ streams onda se dobije absolutna mesavina slova
tako da se ne moze upotreblavati za 'debug' poruke.
Zastiti operaciju funkcije proveri_vreme() sa nekim semaforom ce samo
serializovati operaciju te funkcije. Neki drugi process ili nit je slobodan
da stampa kroz printf() bez smetnje. Serializovati printf() i slicne je
neprakticno.






[ doomed @ 18.01.2002. 12:57 ] @
Citat:
ned:
Zastiti operaciju funkcije proveri_vreme() sa nekim semaforom ce samo
serializovati operaciju te funkcije. Neki drugi process ili nit je slobodan
da stampa kroz printf() bez smetnje. Serializovati printf() i slicne je
neprakticno.


Funkciju treba zastiti sa kriticnom sekcijom. Kriticna sekcija neophodna ( ili barem najpovoljnija ) kada u funkciji imamo neku static promenljivu. Mada reetrant funkcije sa static promenljivima i nije najsrecnije resenje
[ Dragi Tata @ 18.01.2002. 22:16 ] @
Čisto da situacija bude malo jasnija, evo ga source za printf() iz Microsoft-ovog CRT-a:

Code:

int __cdecl printf (
        const char *format,
        ...
        )
/*
 * stdout 'PRINT', 'F'ormatted
 */
{
        va_list arglist;
        int buffing;
        int retval;

        va_start(arglist, format);

        _ASSERTE(format != NULL);

        _lock_str2(1, stdout);

        buffing = _stbuf(stdout);

        retval = _output(stdout,format,arglist);

        _ftbuf(buffing, stdout);

        _unlock_str2(1, stdout);

        return(retval);
}



Jeste li primetili funkcije _lock_str2 i _unlock_str2 ? One služe upravo da obezbede da se ne desi ono
o čemu je Leka pisao.
Ove funkcije su definisane samo ako je definisan makro _MT (multi threaded), a u suprotnom njihova definicija izgleda ovako:

Code:

#else  /* defined (_MT) */


/* macros */
#define _lock_fh(fh)
#define _lock_str(s)
#define _lock_str2(i,s)
#define _lock_fh_check(fh,flag)
#define _mlock(l)
#define _munlock(l)
#define _unlock_fh(fh)
#define _unlock_str(s)
#define _unlock_str2(i,s)
#define _unlock_fh_check(fh,flag)

#define _lock_locale(llf)
#define _unlock_locale(llf)

#endif  /* defined (_MT) */



tj NIŠTA.

Znači, u skladu sa ranije pomenutim: Kod Windows-a (a verujem da je slično i kod Linux-a, ali nisam siguran) postoji jednonitna verzija CRT-a, kada printf nije "reentrantan" ali je zato nešto brži, i višenitna verzija kod kojih su "lock" funkcije definisane i koje obezbeđuju da printf radi bezbedno u višenitnom okruženju, mada malo sporije.
[ leka @ 20.01.2002. 16:13 ] @
Critical Sections je kul stvar, ali nije portabilna... Ja to nisam koristio moram priznati, jer ako nesto koristim za multinitne aplikacije onda je to GNU Portable Threads ...

Ima par veoma portabilnih biblioteka a jedna od njih je svakako Posix Threads, koju mozete da nadjete svuda.