Anti-Jammer-Thread

Hier kann über allgemeine Themen diskutiert werden, die sonst in kein Forum passen.
Insbesondere über Szene, Games, Kultur, Weltgeschehen, Persönliches, Recht, Hard- und Software.
Benutzeravatar
Sternmull
Establishment
Beiträge: 264
Registriert: 27.04.2007, 00:30
Echter Name: Til
Wohnort: Dresden

Re: Anti-Jammer-Thread

Beitrag von Sternmull »

Praktisch gesehen ist es in meinen Augen leider wirklich unrealistisch in einem Prozess sinnvoll weiter machen zu wollen wenn der native Heap am Ende ist. Wie schon erwähnt wird wahrscheinlich irgendein Hornochse in einer der gelinkten Bibliotheken schon dafür sorgen das alles gegen die Wand läuft wenn gerade in seinem Code eine Allokation fehlschlägt... selbst wenn alles drumherum perfekt damit umgehen kann. Das ist leider ein Szenario auf das so gut niemand testet.

Ein ähnliches Problem hat man auch wenn unter Linux der RAM voll ist... dann springt der "OOM killer" an und ballert wahllos Prozesse weg bis es ihm wieder besser geht. Ob das System danach noch mit sich selbst im reinen ist, ist ein Glücksspiel weil auch essenzielle Prozesse wie Dienste etc. verloren gehen können. Die praktische "Lösung" ist auch hier: Dafür sorgen das es gar nicht erst dazu kommt.

Muss ich jetzt auch noch Anti-Jammern... ähm... ach ja: Ist aber alles nicht so schlimm weil ja in der Regel alles gut geht. Und memory overcommitment ist ja auch eine feine Sache wenn man den zur Verfügung stehenden RAM optimal nutzen will :)
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Sternmull hat geschrieben:Praktisch gesehen ist es in meinen Augen leider wirklich unrealistisch in einem Prozess sinnvoll weiter machen zu wollen wenn der native Heap am Ende ist.
Doch doch. Unser Use Case ist ein CAD-Programm, in dem der User Objekte verteilt. Das sind hübsche Objekte. So hübsch, dass jeder zweite User wissen will, was passiert, wenn er statt 20 oder 100 Objekten eine Milliarde anlegt. Man kann beim achtmillionsten Baum sagen: "Jetzt kann ich nicht mehr allokieren; hier brechen wir das ab und vergessen die Schnapsidee". Objekte wieder löschen; kurzes Fehlerzeichen an den User; alles im Lot. Es darf nicht abstürzen, nicht seine ungespeicherten Änderungen löschen; nicht die anderen Zeichnungen schließen. Ich halte das nicht für unrealistisch und bei uns funktioniert es jetzt auch. Leider machen es die erwähnten Hornochsen oft doch unmöglich.
Ein ähnliches Problem hat man auch wenn unter Linux der RAM voll ist... dann springt der "OOM killer" an und ballert wahllos Prozesse weg bis es ihm wieder besser geht. Ob das System danach noch mit sich selbst im reinen ist, ist ein Glücksspiel weil auch essenzielle Prozesse wie Dienste etc. verloren gehen können. Die praktische "Lösung" ist auch hier: Dafür sorgen das es gar nicht erst dazu kommt.
Gibt es das noch (außerhalb von Embedded Systems)? Ich hielt den Overcommit für ein Spukgespenst und dachte, sie hätten das um 1990 aus dem Kernel entfernt ...
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Sternmull
Establishment
Beiträge: 264
Registriert: 27.04.2007, 00:30
Echter Name: Til
Wohnort: Dresden

Re: Anti-Jammer-Thread

Beitrag von Sternmull »

Der Ansatz ist lobenswert und absolut richtig. Leider bricht es dann immer noch auseinander wenn der Nutzer es geschafft hat den Heap bis aufs letzte mit seinen Objekten "zuzumüllen" und dann ein Funktion in Fremdcode aufgerufen wird und erst in dieser die Grenze überschritten wird... und der Fall nicht vernünftig behandelt wird. Es gibt sicherlich einige Bibliotheken die diesen Fall vernünftig behandeln, aber zumindest in den Projekten mit denen ich zu tun habe mach ich mir da keine große Hoffnung.

Was das Spukgespenst angeht: Das treibt auch heute noch sein Unwesen. Es wird bis heute immer mal wieder was dran gefrickelt, aber verschwinden wird das sicherlich auch in den nächsten Jahren nicht. Aber praktisch gesehen empfinde ich das overcommitment durchaus sinnvoll da es dem allozierten Speicher keinen physikalischen zuordnet solange er nicht wirklich beschrieben wird. Der zentrale Knackpunkt dürfte aber fork mit copy on write Semantik sein, denn das ist eine Form von overcommitment die unter unixoiden Systemen wirklich nicht ohne massiven Performance-Einbußen abgestellt werden kann.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Ja, fork() ist das riesen Problem.
Aber praktisch gesehen empfinde ich das overcommitment durchaus sinnvoll da es dem allozierten Speicher keinen physikalischen zuordnet solange er nicht wirklich beschrieben wird.
Es geht ja nicht einmal um physikalischen Speicher, denn wir befinden uns ja in virtueller Speicherverwaltung. Mein naiver Reflex ist, dass man das Overcommit einfach abschaffen und stattdessen die Page File auf 50 GiB vergrößern könnte um den Worst Case zu behandeln. Naja; keine Ahnung.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Sternmull
Establishment
Beiträge: 264
Registriert: 27.04.2007, 00:30
Echter Name: Til
Wohnort: Dresden

Re: Anti-Jammer-Thread

Beitrag von Sternmull »

Das geht auch indem man vm.overcommit_memory=2 setzt.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4254
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Anti-Jammer-Thread

Beitrag von Chromanoid »

CodingCat hat geschrieben:Unity3D ist wirklich phänomenal funktional und ergonomisch mittlerweile. Insbesondere dass alles sofort per No-Bullshit-C#-Code mit minimalem Framework-Ballast zugreifbar ist, inklusive Editor-Erweiterungen, ist eine wahre Freude. So lässt sich tatsächlich nahezu ohne Einstiegshürde in ein paar produktiven Minuten am Tag mal eine Idee für einen prozeduralen Generator ausprobieren und per Editor steuern, ohne dass man sich um Asset Management oder überhaupt Rendering Gedanken machen müsste. Eine nette Abwechslung zum Beispiel wenn man mal wetterbedingt daheim hockt und gerade keine Lust auf Lichttransport hat. ;)
Als ich wegen der ZFX-Action mit Haxe und JTransc rumgebastelt habe, ist mir Dein Beitrag eingefallen. Jetzt hab ich mir Unity endlich mal genauer angeschaut und bin genauso begeistert. Vielleicht wird's ja doch was mit meinem Beitrag :roll: Auch der WebGL-Export scheint echt gut zu funktionieren.
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: Anti-Jammer-Thread

Beitrag von Alexander Kornrumpf »

Chromanoid hat geschrieben: Als ich wegen der ZFX-Action mit Haxe und JTransc rumgebastelt habe, ist mir Dein Beitrag eingefallen. Jetzt hab ich mir Unity endlich mal genauer angeschaut und bin genauso begeistert. Vielleicht wird's ja doch was mit meinem Beitrag :roll: Auch der WebGL-Export scheint echt gut zu funktionieren.
Kannst du kurz deinen Workflow mit Unity beschreiben? Ich weiß es gibt Dokumentation en masse, aber bei mir hat es nie wirklich geklickt.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4254
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Anti-Jammer-Thread

Beitrag von Chromanoid »

Bei mir um die Ecke wurde der Edeka generalüberholt. Da gibt es jetzt täglich so ziemlich den besten "öffentlichen" Mittagstisch, den ich in der Preisklasse bis 8 EUR bisher gegessen habe. Das Essen wird vor den Augen der Kunden frisch zubereitet, sozusagen direkt aus dem Laden in den Topf. Das ganze ist bisher vielleicht etwas kartoffellastig, aber trotzdem abwechslungsreich im Geschmack. Ich bin begeistert :D Mit gutem Essen im Bauch arbeitet es sich doch viel leichter. :)
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Ja; habe gehört, dass Rewe & Edeka das jetzt einführen. Es gab vor einem Jahr ein interessantes Interview dazu mit dem Rewe-Chef (der in der Berliner Filiale auch selber serviert hat) in der er auch die Motivation dahinter beschrieb; vielleicht findest du es noch.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
DerAlbi
Establishment
Beiträge: 269
Registriert: 20.05.2011, 05:37

Re: Anti-Jammer-Thread

Beitrag von DerAlbi »

Ich habe auf Arbeit eine Klasse geschrieben, die einen JTAG-Port abstrahiert. JTAG ist ein Standard-Test/Debug-Hardware-Zugang für Mikrocontroller usw... Das ganze besteht aus einer Statemachine:
Bild
Ich habe nun Code geschrieben, der mir über eine rekursive Funktion den kürzesten Pfad von einem Zustand zum nächsten Zustand berechnet. Nicht mega spannendes so far.
Aber:
Ich hab geschrieben und geschrieben, immer wieder compiliert, um Syntax-Probleme aufzuspüren.
Und dann kams: Den Code laufen lassen. Funktioniert. Einfach so. Kein Debuggen.. einfach nur Perfektion beim ersten Versuch.
Solche Erfolge hat man selten (ich zumindest).. vorallem wenn man rekursive Algorithmen erdenkt. Geilo.

Aber hier bin ich auf frustrierende Beschränkung von constexpr gestoßen. Der Pfad von einem Zustand zum nächsten ist quasi immer konstant. Wenn man zu einem bestimmten Zeitpunkt genau weiß, in welchem Zustand man sich befindet und man genau weiß, wo man hin will ist der rekursiv berechnete Pfad natürlich konstant und müsste sogar zur Compile-Time feststehen. Aber man darf weder schleifen nocht If verwenden. Das stinkt.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Ohne if oder Haltepunkte auch kein assert. Ich hatte mich jahrelang auf constexpr gefreut, und jetzt benutze ich es genau null Mal in meinen Projekten.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von dot »

DerAlbi hat geschrieben:Aber man darf weder schleifen nocht If verwenden. Das stinkt.
Diese Beschränkung wurde bereits mit C++14 geliftet. Du darfst in constexpr Funktionen nun sowohl if als auch Schleifen jeglicher Art verwenden. Du darfst sogar lokale Objekte erzeugen und mutieren... ;)
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Anti-Jammer-Thread

Beitrag von Spiele Programmierer »

Es gibt ein Notfall "if" in C++11. Und zwar der Auswahloperator ? :.
Der nicht ausgewählte Wert muss nicht die constexpr-Bedingungen erfüllen. Sokann man da ein "assert", "throw" oder einen Haltepunkt einfügen.

So kann man ein assert dann als Makro implementieren:

Code: Alles auswählen

static inline void AssertConstexprFailed(char const* Str)
{ //Diese Hilfsfunktion ist notwendig, weil "assert" als Makro implementiert ist und daher nicht als
     assert(Str); //Expression im Auswahloperator verwendet werden kann.
}
#define ASSERT_CONSTEXPR(CONDITION) ((CONDITION) ? 0 : (::AssertConstexprFailed(#CONDITION), 0))
Mit dem Kommaoperator kann man das meiner Meinung nach sogar relativ elegant verwenden.
Zum Beispiel so:

Code: Alles auswählen

template<typename T>
class ArrayView
{
public:
    T* BeginPtr;
    T* EndPtr;
    constexpr T const& operator[](size_t Index) const noexcept //C++11
    {
        return ASSERT_CONSTEXPR(static_cast<size_t>(EndPtr - BeginPtr) > Index), 
            BeginPtr[Index];
    }
};
Mit "Relaxed Constexpr" in C++14 (oder in anderen Worten: So ziemlich überall außer mit dem MSVC) kann man "ASSERT_CONSTEXPR" auch als Statement und einen ; dahinter verwenden.

Einziges Problem an der Methode: Zeilennummer und Datei werden nicht im assert angezeigt.
Das kann man mit einem Lambda umgehen:

Code: Alles auswählen

#define ASSERT_CONSTEXPR(CONDITION) ((CONDITION) ? 0 : ([](){ assert(#CONDITION); }(), 0))
Einziges Problem jetzt noch: Visual Studio ist mit dem Konstrukt mal wieder überfordert.
Für Visual Studio kann man _wassert aufrufen. Das ergibt dann insgesamt:

Code: Alles auswählen

	
#if !defined(NDEBUG)
    #ifdef _MSC_VER
        //Der Microsoft Compiler stürzt mit der allgemeinen Variante leider gelegentlich mit einem Internal Error ab.
        #ifndef _CRT_WIDE_
            #define _CRT_WIDE_(S) L ## S
        #endif
        #ifndef _CRT_WIDE
            #define _CRT_WIDE(S) _CRT_WIDE_(S)
        #endif
        #define ASSERT_CONSTEXPR(CONDITION) ((CONDITION) ? 0 : (::_wassert(_CRT_WIDE(#CONDITION), _CRT_WIDE(__FILE__), __LINE__), 0))
    #else
        #define ASSERT_CONSTEXPR(CONDITION) ((CONDITION) ? 0 : ([](){ assert(!#CONDITION); }(), 0))
    #endif
#else
    #define ASSERT_CONSTEXPR(CONDITION) ((CONDITION) ? 0 : 0)
#endif
Visual Studio hat übrigens wirklich, wirklich viele Bugs in Verbindung mit constexpr!
Da muss man echt aufpassen. Der Beispielcode den ich gerade zu dem Makro erstellt habe, kompiliert zu falschen Code. "EndPtr" wird einfach auf 0 gesetzt! :evil:
Einen ähnlichen Fehler (sieht mir nicht nach dem Gleichen aus) habe ich schon vor Monaten reportet, aber es ist kein Bugfix in Sicht:
https://connect.microsoft.com/VisualStu ... ls/2513213

EDIT:
Hm, scheint mir eigentlich alles eher ein Fall für den Jammer-Thread.
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: Anti-Jammer-Thread

Beitrag von Alexander Kornrumpf »

DerAlbi hat geschrieben: Aber hier bin ich auf frustrierende Beschränkung von constexpr gestoßen. Der Pfad von einem Zustand zum nächsten ist quasi immer konstant. Wenn man zu einem bestimmten Zeitpunkt genau weiß, in welchem Zustand man sich befindet und man genau weiß, wo man hin will ist der rekursiv berechnete Pfad natürlich konstant und müsste sogar zur Compile-Time feststehen.
Für mich klingt das, um mal ein Beispiel zu geben, [edit] vergleichbar mit einer fiktiven Version von bison/yacc, die so funktionieren würde[/edit], dass man dem Compiler den Code von yacc und eine Grammatik gibt und dann bekommt man ein Kompilat, das immer noch die komplette Funktionalität von yacc hat aber außerdem diese Grammatik parsen kann.

Auch nach längerem Nachdenken komme ich auf keinen Fall in dem man das wollen würde.
Zuletzt geändert von Alexander Kornrumpf am 26.08.2016, 14:50, insgesamt 1-mal geändert.
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von xq »

Alexander Kornrumpf hat geschrieben:Für mich klingt das, um mal ein Beispiel zu geben, als würden bison/yacc so funktionieren, dass man dem Compiler den Code von yacc und eine Grammatik gibt und dann bekommt man ein Kompilat, das immer noch die komplette Funktionalität von yacc hat aber außerdem diese Grammatik parsen kann.
Das ist so nicht ganz richtig. Bison/Yacc sind beides Tools, welche selbst nur eine Bison-Grammatik verstehen. Diese lesen sie ein, wandeln sie in einen Automaten um und erzeugen für diesen Automaten C-Code, welcher die vom Automaten erkannte Grammatik erkennt. Von dem Code, der in Bison werkelt ist nacher keine einzige Zeile mehr vorhanden.
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: Anti-Jammer-Thread

Beitrag von Alexander Kornrumpf »

MasterQ32 hat geschrieben: Das ist so nicht ganz richtig. Bison/Yacc sind beides Tools, welche selbst nur eine Bison-Grammatik verstehen. Diese lesen sie ein, wandeln sie in einen Automaten um und erzeugen für diesen Automaten C-Code, welcher die vom Automaten erkannte Grammatik erkennt. Von dem Code, der in Bison werkelt ist nacher keine einzige Zeile mehr vorhanden.
Das ist mir bekannt. Und ungefähr so "sollte" das hier diskutierte Tool meiner Meinung nach auch funktionieren. Was ich geschildert habe ist ein fiktives bison/yacc, das so funktioniert, wie ich glaube dass DerAlbi will das sein Tool funktioniert, mit dem Ziel per Analogie aufzuzeigen, dass das vielleicht nicht ideal ist.

Habe es per EDIT eindeutiger gemacht.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Ich habe meine komplette Fehlerbehandlung auf was eigenes Callback-basiertes umgestellt, und es war wohl die beste Entscheidung meiner Programmierlaufbahn.

Im Grund nimmt jetzt jede Funktion dieses struct entgegen:

  struct Supervisor {
    void * context;
    intptr_t (__stdcall * onEvent)(void * context, int event, void const * more, char * callStack);
  };


Passiert irgendwas, das nicht lokal behandelt werden kann, wird das onEvent-Callback aufgerufen. Erfordert das irgendeine Entscheidung des Users, wird der Rückgabewert ausgewertet. Braucht man zusätzliche Details (bei nicht vorhandener Datei z.B. den Pfad), wird das in dem Zusatzzeiger übergeben. Und alle Funktionen geben nun nur noch ein bool zurück, ob ein kritischer Abbruch vorliegt (Grund egal, wurde ja vorher an Ort und Stelle ausdiskutiert).

Die Event-Codes dürfen nicht doppelt vorkommen. Das ist eine globale Liste, die man warten muss, und davor hat es mir immer gegraust. Aber nach einem Jahr kann ich sagen: Sie ist es total wert, und 2³² Werte sind VIEL Platz.

Fortschrittsbalken mit Abbruchfunktion? Der Algorithmus muss einfach regelmäßig

  if(0 == report(supervisor, current_percentage, 0, 0)) {
    break;
  }


aufrufen. Und BUMM, sind alle UI-Probleme, die ich je hatte, gelöst:
  • Lokalisierung? Lächerlich einfach. In der Logik kommen nur noch Codes vor, die irgendwo im Callback zu Text umgewandelt werden. Von minimal-C-Konsolenanwendungen über „ich schreib alles in log.txt und lass es dann crashen“ bis zu High-End-C#-Programmen mit lokalisierter Grafik-UI, kein Problem mehr. Wenn ich meine Logik in einer Shell Extension verwenden will, darf sie nur noch den ersten Fehler als 4-B-COM-HRESULT zurückgeben? Ist in 30 Sekunden erledigt – ohne Logikänderungen.
  • Sprachbarrieren? struct + Callback kann jede Sprache. In 20 Zeilen hat man einen C#-Wrapper mit hübschen onError(), onResume, onD3D11Error(HResult)-Methoden.
  • Fortschrittsbalken & Abbruchfunktion hatten wir ja gerade. Und das optional beim Einbinden der Logik. Jeder Logik, sogar verschachtelt (Callbacks verketten).
  • Bei Fehlern erneut versuchen (Login abgelaufen, erneut einloggen! WLAN abgerissen; wiederholen, sobald die Verbindung wieder steht! Datenträger X einlegen!)? Geradezu lächerlich einfach, weil die UI einfach 1 oder 0 zurückgeben muss. Versucht das mal mit Codebases, die auf Exceptions aufbauen – wegschmeißen, neu machen; wegschmeißen, …
  • Welche Fehler kritisch sind, bestimmt jetzt die UI. Microsoft musste für IntelliSense einen neuen Compiler bauen, weil der Alte dafür entworfen war, jeden Fehler als kritisch zu behandeln? lol, Amateure! Wenn jetzt ein INI_PARSE_ERROR_EXPECTED_NEWLINE ankommt, kann ich mir aussuchen, ob ich den Text im Editor rot unterkringle oder das direkt das ganze System crashe!
  • Meine Fresse, erzeugt das geilen Code. Ein einfaches if(nullptr in die Logik, und der Compiler schmeißt jeden Code, der entfernt mit Fehlerbehandlung/Fortschrittsbehandlung/Userfragen zu tun hat aus der EXE raus, bis aufs letzte Byte.
Bleibt noch der callStack-Parameter:
  • Leute schreiben gern __FILE__, __LINE__, __FUNCTION__ in ihre Return Values/Exceptions/andere primitive Fehlerreflexe
  • das System freut sich über die Unmenge Strings
  • Angreifer freuen sich über die Funktionsnamen, den sie im Code wiederfinden
  • Programmierer freuen sich über die Erkenntnis, dass custom_memcpy() einen Nullzeiger hat, sind aber total ratlos, was denn kopiert werden sollte und was das Programm gerade macht
  • Call Stacks sind 1337 – kompakter und aussagekräftiger
  • dummerweise funktionieren die nicht über Sprachgrenzen hinweg – wenn sich die C#-UI oder die Python-Testfälle im der C#-/Python-Callback befinden, haben sie keinen Zugriff auf den Call Stack, der den Fehler eskaliert hat
  • also Call Stack einfach als ASCII-Text mitliefern
  • natürlich nicht bei trivialen Dingen wie Fortschrittsinformationen
Nächster Schritt? Die ganze Umgebung durch Callbacks virtualisieren.
  • Dateisystem, damit meine ganze Dateiverarbeitung sowohl mit der Platte als auch in Netzwerkstreams als auch in den PAK-Archiven von Spielen funzt (klingt wie Standard-C-Streams – nur, dass ich mit denen über Persönliches zerstritten bin)
  • Threads & Locks, damit ich meine geometrischen Algorithmen mit einer Zeile zwischen seriell/Thread Pool’d/Threaded portieren kann (OHNE TEMPLATES, weil bei mir noch immer alles in unter einer Sekunde kompiliert)
  • Speicheranforderungen, damit ich Systeme über verschiedene Anforderungen hinweg portieren kann (bei Servern immer ordentlich aufräumen vs. bei kleinen Apps einfach den Speicher zukleistern und alle Jubeljahre die Pools löschen, war bei Assimp ein Hauptproblem)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4254
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Anti-Jammer-Thread

Beitrag von Chromanoid »

Klingt ziemlich cool. Außer die Event-IDs... Das läuft aus meiner Sicht schnell auf unangenehme Connascence of meaning hinaus. Kannst Du nicht statt einer Event-ID ein eigenes Objekt übergeben, dass typisiert wie ein eigener Exception-Typ/Event-Typ alle wichtigen Daten enthält? Der Lebenszyklus des Events könnte doch auf den Aufruf des Callbacks begrenzt sein. Im Grunde willst Du mit den Event-Codes doch eine Art Reflection machen, würde da nicht dieser typeid-Operator passen? Vielleicht könnte man außerdem noch ein Cause-Event hinzufügen, damit Du Events schachteln kannst, sonst hast Du entweder leaky Abstractions oder man kann die Fehler nur schlecht analysieren.

Mich würde noch interessieren, wie Du mit Prozessen umgehst, die nicht "graceful" weiterbearbeitet werden können, egal was der User sagt. Also ein Fall wo man normalerweise eine Exception bis zum User durchreicht. So wie ich Deine Ausführungen verstehe, würdest Du anhand der Event-ID interpretieren, dass der Prozess sowieso nicht weitergeht und dann nur eine Fehlermeldung anzeigen? Da fände ich dann wirklich ein Objekt schöner das über die Hierarchie oder so abbildet, ob es sich um was kritisches handelt. Wie würdest Du mit Fällen umgehen, in denen man als Aufrufer gerne weitere Fehlerbehandlung durchführen möchte, einen Supervisor davor schalten, der nur bestimmte Events weiter propagiert? Bei der generischen Fehlerbehandlung vermisst man Reflection mal tatsächlich oder?
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: Anti-Jammer-Thread

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben:Ich habe meine komplette Fehlerbehandlung auf was eigenes Callback-basiertes umgestellt, und es war wohl die beste Entscheidung meiner Programmierlaufbahn.

Im Grund nimmt jetzt jede Funktion dieses struct entgegen:

  struct Supervisor {
    void * context;
    intptr_t (__stdcall * onEvent)(void * context, int event, void const * more, char * callStack);
  };
Ist geil oder? Moderne Programmiersprachen und ihre Idiome sind das Ergebnis von ich weiß nicht wievielen Mannstunden klunger Menschen, aber was man eigentlich will ist ein simples Paar aus Daten und tuWasMitDenDaten.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von dot »

Krishty hat geschrieben:Passiert irgendwas, das nicht lokal behandelt werden kann, [...]
Gibt's dafür nicht Exceptions? :P
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Chromanoid hat geschrieben:Außer die Event-IDs... Das läuft aus meiner Sicht schnell auf unangenehme Connascence of meaning hinaus. Kannst Du nicht statt einer Event-ID ein eigenes Objekt übergeben, dass typisiert wie ein eigener Exception-Typ/Event-Typ alle wichtigen Daten enthält? Der Lebenszyklus des Events könnte doch auf den Aufruf des Callbacks begrenzt sein. Im Grunde willst Du mit den Event-Codes doch eine Art Reflection machen, würde da nicht dieser typeid-Operator passen?
Ich übergebe für Events, bei denen eine Event-ID nicht ausreicht, durchaus den Rest über den Zeiger in more! Aktuell z.B. bei Zugriffsfehlern auf Registry-Schlüssel den Hive, Pfad, und Win32-Fehlercode. Da müssen sich natürlich wieder beide Programmteile einigen, aber sowas ändert sich nicht gerade häufig.
Mich würde noch interessieren, wie Du mit Prozessen umgehst, die nicht "graceful" weiterbearbeitet werden können, egal was der User sagt. Also ein Fall wo man normalerweise eine Exception bis zum User durchreicht. So wie ich Deine Ausführungen verstehe, würdest Du anhand der Event-ID interpretieren, dass der Prozess sowieso nicht weitergeht und dann nur eine Fehlermeldung anzeigen? Da fände ich dann wirklich ein Objekt schöner das über die Hierarchie oder so abbildet, ob es sich um was kritisches handelt. Wie würdest Du mit Fällen umgehen, in denen man als Aufrufer gerne weitere Fehlerbehandlung durchführen möchte, einen Supervisor davor schalten, der nur bestimmte Events weiter propagiert? Bei der generischen Fehlerbehandlung vermisst man Reflection mal tatsächlich oder?
Habe ich das mti der vorherigen Antwort abgehakt? Also bei Dateizugriffsfehler z.B. kann sich die UI dann aussuchen, ob sie einfach via int 3 in den Debugger crasht oder mit den Win32-Error-Codes und dem Pfad Pathologie betreibt und ein hübsches „die Datei ist noch in Bearbeitung durch XYZ“ anzeigt.
dot hat geschrieben:Gibt's dafür nicht Exceptions? :P
Wenn mein Datei-Parser normalerweise bei kaputten Thumbnails abbricht, ich aber für die Forensik einen kompatiblen Modus brauche, der so viele Daten wie möglich weiter birgt – dann schreibe ich

  catch(BrokenThumbnailException const & e) {
    log("broken Thumbnail @%p", e.offsetInFile);
    resume;
  }


? Wenn ich das könnte, würde ich mir tatsächlich Arbeit sparen. Geht aber leider nur mit SEH, und darauf will ich nicht aufbauen müssen …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Anti-Jammer-Thread

Beitrag von Spiele Programmierer »

Ich bin ehrlich gesagt etwas skeptisch. Wie zum Beispiel allokierst du Speicher so dass beim Fehlschlagen so ein Callback aufgerufen wird? Brauchen dann nicht unglaublich viele Funktionen so ein Callback? String-Funktionen stelle ich mir extrem anstrengend zu verwenden vor.

Die Error-IDs sind für meinen Geschmack auch zu zentralistisch verwaltet. Warum übergibst du nicht zB. einen Pointer zu einer kleinen statischen konstanten Struktur die dann am besten auch gleich einen String mit der Fehlerbeschreibung enthält (+ ggf. weitere Informationen). Das hätte in meinen Augen viele Vorteile: Es ist Typsicher, es kann beliebig erweitert werden, man muss keine IDs mehr zuweiten, alle weiteren Informationen sind dann ganz lokal bei der Fehlerdefinition.

Meine Erfahrung in C++ ohne Exceptions sind sehr negativ. Und das, obwohl ich eigentlich alles andere als ein Freund der Exceptions bin, aber in C++ fehlen einach alternative Sprachmittel. Das geht damit schon los, dass RAII praktisch unbrauchbar wird, weil man im Konstruktor keine Fehlercodes zurückgeben kann. Falls im Konstruktor mehrere Objekte konstruiert werden die jeweils Fehler auslösen können, wird das Konzept der Initialisierungsliste zum Problem.
Überhaupt ist es ausgesprochen mühselig, auf alle Rückgabewerte zu reagieren:

Code: Alles auswählen

std::unique_ptr<GameObj> GameObj(a, b, c, d, e);

//Stattdessen....

std::unique_ptr<GameObj> GameObj;
{
    ErrorCodeT ErrorCode;
    if (!(GameObj = std::make_unique<GameObj>(a, b, c, d, e, ErrorCode)))
         return ErrorCodeT::OutOfMemory;
    if (ErrorCode != ErrorCodeT::Success)
         return ErrorCode;
}
Aber das Schlimmste ist, dass man das nichtmal in ein Makro packen kann, weil die notwendigen "returns" keine Expressions sind!
Ich habe begonnen, mich wirklich nach GCCs Expression Statements oder insgesamt Rusts System zur Fehlerrückgabe zu sehnen.

EDIT:
Wenn mein Datei-Parser normalerweise bei kaputten Thumbnails abbricht, ich aber für die Forensik einen kompatiblen Modus brauche, der so viele Daten wie möglich weiter birgt – dann schreibe ich ...
Nun, dafür müsstest du die Methode in mehrere Schritte unterteilen und dann im User Code über alle Thumbnails iterieren, so dass du jeden einzelnen Aufruf abfangen kannst und mit dem nächsten Thumbnail fortfahren.
Evt. könnten dabei Coroutines in C++17 weiterhelfen, allerdings weiß ich darüber zu wenig.

Ansonsten spricht in dem Fall nichts gegen ein ganz lokales Callbacksystem ohne globale IDs etc.?
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Spiele Programmierer hat geschrieben:Ich bin ehrlich gesagt etwas skeptisch. Wie zum Beispiel allokierst du Speicher so dass beim Fehlschlagen so ein Callback aufgerufen wird?
Ich verstehe gerade nicht ganz – wofür Speicher? Um Stack Overflows zu vermeiden?
Spiele Programmierer hat geschrieben:Warum übergibst du nicht zB. einen Pointer zu einer kleinen statischen konstanten Struktur die dann am besten auch gleich einen String mit der Fehlerbeschreibung enthält (+ ggf. weitere Informationen). Das hätte in meinen Augen viele Vorteile: Es ist Typsicher, es kann beliebig erweitert werden, man muss keine IDs mehr zuweiten, alle weiteren Informationen sind dann ganz lokal bei der Fehlerdefinition.
Wie identifiziere ich denn von Python aus, dass es sich bei diesem Zeiger um eine FileAccessException handelt? Durch den String, der da drin ist? Dann habe ich statt int-IDs nun char *-IDs, das ist keine Verbesserung sondern nur viel Bloat. Oder ist der String nur zur Convenience da, wie std::exception::what()? In dem Fall ist er nutzlos weil die UI ihn erst ins Chinesische übersetzen muss um ihn anzuzeigen, und darum alle Strings im Voraus bekannt sein müssen.
Meine Erfahrung in C++ ohne Exceptions sind sehr negativ. Und das, obwohl ich eigentlich alles andere als ein Freund der Exceptions bin, aber in C++ fehlen einach alternative Sprachmittel. Das geht damit schon los, dass RAII praktisch unbrauchbar wird, weil man im Konstruktor keine Fehlercodes zurückgeben kann. Falls im Konstruktor mehrere Objekte konstruiert werden die jeweils Fehler auslösen können, wird das Konzept der Initialisierungsliste zum Problem.
Überhaupt ist es ausgesprochen mühselig, auf alle Rückgabewerte zu reagieren:
Weißt du, was das Schöne am zweiten Quelltext ist? Dass ihn jeder sofort versteht. Ich kann den Praktikanten mit C#-Erfahrung draufgucken lassen und der weiß sofort, ob Fehler behandelt werden oder nicht. Im ersten Beispiel muss ich ihn erstmal eine Woche in C++-Idiome einarbeiten, dann die Eigenheiten der verschiedenen Smart Pointers erklären, und dann C++-Wrapper für zwei C-Fremdbibliotheken schreiben, weil sie Parameter für a und b sind.
Ich habe begonnen, mich wirklich nach GCCs Expression Statements oder insgesamt Rusts System zur Fehlerrückgabe zu sehnen.
Danke für den Link! Wäre Rust ein, zwei Jahre früher gekommen … ach wer weiß :(
Nun, dafür müsstest du die Methode in mehrere Schritte unterteilen und dann im User Code über alle Thumbnails iterieren, so dass du jeden einzelnen Aufruf abfangen kannst und mit dem nächsten Thumbnail fortfahren.
Evt. könnten dabei Coroutines in C++17 weiterhelfen, allerdings weiß ich darüber zu wenig.
Jetzt hat mein C++-zu-C#-Interface statt einer Funktion und einem Callback
  • 30 verstreute Funktionen
  • eine Iteratorklasse
  • einen Wrapper auf C++-Seite, der alle C++-Exceptions fängt und in was .NET-kompatibles übersetzt, weil man Exceptions nicht in andere Sprachen schmeißen darf und wenn doch, dann schlecht auswerten kann
  • die Coroutines crashen erstmal die Common Language Runtime weil da irgendwas nicht Fiber-Safe war
Und jeder, der da jemals einen Bug beheben soll, muss sich erstmal in alles einarbeiten. Nein. Einfach nein.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von dot »

Sehr guter Talk zum Thema:

[youtube]fOV7I-nmVXw[/youtube]

;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Hmm … ist doch C++-spezifisch, oder? Wie lösen dispatcher() und transmogrify() das Problem einer globalen Fehlerliste, wenn eine andere Sprache Fehler behandeln soll?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Anti-Jammer-Thread

Beitrag von Spiele Programmierer »

Ich verstehe gerade nicht ganz – wofür Speicher? Um Stack Overflows zu vermeiden?
:?: Naja, wenn man zum Beispiel ein paar Strings beliebiger Länge in einer Datenstruktur braucht.
Die Allokation kann schließlich jederzeit fehlschlagen. (malloc gibt 0 zurück, std::bad_alloc fliegt)
Meine Erfahrung ist, dass es sehr kompliziert ist, derartige Fehler ohne Exceptions richtig abzufangen.
Wie identifiziere ich denn von Python aus, dass es sich bei diesem Zeiger um eine FileAccessException handelt?
Ich habe keine Erfahrung mit Python. In C# ist das auf jeden Fall kein Problem. Du vergleichst einfach ob der IntPtr gleich dem IntPtr zu dem "FileAccessException" Speicherblock ist.
In dem Fall ist er nutzlos weil die UI ihn erst ins Chinesische übersetzen muss um ihn anzuzeigen, und darum alle Strings im Voraus bekannt sein müssen.
Sehe ich anders. Ein solcher Fehler sollte ja auch idR. nicht direkt im Benutzerinterface erscheinen. Es ist eine Information für die Techniker.

Was ich noch viel schrecklicher finde: Lokalisierte Fehlermeldungen! Die einzige Person die viele Fehler beheben könnte, versteht ihn dann nicht mehr und "der gebildete Nutzer" ist nicht mehr in der Lage im Internet nach der Fehlermeldung zu suchen.
Weißt du, was das Schöne am zweiten Quelltext ist? Dass ihn jeder sofort versteht. Ich kann den Praktikanten mit C#-Erfahrung draufgucken lassen und der weiß sofort, ob Fehler behandelt werden oder nicht.
Ich glaube, da stimme ich nicht ganz zu. Wenn der Praktikant nur die Funktionsweise des Codes verstehen will, ist die erste Variante ganz offensichtlich prägnanter bezüglich der High Level Funktionsweise. Wenn der Praktikant den Code schreibt und mit der ersten Variante verwirrt ist, dann würde er bestimmt nicht alle Fehler in der zweiten Variante richtig behandeln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Spiele Programmierer hat geschrieben:
Ich verstehe gerade nicht ganz – wofür Speicher? Um Stack Overflows zu vermeiden?
:?: Naja, wenn man zum Beispiel ein paar Strings beliebiger Länge in einer Datenstruktur braucht.
Die Allokation kann schließlich jederzeit fehlschlagen. (malloc gibt 0 zurück, std::bad_alloc fliegt)
Meine Erfahrung ist, dass es sehr kompliziert ist, derartige Fehler ohne Exceptions richtig abzufangen.
Achso – bisher hatte ich noch nichts mit dynamischer Länge. Dateinamen etc. liegen ja an dem Punkt, an dem man sie braucht, sowieso schon fertig vor.
Spiele Programmierer hat geschrieben:
Wie identifiziere ich denn von Python aus, dass es sich bei diesem Zeiger um eine FileAccessException handelt?
Ich habe keine Erfahrung mit Python. In C# ist das auf jeden Fall kein Problem. Du vergleichst einfach ob der IntPtr gleich dem IntPtr zu dem "FileAccessException" Speicherblock ist.
Und wo kriege ich den her? #include FileAccessException.hpp wird ja schlecht gehen … alle möglichen Probleme als DLL-Export definieren und die Definitionen nach C# übersetzen und synchron halten?
Spiele Programmierer hat geschrieben:
In dem Fall ist er nutzlos weil die UI ihn erst ins Chinesische übersetzen muss um ihn anzuzeigen, und darum alle Strings im Voraus bekannt sein müssen.
Sehe ich anders. Ein solcher Fehler sollte ja auch idR. nicht direkt im Benutzerinterface erscheinen. Es ist eine Information für die Techniker.

Was ich noch viel schrecklicher finde: Lokalisierte Fehlermeldungen! Die einzige Person die viele Fehler beheben könnte, versteht ihn dann nicht mehr und "der gebildete Nutzer" ist nicht mehr in der Lage im Internet nach der Fehlermeldung zu suchen.
Bis zu welchem Grad die Fehlermeldungen angezeigt und lokalisiert werden sollen, da scheiden sich die Geister und die Anwendungsfälle sowieso (eine Management-Konsole auf einem Server hat andere Anforderungen als eine internationalisierte App), aber wenn diese Datenstrukturen erst garnicht den Benutzer erreichen sollen, kann man sich den String-Bloat auch sparen und einfach eine ID reinschreiben. Damit wären wir … wieder bei dem, was ich mache.
Spiele Programmierer hat geschrieben:
Weißt du, was das Schöne am zweiten Quelltext ist? Dass ihn jeder sofort versteht. Ich kann den Praktikanten mit C#-Erfahrung draufgucken lassen und der weiß sofort, ob Fehler behandelt werden oder nicht.
Ich glaube, da stimme ich nicht ganz zu. Wenn der Praktikant nur die Funktionsweise des Codes verstehen will, ist die erste Variante ganz offensichtlich prägnanter bezüglich der High Level Funktionsweise.
Auch das ist wohl Geschmackssache – ich jedenfalls bevorzuge viel dummen Code, an dem man sofort erkennen kann was das Programm macht, gegenüber wenig tiefgründigem Code, bei dem man erstmal zehn Klassen analysieren muss um zu verstehen, was er unter Speicherdruck macht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Anti-Jammer-Thread

Beitrag von Spiele Programmierer »

aber wenn diese Datenstrukturen erst garnicht den Benutzer erreichen sollen, kann man sich den String-Bloat auch sparen und einfach eine ID reinschreiben.
Ich denke es gibt auch noch eine Zwischenstuffe. Als Entwickler hat man dann den Vorteil, dass man nicht immer die Fehler-IDs nachschlagen muss. Was ist wenn man mal eine ID ändert oder entfernt? Was ist wenn ein technisch versierter Nutzer das Problem verstehen will?

Die Windowsfehlercodes (Oder noch schlimmer: errno) sind diesbezüglich zum Beispiel ein Negativbeispiel. Man kriegt entweder eine kryptische ID von der man nichtmal entfernt das Problem ablesen kann oder mit ein paar komplizierten WinAPIs bekommt man eine lokalisierte Fehlermeldung ohne genauere Details.
Und wo kriege ich den her? #include FileAccessException.hpp wird ja schlecht gehen … alle möglichen Probleme als DLL-Export definieren und die Definitionen nach C# übersetzen und synchron halten?
Nun, du musst dann halt die IDs nach C# übersetzen. Scheint mir etwa auf das selbe rauszukommen.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4254
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Anti-Jammer-Thread

Beitrag von Chromanoid »

Könnte man nicht sonst pro Event-ID eine Methode anlegen, die auch per DLL exportiert wird? Die ID-Methoden würden eine Event-ID auf Basis der Basisadresse der DLL (oder sowas) und eines DLL-lokalen Zählers raus geben (oder vielleicht UUIDs?). Die IDs sind damit über Modulgrenzen hinweg eindeutig und die Entwickler würden mitbekommen, wenn ein Event entfernt wird, da die Methode in der DLL nicht mehr vorliegt. Man könnte dann noch mit ein bisschen Metaprogrammierung eine Auflösefunktion von IDs zu Methodenname in die Module/DLLs mit Supervisor-Funktion packen, so dass man einen technischen Kürzel aus der Event-ID erzeugen kann. Wenn man dann mal ein Event grundlegend überarbeitet, so dass die Verarbeitung bestehender Routinen kaputt geht, kann man das Event bzw. die Event-ID-Rückgabemethode einfach umbenennen und schon gibt's ordentliche Compile/Laufzeit-Fehler bei allen Beteiligten.
Zuletzt geändert von Chromanoid am 02.09.2016, 22:30, insgesamt 1-mal geändert.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Spiele Programmierer hat geschrieben:Nun, du musst dann halt die IDs nach C# übersetzen. Scheint mir etwa auf das selbe rauszukommen.
Naja … ein enum mit 200 Einträgen und ein Dutzend structs ist was anderes als 200 Klassen :)
Chromanoid hat geschrieben:Könnte man nicht sonst pro Event-ID eine Methode anlegen, die auch per DLL exportiert wird? Die ID-Methoden würden eine Event-ID auf Basis der Basisadresse der DLL (oder sowas) und eines DLL-lokalen Zählers raus geben (oder vielleicht UUIDs?). Die IDs sind damit über Modulgrenzen hinweg eindeutig und die Entwickler würden mitbekommen, wenn ein Event entfernt wird, da die Methode in der DLL nicht mehr vorliegt.
Alles, was mit DLL-Export zu tun hat, kann vom Compiler nicht mehr wegoptimiert werden, wenn es nicht benutzt wird. Da ist man dann wieder beim Bloat-Problem.

Das mit den UUIDs ist aber eine gute Idee. Ich hatte mir mal vorgenommen, die Error-IDs aus MAC und Datum/Uhrzeit zu hashen. Mit 32 Bits würde man schon auf kollisionsfreie IDs kommen, selbst wenn mehr als vier Leute mit meinem Kram arbeiten würden.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten