Skip to content

Commit 9ad57d3

Browse files
committed
get-batch: amend abandoned work-item cleanup (part three)
* work item's ownership: 3-way enum now * part three, prev. cd07a4e (taking care of extremely unlikely races) Signed-off-by: Alex Aizman <alex.aizman@gmail.com>
1 parent cd07a4e commit 9ad57d3

2 files changed

Lines changed: 46 additions & 22 deletions

File tree

xact/xs/moss.go

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
21872197
type (
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+
22032217
func (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

xact/xs/pool.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func freeMossWi(a *basewi) {
5555
// debug-assert basewi0 (using it to reset the instance)
5656
debug.Assert(basewi0.clean.Load()) // (note: see Tinit)
5757
debug.Assert(basewi0.abandoned.Load() == nil)
58-
debug.Assert(!basewi0.owned.Load())
58+
debug.Assert(basewi0.owned.Load() == wiownNone)
5959

6060
rxents := a.recv.m
6161
*a = basewi0 //nolint:govet // reset; atomic.noCopy does not apply

0 commit comments

Comments
 (0)