Konkurs #3: automatyczne zwalnianie pamięci

Konkurs #3: automatyczne zwalnianie pamięci
Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
6

Tym razem zabawa w małe GC w Pascalu;

Należy tak zmodyfikować ten kod:

Kopiuj
// miejsce na Twój kod

Procedure MyProc;
Var Data: PByte;
    I   : uint16;
    // miejsce na Twój kod
Begin
 // miejce na Twój kod

 Data := AllocMem(10);

 For I := 0 To 9 Do
  Data[I] := Random(High(Byte));

 For I := 0 To 9 Do
  Write(Data[I], ' ');

 Writeln;
End;

Begin
 MyProc;

 Writeln('-- end --');
 Readln;
End. 

Aby przy wyjściu z funkcji MyProc, pamięć zaalokowana przy pomocy AllocMem była automatycznie zwalniana.

Nie można modyfikować już napisanego kodu (poza usunięciem tych komentarzy :P), a swój kod można wstawiać jedynie w miejsca oznaczone tymi trzema komentarzami (czyli przed procedurą MyProc, w jej lokalnych zmiennych oraz na samym jej początku).

Kompilator: Delphi lub FPC (można używać dowolnych przełączników), kod nie musi być przenośny między platformami/systemami, nie może również zawierać żadnych wycieków pamięci.

Czas: od dzisiaj przez tydzień, czyli do południa 7 grudnia, rozwiązania proszę przesyłać mi na PW ;)
Nagroda: +3.5 punktu do szacunku ;]

Powodzenia! :D


edytowany 2x, ostatnio: Patryk27
spartanPAGE
Gdzie kolejne konkursy?!
Patryk27
Jak tylko mi przyjdzie coś ciekawego na myśl :P
lukasz1235
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad 8 lat
  • Postów:1105
0

Mam pytanie.
Czy trzeba faktycznie zrobić coś w stylu GC czy wystarczy po prostu zwolnić Data?

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
0

Ten "odśmiecacz" powinien działać dla nieokreślonej liczby zmiennych w dowolnych procedurach/funkcjach, więc zwalnianie jedynie zmiennej "Data" nie wchodzi jako-tako w grę.


edytowany 2x, ostatnio: Patryk27
Azarien
no teraz to zmieniasz zasady ;-| trzeba było tak od razu.
Patryk27
Mea culpa, jak pisałem pierwszego posta to byłem nieco przemęczony :P Ale jedyna zmiana to i tak ta, że powinien działać w dowolnej procedurze/funkcji (bo o nieokreślonej liczbie zmiennych wspomniałem na początku: "pamięć zaalokowana przy pomocy AllocMem").
TA
  • Rejestracja:ponad 11 lat
  • Ostatnio:około 6 lat
1

Ehh, a chciałem zrobić taki cheat :P

Kopiuj
Procedure MyProc;
Var Data: PByte;
    I   : uint16;
    // miejsce na Twój kod
    x: TBytes;
    info: Pointer;
Begin
 // miejce na Twój kod
  info := TypeInfo(TBytes);
 asm
     mov eax, dword [@exit+1]   // pobranie rozmiaru tablicy z linijki:
                                           // Data := AllocMem(10);
     // przygotowanie parametrów dla funkcji DynArraySetLength
     push eax                   // odłożenie na stos rozmiaru tablicy
     mov eax, esp             // pobranie wskaźnika na rozmiar tablicy
     push eax                   // odłożenie na stos wskaźnika na rozmiar tablicy
     lea eax, x                  // eax = adres tablicy
     mov ecx, 1                // ecx = liczba wymiarów tablicy
     mov edx, info;            // edx = wskaźnik na typ tablicy
     call DynArraySetLength

     mov eax, x            // Data := @x[0];
     mov Data, eax       // Data := @x[0];
     pop eax                // czyszczenie stosu - usuniecie rozmiaru tablicy
     lea eax, dword [@exit+13]  // ominiecie wszystkich instrukcji
     jmp eax                           // związanych z Data := AllocMem(10);
     @exit:
 end;
 Data := AllocMem(10);

 For I := 0 To 9 Do
  Data[I] := Random(High(Byte));

 For I := 0 To 9 Do
  Write(Data[I], ' ');

 Writeln;
End;
vpiotr
ASM? ała
TA
to jest bardziej ciekawostka niż poprawne rozwiązanie :P Poza tym nie rozwiązuje to doprecyzowanego zadania (patrz post wyżej).
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
3

Rozwiązanie typu GOOP (= Good Old Objective Pascal)

Kopiuj
unit MojAlokatorUnit;

class MojAlokator
public
  class function AllocMem(Size: PtrUInt): pointer; // uruchom globalny AllocMem i dodaj do fListaBlokow
  class procedure FreeAll(); 
private
  fListaBlokow: cos-tam;
end;

function AllocMem(Size: PtrUInt): pointer; // wywołuje MojAlokator.AllocMem lub robi sama całą robotę na globalnej zmiennej (fuj)

finalization
  MojAlokator.FreeAll();
end.
edytowany 3x, ostatnio: vpiotr
TA
przeczytaj treść zadania, szczególnie fragment: "Nie można modyfikować już napisanego kodu"
vpiotr
no dobra, w takim razie można usunąć pierwszą linijkę z tego kodu i dodać funkcje globalną w MojAlokatorUnit.
vpiotr
poprawiłem żeby uniknąć konfuzji
TA
Nie to żebym się czepiał, ale przeczytaj dokładnie zadanie. Mała podpowiedź: "Aby przy wyjściu z funkcji MyProc, pamięć zaalokowana przy pomocy AllocMem była automatycznie zwalniana."
TA
Zapomniałbym, kolejny cytat: "rozwiązania proszę przesyłać mi na PW ;)"
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
3

Aby przy wyjściu z funkcji MyProc, pamięć zaalokowana przy pomocy AllocMem była automatycznie zwalniana.

Kopiuj
Procedure MyProc;
Var Data: PByte;
    I   : uint16;
    MemGuard: IMemGuard;
Begin
    MemGuard := MojAlokator.NewGuard;

...

unit MojAlokatorUnit;
type
  TMemGuard: class(TInterfacedObject, IMemGuard)
  public
 // wywolane z MojAlokator.NewGuard z parametrem = fListaBlokow.Count
     constructor Create(BlockCnt: integer); virtual;
// wywolaj MojAlokator.FreeUntil(fBlockCnt) - zwalnia wszystkie bloki powyżej wskazanej bariery - zaczynając od fListaBlokow.Count - 1
     destructor Destroy; override; 
  private
     fBlockCnt: integer;   
  end;
vpiotr
PS, sorry za mieszanie C++ z Pascalem, ale dawno w tym drugim nie robiłem...
Patryk27
I to jest rozwiązanie, na które ja wpadłem podczas opracowywania tego konkursu (chociaż napisałem to nieco inaczej, to zasada działania pozostaje ta sama) :P Zobaczymy - może ktoś jeszcze na coś wpadnie ;)
Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
2

Przyszła pora na rozstrzygnięcie konkursu:
Łącznie zostały przedstawione trzy rozwiązania, jedno przez @lukasz1235 , drugie @vpiotr oraz trzecie przez @Tajiri (edit: właściwie to w innej kolejności chronologicznej, ale nie chce mi się przeformatowywać postu ;p).

Łukaszowe rozwiązanie (http://ideone.com/JxXGKK dla x86 oraz http://pastebin.com/wzh0UArC dla x86-64) opiera się, z tego co widzę, na nadpisywaniu adresu powrotu, przez co po opuszczeniu funkcji, sterowanie kodu przechodzi do procedury exitHandler, która zwalnia bloki, a następnie skacze pod właściwy adres.

Rozwiązanie zaproponowane przez Piotra oraz Tajiri jest tym, na które również wpadłem i ja wpadłem podczas pisania treści tego konkursu, a polega na tym założeniu, że interfejsy są zarządzane specjalnie przez kompilator (refcountowane; podobnie sprawa ma się do stringów oraz dynamicznych tablic).

Tajiri łącznie podesłał dwa rozwiązania; jedno, które możecie zobaczyć w jego poście powyżej, a które nie pasuje w 100% do założeń konkursu, oraz drugie, którego zasada działania jest właściwie identyczna do kodu Piotra, więc nie wymaga specjalnego omówienia - http://www.speedyshare.com/5dBy8/rozwiazanieXE4.zip

A mój kod korzysta dodatkowo z makr Free Pascala - http://ideone.com/rtiqP6 ;)

Więc +3.5 punktu szacunku dla Łukasza, Piotra oraz Tajiri i dzięki za udział! ;)


edytowany 2x, ostatnio: Patryk27
Zobacz pozostałe 2 komentarze
KA
No ta co podałeś w zadaniu :)
Patryk27
MyProc czy AllocMem (zakładam, że odnosisz się do mojego kodu)? Bo MyProc jest luźna, a AllocMem nie określiłem mianem procedury/funkcji ;)
KA
eee a nie teraz widzę już więcej nie piję tylko wykorzystałeś IFree a procedura jest "luźna"
vpiotr
Jesli AllocMem jest zmienna globalna w makrze to program sie "przewroci" przy zagniezdzeniu wywolan (gdy wywolamy allocmem przed zakonczeniem korzystania z poprzedniego wyniku).
Patryk27
@vpiotr: dlatego AllocMem zostało zdefiniowane po TFree.Allocate :P
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0

@Patryk27: Zarówno do mojego jak i Twojego rozwiązania są przypadki patologiczne.

Do Twojego (zagnieżdżenie):
http://ideone.com/vF9zy3

Do mojego - musiałyby być takie alokacje:

  • alokacja A
  • alokacja B
  • zwolnienie A
  • zwolnienie B

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.