Posts by HelmutB

    kls mit "nicht so wichtig", meinte ich eher, ein Weglassen des Lock auf mutexReceiver in DetachAllReceivers() hat wahrscheinlch weniger Nebenwirkungen als das auf den Device-mutex in SetCamSlot().

    Der Aufruf von Detach() kann nur von außerhalb des "großen" mutexReceiver-Lock stattfinden. Wie wäre es, innerhalb eines kurzen Lock die *Receiver in ein lokale Kopie auzulesen und nach dem Lock das Detach() über die Kopie aufzurufen.

    So in etwa:

    LG Helmut

    Ich bin inzwischen bei Version 6 des AAC_RDS Patch angelangt. v4 und v5 haben TomJoad ich intern bei testen"verbraucht". Die <DSE> Erkennung ist inzwischen ziemlich gut und es gehen fast keine RDS-Messages mehr verloren.


    Ich habe festgestellt, das das radio-plugin mit asprintf() arbeitet um den Radiotext zu speichern. Leider wird aber fast überall auf ein free() der nicht mehr benötigten Daten vergessen.

    Ich habe daher noch einen "extension" Patch der nun char-Arrays mit fixer Länge verwendet, viele unnötige Zeilenumbrüche entfernt die den Code zum Teil schwer lesbar gemacht machen und der auch einige kleinere Korrekturen enthält.

    Zusätzlich zeigt das Plugin nun bis zu 6 RT+ Tags an - zu Titel und Interpret jetzt auch Album, Orchester, Komponist und Dirigent die z.B. bei BR Klassik sehr zuverlässig mitübertragen werden.

    Dieser Erweiterunsgspatch ist etwas grösser, die grundsätzliche Funktiosweise des Plugins bleibt aber gleich.


    Für den des AAC_RDSv6 Patch habe ich auch eine Version für den aktuellen Stand auf https://projects.vdr-developer.org/git/vdr-plugin-radio.git/ vom 15.7.2018 von Ulrich Eckhardt .

    Hier zwei Screenshots.


    Helmut

    Dann bin ich ziemlich sicher, dass der Deadlock hier ausgelöst wurde. Da durch DetachAllReceivers() alle Receiver auf "NULL" gesetzt werden, ist hier ein Lock auf die Receiverliste vermutlich auch nicht so wichtig.


    Backref-Fehler sagt mir jetzt nichts. Die 750 TS-Fehler zu Beginn sind unverschlüsselten Pakete die man bei einem Programmwechsel zwangsläufig erhält. Es dauert etwas bis die erste ECM empfangen wird und das Modul mit der Entschlüsselung beginnen kann. Das ist im Live Modus nicht anders, ein Bild gibt es erst mit entschlüsselten TS-Paketen.


    LG Helmut

    Vielleicht kommt der Deadlock von cDevice::DetachAllReceivers.

    Wenn Thread 1 bereits hier den Lock auf mutexReceiver hält, wird er in cDevice::Detach() nicht freigegeben. Und damit würde der Thread 41 in cDevice::Action() nicht aus der for-Schleife herauskommen und das Unlock() am Ende von while(Running() nie stattfinden.

    Kommentiere einmal die MuteLock Zeile() in DetachAllReceivers aus.

    Code
    1. void cDevice::DetachAllReceivers(void)
    2. {
    3. // cMutexLock MutexLock(&mutexReceiver); <<------- hier
    4. for (int i = 0; i < MAXRECEIVERS; i++)
    5. Detach(receiver[i]);
    6. }

    Ich frage mich allerdings, wieso dann ein normales Umschalten von einem verschlüsseltem Sender funktioniert. Veilleichts liegt es an DDCI2.

    Helmut

    Ja, leider.Und jetzt habe ich ein zweites Mal darüber nachgedacht und fürchte, der Erkenntnisgewinn wird sich in Grenzen halten. Das es mit einem CamSlot->Assign(NULL) ohne Wartezeit funktioniert hat dein Versuch ohne LOCK_THREAD ja schon gezeigt.

    Gibt es das Problem ohne MTD auch (bei den camtweaks-Flags 0x4 dazunehmen) ?

    Helmut

    Wenn ich genauer darüber nachdenke, wird der Patch oben nichts verbessern, da sich cDevice::Runnng() den ThreadLock dann doch wieder vor Detach() holen wird.

    Also als Test gleich den ThreadLock in Detach(), ohne den Patch vom vorigen Post.

    Diff
    1. --- a/device.c.orig 2022-01-09 18:09:02.280144125 +0100
    2. +++ b/device.c 2022-01-09 19:25:08.980216094 +0100
    3. @@ -1823,6 +1823,7 @@ bool cDevice::AttachReceiver(cReceiver *
    4. void cDevice::Detach(cReceiver *Receiver)
    5. {
    6. + LOCK_THREAD;
    7. if (!Receiver || Receiver->device != this)
    8. return;
    9. bool receiversLeft = false;

    Das ist aber nicht als Lösung sondern nur als Test gedacht.

    Fourty2 :

    cDevice::Detach() kann in cDevice::Running() auch innerhalb von for (int i = 0; i < MAXRECEIVERS; i++) { } den Receiver aus der Liste entfernen, für das nachfolgende cDevice::SetCamSlot(NULL) muss aber immer auf das Ende der for-Schleife und das Unlock() gewartet werden.

    cMutexLock MutexLock(&mutexReceiver); in der for-Schleife hat sicher einen Performancegrund, in deinem Fall aber vielleicht irgendeinen Nebeneffekt, da Detach() halbfertig auf das Unlock() wartet.


    Kanst du testweise den mutexReceiver Lock() aus der Schleife herausholen:

    Helmut


    Ok, soweit verstanden.

    Dass "cRecordControls::Start()->cDevice::GetDevice()->...Device::SetCamSlot()" von Thread 1 auf den Device-Thread warten müss ist klar, aber wann muss Thread 40 auf Thread 1 warten?

    Wenn Thread 1 der VDR MainThread ist, kann ein Lock darauf dann nicht nur über den MainThreadHook() von einem Plugin kommen ? Ich muss mir PLUGINS.html doch einmal durchlesen.

    LG Helmut

    Hallo Fourty2 ,

    ich habe auch noch keine Erklärung, wie es zum Deadlock kommt.

    Faszinierend aber, welche Informationen du gdb entlocken kannst :) . Leider sind ein paar interessante Werte "optimized out".

    Etwas im deinem Post #12 ist mir unklar:

    Im 1. Backtrace von Device::SetCamSlot()

    Code
    1. #1 0x00007ff67808c714 in __GI___pthread_mutex_lock (mutex=mutex@entry=0x561fcd2fd630) at ../nptl/pthread_mutex_lock.c:80
    2. #2 0x0000561fcc646639 in cMutex::Lock (this=0x561fcd2fd630) at thread.c:224
    3. #3 0x0000561fcc646dc0 in cThread::Lock (this=<optimized out>) at thread.h:94
    4. #4 cThreadLock::Lock (this=0x7fff53955620, Thread=<optimized out>) at thread.c:430
    5. #5 0x0000561fcc5a3fec in cDevice::SetCamSlot (this=0x561fcd2fd610, CamSlot=CamSlot@entry=0x0) at device.c:450

    und von cDevice::Action() in Thread 40

    Code
    1. #1 0x00007ff67808c714 in __GI___pthread_mutex_lock (mutex=mutex@entry=0x561fcd2fff50) at ../nptl/pthread_mutex_lock.c:80
    2. #2 0x0000561fcc646639 in cMutex::Lock (this=0x561fcd2fff50) at thread.c:224
    3. #3 0x0000561fcc646d5f in cMutexLock::Lock (this=0x7ff5a4ff5dd0, Mutex=<optimized out>) at thread.c:404
    4. #4 0x0000561fcc5a7c3f in cDevice::Action (this=0x561fcd2fd610) at device.c:1701

    Die beiden Mutexe sind doch unterschiedlich, d.h. von verschiedenen Threads - oder irre ich mich?

    TomJoad - du hast recht. Die Ursache ist, dass die <FIL> Erkennung nicht bem ersten Treffer bleibt.

    Ich habe bisher nur gültige <FIL> Elemente mit 0-Byte Länge gesehen, immer unmitttelbar vor dem <TERM>.
    Hier vorab ein Einzeiler zur Korrektur..

    LG Helmut

    Hallo Carsten,

    dein Test mit ts2shout ist sehr hilfreich - jetzt sehe ich, dass der Transaktions / Sequence Counter fortlaufend ist und mein Code doch viele DSE's nicht oder falsch erkennt.

    Ich werde es mir auf "NDR 2" genauer ansehen. Da hier immer ganze RDS-Meldungen im Stück übertragen werden, sollte ich den Fehler doch leichter finden können.

    TomJoad hat mich auch auf einen Fehler bei kurzen Frames die innerhalb eines einzigen TS-Pakets liegen aufmerksam gemacht, das sollte ich mir auch gleich ansehen.

    LG Helmut

    Hallo Carsten!

    Durch das rückwärts lesen und dem Versuch ein <DSE> nur durch '0x80' und einem passenden Längen-Byte zu erkennen, lassen sich falsche Treffer leider nicht ganz vermeiden. Es ist also schon möglich, dass einige RDS-Daten dadurch nicht richtig erkannt werden, es sollten aber wirklich nur ganz wenige sein.


    Beim Text fällt mir auf, dass das letzte Byte direkt nach "Nea" 0x0D ist, ein <newline>, Also ist die Textzeile komplett.

    Und Some say - Nea gibt es offensichtlich wirklich :).


    Ich bin mir nicht sicher, ob der Index wirklich immer fortlaufend gesendet wird. Ich habe auch immer wieder gleichmässige Sprünge in gesehen, oder einfach ohne Index - nur 0.

    Siehst du RDS-Pakete mit Index 0x89 bis 0x9B wenn du diese Aufnahme durch ts2shout mit deinem ffmpeg schickst?


    LG Helmut

    kls Ich erwarte gar nicht, dass du es übernimmst, es interessiert mich nur selbst, ob es bei langlebigen Listen oder Hashes die sich stärk ändern ev. Vorteile bringt.

    Ich stelle mir die Sache auch eher einfach vor:


    cListGarbageCollector bekommt zum Put() noch ein Get() und jeder GarbageCollector wird einer neuen cList::GarbageCollectors hinzugefügt. Ein cGarbageCollectors::Purge() arbeitet diese Liste dann ab.

    Bei cHashwäre es auch sehr einfach. cHashBase ergänze ich um einen Pointer für einen GarbageCollector und in cHashBase::Add()und cHashBase:Del()wird mit oder ohne GarbageCollector gearbeitet. Da würde sich nach außen nichts ändern.

    Ähnlich bei cListBase, hier würde ich useGarbageCollectorzu einem Pointer auf einen GarbageCollector machen, cListBase::Del()berücksichtigt jetzt schon einen GarbageCollector, neue Objecte werden entweder direkt über cGarbageColletor::Get() geholt oder mit newerstellt.


    Ich werde einmal so versuchen und wenn etwas sinnvolles herauskommt kann ich es ja posten.

    LG Helmut

    Von dem ListGarbageCollector gibt es doch nur eine Instanz,

    Ja, es gibt derzeit nur den einen, Dazu habe ich aber eine andere Idee:


    Es ist ja nicht notwendig, im ListGarbageCollector alte cEventszu löschen und dafür in eit.c. immer neue zu erstellen. Diese könnten ja stattdesen aus dem GarbageCollector geholt und wiederverwendet werden.

    Ich habe auch schon damit begonnen, einer cList bei Bedarf einen eigenen GarbageCollector mitgeben zu können, Das sollte die vielen "new" und "delete" Operationen beim EPG-Scan nach einer gewissen Zeit überhaupt nicht mehr notwendig machen.Und wo etwas wäre auch bei cHash nützlich, da die beiden Hashes eventsHashID und eventsHashStartTimeebenfalls permanent cHash-Elemente erstellen und löschen.


    Helmut

    Ich glaube auch nicht, dass malloc_trim() wirklich notwendig ist. Er zeigt nur, dass der Speicherverbrauch des VDR doch nicht bis ins Unendliche anwächst.

    Ich vermute, dass der Speichermanager sehr kleine Speicherblöcke nicht sofort in seine Tabellen und Listen für freien Speicher einträgt, sondern vorerst nur in einer Art Todo-Liste und sich erst dann darüm kümmert, wenn freier physischer Speicher tatsächlich knapper wird (oder er mit malloc_trim dazu aufgefordert wird).


    Ein mögliches Speicherleck hätte ich noch gefunden: in CreateFont wäre im Fall von !f->Hight() der cFont *f vor dem folgendem new cDummyFontfreizugeben.

    Code
    1. Font *cFont::CreateFont(const char *Name, int CharHeight, int CharWidth)
    2. {
    3. cString fn = GetFontFileName(Name);
    4. cFont *f = *fn ? new cFreetypeFont(fn, CharHeight, CharWidth) : NULL;
    5. if (!f || !f->Height()) <-------------------------------------------------HIER
    6. f = new cDummyFont(CharHeight, CharWidth);
    7. return f;
    8. }

    LG Helmut

    Da ich bei meinen Test immer den eitFilter deaktiviere und nur die Events der epg.data einlese habe ich in epg.c immer mehr Informationen daraus übersprungen und nicht in cEvents eingelesen. Schlußendlich habe ich auch die beiden Hashes für Starttime und EventID nicht mehr befüllt und es wurden nur noch (fast) leere Events zu den Schedule-Listen hinzugefügt.

    Es hat sich aber auch hier gezeigt, das nach dem Entfernen von fast 50% der Events kein RAM feigegeben wird: Ein Memory-Leak würde ich aber durch die starke Vereinfachung nun mit ziemlicher Sicherheit auch ausschließen.


    Vielleicht hat diese "Nichtfreigabe" von eigentlich unbenutzten Speicher auch einen tieferen Sinn:

    Der VDR alloziert vermutlich einige Hundert kleine Speicheblöcke pro Minute um sie in diverse Listen zu verarbeiten und anschließend wieder freizugegeben. Es wird sich damit zwangsweise eine starke Fragmentierung im RAM ergeben das zwar viele, aber eher kleine zusammenhängende Bereiche von freiem Speicher enthält.

    Möglicherweise hält sich der Speicher-Manager - solange es noch genügend "grossen" freien physischen Speicher gibt - zurück und wartet, ob sich durch kommende free's/delete's mehrere kleine Speicheblöcke zu einem grösseren Vereinen.

    Das ist aber nur eine Vermutung.


    Ich bin dann auf die Funktion malloc_trim() gestossen, Durch diese wird der Speichermanager veranlasst, den im obersten Bereich des Heap nicht benötigten Speicher sofort freizugegeben.


    Ich habe diese Funktion jetzt in den ListGarbageCollector eingebaut und kann beobachten, daß sich seit dem zweiten Transponderdurchlauf der Wert von "RES" ziemlich eingependelt hat und sich nun - ausgehend von ca. 155 MiB - seit ca. 1 Stunde ziemlich Konstant zwischen 251 MiB und 253 MiB bewegt.


    Im Anhang der malloc_trim Patch, vielleicht kann noch jemand dieses Verhalten bestätigen.

    Zusätzlich auch noch ein Patch der beim Schedule-Cleanup die Anzahl und Veränderung der EPG-Events ins syslog schreibt.

    Hier die Version 3 des radio-1.10 AAC_RDS Patch mit einer kleinen Änderung durch die die Option "kein Standbild" im Setup-Menü eingestellt werden kann. Der Plugin-Parameter "-x" darf nicht mehr verwendet werden.


    Im neuen vdr-2.5.6-RDSPid-v2.patch wird die RDS-Pid und Type nun als Erweiterung der tpid in die channels.conf geschrieben: :0+123@17;...:.

    Der Zugriff erfolgt über cChannnels::Upid() und cChannnels::Utype().


    Im dazu passenden radio-1.1.0-01-AAC_RDS-RdsPid-v2.patch wird die Upid nun verwendet um festzustellen, ob die RDS-Daten direkt im Audiostream oder einem separaten Stream übertragen werden. Programme ohne RDS-Pid werden nicht mehr auf RDS-Daten geprüft.


    Das erweiterte channels Format sollte rückwärtskompatibel sein und auch von einem ungepatchten VDR wieder eingelesen werden können.

    Läuft bei mir bisher fehlerfrei, ich habe es allerdings noch nicht sehr intensiv geprüft.

    Dann kann man die Tpid bei Radiosendern mit Extrapid für RDS zum Speichern dieser Pid in der channels.conf verwenden. Meines Wissens gibt es keinen Radiosender, wo die Teletextpid aktiv wäre.

    Ich habe dir ja zugestimmt, dass diese Betrachtung grundsätzlich logisch ist. Aber eine doppelbelegung der tpid würde wahrscheinlich nicht nur beim teletext-Plugin Anpassungen erfordern, es gibt ja auch ander Plugins die die tpid zumindest weitergeben.

    Ich habe inzwischen einen Patch, der die Radiotext-Pid als Erweiterung der tpid in die channels.conf schreibt, und auch einen daran angepassten "radio-1.1.0-00-AAC_RDS.patch". Die so geänderte channels.conf sollte auch witerhin von einem ungepatchten VDR eingelesen werden können, ich habe es aber noch nicht ausführlich getestet.

    Ich werde diese Erweiterung am Abend posten.

    LG Helmut