Probleme mit aktueller Blocking Implementation

Begonnen von Dirk, 03 August 2013, 13:35:57

Vorheriges Thema - Nächstes Thema

Dirk

Hallo Norbert,

ich habe mir das ganze einmal angesehen.
Sieht soweit auch ganz gut aus. Allerdings habe ich noch nicht alles verstanden. Denke ich werde das mal versuchen für meine Zwecke entsprechend zu benutzen.

Ein paar Fragen hab ich aber noch:
Zitatwarten per select bremst fhem bei höheren Pollfrequenzen aber auch merklich aus
In welchem Zusammenhang? Dachte das Threading sollte das verhindern?

Zitatwahlweise asynchron in einem Thread oder auch synchron, wenn die Plattform keine Threads unterstützt
welche Platform währe das z.B.? Wie läuft die Abarbeitung denn dann synchron? Wieder blockierend?

ZitatDie Kommunikation mit dem Background-thread erfolgt über 2 Queues
Wieso über 2 Queues? Wenn man mit Threads arbeitet sollte doch auch eine "Kommunikation" über shared vriablen möglich sein?

Gruß
Dirk

ntruchsess

Hallo Dirk,

Zitat von: Dirk schrieb am Di, 27 August 2013 14:19
Zitatwarten per select bremst fhem bei höheren Pollfrequenzen aber auch merklich aus
In welchem Zusammenhang? Dachte das Threading sollte das verhindern?
Die Aussage zum 'select' bezog sich auf den synchronen Fall ohne Threads.

Zitat von: Dirk schrieb am Di, 27 August 2013 14:19
Zitatwahlweise asynchron in einem Thread oder auch synchron, wenn die Plattform keine Threads unterstützt
welche Platform währe das z.B.? Wie läuft die Abarbeitung denn dann synchron? Wieder blockierend?
Naja, man kann ja nicht einfach davon ausgehen, dass jeder sein Perl mit Threadunterstützung compiliert hat. Auf Systemen mit wenig Ram macht das perl-threading auch wenig Sinn, da alle(!) vorhandenen Variablen beim Thread-start dupliziert werden. Ich weiß z.B. nicht, ob perl auf der Fritzbox Threads unterstützt. Der code ist jedenfalls so geschrieben, dass er beim Nichtvorhandensein von Threads trotzdem laufen müsste, dann natürlich (bei den kurzen, protokollbedingten Delays) wieder blockierend.

Zitat von: Dirk schrieb am Di, 27 August 2013 14:19
ZitatDie Kommunikation mit dem Background-thread erfolgt über 2 Queues
Wieso über 2 Queues? Wenn man mit Threads arbeitet sollte doch auch eine "Kommunikation" über shared vriablen möglich sein?
Mal ganz abgesehen davon, dass das zu lösende Problem (asynchrones Abarbeiten von Requests) an sich die Verwendung von Queues praktisch schon aufdrängt (irgendwo müssen die noch nicht abgearbeiteten Requests ja hin...), ist das Verwenden von über Queues gekoppelten Workern ein sehr bewährtes Pattern das hilft viele beim Multithreading mögliche Laufzeitfehler zu vermeiden. Wenn man shared Variablen direkt benutzt muss man sich um alles, was mit Multithreadding zu tun hat, selber kümmern (und darf alle dabei möglichen Fehler selber machen ;-) ). Schau Dir mal an, was Plattformen mit guter Unterstützung von Multithreadding so mitbringen (z.B. den Executor in Java). Da findest Du dieses Pattern (mit gutem Grund) überall wieder.

Gruß,

Norbert

while (!asleep()) {sheep++};

Dirk

Hallo Norbert,

Danke für deine Infos.
ZitatNaja, man kann ja nicht einfach davon ausgehen, dass jeder sein Perl mit Threadunterstützung compiliert hat.
Das stimmt natürlich.

Zitatdann natürlich (bei den kurzen, protokollbedingten Delays) wieder blockierend.
Und genau diesen Fall möchte ich auf jeden Fall verhindern.
Also fällt Threads schon mal weg. Bleibt also noch das Forken vom FHEM-Prozess, so mache ich das im Moment ja schon, nur das werde ich dann so umstellen, dass das es nur einen Parallelen Prozess dafür gibt. Und dieser übernimmt dann die Kommunikation.
Die alternative ist noch ein kleiner separater Perl-Prozess der diese Kommunikation ausschließlich übernimmt. Vor allem auf Systemen mit wenig RAM, muss dieser Prozess ja nicht das ganze FHEM-Enviroment mit sich "rumschleppen". Daher favorisiere ich derzeit diese Version.

Gruß
Dirk

ntruchsess

perl-threads sollten möglichst früh nach Prozessstart erzeugt werden, damit sie nicht unnötigen Ballast (in Form von belegtem Ram) mitkriegen. Wenn man Threads verwendet, dann sollten die entsprechenden Devices möglichst weit 'vorne' beim FHEM-start angeordnet sein. Forken ist vom Ram-bedarf her natürlich nicht besser.

ein kleiner separater perl-process der die Requests asynchron abarbeitet kann im Prinzip exakt so wie mein Worker-thread implementiert sein (Nur mit einer Socket-verbindung zwischen FHEM und dem Worker-prozess anstelle der beiden Queues). Das ist genau das gleich Pattern. Viel Erfolg dabei! :-)

Gruß,

Norbert
while (!asleep()) {sheep++};

ntruchsess

Zitat von: Dirk schrieb am Mi, 28 August 2013 11:38
Zitatdann natürlich (bei den kurzen, protokollbedingten Delays) wieder blockierend.
Und genau diesen Fall möchte ich auf jeden Fall verhindern.

Hallo Dirk,

im Prinzip habe ich das mit dem separaten Daemon-Prozess auch mal durchdacht - das wäre im Prinzip im Hinblick auf Zeitverhalten und Resourcenverbrauch eigentlich optimal (wenn man vom Kommunikationsoverhead über Sockets absieht). Nur wollte ich mich erst mal nicht mit der Steuerung des parallel laufenden Prozesses belasten (Themen wie automatisches Reconnect usw. sollen dann ja stabil gelöst sein), also habe ich das erst mal mit Threads gemacht. Lässt sich mit der gewählten Architektur (Kommunikation ausschließlich über Queues) ja vergleichsweise einfach in Multiprozessing umbauen. Wäre schön, wenn Du die Kommunikation und Multiprozesssteuerung so wiederverwendbar schreiben würdest, dass ich das einfach auf mein asynchrones OWX-modul übertragen könnte.

Gruß,

Norbert

while (!asleep()) {sheep++};

Dirk

Hallo Norbert,

ZitatWäre schön, wenn Du die Kommunikation und Multiprozesssteuerung so wiederverwendbar schreiben würdest, dass ich das einfach auf mein asynchrones OWX-modul übertragen könnte.
Diese Idee unterstütze ich.
Ich mache hier von FHEM explizit keinen Fork, da ich hier gerne auch etwas Ressourcen sparen möchte. Allerdings benutze ich einige Bibliotheken von FHEM.

Aktuell habe ich ein "server.pl" File welches einen lokalen Socket öffnet und Befehle per TCP entgegen nimmt. Darüber läuft dann die Kommunikation mit FHEM. Ich benutze hierfür TcpServerUtils.pm
Eine zweite Verbindung wird zu einem seriellen Device geöffnet. Hier kommen die Funktionen aus DevIo.pm zur Anwendung. Daher werden hier alle darin funktionierenden Verbindungen unterstützt.

Im Prinzip läuft im "server.pl" ein Loop, welcher beide FD's jeweils mit Select abfragt.
Alles zeitkritische und blockierende läuft hier parallel zu FHEM dann in diesem Prozess.

Das "server.pl" stellt das "Programm" für den neuen Prozess dar. Hier drin erfolgt auch die Konfiguration für die jeweilige spezifischen Anforderungen.
Alle wieder verwendbaren Funktionen, auch "sub main" mit der Schleife, sind aktuell in DevicedTools.pm. (Ein besserer Name ist mir aktuell nicht eingefallen)

ZitatNur wollte ich mich erst mal nicht mit der Steuerung des parallel laufenden Prozesses belasten
Man muss mal sehen was man hier steuern muss.
Eigentlich muss man ja "nur" dafür sorgen dass der Prozess gestartet und beendet wird. Das hatte ich vor im zugehörigen FHEM-Modul in DefFn und in UndefFn zu erledigen.

Beim Absturz von FHEM bleibt der Prozess dann natürlich "verweist". Da brauchts noch eine Idee. Ggf. beim Start von FHEM nach entsprechenden bereits laufenden Prozessen suchen und dann killen bzw. einfach weiter verwenden.

ZitatThemen wie automatisches Reconnect usw. sollen dann ja stabil gelöst sein
Ja Klar

Wie du siehst gibt es noch ein paar offene Punkte. Ideen nehme ich gerne mit auf :)
Ich schicke dir mal meinen Code wenn ich soweit fertig bin. Dann kann man den sicher noch etwas verfeinern.

Da ich hier im Moment auch einige Funktionen benutzen möchte, welche sich derzeit nur in fhem.pl befinden wurde ich vorschlagen diese Funktionen (Log, Timer, usw) in eine allgemeine Util.pm uder ähnlich auszulagern. Wenn ich mit der Entwicklung hier fertig bin, würde ich das Thema noch mal besprechen wollen.

Gruß
Dirk

justme1968

wäre es nicht passend BlokingCall so zu erweitern das die komminkation mit dem parent nicht mehr (unbedingt) über die telnet verbindung geschehen muss sondern über ein socket? socketpair bietet sich hier geradezu an.

ich finde es vor allem wichtig alles was das hauskeeping beim forken angeht so gut wie möglich an einer stelle (und das ist bisher BlockingCall) zu sammeln. und an keiner stelle in BlockingCall ist vorgegeben das der gestartete child prozess nicht 'ewig' laufen darf.

wenn der child prozess eine verbindung zum parent hat sollte er auch bemerken können wenn der parent nicht mehr da ist und sich selber beenden. alles andere wie suchen beim neustarten und beenden ist fehleranfällig und nicht zu verlässig.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Dirk

Zitatwäre es nicht passend BlokingCall so zu erweitern
Das wollte ich eben nicht.
Wenn wir FHEM forken dann schleppen wir in der Kopie jede Menge Code mit rum den man warscheinlich gar nicht benötigt und unnütz Speicher belegt. Vor allem kleineren Systemen wie der FritzBox würde das entgegen kommen. Daher ist die Idee einen leichtgewichtigen Service zu bauen, der nur die Schnittstelle zu der Hardware bereitstellt.

Zitatalles andere wie suchen beim neustarten und beenden ist fehleranfällig und nicht zu verlässig.
Theoretisch muss der Prozess beim Neustarten von FHEM dann gar nicht mal beendet und neu gestartet werden. Dieser könnte einfach weiterlaufen und FHEM benutzt diesen dann weiter wenn es merkt dass es noch läuft.
Der Vorteil währ, der Prozess könnte weiterhin Nachrichten von der Hardware empfangen und für FHEM ggf. buffern. So würden bei Absturz / Neustart von FHEM hier auch keine Nachrichten verloren gehen.

Gruß
Dirk

rudolfkoenig

Ich moechte BlockingCall als solches belassen: fuer Funktionsaufrufe, die laenger blockieren koennen, und wo der Autor sich erstmal nicht die Muehe machen will, Daten per select blockierungsfrei einzulesen. Ich sehe BlockingCall als QuickHack, nicht als richtige Loesung.

Dienste, die Zeitkritisches erledigen wollen, und die sich von FHEM wg. diversen Gruenden (Benutzer gestartetes sleep, langwierige SVG-Berechnung, etc) nicht ausbremsen lassen wollen, wuerde ich als separaten Server implementieren, also so, dass die auch eigenstaendig weiterlaufen, und sie FHEM als Client bedienen.

Bin gerne bereit FHEM so umzubauen, dass das leichter zu implementieren ist, aber nicht vor dem Release.

justme1968

das mit dem unnützen speicher würde ich zumindest erst mal verifizieren. das forken alleine verdoppelt ja nicht den speicherbedarf.

etwas kleines und leichtes unabhängig von fhem zu haben das aufgaben als extra server/daemon/dienst übernimmt ist sicher ein guter weg. mir ging  es nur um den das starten und herstellen der kommunikation fhem - dämon. das sollte an einer zentralen stelle passieren. ich denke blockingcall wäre gut weil ich auch für die bisherigen aufgaben die telnet version als nicht optimal ansehe.

ZitatTheoretisch muss der Prozess beim Neustarten von FHEM dann gar nicht mal beendet und neu gestartet werden. Dieser könnte einfach weiterlaufen und FHEM benutzt diesen dann weiter wenn es merkt dass es noch läuft.
Der Vorteil währ, der Prozess könnte weiterhin Nachrichten von der Hardware empfangen und für FHEM ggf. buffern. So würden bei Absturz / Neustart von FHEM hier auch keine Nachrichten verloren gehen.

die idee finde ich sehr gut.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Dirk

ZitatBin gerne bereit FHEM so umzubauen, dass das leichter zu implementieren ist, aber nicht vor dem Release.
Natürlich nicht :)

Ich würde meine derzeitige Lösung die Tage dann mal hier veröffentlichen. Ich vermute damit werden wir der Gesamtlösung sicher noch nicht genüge getan haben. Aber das kann man sicher als weitere Diskussionsgrundlage nehmen können.

Bisher habe ich die Logging- und die Timer-Funktionen ausgemacht, welche man aus fhem.pl auslagern könnte.
Ach, DoTrigger noch.

Ggf. auch den Mainloop. Somit könnte man sich überlegen ob man selbst fhem.pl dann auf einer Zentralen "serverTools.pm" oder wie auch immer die dann heißt aufsetzen lassen könnte.

Gruß
Dirk

justme1968

oben gab es den satz das dies der erste fall ist bei dem sich das problem nicht ohne einen zweiten prozess lösen lässt. ich denke es gibt inzwischen mindestens einen zweiten fall. die augenblickliche owserver nonblocking implementierung ist leider völlig wirkungslos (Link)und es gibt meiner meinung nach auch keine lösung ohne eigenen prozess da alle werte immer vom owfs geholt werden und man ohne diesen prozess das blockieren nicht in den griff bekommt.

ich habe inzwischen eine prototypische lösung mit einem nonBlockingCall der es erlaubt den geforkten prozess einmal zu starten und laufen zu lassen und die dann mit zwei pipes bidirektional zwischen parent und child kommuniziert. in parent und child läuft alles mit selectlist und ReadFn. parent und child erkennen auch wenn eine seite sich beendet und reagieren mit neustart des childs bzw. mit selber beenden.

im unterschied zum normalen BlockingCall werden nach dem forken alle filedescriptoren geschlossen und alle anderen module per undefine beendet und gelöscht. es gibt zwar noch ein paar kleinere unsauberkeiten aber nichts was sich nicht mit ein paar konventionen lösen ließe.

bevor ich jetzt einfach weiter mache würde ich gerne noch mal anregen die drei unterschiedlichen ansätze (dirk,rudi und meinen) zu vergleichen und schauen welche einsatzgebiete sich jeweils abdecken lassen. icha möchte zwar erst mal nur owserver blockierungsfrei zum laufen bekommen aber dafür das rad nicht neu erfinden bzw natürlich infrastruktur die demnächste kommen könnte mit nutzen. z.b. eine generelle ReadFromParentFn die im child statt der ReadFn bedient wird oder ein konstrukt das wie ParseFn funktioniert.

das vor allem rudis ansatz ein grösserer umbau ist und erst nach 5.5 kommt ist klar. aber vielleicht hast du ein paar minuten und kannst etwas genauer beschreiben wie es dann aussehen könnte. vor allem wie genau das starten der neuen 'server' funktioniert wenn z.b. zur laufzeit ein neues device definiert wird das so einen server braucht oder sogar zwei die zwar den selben server aber eben nicht genau den gleichen sondern eben eine zweite instanz brauchen.

vielleicht ist es auch eine option bei so einem umbau auch den normalen fhem prozess in zwei teile zu teilen. einen 'monitor' der alle anderen teile startet und niemals blockiert :) und einen (bzw mit den servern dann mehrere) weitere die die arbeit machen.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

> aber vielleicht hast du ein paar minuten und kannst etwas genauer beschreiben wie es dann aussehen könnte

Idee: ein Modul, der externe Prozesse startet, aber auch Perl-Programme aus FHEM/xxx. Per Attribut kann man shutdown/startup Verhalten definieren, oder Ueberwachungsmethode (kill -0, tcp/port, etc). fhem.pl muesste erweitert werden, damit die Ladereihenfolge solcher Module beeinflusst werden kann. Prozesse aus FHEM/xxx kommunizieren per TCP/IP mit dem Haupt-FHEM, und sollten eine zusaetzliche,"tote" TCP/IP Verbindung fuer die Ueberwachung ermoeglichen.

Das Teilen von FHEM sehe ich nocht nicht, da der Datenaustausch bzw. Verteilung (Readings/Events/usw) in meinen Augen zu aufwendig ist. Es kommt dazu, dass ein geforkter perl Prozess mehr Speicher belegt, als man denkt, das uebliche COW funktioniert nur teilweise. Das ist nur eine Beobachtung aus anderen Projekten mit grossen (GB+) Datenmengen, wenn jemand hier mehr weiss, dann her damit.

Dirk

Hallo Zusammen,

ich bin mit meiner Implementation schon ziemlich weit.
Der HM485-Interface-Server läuft.

Hier ein Paar Informationen zur aktuellen Implementation:

Ich habe ein Modul ServerTools.pm erstellt. Dieses stellt allgemeine Funktionen zur Verfügung.
ServerTools.pm wiederrum nutzt DevIo.pm und TcpServerUtils.pm
Im Prinzip stellt ServerTools die Initialisation von einem Serial-Device (DevIo) und dem TCP-device (TCPServerUtils) zur Verfügung und den Main-Loop, der dann beide Devices per Select "abholt" bzw. Daten zu den Devices schickt.
Somit kann man hiermit einen kleinen Server bauen, der die Brücke zwischen "dummer" Hardware und FHEM darstellt. in diesem Server können dann Zeitaufwendige Sachen erledigt werden ohne FHEM zu blockieren bzw. zeitkritische Aufgaben erledigt werden, ohne dass FHEM bei der Abarbeitung "stört".

Der eigentliche Server-Prozess ist eine eigenständige .pl Datei welche die ServerTools einbindet. Hier habe ich auf einen Fork Verzichtet, um eine "leichtgewichtigen" Prozess zu Verfügung zu haben. Hier drin werden dann auch alle Gerätespezifischen Funktionen abgearbeitet.

Achja: In Servertools ist derzeit eine "Abgespeckte" Kopie von Log3. Daher währ die Idee Log3 aus fhem.pl in ein eigenes Modul auszulagern. Ggf. zusammen mit anderen oft verwendeten Funktionen in eine Utils.pm ö.ä. Dann könnte man die hier direkt verwenden. Das vermeidet Codeduplizität.

Das Starten des Prozesses passiert bei mir im Moment noch im zugehörigen FHEM-Modul (HM485_INTERFACE.pm. Das könnte man nach Rudis Vorschlag auch noch allgemein zur Verfügung stellen.

Eine Prozessüberwachung erfolgt hier im Moment "nur" bei einem FHEM-Start über ps und entsprechenden commandline Vergleichen. Somit wird ein bereits laufender Prozess erkannt. Der Start des Prozesses erfolgt im Moment mit system(), so dass der Prozess bei einem "normalem" Ende von FHEM mit terminiert wird. Bei einem möglichem Abstuz von FHEM dann aber nicht. bei einem Neustart wird der bereits laufende Prozess aber erkannt und "wiederbenutzt". Wie gesagt, die Prozessüberwachung muss hier noch ausgebaut werden.

Grundsätzlich ist der HM485-Interface-Server so ausgelegt, dass dieser Unabhängig von FHEM laufen kann. Nicht abgeholte Nachrichten werden im Server gepuffert und an den Client ausgegeben sobald er wieder verbunden ist.

Hiermit will ich erstmal anfragen, ob sich hier schon mal jemand das ganze ansehen möchte.
Die Module sind zwar noch nicht ganz fertig, funktionieren aber schon soweit, und ich denke die kann man schon mal als Diskussionsgrundlage heranziehen.

Gruß
Dirk

justme1968

ich würde gerne mal schauen bzw. versuchen es mit/in OWServer zu verwenden.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968