Implementeren van reference counting

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
In mijn C applicatie heb ik reference counting toegepast om op bepaalde punten aan automatisch geheugenbeheer te doen. Bijv. een consumer/listener waarbij data wordt verzonden naar alle (interne) listereners en automatisch weer wordt opgeruimd zodra alle listeners de data hebben ontvangen en verwerkt. Ook in mijn embedded LUA modules implementatie gebruik in reference counting om te volgen wanneer mijn eigen objecten (lightuserdata) opgeruimd kunnen worden

Alles werkt naar behoren, dat is het punt niet. Echter viel me tijdens het lezen over bestaande reference counting implementaties iets op. Nergens zie ik gebruik gemaakt worden van sempahores. Ik doe dat wel, want ze lijken me daar perfect voor gemaakt.

C:
1
2
3
void ref(struct foo *bar) {
   sem_post(bar->ref)
}


C:
1
2
3
4
5
6
void unref(struct foo *bar) {
   int x = sem_trywait(bar->ref);
   if(x == -1 && errno == EAGAIN) {
      bar->free(bar);
   }
}


Oftewel, maak ik een denkfout door semaphores te gebruiken en waarom? Of ben ik juist geniaal bezig om ze juist wel te gebruiken en zou iedereen dat eigenlijk moeten doen :+ ?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • +1 Henk 'm!

  • ThomasG
  • Registratie: Juni 2006
  • Nu online
Een semaphore is relatief duur, waardoor het een dure reference counting implementatie wordt (hoe duur hangt af van het platform). Of die overhead een probleem vormt, hangt af van de applicatie. De maintainability is nu wel hoger.

[ Voor 8% gewijzigd door ThomasG op 02-09-2019 09:48 ]


Acties:
  • 0 Henk 'm!

  • Rmg
  • Registratie: November 2003
  • Laatst online: 17-09 15:31

Rmg

Semaphore is POSIX en geen onderdeel van de libc. Dat is een nadeel als je niet op *nix systemen werkt.

Wat is de implementatie van je free() trouwens, ik zie in de semaphore.h niet zo een functie om te decrementen, of doe je gewoon een sem_trywait? *las over de sem_trywait heen
CurlyMo schreef op maandag 2 september 2019 @ 10:00:

Ik maak gebruik van libuv, die een abstract heeft over deze platform specifieke functies. Dat is dus gedekt.
Dat is voor jouw applicatie dus prima ;) maar is een (mogelijke) verklaring voor deze vraag
Echter viel me tijdens het lezen over bestaande reference counting implementaties iets op. Nergens zie ik gebruik gemaakt worden van sempahores.

[ Voor 59% gewijzigd door Rmg op 02-09-2019 10:04 ]


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
ThomasG schreef op maandag 2 september 2019 @ 09:47:
De maintainability is nu wel hoger.
Waarom?

Of dit moment merk ik nog geen performance problemen.
Rmg schreef op maandag 2 september 2019 @ 09:49:
Semaphore is POSIX en geen onderdeel van de libc. Dat is een nadeel als je niet op *nix systemen werkt.
Ik maak gebruik van libuv, die een abstract heeft over deze platform specifieke functies. Dat is dus gedekt.




Wat is de gangbare methode om het wel te doen? CAS?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • DroogKloot
  • Registratie: Februari 2001
  • Niet online

DroogKloot

depenisvanjezus

Close, in C++ shared_ptr implementaties worden meestal fetch_add en fetch_sub atomics gebruikt. De dtor (fetch_sub) heeft dan wel een bepaalde memory ordering constraint nodig om double deletion te voorkomen.
CurlyMo schreef op maandag 2 september 2019 @ 09:29:
Nergens zie ik gebruik gemaakt worden van sempahores. Ik doe dat wel, want ze lijken me daar perfect voor gemaakt.
Als er geen threads in het spel zijn is een semaphore natuurlijk overkill, je hebt dan genoeg aan een simpele int* refctr = (int*) calloc(1, sizeof(int)).

[ Voor 53% gewijzigd door DroogKloot op 02-09-2019 11:57 ]


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
DroogKloot schreef op maandag 2 september 2019 @ 11:43:
Als er geen threads in het spel zijn is een semaphore natuurlijk overkill, je hebt dan genoeg aan een simpele int* counter = (int*) calloc(1, sizeof(int)).
Er zijn threads in het spel :)

Ik moet alleen even dubbel checken of in mijn programma een thread de reference kan ophogen terwijl een andere thread het onderliggende object al aan het verwijderen was.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
@ThomasG @Rmg @DroogKloot

Om even terug te komen op dit probleem. Reference counting heb ik succesvol kunnen implementeren behalve dat ik soms tegen een deadlock aanloop.

Stukje context. Mijn C software maakt gebruik van Lua modules. Het is mogelijk om in lua een multilevel associative array aan te maken. Delen van die arrays moeten passed by refence toegewezen kunnen worden aan een andere array. Aangezien mijn software multithreaded is moet ik per niveau bij houden hoeveel referenties er aan een array deel hangen en als die 0 is, dan kan er opgeruimd worden.Binnen de Lua modules wordt er altijd een bepaald stukje logica uitgevoerd en dan is de state weer voorbij. De acties die op een state gebeuren zijn dus elke keer erg beperkt. Aan het eind van elke state moet worden gekeken welke array delen nog een referentie hebben en welke delen niet.

Wat ik nu heb gedaan.

Bij het aanmaken van de array binnen mijn state:
- Lock A: Aanmaken / bewerken array, ophogen reference
- Lock B: Abonneren op state gc voor de benodigde dereference.

Wanneer de state ophoudt te bestaan is de volgorde:
- Lock B: Alle gc functies aanroepen (dus ook de derefence functie)
- Lock A: Array opruimen wanneer reference 0 is.

Helgrind vind mijn implementatie alleen niet goed. Dat kan ook kloppen gezien de deadlock die ik u en dan ervaar. De output is als volgt, waarin dus geklaagd wordt over niet consequente volgorde van mijn acties:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
==1781== Observed (incorrect) order is: acquisition of lock at 0x5996BA0
==1781==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==1781==    by 0x4EC7E30: uv_mutex_lock (thread.c:330)
==1781==    by 0x4F5E0BB: _plua_clear_state (lua.c:1668)
==1781==    by 0x1769F9: test_c_lua_metatable (lua_c_metatable.c:824)
==1781==    by 0x4ECC362: CuTestRun (CuTest.c:162)
==1781==    by 0x4ECCA34: CuSuiteRun (CuTest.c:323)
==1781==    by 0x12DB3C: RunAllTests (alltests.c:273)
==1781==    by 0x12C4C9: main (alltests.c:328)
==1781==
==1781==  followed by a later acquisition of lock at 0x79366B8
==1781==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==1781==    by 0x4EC7E30: uv_mutex_lock (thread.c:330)
==1781==    by 0x4F5C4DE: plua_metatable_free (lua.c:968)
==1781==    by 0x4F5C710: plua_metatable_unref (lua.c:316)
==1781==    by 0x4F5E13E: _plua_clear_state (lua.c:1675)
==1781==    by 0x1769F9: test_c_lua_metatable (lua_c_metatable.c:824)
==1781==    by 0x4ECC362: CuTestRun (CuTest.c:162)
==1781==    by 0x4ECCA34: CuSuiteRun (CuTest.c:323)
==1781==    by 0x12DB3C: RunAllTests (alltests.c:273)
==1781==    by 0x12C4C9: main (alltests.c:328)
==1781==
==1781== Required order was established by acquisition of lock at 0x79366B8
==1781==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==1781==    by 0x4EC7E30: uv_mutex_lock (thread.c:330)
==1781==    by 0x4F65178: plua_metatable_index (lua.c:1226)
==1781==    by 0x4F65518: plua_metatable__index (lua.c:1284)
==1781==    by 0x65991A5: ??? (in /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2.0.4)
==1781==    by 0x65DE97F: lua_pcall (in /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2.0.4)
==1781==    by 0x4F5ED11: plua_pcall (lua.c:2727)
==1781==    by 0x1769CB: test_c_lua_metatable (lua_c_metatable.c:711)
==1781==    by 0x4ECC362: CuTestRun (CuTest.c:162)
==1781==    by 0x4ECCA34: CuSuiteRun (CuTest.c:323)
==1781==    by 0x12DB3C: RunAllTests (alltests.c:273)
==1781==    by 0x12C4C9: main (alltests.c:328)
==1781==
==1781==  followed by a later acquisition of lock at 0x5996BA0
==1781==    at 0x4C3403C: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==1781==    by 0x4EC7E30: uv_mutex_lock (thread.c:330)
==1781==    by 0x4F5DA01: plua_gc_reg (lua.c:2529)
==1781==    by 0x4F64BE1: push_plua_metatable (lua.c:861)
==1781==    by 0x4F653CF: plua_metatable_index (lua.c:1260)
==1781==    by 0x4F65518: plua_metatable__index (lua.c:1284)
==1781==    by 0x65991A5: ??? (in /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2.0.4)
==1781==    by 0x65DE97F: lua_pcall (in /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2.0.4)
==1781==    by 0x4F5ED11: plua_pcall (lua.c:2727)
==1781==    by 0x1769CB: test_c_lua_metatable (lua_c_metatable.c:711)
==1781==    by 0x4ECC362: CuTestRun (CuTest.c:162)
==1781==    by 0x4ECCA34: CuSuiteRun (CuTest.c:323)
==1781==
==1781==  Lock at 0x79366B8 was first observed
==1781==    at 0x4C37F2A: pthread_mutex_init (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==1781==    by 0x4EC7D32: uv_mutex_init (thread.c:283)
==1781==    by 0x4F6801F: plua_metatable_init (table.c:394)
==1781==    by 0x1768B5: test_c_lua_metatable (lua_c_metatable.c:692)
==1781==    by 0x4ECC362: CuTestRun (CuTest.c:162)
==1781==    by 0x4ECCA34: CuSuiteRun (CuTest.c:323)
==1781==    by 0x12DB3C: RunAllTests (alltests.c:273)
==1781==    by 0x12C4C9: main (alltests.c:328)
==1781==  Address 0x79366b8 is 40 bytes inside a block of size 88 alloc'd
==1781==    at 0x4C30F2F: malloc (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==1781==    by 0x4EEC0F3: __malloc (mem.c:115)
==1781==    by 0x4F67FD1: plua_metatable_init (table.c:386)
==1781==    by 0x1768B5: test_c_lua_metatable (lua_c_metatable.c:692)
==1781==    by 0x4ECC362: CuTestRun (CuTest.c:162)
==1781==    by 0x4ECCA34: CuSuiteRun (CuTest.c:323)
==1781==    by 0x12DB3C: RunAllTests (alltests.c:273)
==1781==    by 0x12C4C9: main (alltests.c:328)
==1781==  Block was alloc'd by thread #1


Ik weet alleen niet hoe je dit redelijkerwijs anders implementeert.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

(Oudere reactie, ik weet het ;))
CurlyMo schreef op maandag 2 september 2019 @ 09:29:

Oftewel, maak ik een denkfout door semaphores te gebruiken en waarom?
Ja, dat maak je. Semaphores zijn locking primitives in multithreading. Ja, natuurlijk kún je ze gebruiken voor reference counting, net zoals je hard links op een disk kunt gebruiken voor reference counting. Ik neem aan dat je in dat voorbeeld wel inziet dat dat totaal buiten het beoogde doel van hard links ligt :). Je bent helemaal niet geintereseerd in alle bookkeeping en functionaliteit die verder bij semaphores komt kijken, het enige waar jij in geinteresseerd bent in het (atomair) ophogen en verlagen van een counter.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 11:53:
(Oudere reactie, ik weet het ;))

[...]

Ja, dat maak je. Semaphores zijn locking primitives in multithreading. Ja, natuurlijk kún je ze gebruiken voor reference counting, net zoals je hard links op een disk kunt gebruiken voor reference counting. Ik neem aan dat je in dat voorbeeld wel inziet dat dat totaal buiten het beoogde doel van hard links ligt :). Je bent helemaal niet geintereseerd in alle bookkeeping en functionaliteit die verder bij semaphores komt kijken, het enige waar jij in geinteresseerd bent in het (atomair) ophogen en verlagen van een counter.
Sorry, ik gebruik geen semaphores meer, maar een fetch_add en fetch_sub.

Probleem blijft hoe ik voorkom dat ik deadlock situaties kom door een asynchrone locking volgorde?

Binnen elke state leven er namelijk een #X aantal referenties naar het object. Na elke state draait een garbage collector die in ieder geval al zijn eigen referenties #X naar het object weghaalt. Maar dat leidt mogelijk wel tot het opruimen van het object, waardoor ik dus in de asynchrone locking terecht kom.

[ Voor 15% gewijzigd door CurlyMo op 12-06-2020 11:58 ]

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Door locks die je tegelijk houdt altijd in dezelfde volgorde te locken. Zonder code is het lastig om er iets over te zeggen, maar ik snap eigenlijk helemaal niet zo wat nou het doel van lock A is. Voor reference counting heb je totaal geen locking nodig.

[ Voor 58% gewijzigd door .oisyn op 12-06-2020 12:04 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 12:00:
Door locks die je tegelijk houdt altijd in dezelfde volgorde te locken.
Maar hoe?!?

Als ik een object wil registeren in de gc lijst dan zal ik eerst het object moeten zien aan te maken, wat ik dus schreef:
Lock A: Aanmaken / pushen object
Lock B: Registreren in gc functie.

Zodra de gc draait voor die state, dan wordt de gc lijst eerst gelocked en daarna worden alle objecten opgeruimd zodra alle referenties weg zijn. Dus:
Lock B: Draaien geregistreerde gc functies
Lock A: Verwijderen aangemaakt object.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Moet ik nu gaan raden hoe je code werkt :? Waarom lock je überhaupt iets.

[ Voor 8% gewijzigd door .oisyn op 12-06-2020 12:05 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 12:04:
[...]

Moet ik nu gaan raden hoe je code werkt :?
Ik probeer abstract uit te leggen hoe het werkt ja. Als dat niet duidelijk genoeg is, dan hoor ik dat graag gericht op de voorbeelden die ik geef :)
Waarom lock je überhaupt iets.
Omdat het multithreaded draait.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

CurlyMo schreef op vrijdag 12 juni 2020 @ 12:06:
[...]

Ik probeer abstract uit te leggen hoe het werkt ja. Als dat niet duidelijk genoeg is, dan hoor ik dat graag gericht op de voorbeelden die ik geef :)
Er valt niets zinnigs over te zeggen omdat je totaal geen informatie geeft :)
Omdat het multithreaded draait.
Dat is geen antwoord. Als meerdere threads de data niet sharen heb je ook geen locking nodig. Dus, waarom hebben de bewerkingen die je wilt doen locks nodig? Voor het manipuleren van een reference count en de gevolgen ervan heb je typisch geen locks nodig, bijvoorbeeld.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 12:08:
[...]

Er valt niets zinnigs over te zeggen omdat je totaal geen informatie geeft :)
Ik probeer het opnieuw.

Mijn programma heeft 4 lua states en heeft een threadpool van 4 threads. Er kunnen dus maximaal 4 lua states tegelijk in een van die threads een stukje lua logica uitvoeren. Elke keer als één van de threads lua code heb laten draaien wordt er een garbage collector aangeroepen.

Mijn programma maakt het mogelijk om in lua asynchrone processen te starten. Neem een timer.
Lua:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
local M = {};

function M.timer(timer)
    print("timer");
end

function M.run()
    local timer = pilight.async.timer();
    local timer1 = pilight.async.timer();

    timer.setUserdata({['timer']=timer1()});

    timer.setCallback("timer");
    timer.setTimeout(100);
    timer.setRepeat(0);
    timer.start();
end

return M;

Hier wordt de M.run functie uitgevoerd door thread 1 state 3. In de functie wordt een object aangemaakt timer1 en doorgegeven aan de userdata van timer. Er zijn nu dus een aantal relevante dingen gebeurt:
- timer en timer1 hebben bij het aanmaken zichzelf geregistreerd in de garbage collector van state 3. Aangezien meerdere threads tegelijk met timer of timer1 bezig kunnen zijn worden beide eerst gelocked voor registratie. Ook garbage collector lijst wordt gelocked.
- timer is actief ook na het draaien van de M.run functie en mag dus niet opgeruimd worden.
- timer1 is toegekend als userdata aan timer. Die mag dus ook niet opgeruimd worden.

Zodra de M.run functie volledig gedraaid is en er dus een timer is aangemaakt wordt de garbage collector van state 3 aangeroepen. Die stelt vast dat beide objecten nog een referentie hebben en er wordt dus niks opgeruimd.

Wanneer de timer weer word geactiveerd wordt gekeken naar welke thread en state vrij zijn en wordt de M.timer functie uitgevoerd. Bijv. thread 4 en state 2. Aangezien we het timer object willen kunnen gebruiken wordt er een nieuwe referentie aangemaakt en het object registreert zichzelf in de garbage collector van state2.

Je krijgt dus:
Timer A lock
GC state 2 lock

Aangezien de timer maar 1x draait mag aan het eind van de M.timer functie de boel opgeruimd worden. Hier krijg je dus:
GC state 2 lock
Timer A lock
Dat is geen antwoord. Als meerdere threads de data niet sharen heb je ook geen locking nodig. Dus, waarom hebben de bewerkingen die je wilt doen locks nodig? Voor het manipuleren van een reference count en de gevolgen ervan heb je typisch geen locks nodig, bijvoorbeeld.
Ik probeer mijn gedachten toetsen en wellicht heb ik opnieuw denkfouten gemaakt. Ik ben in de veronderstelling dat mijn opzet locks nodig heeft.

Ik hoop dat dat met mijn uitleg hierboven beter kan bekritiseren.



Ik vraag mezelf ook langzaam af of de garbage collector wel gemanipuleerd kan worden van buiten de eigen state. Als dat inderdaad niet kan, dan is daar ook geen lock nodig.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Kun je ook eens de unlocks erbij zetten en welke code je tijdens het hebben van de locks aanroept?
Ik vraag mezelf ook langzaam af of de garbage collector wel gemanipuleerd kan worden van buiten de eigen state. Als dat inderdaad niet kan, dan is daar ook geen lock nodig.
Dat was dus ook een vraag die ik had. Een GC behoort toch tot een enkele state? En states worden toch niet geshared?

Een andere vraag die ik had is, waarom het hebben van de GC lock moet overlappen met het hebben van de timer lock.

[ Voor 68% gewijzigd door .oisyn op 12-06-2020 12:42 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 12:40:
Kun je ook eens de unlocks erbij zetten en welke code je tijdens het hebben van de locks aanroept?
Deel 1
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
static int plua_metatable_index(lua_State *L, struct plua_metatable_t *node) {
...
    uv_mutex_lock(&node->lock);
...
                case LUA_TTABLE: {
                    push_plua_metatable(L, (struct plua_metatable_t *)node->table[x].val.void_);
                } break;
...

    uv_mutex_unlock(&node->lock);

    return 0;
}


C:
1
2
3
4
5
6
7
8
9
10
11
void push_plua_metatable(lua_State *L, struct plua_metatable_t *table) {
    if(table->type != PLUA_TABLE) {
        luaL_error(L, "table object required but %s object passed", plua_interface_to_string(table->type));
    }

    atomic_inc(table->ref);

    plua_gc_reg(L, table, plua_metatable_unref);

    lua_newtable(L);
...


C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void plua_gc_reg(lua_State *L, void *ptr, void (*callback)(void *ptr)) {
    struct lua_state_t *state = NULL;

    if(L == NULL) {
        state = &lua_state[NRLUASTATES];
    } else {
        state = plua_get_current_state(L);
    }
    assert(state != NULL);

    uv_mutex_lock(&state->gc.lock);

...

    state->gc.list[slot]->callback = callback;

    uv_mutex_unlock(&state->gc.lock);
}


Deel 2
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void _plua_clear_state(struct lua_state_t *state, char *file, int line) {
    int i = 0;
    uv_mutex_lock(&state->gc.lock);

    for(i=0;i<state->gc.nr;i++) {
        if(state->gc.list[i]->free == 0) {
...
                state->gc.list[i]->callback(state->gc.list[i]->ptr);
...
        }
...
    }
...
    uv_mutex_unlock(&state->gc.lock);
...
}


C:
1
2
3
4
static void plua_metatable_unref(void *ptr) {
    struct plua_metatable_t *table = ptr;
    plua_metatable_free(table);
}


C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void plua_metatable_free(struct plua_metatable_t *table) {
    int x = 0;

    uv_mutex_lock(&table->lock);
    if(atomic_dec(table->ref) == 0) {
        for(x=0;x<table->nrvar;x++) {
            uv_mutex_lock(&table->table[x].lock);
...
            if(table->table[x].val.type_ == LUA_TTABLE) {
                plua_metatable_free(table->table[x].val.void_);
            }
            uv_mutex_unlock(&table->table[x].lock);
...
        uv_mutex_unlock(&table->lock);
        uv_mutex_destroy(&table->lock);
        FREE(table);
    } else {
        uv_mutex_unlock(&table->lock);
    }
}

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom lock je de node als je 'm toevoegt aan de table? Je doet dan toch niets met de node zelf?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 12:59:
Waarom lock je de node als je 'm toevoegt aan de table? Je doet dan toch niets met de node zelf?
Je bedoelt in plua_metatable_index?

Dit is de hele functie:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
static int plua_metatable_index(lua_State *L, struct plua_metatable_t *node) {
    char buf[128] = { '\0' }, *p = buf;
    char *error = "string or number expected, got %s";
    int x = 0, match = 0;

    if(node == NULL) {
        logprintf(LOG_ERR, "internal error: table object not passed or already freed");
        return 0;
    }

    sprintf(p, error, lua_typename(L, lua_type(L, -1)));

    luaL_argcheck(L,
        ((lua_type(L, -1) == LUA_TSTRING) || (lua_type(L, -1) == LUA_TNUMBER)),
        1, buf);

    uv_mutex_lock(&node->lock);

    for(x=0;x<node->nrvar;x++) {
        match = 0;
        uv_mutex_lock(&node->table[x].lock);
        switch(lua_type(L, -1)) {
            case LUA_TNUMBER: {
                if(node->table[x].key.type_ == LUA_TNUMBER &&
                    node->table[x].key.number_ == (int)lua_tonumber(L, -1)) {
                    match = 1;
                }
            } break;
            case LUA_TSTRING: {
                if(node->table[x].key.type_ == LUA_TSTRING &&
                 strcmp(node->table[x].key.string_, lua_tostring(L, -1)) == 0) {
                    match = 1;
                }
            }
        }
        if(match == 1) {
            switch(node->table[x].val.type_) {
                case LUA_TBOOLEAN: {
                    lua_pushboolean(L, node->table[x].val.number_);
                } break;
                case LUA_TNUMBER: {
                    lua_pushnumber(L, node->table[x].val.number_);
                } break;
                case LUA_TSTRING: {
                    lua_pushstring(L, node->table[x].val.string_);
                } break;
                case LUA_TLIGHTUSERDATA: {
                    lua_pushlightuserdata(L, node->table[x].val.void_);
                } break;
                case LUA_TTABLE: {
                    push_plua_metatable(L, (struct plua_metatable_t *)node->table[x].val.void_);
                } break;
                default: {
                    lua_pushnil(L);
                } break;
            }
            uv_mutex_unlock(&node->table[x].lock);
            uv_mutex_unlock(&node->lock);

            return 1;
        }
        uv_mutex_unlock(&node->table[x].lock);
    }

    lua_pushnil(L);

    uv_mutex_unlock(&node->lock);

    return 0;
}

[ Voor 78% gewijzigd door CurlyMo op 12-06-2020 13:00 ]

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dat verklaart nog niet waarom je de lock moet hebben in push_plua_metatable()

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 13:02:
Dat verklaart nog niet waarom je de lock moet hebben in push_plua_metatable()
Welke lock bedoel je precies?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

CurlyMo schreef op vrijdag 12 juni 2020 @ 13:03:
[...]

Welke lock bedoel je precies?
De lock die je op regel 17 neemt, waarom moet je die nog hebben in de functiecall van regel 51

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 13:05:
[...]


De lock die je op regel 17 neemt, waarom moet je die nog hebben in de functiecall van regel 51
Goeie vraag. Ik lock te lang inderdaad :D

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Omgekeerd geldt dat ook, als je de GC runt (waarbij we maar even in het middel laten of je daar überhaupt een lock voor nodig hebt), waarom moet je die GC lock nog steeds hebben als je de timer opruimt. En eigenlijk veel belangrijker, waarom heb je de timer lock nodig als je al bepaald hebt dat daar geen referenties meer naar zijn.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
.oisyn schreef op vrijdag 12 juni 2020 @ 13:09:
Omgekeerd geldt dat ook, als je de GC runt (waarbij we maar even in het middel laten of je daar überhaupt een lock voor nodig hebt), waarom moet je die GC lock nog steeds hebben als je de timer opruimt. En eigenlijk veel belangrijker, waarom heb je de timer lock nodig als je al bepaald hebt dat daar geen referenties meer naar zijn.
Daarom vraag ik tweakers om hulp _/-\o_ Geeft me inderdaad weer wat duwtjes in de goeie richting.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
Ben je het overigens wel eens met mijn logica om een garbage collector en reference counting op deze manier in te zetten?

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • eheijnen
  • Registratie: Juli 2008
  • Niet online
Regel 36 --> match == 1
activeert regel 60 --> return 1

Hierdoor keert de functie terug en blijft de mutex locked

Wie du mir, so ich dir.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
eheijnen schreef op vrijdag 12 juni 2020 @ 14:17:
Regel 36 --> match == 1
activeert regel 60 --> return 1

Hierdoor keert de functie terug en blijft de mutex locked
Alle locks worden voor die tijd gewoon unlocked. Dat is het probleem niet.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • eheijnen
  • Registratie: Juli 2008
  • Niet online
klopt, mijn fout...

Wie du mir, so ich dir.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
Aangezien ik van dit soort topics veel leer, wordt ik er ook altijd een beetje onzeker van :) Ik heb de aanpassingen doorgevoerd wat betreft plua_metatable_index en nog aanvullende aanpassingen gedaan om locks sneller te unlocken.

Voornaamste verschil is dat ik nu óók de waardes van node->table[x] buffer zodat ik de unlock kan doen voordat ik een externe functie aanroep.

Zitten hier nog denkfouten in:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
static int plua_metatable_index(lua_State *L, struct plua_metatable_t *node) {
    char buf[128] = { '\0' }, *p = buf;
    char *error = "string or number expected, got %s";
    int x = 0, match = 0;

    if(node == NULL) {
        logprintf(LOG_ERR, "internal error: table object not passed or already freed");
        return 0;
    }

    sprintf(p, error, lua_typename(L, lua_type(L, -1)));

    luaL_argcheck(L,
        ((lua_type(L, -1) == LUA_TSTRING) || (lua_type(L, -1) == LUA_TNUMBER)),
        1, buf);

    uv_mutex_lock(&node->lock);

    for(x=0;x<node->nrvar;x++) {
        match = 0;
        uv_mutex_lock(&node->table[x].lock);
        switch(lua_type(L, -1)) {
            case LUA_TNUMBER: {
                if(node->table[x].key.type_ == LUA_TNUMBER &&
                    node->table[x].key.number_ == (int)lua_tonumber(L, -1)) {
                    match = 1;
                }
            } break;
            case LUA_TSTRING: {
                if(node->table[x].key.type_ == LUA_TSTRING &&
                 strcmp(node->table[x].key.string_, lua_tostring(L, -1)) == 0) {
                    match = 1;
                }
            }
        }
        if(match == 1) {
            switch(node->table[x].val.type_) {
                case LUA_TBOOLEAN: {
                    uv_mutex_unlock(&node->lock);

                    float n = node->table[x].val.number_;
                    uv_mutex_unlock(&node->table[x].lock);

                    lua_pushboolean(L, n);
                } break;
                case LUA_TNUMBER: {
                    uv_mutex_unlock(&node->lock);

                    float n = node->table[x].val.number_;
                    uv_mutex_unlock(&node->table[x].lock);

                    lua_pushnumber(L, n);
                } break;
                case LUA_TSTRING: {
                    uv_mutex_unlock(&node->lock);

                    char *str = NULL;
                    if((str = STRDUP(node->table[x].val.string_)) == NULL) {
                        OUT_OF_MEMORY
                    }
                    uv_mutex_unlock(&node->table[x].lock);

                    lua_pushstring(L, str);
                    FREE(str);
                } break;
                case LUA_TLIGHTUSERDATA: {
                    uv_mutex_unlock(&node->lock);

                    void *v = node->table[x].val.void_;
                    uv_mutex_unlock(&node->table[x].lock);

                    lua_pushlightuserdata(L, v);
                } break;
                case LUA_TTABLE: {
                    uv_mutex_unlock(&node->lock);

                    void *v = node->table[x].val.void_;
                    uv_mutex_unlock(&node->table[x].lock);

                    push_plua_metatable(L, (struct plua_metatable_t *)v);
                } break;
                default: {
                    uv_mutex_unlock(&node->lock);
                    uv_mutex_unlock(&node->table[x].lock);
                    lua_pushnil(L);
                } break;
            }

            return 1;
        }
        uv_mutex_unlock(&node->table[x].lock);
    }

    lua_pushnil(L);

    uv_mutex_unlock(&node->lock);

    return 0;
}

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 17-09 22:26
Na een flink portie helgrinden werkt alles ondertussen naar behoren :) De tips hier waren zeker doorslaggevend.

Sinds de 2 dagen regel reageer ik hier niet meer

Pagina: 1