FHEM Forum

FHEM - Entwicklung => Wunschliste => Thema gestartet von: Thyraz am 09 Februar 2018, 14:25:39

Titel: [gelöst] Perl Alternative zu Fhem-Sleep/InternalTimer welche anonyme Sub erlaubt
Beitrag von: Thyraz am 09 Februar 2018, 14:25:39
Hallo zusammen,

nachdem mir das Thema während der letzten Monate immer wieder aufgestoßen ist,
schreib ich doch mal einen Erweiterungswunsch dazu:

Mich stört, dass ich in Perl Funktionen oder Modulen zum Verzögern von Befehlen immer auf InternalTimer zurückgreifen muss.
Genauer gesagt, dass man bei InternalTimer einen Funktionsname statt einen Zeiger oder alternativ eine anonyme Sub übergibt.

Dadurch müssen die verzögerten Befehle in eine extra Funktion ausgelagert werden, was bei Ein- bis Zweizeilern den Code unnötig fragmentiert.
Z.B. Aufruf des ganzen in einem Notify, Verzögerter Code in einer Sub in einer MyUtils.
Wenn man asynchrone Verarbeitung aus anderen Sprachen mit verzögerten Inline-Blöcken/-Closures kennt, weckt das eben Begehren. ;)
In Notifies kann man ggf. noch auf sowas ausweichen:

fhem("sleep 5;{Log 1, 'Test'}");

Spätestens in Modulen fühlt sich das aber "dirty" an.
Auch fehlt dann ein korrektes Syntaxhighlighting.

Ein Beispiel für eine ähnliche Problemstellung wurde in HttpUtils bei den nonblocking Funtkionen bereits hervorragend umgesetzt.
Man kann den Callback entweder als Zeiger auf eine extra Funktion (bei viel Code) angegeben, oder eben eine anonyme Sub direkt Inline schreiben:


HttpUtils_NonblockingGet({
url         => "bla",
hash        => $hash,
callback    => sub() {
Log 1, "Ich bin ein Inline-Callback;
}
});


Ich träume jetzt von etwas Ähnlichem für NoneBlocking Sleeps nach dem Schema:

delay(5, sub() {
  Log 1, "Ich bin ein 5 Sekunden verzögerter Befehl;
}


Gäbe es weitere Interessenten an so etwas?
Und wäre es überhaupt Möglich das ohne extreme Änderungen an den Innereien von Fhem?
Weiß nicht wie die Timer und ihre Callbacks intern gespeichert werden und ob es einen Grund gibt, warum das bei HTTPUtils keine Problem machen könnte,
bei Internen Timern hingegen schon.
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: herrmannj am 09 Februar 2018, 14:28:48
gute Idee. Als "at" eingecheckt ;)
https://fhem.de/commandref_DE.html#at
define a5 at +00:00:05 {Log 1, 'Test'}
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: Thyraz am 09 Februar 2018, 14:31:42
Hm.. irgendwie schon, irgendwie "jein". ;)

Die "at"s landen dann ja als Modulinstanzen sichtbar in Fhem.
Soll sowas wirklich im Quellcode eines offiziellen Fhem Modul stehen?  :o

Könnte mir vorstellen, die Begeisterung der User wäre verhalten, wenn da andauernd "at"s auftauchen und verschwinden.

Das FHEM Sleep wird ja nicht umsonst gern als unbenanntes at bezeichnet.
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: CoolTux am 09 Februar 2018, 20:28:31
Wenn es Dir um die Anwendung in einem Modul geht und da um Verzögerung, dann erzähl Mal was Du genau machen willst. Es gibt viele Wege und man muss ja nicht unbedingt verzögern.
Du kannst mit NotifyFn arbeiten oder auch mit einer Queue.
Kommt halt immer auf die Anforderung an.
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: Thorsten Pferdekaemper am 09 Februar 2018, 20:54:14
Zitat von: Thyraz am 09 Februar 2018, 14:25:39Genauer gesagt, dass man bei InternalTimer einen Funktionsname statt einen Zeiger oder alternativ eine anonyme Sub übergibt.
Hast Du das ausprobiert? Ich übergebe zumindest manchmal Funktionszeiger und das geht. Ich würde vermuten, dass das auch mit anonymen Subs funktioniert.
Wenn man sich mal das Coding in fhem.pl anschaut, dann sehe ich nicht, warum das nicht gehen sollte.
Gruß,
   Thorsten
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: Thyraz am 09 Februar 2018, 22:04:20
Nein, habe ich tatsächlich nicht...

Da es in der Doku "Der Name der Funktion als Zeichenkette" heißt,
bin ich nicht auf die Idee gekommen, dass dies funktionieren könnte.

Es geht aber tatsächlich.
Und wir bekommen als nette Beigabe die Fähigkeit von Closures, dass sie die Variablen aus ihrem umgebenden Scope an sich binden. :)


my $text = "This text is declared before the delay";

InternalTimer( gettimeofday() + 5, sub() {
    Log 1, "$text";
}, undef);


Man hat ja meist mehrere Variablen die man dem InternalTimer übergeben muss, wofür es den arg Parameter gibt.
Da man aber nur einen Parameter übergeben kann, muss man oft erstmal eine Hash-Ref erstellen.
Das ist dann ja auch wieder zusätzlicher Code.

Kann man sich dank Closure sparen und direkt auf die umgebenden Variablen zugreifen.

Danke für die Rückmeldungen, jetzt muss ich hier erstmal ein wenig Code Refactoring betreiben.  :P
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: Thorsten Pferdekaemper am 09 Februar 2018, 22:27:13
Hi,
so etwas in der Art müsste auch gehen:

InternalTimer( gettimeofday() + 5, sub() {
    my ($arg1,$arg2,$arg3) = @{$_[0]};
    # mach was mit $arg1 etc.
}, ["abc",42,6*9]);

Gruß,
   Thorsten
Titel: Antw:[gelöst] Perl Alternative zu Fhem-Sleep/InternalTimer welche anonyme Sub erlaubt
Beitrag von: Thyraz am 09 Februar 2018, 22:50:09
Klaro, ob Hash-Ref oder Array ist ja erstmal egal.

Aber du hast mehr Tipparbeit durch die Wertübergabe und musst die Werte in der Funktion ja auch wieder aufdröseln.
Ist bei einer Closure unnötig, da du direkt auf die äußeren Variablen zugreifen kannst.

Die Zuweisung der Variable $text in meinem Code war ja nur beispielhaft.
Normal geht es ja um den Zugriff auf bereits bestehende Variablen des äußeren Contexts die dort sowieso bestehen.

Der Code beschränkt sich also auf

InternalTimer( gettimeofday() + 5, sub() {
    Log 1, "$textFromOuterScope $anotherVarFromOutside ...";
}, undef);


Was minimalistischer und einfacher zu lesen ist.
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: herrmannj am 09 Februar 2018, 23:00:06
Zitat von: Thyraz am 09 Februar 2018, 22:04:20
Und wir bekommen als nette Beigabe die Fähigkeit von Closures, dass sie die Variablen aus ihrem umgebenden Scope an sich binden. :)
Achtung! Ideale Quelle für memory leaks

Solange der Timer läuft bleiben die vars im (anonymen) scope. Wenn Du jetzt (direkt oder indirekt) "von innen nach aussen" referenzierst bildet sich eine loop und die Speicher bleibt auf immer und ewig gebunden. Bei zyklischen timern dann immer wieder.
Titel: Antw:[gelöst] Perl Alternative zu Fhem-Sleep/InternalTimer welche anonyme Sub erlaubt
Beitrag von: ToKa am 12 Februar 2018, 21:34:18
Hallo zusammen,

habe jetzt InternalTimer so wie hier beschrieben in meiner myUtils im Einsatz. Das funktioniert wunderbar.

Da ich diese interne Funktion bislang nicht kannte, bin ich mir jetzt aber unsicher, ob man jeden Timer - auch wenn er abgelaufen ist - durch ein RemoveInternalTimer wieder löschen muss?

Beste Grüße
Torsten
Titel: Antw:[gelöst] Perl Alternative zu Fhem-Sleep/InternalTimer welche anonyme Sub erlaubt
Beitrag von: CoolTux am 12 Februar 2018, 21:35:26
Nein muss man nicht. Wenn er abgelaufen ist wird er auch entfernt.
Titel: Antw:[gelöst] Perl Alternative zu Fhem-Sleep/InternalTimer welche anonyme Sub erlaubt
Beitrag von: ToKa am 12 Februar 2018, 21:39:10
Super und danke für die schnelle Rückmeldung!
Titel: Antw:Perl Alternative zu FHEM Sleep bzw. InternalTimer welche anonyme Subs erlaubt
Beitrag von: Thyraz am 04 Januar 2022, 20:30:31

my $text = "This text is declared before the delay";

InternalTimer( gettimeofday() + 5, sub() {
    Log 1, "$text";
}, undef);


Zitat von: herrmannj am 09 Februar 2018, 23:00:06
Achtung! Ideale Quelle für memory leaks

Solange der Timer läuft bleiben die vars im (anonymen) scope. Wenn Du jetzt (direkt oder indirekt) "von innen nach aussen" referenzierst bildet sich eine loop und die Speicher bleibt auf immer und ewig gebunden. Bei zyklischen timern dann immer wieder.

Ich muss mal diese Leiche fleddern, da ich sowas Ähnlich heute wieder eingebaut habe und mich an diesen Thread erinnert habe.
Kann mir jemand erklären warum das genau bei Perl ein Problem ist, also was da intern genau passiert?

Mit ist klar, dass die anonyme Sub / Closure die Variable text bindet, also der RetainCount wahrscheinlich erhöht wird, oder was da auch immer genau an Implementierungsdetails passiert.

Wenn ich das ganze als Übergabeparameter machen würde statt auf die Variable außerhalb zu binden,
würde der Wert ja aber auch im Speicher gehalten werden müssen.
Soweit von der Speicherbelastung ja erstmal kein großer Unterschied.

Aber wie kommt es zum Memory Leak?
Wenn der InternalTimer abgelaufen ist, und die Sub ausgeführt, wird diese doch sicher von InternalTimer wieder freigegeben, oder?
Und wenn die Sub von keinem mehr gehalten wird, dürfte sie sich in Luft auflösen und der RetainCount von $text müsste sich doch auch wieder verringern, wodurch $text von der GarbageCollection aufgeräumt werden kann.

$text hält seinerseits ja wahrscheinlich keinen Verweis auf die anonyme Sub, oder?
Sonst wären Closures ja immer ein Memory Leak durch Circular References.

Titel: Antw:[gelöst] Perl Alternative zu Fhem-Sleep/InternalTimer welche anonyme Sub erlaubt
Beitrag von: herrmannj am 04 Januar 2022, 20:41:00
kommt nicht nicht per se zum leak, kann aber eben leicht passieren. Im Beispiel ist alles ok. Meistens macht so eine sub ja viel mehr.
Titel: Antw:[gelöst] Perl Alternative zu Fhem-Sleep/InternalTimer welche anonyme Sub erlaubt
Beitrag von: Thyraz am 04 Januar 2022, 21:00:23
Danke für die schnelle Antwort. :)