Ich glaube das war so, das jeder Pointer, durch nen "Counter" (weil der immer nur um eins erhöht wird) "geschützt" wird und das Problem war, das wenn die Zeit zw. 2 Zugriffen eines Threads zu lange ist, dass dann das Objekt freigegeben werden kann und durch nen anderen Thread wieder alloziert wird und dann der Counter zufällig den selben Wert hat wie der Thread der nun nach längerer Zeit wieder darauf zugreift, aber das Objekt ist jetzt nicht mehr das gleiche.
Korrekt. Aber ich sags nochmal: Das hat NICHTS mit dem Algorithmus den ich meinte zu tun. Was wiedermal bestätigt, dass man über einen konkreten Algorithmus reden sollte und nicht allgemein über irgendwas undefiniertes. Bei meinem Algorithmus von oben hat der reference-counter nur einen Overflow (was ja erst das Problem auslöst), wenn man ca. 4Mrd Leute hat, die eine Reference auf den Cache halten.
Wovon du hingegen sprichst, ist nicht wirklich reference-counting (wird aber so bezeichnet), sondern eine Art Versionierung bei der der Counter angibt, welche Version des Zeigers man gerade hat. Wenn man den Zeiger verändert, zählt man die Version hoch und alle anderen stellen dann fest, dass sie eine alte Version haben (obwohl eventuell der Zeiger vollkommen gleich ist). Dabei wird aber immer um 1 hochgezählt (und niemals runter), d.h. ein Overflow ist realistisch, aber es ist wohl extrem unwahrscheinlich, dass genau 4Mrd. Änderungen an diesem Zeiger zwischen meinem Snapshot und meinem Überprüfen (zu welchem Zweck auch immer) sind und zusätzlich noch der eigentliche Zeiger genau zu dem Zeitpunkt gleich. Der einzige Nachteil ist wie gesagt, dass man zum Ändern einen DoppelCAS braucht, was es auf x64 teilweise nicht gibt, damit man den Pointer+Counter atomar ändert. Aber ich sags nochmal: Von der Art reference-counting habe ich NICHT geredet und die Art sollte man besser durch hazard pointer ersetzen.
Übrigens kann man mit der Art reference-counting das Objekt nicht vollständig freigeben, weil andere noch auf der Referenz rumnudeln könnten (und auf den Counter vertrauen können müssen), d.h. sie Lösen zwar das ABA-Problem, aber man kann trotzdem nicht freigeben. Hazard pointer dagegen lösen beide Probleme.
und auch ob sie theoretisch 100% sicher sind
ja, das kann ich mit 100%iger Sicherheit sagen bzw. so sicher wie man sich sein kann, wenn man das für eine Queue bereits in einem Theorembeweiser selbst gezeigt hat und einen Beweis in selbigen für einen Stack kennt.
ob die performancemäßig [...] besser oder schlechter sind als Reference-Counting
Besser, im speziellen ist das DoppelCAS unschön.
Nachteil ist halt (im Vergleich zu der Art von reference-counting die ich bezogen auf Caches genannt habe) dass man nicht sobald wie möglich freigeben kann, sondern immer ein paar Referenzen hält und dann mehrere auf einmal freigibt (sonst kommt man nicht auf O(1) amortisiert fürs freigeben).
Ich habe eben ein Paper über Hazard Pointer gelesen und die sollen sehr viel performanter sein als Spinlocks sein.
Die meisten lockfreien Algorithmen skalieren massiv besser als Algorithmen mit Locks (zumindest sagen das alle Performancegraphen die ich dazu in Papern gesehen habe). Im speziellen hat man auch keine Probleme mit priority inversion, Deadlocks oder ein Problem falls ein anderer Thread der einen Lock hält abkackt, das sollte man auch nicht unterschätzen. Und sie skalieren besser, weil andere währenddessen ihre Aktion abarbeiten können, was bei einem Spinlock offensichtlich nicht geht. Auch wenn jeder einzelne Thread ein paar atomare Reads mehr macht.
Aber kann es sein, dass man die Verwaltung der Pointer trotzdem durch Locking sichern muss?
Wenn man ein Array fixer Größe (#Threads * #MaxHazardPointerProThread) dann auf jeden Fall nicht, da es ein Thread nur den eigenen Teil schreib und alle anderen Teile nur liest. Auch spielt es hier keine Rolle ob atomar gelesen wird oder nicht.
Für denn Fall, dass man das dynamisch vergrößern (zumindest was die Threadzahl angeht) behauptet Michaels, dass man es auch lockfrei hinbekommt. Darüber hab ich mir aber noch keine Gedanken gemacht ehrlich gesagt.
Aber wie ich schon einiges weiter oben sagte und FlashBurn zwei Posts weiter oben, kommt es bei der Art von Algorithmen _extrem_ auf die konkreten Details an. Im Speziellen sollte man _niemals_ die Reihenfolge von Reads/Writes auf globale Speicherzellen (die btw. von jedem Paper als atomar angenommen werden) auch nur ein bisschen ändern (auch wenn man meint, dass es korrekt wäre), kann ich aus eigener Erfahrung nur sagen
edit:
Meines Wissens nach gibt es dagegen nur 2 100%-tig sichere Abhilfen: zum einen ein richtiger atomarer Lock [...]
Verstehe ich nicht. Ein CAS macht genau das?
aber soweit ich weiß sind viele von denen auf ein einigermaßen vorhersagbares zeitliches Verhalten des Code angewiesen
Das ist Humbug. Im Speziellen (siehe oben) hat es nichts mit der Zeit zu tun, sondern mit der Anzahl der Änderungen bei Versionierung/Reference-counting und wie oben gesagt, exakt ~4Mrd Änderungen und dann noch der gleiche Pointer ist einfach nur unwahrscheinlich.