@@ -635,14 +635,22 @@ func (q *spanQueue) destroy() {
635
635
636
636
lock (& work .spanSPMCs .lock )
637
637
638
- // Mark each ring as dead. The sweeper will actually free them.
639
- //
640
- // N.B., we could free directly here, but work.spanSPMCs.all is a
641
- // singly-linked list, so we'd need to walk the entire list to find the
642
- // previous node. If the list becomes doubly-linked, we can free
643
- // directly.
638
+ // Remove and free each ring.
644
639
for r := (* spanSPMC )(q .chain .tail .Load ()); r != nil ; r = (* spanSPMC )(r .prev .Load ()) {
645
- r .dead .Store (true )
640
+ prev := r .allprev
641
+ next := r .allnext
642
+ if prev != nil {
643
+ prev .allnext = next
644
+ }
645
+ if next != nil {
646
+ next .allprev = prev
647
+ }
648
+ if work .spanSPMCs .all == r {
649
+ work .spanSPMCs .all = next
650
+ }
651
+
652
+ r .deinit ()
653
+ mheap_ .spanSPMCAlloc .free (unsafe .Pointer (r ))
646
654
}
647
655
648
656
q .chain .head = nil
@@ -685,6 +693,11 @@ type spanSPMC struct {
685
693
// work.spanSPMCs.lock.
686
694
allnext * spanSPMC
687
695
696
+ // allprev is the link to the previous spanSPMC on the work.spanSPMCs
697
+ // list. This is used to find and free dead spanSPMCs. Protected by
698
+ // work.spanSPMCs.lock.
699
+ allprev * spanSPMC
700
+
688
701
// dead indicates whether the spanSPMC is no longer in use.
689
702
// Protected by the CAS to the prev field of the spanSPMC pointing
690
703
// to this spanSPMC. That is, whoever wins that CAS takes ownership
@@ -711,7 +724,11 @@ type spanSPMC struct {
711
724
func newSpanSPMC (cap uint32 ) * spanSPMC {
712
725
lock (& work .spanSPMCs .lock )
713
726
r := (* spanSPMC )(mheap_ .spanSPMCAlloc .alloc ())
714
- r .allnext = work .spanSPMCs .all
727
+ next := work .spanSPMCs .all
728
+ r .allnext = next
729
+ if next != nil {
730
+ next .allprev = r
731
+ }
715
732
work .spanSPMCs .all = r
716
733
unlock (& work .spanSPMCs .lock )
717
734
@@ -748,6 +765,8 @@ func (r *spanSPMC) deinit() {
748
765
r .head .Store (0 )
749
766
r .tail .Store (0 )
750
767
r .cap = 0
768
+ r .allnext = nil
769
+ r .allprev = nil
751
770
}
752
771
753
772
// slot returns a pointer to slot i%r.cap.
@@ -780,22 +799,26 @@ func freeDeadSpanSPMCs() {
780
799
unlock (& work .spanSPMCs .lock )
781
800
return
782
801
}
783
- rp := & work .spanSPMCs .all
784
- for {
785
- r := * rp
786
- if r == nil {
787
- break
788
- }
802
+ r := work .spanSPMCs .all
803
+ for r != nil {
804
+ next := r .allnext
789
805
if r .dead .Load () {
790
806
// It's dead. Deinitialize and free it.
791
- * rp = r .allnext
807
+ prev := r .allprev
808
+ if prev != nil {
809
+ prev .allnext = next
810
+ }
811
+ if next != nil {
812
+ next .allprev = prev
813
+ }
814
+ if work .spanSPMCs .all == r {
815
+ work .spanSPMCs .all = next
816
+ }
817
+
792
818
r .deinit ()
793
819
mheap_ .spanSPMCAlloc .free (unsafe .Pointer (r ))
794
- } else {
795
- // Still alive, likely in some P's chain.
796
- // Skip it.
797
- rp = & r .allnext
798
820
}
821
+ r = next
799
822
}
800
823
unlock (& work .spanSPMCs .lock )
801
824
}
0 commit comments