Reentrant function

C,C++,C#

Moderátori: psichac, Moderátori

Dumitru
Stály člen
Stály člen
Príspevky: 488
Dátum registrácie: 06 Nov 2011, 22:19
Vek: 33

Reentrant function

Príspevok od používateľa Dumitru » 07 Okt 2024, 09:47

Ahojte všetci vieme že ak chceme použiť funkciu v prerušení musí byť tzv. Reentrant, a teda

1. nesmie používať globálne alebo statické premenne
2. nesmie zavolať inú funkciu ktorá je Non - Reentrant

príklad Non - Reentrant funkcie

Kód: Vybrať všetko


uint8_t i = 0;

void myfunction (void)
{
    i++;
}

to je celkom jasne

ale čo napríklad ak funkciu prepíšem takto

Kód: Vybrať všetko


uint8_t i = 0;

void myfunction (uint8_t* data)
{
   * data++;
}

myfunction (&i);

pripadne

Kód: Vybrať všetko


uint8_t i = 0;

void myfunction (void)
{
    uint8_t* data = &i;
   *data++;
}

Na jednej stane som dodržal že vo funkcie nevyužívam globálne premenne ale aj tak mením ich hodnotu :)
je to potom reentrant funkcia alebo nie ?
0

Používateľov profilový obrázok
budvar10
Ultimate člen
Ultimate člen
Príspevky: 1567
Dátum registrácie: 15 Dec 2014, 10:55
Bydlisko: Košice

Re: Reentrant function

Príspevok od používateľa budvar10 » 07 Okt 2024, 10:54

Vpodstate je to to isté, takže nie je.
Avšak zmeniť premennú, ak jej predchádzajúci obsah nie je dôležitý a nie je dôležitý ani pre nasledujúci kód vo funkcii, myslím, že je to OK.
Oprava, ani toto nie je.

Nalepšie je, aby vracala hodnotu, s ktorou sa bude pracovať ďalej, napríklad zapísať do globálnej premennej.

Doplním jednoduchý myšlienkový pochod:
1. Zavolá sa funkcia a niekde sa preruší.
2. Znovu sa zavolá tá istá funkcia z niektorého iného miesta v programe a aj sa dokončí.
3. Dokončí sa prvé volanie funkcie.
Výsledok musí byť vždy transparentný. To je re-entrant.
0

Dumitru
Stály člen
Stály člen
Príspevky: 488
Dátum registrácie: 06 Nov 2011, 22:19
Vek: 33

Re: Reentrant function

Príspevok od používateľa Dumitru » 07 Okt 2024, 12:03

Potom dane pravidlo nie je možne dodržať pretože ak chceme v interrupte niečo z niektorej periférie prijať pripadne odoslať vždy dáta berieme pripadne ukladáme do nejakého buffru. Teoreticky by sa to dalo vyriešiť bez funkcie rovno v ISR ale to by neexistovali callback funkcie ...

Niekto môže povedať nastav si príznak a vyrieš to v maine ale to nie je vždy možne napríklad pri I2C slave kde je nutne dáta odoslať v podstate hneď .
0

om4air
Okoloidúci
Okoloidúci
Príspevky: 3
Dátum registrácie: 07 Máj 2024, 09:42

Re: Reentrant function

Príspevok od používateľa om4air » 07 Okt 2024, 17:17

Samotné telo myfunction(uint8_t* data) z druhého príkladu reentrantné môže byť, ak nemodifikuje iné premenné než tie čo má na stacku, lenže funkcia/miesto skade sa zavolá myfunction() kde už je pointer nasmerovaný na globálnu alebo inak zdieľanú premennú, to už reentrantné nie je - aspoň teda ak to nie je const pointer, alebo bez mechanizmu synchronizácie (kritické sekcie, semafor ap). Do toho ešte atomickosť zápisu/čítania, niečo iné môže byť priradenie vs inkrement/dekrement alebo iné operácie, teda koľko inštrukcií vykoná zápis a či počas nich môže dôjsť k prerušeniu. Veci závisia aj od kompilátora a platformy.
Predpokladám, že si nemôžeš dovoliť zakázať prerušenia alebo rozumne malý buffer na stack...

Príklad druhej funkcie, ktorá je vždy reentrantná, aj ak pointer smeruje ku globálnej premennej, by mohol byť napr.

Kód: Vybrať všetko

uint8_t myfunction(const uint8_t* data)
{
  if (data) 
    return *data + 1;
  return 0;
}
s tým, že si návratovú hodnotu ponecháš na stacku - v lokálnych premennách alebo ako kópiu vo vstupných argumentoch iných funkcií (pass by value).
0
Naposledy upravil/-a om4air v 07 Okt 2024, 18:20, upravené celkom 1 krát.

Používateľov profilový obrázok
budvar10
Ultimate člen
Ultimate člen
Príspevky: 1567
Dátum registrácie: 15 Dec 2014, 10:55
Bydlisko: Košice

Re: Reentrant function

Príspevok od používateľa budvar10 » 07 Okt 2024, 18:20

Ani dvojka nie je reentrant. Funkcia proste nesmie modifikovať globálnu premennú. Môže ju načítať dol lokálnej a potom vrátiť hodnotu a už je na starosti vyššie v programe, čo s vrátenou hodnotou.
Je potrebné si uvedomiť napr., že aj jednoduchý inkrement môže byť preložený ako viac inštrukcií. Céčko nie je ASM. Ak nastane prerušenie v polke, ako som uviedol predtým, a zavolá sa ešte raz daná funkcia, to bude bordel. Ale aj keby prebehol inkrement a až potom sa zavolá druhýkrát pred ukončením funkcie, lebo to je nabetón ďalšia inštrukcia, tak tam v globálnej nebude očakávaná hodnota.
0

peterple
Ultimate člen
Ultimate člen
Príspevky: 2382
Dátum registrácie: 25 Jún 2013, 21:06
Bydlisko: Krajné
Vek: 58
Kontaktovať používateľa:

Re: Reentrant function

Príspevok od používateľa peterple » 07 Okt 2024, 22:07

Dumitru napísal:
07 Okt 2024, 09:47

Na jednej stane som dodržal že vo funkcie nevyužívam globálne premenne ale aj tak mením ich hodnotu :)
je to potom reentrant funkcia alebo nie ?
Ale figu si dodržal že nepoužívaš globálne premenné. Vznikne tá premenná keď prídeš do funkcie a zanikne keď z nej odídeš? Nie, tak nie je lokálna ale globálna. To že si si dal jej adresu do lokálnej premennej na veci nič nezmení.

S buframi je to tak, že sa robia kruhové fronty. Potom môžeš používať aj staticky alokovaný buffer. Viz pozri si ako má Arduino urobenú triedu Serial. Máš tam všetko aj to ako urobiť atomický prístup k pointrom ktoré na 8bitovom železe nevieš pristupovať jednou inštrukciou.
0
Ukáž múdremu chybu a on sa ti poďakuje. Ukáž chybu hlupákovi a on sa urazí.

Dumitru
Stály člen
Stály člen
Príspevky: 488
Dátum registrácie: 06 Nov 2011, 22:19
Vek: 33

Re: Reentrant function

Príspevok od používateľa Dumitru » 07 Okt 2024, 23:11

Ja som sa len nato opýtal môžem sa mýliť tak som sa uistil :)

Na avr pre UART som si robil aj sám kruhový buffer ale tam som to cele urobil v ISR rutine
Do buffru som len vložil data a spustil prenos ďalej už v ISR sa počítalo head a tail poposielalo.... a callback funkciami som sa netrápil.

Teraz som prešiel na 32-bitové a tam vidím napríklad toto

Kód: Vybrať všetko


/**
  * @brief  Slave Address Match callback.
  *   hi2c Pointer to a I2C_HandleTypeDef structure that contains
  *                the configuration information for the specified I2C.
  *   TransferDirection: Master request Transfer Direction (Write/Read), value of @ref I2C_XferOptions_definition
  *   AddrMatchCode: Address Match Code
  * @retval None
  */
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
  Transfer_Direction = TransferDirection;
  if (Transfer_Direction != 0)
  {
     /*##- Start the transmission process #####################################*/
  /* While the I2C in reception process, user can transmit data through
     "aTxBuffer" buffer */
  if (HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, (uint8_t *)aTxBuffer, TXBUFFERSIZE, I2C_FIRST_AND_LAST_FRAME) != HAL_OK)

    {
    /* Transfer error in transmission process */
    Error_Handler();
  }

  }
  else
  {

      /*##- Put I2C peripheral in reception process ###########################*/
  if (HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, (uint8_t *)aRxBuffer, RXBUFFERSIZE, I2C_FIRST_AND_LAST_FRAME) != HAL_OK)
    {
    /* Transfer error in reception process */
    Error_Handler();
  }

  }

}

Kód: Vybrať všetko

(I2C_HandleTypeDef *hi2c
 (uint8_t *)aRxBuffer
(uint8_t *)aTxBuffer
a to oproti avr toto prerušenie vie prerušiť nejaké s výšou prioritou

:rolleyes:
0

Napísať odpoveď