@@ -181,12 +181,12 @@ type (
181181 mtx sync.Mutex
182182 }
183183 stats moss
184- started int64 // (mono)
185- soft int // number of soft errors must be <= maxSoftErrs; see definition above
186- idx int // current position in outgoing batch
187- clean atomic.Bool // true: wi.cleanup() done _or_ recycled via mem-pool
188- owned atomic.Bool // ownership: asm() path vs gc; Inc/DecPending() symmetry
189- awfin atomic.Bool // true: need to close (outgoing) archive writer
184+ started int64 // (mono)
185+ soft int // number of soft errors must be <= maxSoftErrs; see definition above
186+ idx int // current position in outgoing batch
187+ clean atomic.Bool // true: wi.cleanup() done _or_ recycled via mem-pool
188+ owned atomic.Int32 // ownership: asm() path vs gc; Inc/DecPending() symmetry
189+ awfin atomic.Bool // true: need to close (outgoing) archive writer
190190
191191 // mutual gates: `abandoned` stops new Rx admissions; `inRx` counts in-flight _recvObj
192192 inRx atomic.Int32
@@ -550,8 +550,12 @@ func (r *XactMoss) PrepRx(req *apc.MossReq, config *cmn.Config, smap *meta.Smap,
550550 return err
551551 }
552552
553- debug .Assert (! wi .owned .Load ())
554- wi .clean .Store (false ) // (see also: freeMossWi)
553+ debug .Assert (wi .owned .Load () == wiownNone )
554+
555+ wi .abandoned .Store (nil )
556+ wi .owned .Store (wiownNone )
557+ wi .clean .Store (false ) // (last; see also: freeMossWi)
558+
555559 r .pendingCnt .Inc ()
556560 wi .awfin .Store (true )
557561
@@ -661,7 +665,7 @@ func (r *XactMoss) Assemble(req *apc.MossReq, w http.ResponseWriter, wid string)
661665 }
662666 wi := a .(* basewi )
663667
664- if ! wi .owned .CAS (false , true ) {
668+ if ! wi .owned .CAS (wiownNone , wiownASM ) {
665669 if err := wi .errAbandoned (); err != nil {
666670 return err
667671 }
@@ -694,15 +698,15 @@ func (r *XactMoss) Assemble(req *apc.MossReq, w http.ResponseWriter, wid string)
694698 }
695699 }
696700
697- var (
698- ok = wi .cleanup (false /*xdone*/ )
699- owned = wi .owned .CAS (true , false )
700- )
701- debug .Assert (owned ) // asm path must still own it here
701+ ok := wi .cleanup (false /*xdone*/ )
702+ debug .Assert (wi .owned .Load () == wiownASM )
703+
702704 if ok {
703705 freeMossWi (wi )
706+ } else {
707+ owned := wi .owned .CAS (wiownASM , wiownNone )
708+ debug .Assert (owned )
704709 }
705-
706710 return err
707711}
708712
@@ -2184,6 +2188,12 @@ const (
21842188 gcAbandonedMaxIters = 16 // to limit the time spent in HK callback
21852189)
21862190
2191+ const (
2192+ wiownNone int32 = iota
2193+ wiownASM
2194+ wiownGC
2195+ )
2196+
21872197type (
21882198 mossGC struct {
21892199 r * XactMoss
@@ -2200,12 +2210,16 @@ func (gc *mossGC) do() {
22002210 gc .r .pending .Range (gc .visit )
22012211}
22022212
2213+ func (gc * mossGC ) recycled (wi * basewi ) bool {
2214+ return wi .wid == "" || wi .started == 0 || wi .started > gc .cutoff // skip it
2215+ }
2216+
22032217func (gc * mossGC ) visit (_ , value any ) bool {
22042218 gc .iters ++
22052219 cont := gc .iters < gcAbandonedMaxIters
22062220
22072221 wi := value .(* basewi )
2208- if wi . started == 0 || wi . started > gc .cutoff {
2222+ if gc .recycled ( wi ) {
22092223 return cont
22102224 }
22112225
@@ -2228,6 +2242,10 @@ func (gc *mossGC) mark(wi *basewi, age time.Duration) bool {
22282242 if ! wi .abandoned .CompareAndSwap (nil , err ) {
22292243 return false
22302244 }
2245+ if gc .recycled (wi ) {
2246+ wi .abandoned .CompareAndSwap (err , nil ) // undo
2247+ return false
2248+ }
22312249
22322250 r := gc .r
22332251 r .AddErr (err )
@@ -2245,14 +2263,17 @@ func (gc *mossGC) cleanup(wi *basewi, age time.Duration, cont bool) bool {
22452263 if r .IsAborted () || r .IsDone () {
22462264 return false // stop #1
22472265 }
2266+ if gc .recycled (wi ) {
2267+ return cont
2268+ }
22482269
22492270 // in-flight Rx => stuck Rx
22502271 if wi .inRx .Load () > 0 {
22512272 go r ._fatal (wi .wid , "Rx" , age )
22522273 return false // stop #2
22532274 }
22542275
2255- if ! wi .owned .CAS (false , true ) {
2276+ if ! wi .owned .CAS (wiownNone , wiownGC ) {
22562277 if wi .clean .Load () {
22572278 return cont
22582279 }
@@ -2262,12 +2283,15 @@ func (gc *mossGC) cleanup(wi *basewi, age time.Duration, cont bool) bool {
22622283 }
22632284
22642285 // gc claims ownership (no IncPending was done, so no DecPending owed)
2286+ if gc .recycled (wi ) {
2287+ wi .owned .CAS (wiownGC , wiownNone )
2288+ return cont
2289+ }
22652290 if ! wi .cleanup (false ) {
2266- debug . Assertf ( false , "%s: GC failed to cleanup abandoned wi %q, age %v" , r . Name (), wi . wid , age )
2267- go r . _fatal ( wi .wid , "abandoned cleanup" , age )
2268- return false // stop #4
2291+ // benign: asm cleaned/recycled after gc.Range observed it
2292+ wi .owned . CAS ( wiownGC , wiownNone )
2293+ return cont
22692294 }
2270-
22712295 if ! gc .warn2 || cmn .Rom .V (4 , cos .ModXs ) {
22722296 nlog .Warningln (r .Name (), "GC cleaned abandoned wi" , wi .wid , "age:" , age )
22732297 gc .warn2 = true
0 commit comments