Autor Thema: Timerverwaltung - lib für parallele Funktionsaufrufe  (Gelesen 2487 mal)

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 14632
  • "Developer"?!? Meistens doch eher "User"
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #15 am: 03 Mai 2021, 14:48:20 »
passt doch. Wenn er nicht mehr benötigt wird musst Du ihn halt "abräumen".
Soweit erkennbar, funktioniert das.

Zitat
Pass nur auf: klassisches mem leak:
$hash->{TIMER}->{$id}->{'hash'}->{TIMER}->{$id}->{'hash'}->{TIMER}->{$id}->{'hash'}->{TIMER}->{$id}->{'hash'} ...
Darüber muss ich wohl noch eine Weile nachdenken. Wenn es ein akutes Thema ist, ist das "schon immer" ein Problem; nach meinem bisherigen Verständnis wird aber nicht $hash komplett da reingeschrieben, sondern nur die Referenz auf $hash, was unschädlch sein sollte.

(Konkret: der Schnippsel ab hier:)
sub setRegisteredInternalTimer {
[...]

bei ASC habe ich das erkennen an den funcHash gebunden. Sowohl für Sunrise und Sunset Fahrten als auch die SelfDefense Fahrten habe ich eigene TimerHashs welche entsprechend an das Rolloobject gebunden werden.
Das ist dann dasselbe Prinzip, soweit erkennbar (der Modulcode ist für jemanden Ungeübten wie mich praktisch nicht mehr zu überblicken); nur dass eben der Hash woanders "aufgehoben" wird.

Hi,
ich glaube, dass es ganz nett wäre, wenn wir Timer hätten, die in etwa so wie setTimeout / clearTimeout in JavaScript funktionieren.
Also in etwa so:
my $timerHandle = setTimer($timeStamp, \&myFunc, $arg1, $arg2, $arg3,...);

clearTimer($timerHandle);
Das kann man sich natürlich auch selbst basteln...
Gruß,
   Thorsten
Na ja, es ging grade drum, das nicht umbedingt immer wieder selbst basteln zu müssen (das geht ja "immer"), sondern eben um einen Weg, der (u.a. memory-leak-) sicher ist...

Für's erste täte es mir genau ein weiteres Argument bei InternalTimer, beliebig viele müssen es ja gar nicht sein, das macht es im Zweifel nur unnötig kompliziert...


Mit anonymen subs habe ich auch schon rumexperimentiert, ala (Pseudocode):  InternalTimer($ts, sub _doTimer->($hash,'arg1'), $hash);
}
Das war mir aber zu suspekt für irgendwelche unangenehmen Nebenwirkungen und - soweit ich mich entsinne - kann man in fhemdebug timerList auch nicht mehr erkennen, wer für sowas verantwortlich ist.

Oder abwarten dass die Funktion aufgerufen wird, und dann nichts machen.
Ja, was aber voraussetzt, dass die Funktion noch gefunden wird zum Aufrufzeitpunkt. Ich meine damit mal "Probleme" gehabt zu haben, wenn die letzte Modulinstanz vor Timer-Ablauf gelöscht wurde. (Probleme = kompletter FHEM-Crash im Testsystem...)

Zitat
Ob was zu machen ist oder nicht, kann man im Hash ablegen.
Klar, sobald man eine "erweiterte Datenstrktur" übergibt (egal, ob Hash oder Array), kann man alles mögliche im Folgecode entscheiden, der muss nur wieder gefunden werden bzw. noch geladen sein (und mit den Argumenten umgehen können).

In Array statt Hash sehe ich jetzt für meinen Anwendungsfall keinen wirklichen Vorteil, oder übersehe ich was (v.a. in Bezug auf das meory leak-Thema)?
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline Thorsten Pferdekaemper

  • Developer
  • Hero Member
  • ****
  • Beiträge: 6231
  • Finger weg von der fhem.cfg
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #16 am: 03 Mai 2021, 15:05:12 »
Wenn es ein akutes Thema ist, ist das "schon immer" ein Problem; nach meinem bisherigen Verständnis wird aber nicht $hash komplett da reingeschrieben, sondern nur die Referenz auf $hash, was unschädlch sein sollte.
$hash ist ja die Referenz, sonst wär's %hash...
Memory Leaks entstehen ja gerade dadurch, dass man zyklische Referenzen hat. Dann kann das der Garbage Collector nicht mehr wegräumen und es bleibt ewig stehen. Abhilfe schafft bleiben lassen oder
https://perldoc.perl.org/Scalar::Util#weaken

Zitat
Na ja, es ging grade drum, das nicht umbedingt immer wieder selbst basteln zu müssen (das geht ja "immer"), sondern eben um einen Weg, der (u.a. memory-leak-) sicher ist...
Ganz meine Meinung...

Gruß,
   Thorsten
FUIP

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24159
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #17 am: 03 Mai 2021, 15:11:29 »
Zitat
Das ist nicht ganz dasselbe. Es funktioniert nur, wenn die Argumente immer unterschiedlich sind.
Nope. Es funktioniert, da "my $timerHandle" immer unterschiedlich ist.

Zitat
Wenn es ein akutes Thema ist, ist das "schon immer" ein Problem;
Ich gehe davon aus, aus dem gleichen Grund muss man bei XML::Dom dispose explizit aufrufen.

Zitat
Ja, was aber voraussetzt, dass die Funktion noch gefunden wird zum Aufrufzeitpunkt.
Da sowohl Funktion, wie auch Parameter in der Timerverwaltung gemerkt wurden, sollte das kein Problem sein.
Ob die Funktion mit dem Argument noch was anfangen kann, ist eine andere Geschichte.

Zitat
In Array statt Hash sehe ich jetzt für meinen Anwendungsfall keinen wirklichen Vorteil,
Ich auch nicht, ich wollte die Unterschiede aber minimal halten.

Offline Thorsten Pferdekaemper

  • Developer
  • Hero Member
  • ****
  • Beiträge: 6231
  • Finger weg von der fhem.cfg
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #18 am: 03 Mai 2021, 15:24:38 »
Nope. Es funktioniert, da "my $timerHandle" immer unterschiedlich ist.
Ja, natürlich... (Gibt es eigentlich kein Hand-an-die-Stirn-Icon?) Es wird ja nachher die Referenz verglichen und nicht deren Inhalt.
Gruß,
   Thorsten
FUIP

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 14632
  • "Developer"?!? Meistens doch eher "User"
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #19 am: 03 Mai 2021, 15:30:10 »
Ich gehe davon aus, aus dem gleichen Grund muss man bei XML::Dom dispose explizit aufrufen.
Dann werde ich mir das bei Gelegenheit mal ansehen und auch auf eine "NAME:modifier"-Variante umstellen, die dann $hash wieder aus dem Namen ableitet (und ein return macht, wenn das nicht klappt; eine RenameFn muss dann wohl auch her...).
(Oder hatte ich den Hinweis jetzt falsch verstanden?)
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24159
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #20 am: 03 Mai 2021, 16:02:14 »
Zitat
(Oder hatte ich den Hinweis jetzt falsch verstanden?)
Nein.
Aber ueblicherweise wird beim TimerAufruf oder Timer Loeschen im globalen Hash der Zeiger auf dem TimerHash entfernt, und damit die Endloskette aufgebrochen. Ein Speicherloch existiert nur, wenn zwei Hashes sich gegenseitig merken, und das Modul keinen Zeiger auf einen der beiden mehr hat.

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 14632
  • "Developer"?!? Meistens doch eher "User"
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #21 am: 03 Mai 2021, 16:23:23 »
Ok, dann war es vermutlich aus diesem Grund bisher kein (offensichtliches) Problem.

Gibt es Ideen für eine RenameFn?
Wenn $arg vorher "$old_name:$modifier" war, dann muss das Argument neu dann "$new_name:$modifier" sein.
(Ok, dann beantworte mir die Frage gleich selbst: es muss dann eben $time unter dem alten Namen im "TIMER"-Hash gespeichert gewesen sein, dann sollte das klappen, die alten einfach zu löschen und unter neuem Namen anzulegen).
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 14632
  • "Developer"?!? Meistens doch eher "User"
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #22 am: 03 Mai 2021, 18:14:18 »
es muss dann eben $time unter dem alten Namen im "TIMER"-Hash gespeichert gewesen sein, dann sollte das klappen, die alten einfach zu löschen und unter neuem Namen anzulegen).
Was ggf. noch fehlt, ist die aufzurufende Funktion...

Anbei dann mal ein erster Versuch, das in eine lib zu packen, Doku wäre wohl noch zu ergänzen (wo und wie?)

Eine RenameFn könnte dann so aussehen:
sub Rename {
    my $new_name = shift // return;
    my $old_name = shift // return;

    my $hash = $defs{$new_name} // return;
    return renameAllRegIntTimer($hash, $new_name, $old_name);
}

Eine aufzurufende Funktion könnte sich so aus dem Sumpf ziehen:
sub Twilight_fireEvent {
    my $arg = shift // return;

    my ( $name,$modifier ) = split m{:}xms, $arg;
    my $hash = $defs{$name} // return;
[...]

Meinungen dazu?
« Letzte Änderung: 04 Mai 2021, 14:38:45 von Beta-User »
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24159
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #23 am: 03 Mai 2021, 18:28:09 »
Ich spare mir den Umbennenungsaufwand, indem ich nicht "Name", sondern $defs{Name} merke.
Rename aedert den $hash->{NAME}, und der Rest bleibt gleich.

Offline Thorsten Pferdekaemper

  • Developer
  • Hero Member
  • ****
  • Beiträge: 6231
  • Finger weg von der fhem.cfg
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #24 am: 03 Mai 2021, 19:21:05 »
Dann werde ich mir das bei Gelegenheit mal ansehen und auch auf eine "NAME:modifier"-Variante umstellen, die dann $hash wieder aus dem Namen ableitet (und ein return macht, wenn das nicht klappt; eine RenameFn muss dann wohl auch her...).
(Oder hatte ich den Hinweis jetzt falsch verstanden?)
Falls das die Maßnahme gegen das Memory-Leak ist, dann wären ggf. Weak References besser. Wie schon angemerkt:
https://perldoc.perl.org/Scalar::Util#weaken
Meiner Meinung nach ist das das "Mittel der Wahl" für zyklische Referenzen.
Gruß,
   Thorsten
FUIP

Offline Beta-User

  • Developer
  • Hero Member
  • ****
  • Beiträge: 14632
  • "Developer"?!? Meistens doch eher "User"
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #25 am: 04 Mai 2021, 08:35:17 »
Irgendwie habe ich bei den beiden letztgenannten Hinweisen noch  gedankliche Hänger, vermutlich weil mein Verständnis dessen, was eine Referenz ist irgendwie noch unvollständig ist:
Ich spare mir den Umbennenungsaufwand, indem ich nicht "Name", sondern $defs{Name} merke.
Rename aedert den $hash->{NAME}, und der Rest bleibt gleich.
Da fehlt mir das konkrete Anwendungsbeispiel.
Wenn ich in die von herrmannj vorgeschlagene HASH-Type Variante nehme, also
my $p = {
  'instance' => $hash,
  'foo' => 'bar',
  'id' => $id++,
...
InternalTimer($ts, \&_doTimer, $p);
}

ändere zu
my $p = {
  'instance' => $defs{'instanceName'},
  'foo' => 'bar',
  'id' => $id++,
...
InternalTimer($ts, \&_doTimer, $p);
}
sollte doch eigentlich dasselbe drinstehen, oder? Irgendwie war ich bisher davon ausgegangen, dass $hash (einer FHEM-Device-Instanz) in der FHEM-üblichen Verwendung) dasselbe sei wie $defs{'instanceName'}.
Das scheint aber irgendwie nicht der Fall zu sein.
Oder war es so gemeint, dass der "Text" (z.B.) "HASH(3732AB)" anstatt des Namens verwendet werden soll und dann hinterher einfach quasi automatisch wieder als zur Referenz auf den Hash "erstarkter" Variableninhalt genutzt werden kann :o ?

Falls das die Maßnahme gegen das Memory-Leak ist, dann wären ggf. Weak References besser. Wie schon angemerkt:
https://perldoc.perl.org/Scalar::Util#weaken
Meiner Meinung nach ist das das "Mittel der Wahl" für zyklische Referenzen.
Gruß,
   Thorsten


Demnach erzeugt das "verhashen" einer Variable keine Kopie, sondern eine Referenz und das hier würde einen memory-leak vermeiden, ohne, dass  die Funktionalität leidet:
my $instance = $hash;
weaken($instance);
my $p = {
  'instance' => $instance,
  'foo' => 'bar',
  'id' => $id++,
...
InternalTimer($ts, \&_doTimer, $p);
}
Oder habe ich auch da mal wieder was missverstanden oder übersehen...?
Server: HP-T620@Debian 10, aktuelles FHEM + ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn:MySensors, Weekday-&RandomTimer, Twilight,  AttrTemplate {u.a. mqtt2, mysensors, zwave}

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24159
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #26 am: 04 Mai 2021, 09:22:50 »
Zitat
sollte doch eigentlich dasselbe drinstehen, oder?
Steht ja auch, und eine Aenderung wegen rename ist nicht notwendig.

Wg. weaken: in dem gezeigten Beispiel sehe ich keine zirkulaere Referenz, und keine Notwendigkeit von weaken.
Es waere was Anderes wenn:
- $p _auch_ in $hash eingetragen wird
- beim Aufruf von _doTimer (oder RemoveInternalTimer) $p aus $hash (und umgekehrt $hash aus $p) nicht ausgetragen wird
- $hash spaeter entfernt wird, wieder ohne explizites Entfernen des $p Eintrags.

Anders formuliert:
- perl hat kein Garbage Collection, sondern arbeitet mit Referenz-Zaehler.
- wenn etwas verwendet wird (im hash, Funktionsaufruf, etc), wird der Zaehler erhoeht, und wenn es aus dem Hash entfernt, Ende des Blocks kommt, etc, wird der Zaehler dekrementiert.
- wenn der Zaehler auf 0 dekrementiert wird, dann wird der Speicher freigegeben, und bei allen Unterobjekten der Zaehler dekrementiert.
- bei zirkulaeren Referenzen wird der Zaehler nie auf 0 kommen, deswegen wird es nie freigegeben (ohne kuenstliche Sachen wie weaken, was wiederum ziemlich sicher andere Nebeneffekte hat, sonst waere es nicht optional).

Offline Thorsten Pferdekaemper

  • Developer
  • Hero Member
  • ****
  • Beiträge: 6231
  • Finger weg von der fhem.cfg
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #27 am: 04 Mai 2021, 10:36:27 »
und das hier würde einen memory-leak vermeiden, ohne, dass  die Funktionalität leidet:
my $instance = $hash;
weaken($instance);
my $p = {
  'instance' => $instance,
  'foo' => 'bar',
  'id' => $id++,
...
InternalTimer($ts, \&_doTimer, $p);
}
Nicht ganz. Wenn man eine schwache Referenz einer normalen zuweist, dann wird daraus wieder eine starke. D.h. $p->{instance} ist eine normale Referenz.
So geht es richtig:
my $p = {
  'instance' => $hash,
  'foo' => 'bar',
  'id' => $id++,
...
weaken($p->{instance});
InternalTimer($ts, \&_doTimer, $p);
}
Zusätzlich muss man noch bei der Verwendung abfragen, ob das Teil noch da ist. Bei normalen Referenzen kann man sich darauf verlassen, dass $p->{instance} definiert ist. Bei schwachen Referenzen kann es passieren, dass $p->{instance} "undefiniert" wird. Das passiert, wenn ansonsten keine Referenzen mehr darauf zeigen.
D.h. bei Verwendung muss man immer...
if(defined($p->{instance})) {
 ...
}

Wg. weaken: in dem gezeigten Beispiel sehe ich keine zirkulaere Referenz, und keine Notwendigkeit von weaken.
Ich war davon ausgegangen, dass irgendwo sowas steht wie
$hash->{TIMER} = $p;

Zitat
- perl hat kein Garbage Collection, sondern arbeitet mit Referenz-Zaehler.
Ich dachte "Garbage Collection" ist der Oberbegriff. Die Müllabfuhr kommt halt dann, wenn man sie bestellt (Reference Counting) und nicht regelmäßig überallhin (Mark and Sweep).

Zitat
(ohne kuenstliche Sachen wie weaken, was wiederum ziemlich sicher andere Nebeneffekte hat, sonst waere es nicht optional).
Wie meinst Du das? Man kann es ja schlecht nicht-optional machen. Dann würde nämlich keine Referenz jemals was zählen und alles würde sofort wieder weggeworfen werden. Normalerweise will man das ja nicht.
Das Reference Counting in Perl scheint ja so implementiert zu sein, dass sofort alles freigegeben bzw. schwache Referenzen sofort auf undef gesetzt werden. Dadurch merkt man wahrscheinlich relativ schnell, wenn was falsch läuft. Bei Mark and Sweep können schwache Referenzen natürlich lustig werden.

Gruß,
   Thorsten
FUIP

Offline rudolfkoenig

  • Administrator
  • Hero Member
  • *****
  • Beiträge: 24159
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #28 am: 04 Mai 2021, 10:43:18 »
Zitat
Man kann es ja schlecht nicht-optional machen. Dann würde nämlich keine Referenz jemals was zählen und alles würde sofort wieder weggeworfen werden. Normalerweise will man das ja nicht.
Genau das meinte ich. Man sollte es nicht ohne Nachdenken verwenden.

Offline Thorsten Pferdekaemper

  • Developer
  • Hero Member
  • ****
  • Beiträge: 6231
  • Finger weg von der fhem.cfg
Antw:Timerverwaltung bei vorab unbekannter Timerzahl- best practice?
« Antwort #29 am: 04 Mai 2021, 10:46:55 »
Genau das meinte ich. Man sollte es nicht ohne Nachdenken verwenden.
Full ACK.
FUIP