diff --git a/_examples/barExtender/go.mod b/_examples/barExtender/go.mod
index ca38865..63fb454 100644
--- a/_examples/barExtender/go.mod
+++ b/_examples/barExtender/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/barExtender
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/cancel/go.mod b/_examples/cancel/go.mod
index 6b442db..f99d70f 100644
--- a/_examples/cancel/go.mod
+++ b/_examples/cancel/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/cancel
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/complex/go.mod b/_examples/complex/go.mod
index 2dad190..14fc0d6 100644
--- a/_examples/complex/go.mod
+++ b/_examples/complex/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/complex
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/complex/main.go b/_examples/complex/main.go
index a5d42f4..07dbed2 100644
--- a/_examples/complex/main.go
+++ b/_examples/complex/main.go
@@ -3,7 +3,6 @@ package main
 import (
 	"fmt"
 	"math/rand"
-	"sync"
 	"time"
 
 	"github.com/vbauerster/mpb/v7"
@@ -15,69 +14,51 @@ func init() {
 }
 
 func main() {
-	doneWg := new(sync.WaitGroup)
-	// passed doneWg will be accounted at p.Wait() call
-	p := mpb.New(mpb.WithWaitGroup(doneWg))
 	numBars := 4
+	p := mpb.New()
 
-	var bars []*mpb.Bar
-	var downloadWgg []*sync.WaitGroup
 	for i := 0; i < numBars; i++ {
-		wg := new(sync.WaitGroup)
-		wg.Add(1)
-		downloadWgg = append(downloadWgg, wg)
 		task := fmt.Sprintf("Task#%02d:", i)
-		job := "downloading"
-		b := p.AddBar(rand.Int63n(201)+100,
+		queue := make([]*mpb.Bar, 2)
+		queue[0] = p.AddBar(rand.Int63n(201)+100,
 			mpb.PrependDecorators(
 				decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
-				decor.Name(job, decor.WCSyncSpaceR),
+				decor.Name("downloading", decor.WCSyncSpaceR),
 				decor.CountersNoUnit("%d / %d", decor.WCSyncWidth),
 			),
 			mpb.AppendDecorators(decor.Percentage(decor.WC{W: 5})),
 		)
-		go newTask(wg, b, i+1)
-		bars = append(bars, b)
-	}
+		queue[1] = p.AddBar(rand.Int63n(101)+100,
+			mpb.BarQueueAfter(queue[0], false), // this bar is queued
+			mpb.BarFillerClearOnComplete(),
+			mpb.PrependDecorators(
+				decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
+				decor.OnComplete(decor.Name("\x1b[31minstalling\x1b[0m", decor.WCSyncSpaceR), "done!"),
+				decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_MMSS, 0, decor.WCSyncWidth), ""),
+			),
+			mpb.AppendDecorators(
+				decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
+			),
+		)
 
-	for i := 0; i < numBars; i++ {
-		doneWg.Add(1)
-		i := i
 		go func() {
-			task := fmt.Sprintf("Task#%02d:", i)
-			// ANSI escape sequences are not supported on Windows OS
-			job := "\x1b[31;1;4mつのだ☆HIRO\x1b[0m"
-			// preparing delayed bars
-			b := p.AddBar(rand.Int63n(101)+100,
-				mpb.BarQueueAfter(bars[i]),
-				mpb.BarFillerClearOnComplete(),
-				mpb.PrependDecorators(
-					decor.Name(task, decor.WC{W: len(task) + 1, C: decor.DidentRight}),
-					decor.OnComplete(decor.Name(job, decor.WCSyncSpaceR), "done!"),
-					decor.OnComplete(decor.EwmaETA(decor.ET_STYLE_MMSS, 0, decor.WCSyncWidth), ""),
-				),
-				mpb.AppendDecorators(
-					decor.OnComplete(decor.Percentage(decor.WC{W: 5}), ""),
-				),
-			)
-			// waiting for download to complete, before starting install job
-			downloadWgg[i].Wait()
-			go newTask(doneWg, b, numBars-i)
+			for _, b := range queue {
+				complete(b)
+			}
 		}()
 	}
-	// wait for passed doneWg and for all bars to complete and flush
+
 	p.Wait()
 }
 
-func newTask(wg *sync.WaitGroup, bar *mpb.Bar, incrBy int) {
-	defer wg.Done()
+func complete(bar *mpb.Bar) {
 	max := 100 * time.Millisecond
 	for !bar.Completed() {
 		// start variable is solely for EWMA calculation
 		// EWMA's unit of measure is an iteration's duration
 		start := time.Now()
 		time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)
-		bar.IncrBy(incrBy)
+		bar.IncrInt64(rand.Int63n(5) + 1)
 		// we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
 		bar.DecoratorEwmaUpdate(time.Since(start))
 	}
diff --git a/_examples/decoratorsOnTop/go.mod b/_examples/decoratorsOnTop/go.mod
index 65e4023..d59504f 100644
--- a/_examples/decoratorsOnTop/go.mod
+++ b/_examples/decoratorsOnTop/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/decoratorsOnTop
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/differentWidth/go.mod b/_examples/differentWidth/go.mod
index 31a8a42..f98bfa0 100644
--- a/_examples/differentWidth/go.mod
+++ b/_examples/differentWidth/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/differentWidth
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/dynTotal/go.mod b/_examples/dynTotal/go.mod
index ee1c623..a4133ec 100644
--- a/_examples/dynTotal/go.mod
+++ b/_examples/dynTotal/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/dynTotal
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/dynTotal/main.go b/_examples/dynTotal/main.go
index aabcc42..0be1915 100644
--- a/_examples/dynTotal/main.go
+++ b/_examples/dynTotal/main.go
@@ -16,9 +16,8 @@ func init() {
 func main() {
 	p := mpb.New(mpb.WithWidth(64))
 
-	var total int64
 	// new bar with 'trigger complete event' disabled, because total is zero
-	bar := p.AddBar(total,
+	bar := p.AddBar(0,
 		mpb.PrependDecorators(decor.Counters(decor.UnitKiB, "% .1f / % .1f")),
 		mpb.AppendDecorators(decor.Percentage()),
 	)
@@ -27,21 +26,18 @@ func main() {
 	read := makeStream(200)
 	for {
 		n, err := read()
-		total += int64(n)
 		if err == io.EOF {
+			// triggering complete event now
+			bar.SetTotal(-1, true)
 			break
 		}
-		// while total is unknown,
-		// set it to a positive number which is greater than current total,
-		// to make sure no complete event is triggered by next IncrBy call.
-		bar.SetTotal(total+2048, false)
+		// increment methods won't trigger complete event because bar was constructed with total = 0
 		bar.IncrBy(n)
+		// following call is not required, it's called to show some progress instead of an empty bar
+		bar.SetTotal(bar.Current()+2048, false)
 		time.Sleep(time.Duration(rand.Intn(10)+1) * maxSleep / 10)
 	}
 
-	// force bar complete event, note true flag
-	bar.SetTotal(total, true)
-
 	p.Wait()
 }
 
diff --git a/_examples/io/go.mod b/_examples/io/go.mod
index 35e3563..6a69e7e 100644
--- a/_examples/io/go.mod
+++ b/_examples/io/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/io
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/io/main.go b/_examples/io/main.go
index 339d853..eddd7b9 100644
--- a/_examples/io/main.go
+++ b/_examples/io/main.go
@@ -36,7 +36,7 @@ func main() {
 	defer proxyReader.Close()
 
 	// copy from proxyReader, ignoring errors
-	io.Copy(ioutil.Discard, proxyReader)
+	_, _ = io.Copy(ioutil.Discard, proxyReader)
 
 	p.Wait()
 }
diff --git a/_examples/merge/go.mod b/_examples/merge/go.mod
index b3c7177..7529fd1 100644
--- a/_examples/merge/go.mod
+++ b/_examples/merge/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/merge
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/mexicanBar/go.mod b/_examples/mexicanBar/go.mod
index f9490e9..86aa635 100644
--- a/_examples/mexicanBar/go.mod
+++ b/_examples/mexicanBar/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/mexicanBar
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/multiBars/go.mod b/_examples/multiBars/go.mod
index 1a37e92..40cf5ee 100644
--- a/_examples/multiBars/go.mod
+++ b/_examples/multiBars/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/multiBars
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/panic/go.mod b/_examples/panic/go.mod
index 5d81105..e73160c 100644
--- a/_examples/panic/go.mod
+++ b/_examples/panic/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/panic
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/poplog/go.mod b/_examples/poplog/go.mod
index 0997bc9..7ec705f 100644
--- a/_examples/poplog/go.mod
+++ b/_examples/poplog/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/poplog
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/quietMode/go.mod b/_examples/quietMode/go.mod
index 6d43d51..9dee8a3 100644
--- a/_examples/quietMode/go.mod
+++ b/_examples/quietMode/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/quietMode
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/remove/go.mod b/_examples/remove/go.mod
index 5c0f6bf..37bcf9e 100644
--- a/_examples/remove/go.mod
+++ b/_examples/remove/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/remove
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/reverseBar/go.mod b/_examples/reverseBar/go.mod
index a6b31e6..bb26711 100644
--- a/_examples/reverseBar/go.mod
+++ b/_examples/reverseBar/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/reverseBar
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/singleBar/go.mod b/_examples/singleBar/go.mod
index 9aeaab6..3b2545d 100644
--- a/_examples/singleBar/go.mod
+++ b/_examples/singleBar/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/singleBar
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/spinTipBar/go.mod b/_examples/spinTipBar/go.mod
index 2a372f4..a264435 100644
--- a/_examples/spinTipBar/go.mod
+++ b/_examples/spinTipBar/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/spinTipBar
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/spinnerBar/go.mod b/_examples/spinnerBar/go.mod
index 0875389..66eaa65 100644
--- a/_examples/spinnerBar/go.mod
+++ b/_examples/spinnerBar/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/spinnerBar
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/spinnerDecorator/go.mod b/_examples/spinnerDecorator/go.mod
index c9050fd..d2e4116 100644
--- a/_examples/spinnerDecorator/go.mod
+++ b/_examples/spinnerDecorator/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/spinnerDecorator
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/stress/go.mod b/_examples/stress/go.mod
index 0ed6759..26a308a 100644
--- a/_examples/stress/go.mod
+++ b/_examples/stress/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/stress
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/_examples/suppressBar/go.mod b/_examples/suppressBar/go.mod
index 1856f06..e9cedf4 100644
--- a/_examples/suppressBar/go.mod
+++ b/_examples/suppressBar/go.mod
@@ -4,5 +4,5 @@ go 1.14
 
 require (
 	github.com/mattn/go-runewidth v0.0.13
-	github.com/vbauerster/mpb/v7 v7.3.2
+	github.com/vbauerster/mpb/v7 v7.4.1
 )
diff --git a/_examples/tipOnComplete/go.mod b/_examples/tipOnComplete/go.mod
index c5e030b..100c2a4 100644
--- a/_examples/tipOnComplete/go.mod
+++ b/_examples/tipOnComplete/go.mod
@@ -2,4 +2,4 @@ module github.com/vbauerster/mpb/_examples/tipOnComplete
 
 go 1.14
 
-require github.com/vbauerster/mpb/v7 v7.3.2
+require github.com/vbauerster/mpb/v7 v7.4.1
diff --git a/bar.go b/bar.go
index 646cb47..4991f4f 100644
--- a/bar.go
+++ b/bar.go
@@ -17,31 +17,21 @@ import (
 
 // Bar represents a progress bar.
 type Bar struct {
-	priority int // used by heap
-	index    int // used by heap
-
-	toShutdown        bool
-	toDrop            bool
-	noPop             bool
-	hasEwmaDecorators bool
-	operateState      chan func(*bState)
-	frameCh           chan *frame
-
-	// cancel is called either by user or on complete event
-	cancel func()
-	// done is closed after cacheState is assigned
-	done chan struct{}
-	// cacheState is populated, right after close(b.done)
-	cacheState *bState
-
+	index          int // used by heap
+	priority       int // used by heap
+	hasEwma        bool
+	frameCh        chan *renderFrame
+	operateState   chan func(*bState)
+	done           chan struct{}
 	container      *Progress
+	bs             *bState
+	cancel         func()
 	recoveredPanic interface{}
 }
 
 type extenderFunc func(in io.Reader, reqWidth int, st decor.Statistics) (out io.Reader, lines int)
 
-// bState is actual bar state. It gets passed to *Bar.serve(...) monitor
-// goroutine.
+// bState is actual bar's state.
 type bState struct {
 	id                int
 	priority          int
@@ -52,7 +42,6 @@ type bState struct {
 	lastIncrement     int64
 	trimSpace         bool
 	completed         bool
-	completeFlushed   bool
 	aborted           bool
 	triggerComplete   bool
 	dropOnComplete    bool
@@ -66,29 +55,28 @@ type bState struct {
 	filler            BarFiller
 	middleware        func(BarFiller) BarFiller
 	extender          extenderFunc
+	debugOut          io.Writer
 
-	// runningBar is a key for *pState.parkedBars
-	runningBar *Bar
-
-	debugOut io.Writer
+	afterBar *Bar // key for (*pState).queueBars
+	sync     bool
 }
 
-type frame struct {
-	reader io.Reader
-	lines  int
+type renderFrame struct {
+	reader   io.Reader
+	lines    int
+	shutdown bool
 }
 
 func newBar(container *Progress, bs *bState) *Bar {
 	ctx, cancel := context.WithCancel(container.ctx)
 
 	bar := &Bar{
-		container:    container,
 		priority:     bs.priority,
-		toDrop:       bs.dropOnComplete,
-		noPop:        bs.noPop,
+		hasEwma:      len(bs.ewmaDecorators) != 0,
+		frameCh:      make(chan *renderFrame, 1),
 		operateState: make(chan func(*bState)),
-		frameCh:      make(chan *frame, 1),
 		done:         make(chan struct{}),
+		container:    container,
 		cancel:       cancel,
 	}
 
@@ -97,12 +85,20 @@ func newBar(container *Progress, bs *bState) *Bar {
 }
 
 // ProxyReader wraps r with metrics required for progress tracking.
-// Panics if r is nil.
+// If r is 'unknown total/size' reader it's mandatory to call
+// (*Bar).SetTotal(-1, true) method after (Reader).Read returns io.EOF.
+// Panics if r is nil. If bar is already completed or aborted, returns
+// nil.
 func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
 	if r == nil {
 		panic("expected non nil io.Reader")
 	}
-	return b.newProxyReader(r)
+	select {
+	case <-b.done:
+		return nil
+	default:
+		return b.newProxyReader(r)
+	}
 }
 
 // ID returs id of the bar.
@@ -112,18 +108,18 @@ func (b *Bar) ID() int {
 	case b.operateState <- func(s *bState) { result <- s.id }:
 		return <-result
 	case <-b.done:
-		return b.cacheState.id
+		return b.bs.id
 	}
 }
 
-// Current returns bar's current number, in other words sum of all increments.
+// Current returns bar's current value, in other words sum of all increments.
 func (b *Bar) Current() int64 {
 	result := make(chan int64)
 	select {
 	case b.operateState <- func(s *bState) { result <- s.current }:
 		return <-result
 	case <-b.done:
-		return b.cacheState.current
+		return b.bs.current
 	}
 }
 
@@ -142,7 +138,7 @@ func (b *Bar) SetRefill(amount int64) {
 
 // TraverseDecorators traverses all available decorators and calls cb func on each.
 func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
-	done := make(chan struct{})
+	sync := make(chan struct{})
 	select {
 	case b.operateState <- func(s *bState) {
 		for _, decorators := range [...][]decor.Decorator{
@@ -153,28 +149,56 @@ func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
 				cb(extractBaseDecorator(d))
 			}
 		}
-		close(done)
+		close(sync)
+	}:
+		<-sync
+	case <-b.done:
+	}
+}
+
+// EnableTriggerComplete enables triggering complete event. It's
+// effective only for bar which was constructed with `total <= 0` and
+// after total has been set with (*Bar).SetTotal(int64, false). If bar
+// has been incremented to the total, complete event is triggered right
+// away.
+func (b *Bar) EnableTriggerComplete() {
+	select {
+	case b.operateState <- func(s *bState) {
+		if s.triggerComplete || s.total <= 0 {
+			return
+		}
+		if s.current >= s.total {
+			s.current = s.total
+			s.completed = true
+			go b.forceRefresh()
+		} else {
+			s.triggerComplete = true
+		}
 	}:
-		<-done
 	case <-b.done:
 	}
 }
 
-// SetTotal sets total dynamically.
-// If total is negative it takes progress' current value.
-func (b *Bar) SetTotal(total int64, triggerComplete bool) {
+// SetTotal sets total to an arbitrary value. It's effective only for
+// bar which was constructed with `total <= 0`. Setting total to negative
+// value is equivalent to (*Bar).SetTotal((*Bar).Current(), bool).
+// If triggerCompleteNow is true, total value is set to current and
+// complete event is triggered right away.
+func (b *Bar) SetTotal(total int64, triggerCompleteNow bool) {
 	select {
 	case b.operateState <- func(s *bState) {
-		s.triggerComplete = triggerComplete
+		if s.triggerComplete {
+			return
+		}
 		if total < 0 {
 			s.total = s.current
 		} else {
 			s.total = total
 		}
-		if s.triggerComplete && !s.completed {
+		if triggerCompleteNow {
 			s.current = s.total
 			s.completed = true
-			go b.forceRefreshIfLastUncompleted()
+			go b.forceRefresh()
 		}
 	}:
 	case <-b.done:
@@ -191,7 +215,7 @@ func (b *Bar) SetCurrent(current int64) {
 		if s.triggerComplete && s.current >= s.total {
 			s.current = s.total
 			s.completed = true
-			go b.forceRefreshIfLastUncompleted()
+			go b.forceRefresh()
 		}
 	}:
 	case <-b.done:
@@ -220,7 +244,7 @@ func (b *Bar) IncrInt64(n int64) {
 		if s.triggerComplete && s.current >= s.total {
 			s.current = s.total
 			s.completed = true
-			go b.forceRefreshIfLastUncompleted()
+			go b.forceRefresh()
 		}
 	}:
 	case <-b.done:
@@ -242,9 +266,9 @@ func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
 		}
 	}:
 	case <-b.done:
-		if b.cacheState.lastIncrement > 0 {
-			b.cacheState.decoratorEwmaUpdate(dur)
-			b.cacheState.lastIncrement = 0
+		if b.bs.lastIncrement > 0 {
+			b.bs.decoratorEwmaUpdate(dur)
+			b.bs.lastIncrement = 0
 		}
 	}
 }
@@ -270,44 +294,33 @@ func (b *Bar) SetPriority(priority int) {
 
 // Abort interrupts bar's running goroutine. Abort won't be engaged
 // if bar is already in complete state. If drop is true bar will be
-// removed as well.
+// removed as well. To make sure that bar has been removed call
+// (*Bar).Wait method.
 func (b *Bar) Abort(drop bool) {
-	done := make(chan struct{})
 	select {
 	case b.operateState <- func(s *bState) {
-		if s.completed {
-			close(done)
+		if s.completed || s.aborted {
 			return
 		}
 		s.aborted = true
-		b.cancel()
-		// container must be run during lifetime of this inner goroutine
-		// we control this by done channel declared above
-		go func() {
-			if drop {
-				b.container.dropBar(b)
-			} else {
-				var uncompleted int
-				b.container.traverseBars(func(bar *Bar) bool {
-					if b != bar && !bar.Completed() {
-						uncompleted++
-						return false
-					}
-					return true
-				})
-				if uncompleted == 0 {
-					b.container.refreshCh <- time.Now()
-				}
-			}
-			close(done) // release hold of Abort
-		}()
+		s.dropOnComplete = drop
+		go b.forceRefresh()
 	}:
-		// guarantee: container is alive during lifetime of this hold
-		<-done
 	case <-b.done:
 	}
 }
 
+// Aborted reports whether the bar is in aborted state.
+func (b *Bar) Aborted() bool {
+	result := make(chan bool)
+	select {
+	case b.operateState <- func(s *bState) { result <- s.aborted }:
+		return <-result
+	case <-b.done:
+		return b.bs.aborted
+	}
+}
+
 // Completed reports whether the bar is in completed state.
 func (b *Bar) Completed() bool {
 	result := make(chan bool)
@@ -315,19 +328,28 @@ func (b *Bar) Completed() bool {
 	case b.operateState <- func(s *bState) { result <- s.completed }:
 		return <-result
 	case <-b.done:
-		return true
+		return b.bs.completed
 	}
 }
 
-func (b *Bar) serve(ctx context.Context, s *bState) {
+// Wait blocks until bar is completed or aborted.
+func (b *Bar) Wait() {
+	<-b.done
+}
+
+func (b *Bar) serve(ctx context.Context, bs *bState) {
 	defer b.container.bwg.Done()
+	if bs.afterBar != nil && bs.sync {
+		bs.afterBar.Wait()
+	}
 	for {
 		select {
 		case op := <-b.operateState:
-			op(s)
+			op(bs)
 		case <-ctx.Done():
-			s.decoratorShutdownNotify()
-			b.cacheState = s
+			bs.aborted = !bs.completed
+			bs.decoratorShutdownNotify()
+			b.bs = bs
 			close(b.done)
 			return
 		}
@@ -337,79 +359,62 @@ func (b *Bar) serve(ctx context.Context, s *bState) {
 func (b *Bar) render(tw int) {
 	select {
 	case b.operateState <- func(s *bState) {
+		var reader io.Reader
+		var lines int
 		stat := newStatistics(tw, s)
 		defer func() {
 			// recovering if user defined decorator panics for example
 			if p := recover(); p != nil {
-				if b.recoveredPanic == nil {
-					if s.debugOut != nil {
-						fmt.Fprintln(s.debugOut, p)
-						_, _ = s.debugOut.Write(debug.Stack())
-					}
-					s.extender = makePanicExtender(p)
-					b.toShutdown = !b.toShutdown
-					b.recoveredPanic = p
+				if s.debugOut != nil {
+					fmt.Fprintln(s.debugOut, p)
+					_, _ = s.debugOut.Write(debug.Stack())
 				}
-				reader, lines := s.extender(nil, s.reqWidth, stat)
-				b.frameCh <- &frame{reader, lines + 1}
+				s.aborted = !s.completed
+				s.extender = makePanicExtender(p)
+				reader, lines = s.extender(nil, s.reqWidth, stat)
+				b.recoveredPanic = p
+			}
+			frame := renderFrame{
+				reader:   reader,
+				lines:    lines + 1,
+				shutdown: s.completed || s.aborted,
 			}
-			s.completeFlushed = s.completed
+			if frame.shutdown {
+				b.cancel()
+			}
+			b.frameCh <- &frame
 		}()
-		reader, lines := s.extender(s.draw(stat), s.reqWidth, stat)
-		b.toShutdown = s.completed && !s.completeFlushed
-		b.frameCh <- &frame{reader, lines + 1}
+		if b.recoveredPanic == nil {
+			reader = s.draw(stat)
+		}
+		reader, lines = s.extender(reader, s.reqWidth, stat)
 	}:
 	case <-b.done:
-		s := b.cacheState
-		stat := newStatistics(tw, s)
-		var r io.Reader
+		var reader io.Reader
+		var lines int
+		stat, s := newStatistics(tw, b.bs), b.bs
 		if b.recoveredPanic == nil {
-			r = s.draw(stat)
-		}
-		reader, lines := s.extender(r, s.reqWidth, stat)
-		b.frameCh <- &frame{reader, lines + 1}
-	}
-}
-
-func (b *Bar) subscribeDecorators() {
-	var averageDecorators []decor.AverageDecorator
-	var ewmaDecorators []decor.EwmaDecorator
-	var shutdownListeners []decor.ShutdownListener
-	b.TraverseDecorators(func(d decor.Decorator) {
-		if d, ok := d.(decor.AverageDecorator); ok {
-			averageDecorators = append(averageDecorators, d)
-		}
-		if d, ok := d.(decor.EwmaDecorator); ok {
-			ewmaDecorators = append(ewmaDecorators, d)
+			reader = s.draw(stat)
 		}
-		if d, ok := d.(decor.ShutdownListener); ok {
-			shutdownListeners = append(shutdownListeners, d)
+		reader, lines = s.extender(reader, s.reqWidth, stat)
+		b.frameCh <- &renderFrame{
+			reader: reader,
+			lines:  lines + 1,
 		}
-	})
-	b.hasEwmaDecorators = len(ewmaDecorators) != 0
-	select {
-	case b.operateState <- func(s *bState) {
-		s.averageDecorators = averageDecorators
-		s.ewmaDecorators = ewmaDecorators
-		s.shutdownListeners = shutdownListeners
-	}:
-	case <-b.done:
 	}
 }
 
-func (b *Bar) forceRefreshIfLastUncompleted() {
-	var uncompleted int
+func (b *Bar) forceRefresh() {
+	var anyOtherRunning bool
 	b.container.traverseBars(func(bar *Bar) bool {
-		if b != bar && !bar.Completed() {
-			uncompleted++
-			return false
-		}
-		return true
+		anyOtherRunning = b != bar && bar.isRunning()
+		return !anyOtherRunning
 	})
-	if uncompleted == 0 {
+	if !anyOtherRunning {
 		for {
 			select {
 			case b.container.refreshCh <- time.Now():
+				time.Sleep(prr)
 			case <-b.done:
 				return
 			}
@@ -417,13 +422,25 @@ func (b *Bar) forceRefreshIfLastUncompleted() {
 	}
 }
 
+func (b *Bar) isRunning() bool {
+	result := make(chan bool)
+	select {
+	case b.operateState <- func(s *bState) {
+		result <- !s.completed && !s.aborted
+	}:
+		return <-result
+	case <-b.done:
+		return false
+	}
+}
+
 func (b *Bar) wSyncTable() [][]chan int {
 	result := make(chan [][]chan int)
 	select {
 	case b.operateState <- func(s *bState) { result <- s.wSyncTable() }:
 		return <-result
 	case <-b.done:
-		return b.cacheState.wSyncTable()
+		return b.bs.wSyncTable()
 	}
 }
 
@@ -487,6 +504,26 @@ func (s *bState) wSyncTable() [][]chan int {
 	return table
 }
 
+func (s *bState) subscribeDecorators() {
+	for _, decorators := range [...][]decor.Decorator{
+		s.pDecorators,
+		s.aDecorators,
+	} {
+		for _, d := range decorators {
+			d = extractBaseDecorator(d)
+			if d, ok := d.(decor.AverageDecorator); ok {
+				s.averageDecorators = append(s.averageDecorators, d)
+			}
+			if d, ok := d.(decor.EwmaDecorator); ok {
+				s.ewmaDecorators = append(s.ewmaDecorators, d)
+			}
+			if d, ok := d.(decor.ShutdownListener); ok {
+				s.shutdownListeners = append(s.shutdownListeners, d)
+			}
+		}
+	}
+}
+
 func (s bState) decoratorEwmaUpdate(dur time.Duration) {
 	wg := new(sync.WaitGroup)
 	for i := 0; i < len(s.ewmaDecorators); i++ {
@@ -540,12 +577,12 @@ func (s bState) decoratorShutdownNotify() {
 
 func newStatistics(tw int, s *bState) decor.Statistics {
 	return decor.Statistics{
-		ID:             s.id,
 		AvailableWidth: tw,
+		ID:             s.id,
 		Total:          s.total,
 		Current:        s.current,
 		Refill:         s.refill,
-		Completed:      s.completeFlushed,
+		Completed:      s.completed,
 		Aborted:        s.aborted,
 	}
 }
diff --git a/bar_filler_bar.go b/bar_filler_bar.go
index 54b7bfd..d8bf90a 100644
--- a/bar_filler_bar.go
+++ b/bar_filler_bar.go
@@ -157,9 +157,8 @@ func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
 		return
 	}
 
-	ow := optimisticWriter(w)
-	ow(s.components[iLbound].bytes)
-	defer ow(s.components[iRbound].bytes)
+	mustWrite(w, s.components[iLbound].bytes)
+	defer mustWrite(w, s.components[iRbound].bytes)
 
 	if width == 0 {
 		return
@@ -231,26 +230,24 @@ func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
 	}
 
 	if s.rev {
-		flush(ow, padding, filling)
+		flush(w, padding, filling)
 	} else {
-		flush(ow, filling, padding)
+		flush(w, filling, padding)
 	}
 }
 
-func flush(ow func([]byte), filling, padding [][]byte) {
+func flush(w io.Writer, filling, padding [][]byte) {
 	for i := len(filling) - 1; i >= 0; i-- {
-		ow(filling[i])
+		mustWrite(w, filling[i])
 	}
 	for i := 0; i < len(padding); i++ {
-		ow(padding[i])
+		mustWrite(w, padding[i])
 	}
 }
 
-func optimisticWriter(w io.Writer) func([]byte) {
-	return func(p []byte) {
-		_, err := w.Write(p)
-		if err != nil {
-			panic(err)
-		}
+func mustWrite(w io.Writer, p []byte) {
+	_, err := w.Write(p)
+	if err != nil {
+		panic(err)
 	}
 }
diff --git a/bar_option.go b/bar_option.go
index 4ba4905..8599f0a 100644
--- a/bar_option.go
+++ b/bar_option.go
@@ -59,14 +59,17 @@ func BarWidth(width int) BarOption {
 	}
 }
 
-// BarQueueAfter queues this (being constructed) bar to relplace
-// runningBar after it has been completed.
-func BarQueueAfter(runningBar *Bar) BarOption {
-	if runningBar == nil {
+// BarQueueAfter puts this (being constructed) bar into the queue.
+// When argument bar completes or aborts queued bar replaces its place.
+// If sync is true queued bar is suspended until argument bar completes
+// or aborts.
+func BarQueueAfter(bar *Bar, sync bool) BarOption {
+	if bar == nil {
 		return nil
 	}
 	return func(s *bState) {
-		s.runningBar = runningBar
+		s.afterBar = bar
+		s.sync = sync
 	}
 }
 
diff --git a/bar_test.go b/bar_test.go
index 42451c0..ba288e4 100644
--- a/bar_test.go
+++ b/bar_test.go
@@ -2,6 +2,7 @@ package mpb_test
 
 import (
 	"bytes"
+	"context"
 	"fmt"
 	"io/ioutil"
 	"strings"
@@ -19,17 +20,85 @@ func TestBarCompleted(t *testing.T) {
 	total := 80
 	bar := p.AddBar(int64(total))
 
-	var count int
-	for !bar.Completed() {
-		time.Sleep(10 * time.Millisecond)
-		bar.Increment()
-		count++
+	if bar.Completed() {
+		t.Fail()
+	}
+
+	bar.IncrBy(total)
+
+	if !bar.Completed() {
+		t.Error("bar isn't completed after increment")
 	}
 
 	p.Wait()
-	if count != total {
-		t.Errorf("got count: %d, expected %d\n", count, total)
+}
+
+func TestBarAborted(t *testing.T) {
+	p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
+	total := 80
+	bar := p.AddBar(int64(total))
+
+	if bar.Aborted() {
+		t.Fail()
 	}
+
+	bar.Abort(false)
+
+	if !bar.Aborted() {
+		t.Error("bar isn't aborted after abort call")
+	}
+
+	p.Wait()
+}
+
+func TestBarEnableTriggerCompleteAndIncrementBefore(t *testing.T) {
+	p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
+	bar := p.AddBar(0) // never complete bar
+
+	for _, f := range []func(){
+		func() { bar.SetTotal(40, false) },
+		func() { bar.IncrBy(60) },
+		func() { bar.SetTotal(80, false) },
+		func() { bar.IncrBy(20) },
+	} {
+		f()
+		if bar.Completed() {
+			t.Fail()
+		}
+	}
+
+	bar.EnableTriggerComplete()
+
+	if !bar.Completed() {
+		t.Fail()
+	}
+
+	p.Wait()
+}
+
+func TestBarEnableTriggerCompleteAndIncrementAfter(t *testing.T) {
+	p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(ioutil.Discard))
+	bar := p.AddBar(0) // never complete bar
+
+	for _, f := range []func(){
+		func() { bar.SetTotal(40, false) },
+		func() { bar.IncrBy(60) },
+		func() { bar.SetTotal(80, false) },
+		func() { bar.EnableTriggerComplete() },
+	} {
+		f()
+		if bar.Completed() {
+			t.Fail()
+		}
+	}
+
+	bar.IncrBy(20)
+
+	if !bar.Completed() {
+		t.Fail()
+	}
+
+	p.Wait()
 }
 
 func TestBarID(t *testing.T) {
@@ -38,19 +107,13 @@ func TestBarID(t *testing.T) {
 	wantID := 11
 	bar := p.AddBar(int64(total), mpb.BarID(wantID))
 
-	go func() {
-		for i := 0; i < total; i++ {
-			time.Sleep(50 * time.Millisecond)
-			bar.Increment()
-		}
-	}()
-
 	gotID := bar.ID()
 	if gotID != wantID {
 		t.Errorf("Expected bar id: %d, got %d\n", wantID, gotID)
 	}
 
-	bar.Abort(true)
+	bar.IncrBy(total)
+
 	p.Wait()
 }
 
@@ -67,11 +130,7 @@ func TestBarSetRefill(t *testing.T) {
 
 	bar.SetRefill(int64(till))
 	bar.IncrBy(till)
-
-	for i := 0; i < total-till; i++ {
-		bar.Increment()
-		time.Sleep(10 * time.Millisecond)
-	}
+	bar.IncrBy(total - till)
 
 	p.Wait()
 
@@ -80,41 +139,13 @@ func TestBarSetRefill(t *testing.T) {
 		strings.Repeat("=", total-till-1),
 	)
 
-	got := string(getLastLine(buf.Bytes()))
+	got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
 
 	if !strings.Contains(got, wantBar) {
 		t.Errorf("Want bar: %q, got bar: %q\n", wantBar, got)
 	}
 }
 
-func TestBarHas100PercentWithOnCompleteDecorator(t *testing.T) {
-	var buf bytes.Buffer
-
-	p := mpb.New(mpb.WithWidth(80), mpb.WithOutput(&buf))
-
-	total := 50
-
-	bar := p.AddBar(int64(total),
-		mpb.AppendDecorators(
-			decor.OnComplete(
-				decor.Percentage(), "done",
-			),
-		),
-	)
-
-	for i := 0; i < total; i++ {
-		bar.Increment()
-		time.Sleep(10 * time.Millisecond)
-	}
-
-	p.Wait()
-
-	hundred := "100 %"
-	if !bytes.Contains(buf.Bytes(), []byte(hundred)) {
-		t.Errorf("Bar's buffer does not contain: %q\n", hundred)
-	}
-}
-
 func TestBarHas100PercentWithBarRemoveOnComplete(t *testing.T) {
 	var buf bytes.Buffer
 
@@ -127,10 +158,7 @@ func TestBarHas100PercentWithBarRemoveOnComplete(t *testing.T) {
 		mpb.AppendDecorators(decor.Percentage()),
 	)
 
-	for i := 0; i < total; i++ {
-		bar.Increment()
-		time.Sleep(10 * time.Millisecond)
-	}
+	bar.IncrBy(total)
 
 	p.Wait()
 
@@ -154,10 +182,7 @@ func TestBarStyle(t *testing.T) {
 	bs.Rbound(string(runes[4]))
 	bar := p.New(int64(total), bs, mpb.BarFillerTrim())
 
-	for i := 0; i < total; i++ {
-		bar.Increment()
-		time.Sleep(10 * time.Millisecond)
-	}
+	bar.IncrBy(total)
 
 	p.Wait()
 
@@ -167,7 +192,7 @@ func TestBarStyle(t *testing.T) {
 		string(runes[2]),
 		string(runes[4]),
 	)
-	got := string(getLastLine(buf.Bytes()))
+	got := string(bytes.Split(buf.Bytes(), []byte("\n"))[0])
 
 	if !strings.Contains(got, wantBar) {
 		t.Errorf("Want bar: %q:%d, got bar: %q:%d\n", wantBar, utf8.RuneCountInString(wantBar), got, utf8.RuneCountInString(got))
@@ -197,15 +222,12 @@ func TestBarPanicBeforeComplete(t *testing.T) {
 		)),
 	)
 
-	for i := 0; i < total; i++ {
-		time.Sleep(10 * time.Millisecond)
-		bar.Increment()
-	}
+	bar.IncrBy(total)
 
 	p.Wait()
 
 	if pCount != 1 {
-		t.Errorf("Decor called after panic %d times\n", pCount-1)
+		t.Errorf("Decorator called after panic %d times, expected 1\n", pCount)
 	}
 
 	barStr := buf.String()
@@ -237,15 +259,12 @@ func TestBarPanicAfterComplete(t *testing.T) {
 		)),
 	)
 
-	for i := 0; i < total; i++ {
-		time.Sleep(10 * time.Millisecond)
-		bar.Increment()
-	}
+	bar.IncrBy(total)
 
 	p.Wait()
 
-	if pCount > 2 {
-		t.Error("Decor called after panic more than 2 times\n")
+	if pCount != 1 {
+		t.Errorf("Decorator called after panic %d times, expected 1\n", pCount)
 	}
 
 	barStr := buf.String()
@@ -255,33 +274,28 @@ func TestBarPanicAfterComplete(t *testing.T) {
 }
 
 func TestDecorStatisticsAvailableWidth(t *testing.T) {
-	total := 100
-	down := make(chan struct{})
-	checkDone := make(chan struct{})
+	var called [2]bool
 	td1 := func(s decor.Statistics) string {
 		if s.AvailableWidth != 80 {
 			t.Errorf("expected AvailableWidth %d got %d\n", 80, s.AvailableWidth)
 		}
+		called[0] = true
 		return fmt.Sprintf("\x1b[31;1;4m%s\x1b[0m", strings.Repeat("0", 20))
 	}
 	td2 := func(s decor.Statistics) string {
-		defer func() {
-			select {
-			case checkDone <- struct{}{}:
-			default:
-			}
-		}()
 		if s.AvailableWidth != 40 {
 			t.Errorf("expected AvailableWidth %d got %d\n", 40, s.AvailableWidth)
 		}
+		called[1] = true
 		return ""
 	}
-	p := mpb.New(
-		mpb.WithWidth(100),
-		mpb.WithShutdownNotifier(down),
+	ctx, cancel := context.WithCancel(context.Background())
+	refresh := make(chan interface{})
+	p := mpb.NewWithContext(ctx, mpb.WithWidth(100),
+		mpb.WithManualRefresh(refresh),
 		mpb.WithOutput(ioutil.Discard),
 	)
-	bar := p.AddBar(int64(total),
+	_ = p.AddBar(0,
 		mpb.BarFillerTrim(),
 		mpb.PrependDecorators(
 			decor.Name(strings.Repeat("0", 20)),
@@ -292,20 +306,14 @@ func TestDecorStatisticsAvailableWidth(t *testing.T) {
 			decor.Any(td2),
 		),
 	)
-	go func() {
-		for {
-			select {
-			case <-checkDone:
-				bar.Abort(true)
-			case <-down:
-				return
-			}
+	refresh <- time.Now()
+	cancel()
+	p.Wait()
+	for i, ok := range called {
+		if !ok {
+			t.Errorf("Decorator %d isn't called", i+1)
 		}
-	}()
-	for !bar.Completed() {
-		bar.Increment()
 	}
-	p.Wait()
 }
 
 func panicDecorator(panicMsg string, cond func(decor.Statistics) bool) decor.Decorator {
diff --git a/barbench_test.go b/barbench_test.go
index 7926afa..d696134 100644
--- a/barbench_test.go
+++ b/barbench_test.go
@@ -29,25 +29,14 @@ func benchBody(n int, b *testing.B) {
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 		for j := 0; j < n; j++ {
+			bar := p.AddBar(total)
 			switch j {
 			case n - 1:
-				bar := p.AddBar(total)
-				for c := 0; c < total; c++ {
-					bar.Increment()
-				}
-				if !bar.Completed() {
-					b.Fail()
-				}
+				complete(b, bar)
 			default:
 				wg.Add(1)
 				go func() {
-					bar := p.AddBar(total)
-					for c := 0; c < total; c++ {
-						bar.Increment()
-					}
-					if !bar.Completed() {
-						b.Fail()
-					}
+					complete(b, bar)
 					wg.Done()
 				}()
 			}
@@ -56,3 +45,12 @@ func benchBody(n int, b *testing.B) {
 	}
 	p.Wait()
 }
+
+func complete(b *testing.B, bar *Bar) {
+	for i := 0; i < total; i++ {
+		bar.Increment()
+	}
+	if !bar.Completed() {
+		b.Fail()
+	}
+}
diff --git a/cwriter/writer.go b/cwriter/writer.go
index eaf541c..fac15b3 100644
--- a/cwriter/writer.go
+++ b/cwriter/writer.go
@@ -11,7 +11,7 @@ import (
 // ErrNotTTY not a TeleTYpewriter error.
 var ErrNotTTY = errors.New("not a terminal")
 
-// http://ascii-table.com/ansi-escape-sequences.php
+// https://github.com/dylanaraps/pure-sh-bible#cursor-movement
 const (
 	escOpen  = "\x1b["
 	cuuAndEd = "A\x1b[J"
diff --git a/debian/changelog b/debian/changelog
index 32c9fea..39ab09e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-vbauerster-mpb (7.4.1-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 14 Mar 2022 11:44:35 -0000
+
 golang-github-vbauerster-mpb (7.3.2-1) unstable; urgency=medium
 
   * Team upload.
diff --git a/decor/decorator.go b/decor/decorator.go
index 9fec57b..aad7709 100644
--- a/decor/decorator.go
+++ b/decor/decorator.go
@@ -47,8 +47,8 @@ const (
 // Statistics consists of progress related statistics, that Decorator
 // may need.
 type Statistics struct {
-	ID             int
 	AvailableWidth int
+	ID             int
 	Total          int64
 	Current        int64
 	Refill         int64
diff --git a/decor/optimistic_string_writer.go b/decor/optimistic_string_writer.go
index ea9fda7..c6a3438 100644
--- a/decor/optimistic_string_writer.go
+++ b/decor/optimistic_string_writer.go
@@ -2,11 +2,9 @@ package decor
 
 import "io"
 
-func optimisticStringWriter(w io.Writer) func(string) {
-	return func(s string) {
-		_, err := io.WriteString(w, s)
-		if err != nil {
-			panic(err)
-		}
+func mustWriteString(w io.Writer, s string) {
+	_, err := io.WriteString(w, s)
+	if err != nil {
+		panic(err)
 	}
 }
diff --git a/decor/percentage.go b/decor/percentage.go
index 6e7f5c6..e726689 100644
--- a/decor/percentage.go
+++ b/decor/percentage.go
@@ -23,12 +23,11 @@ func (s percentageType) Format(st fmt.State, verb rune) {
 		}
 	}
 
-	osw := optimisticStringWriter(st)
-	osw(strconv.FormatFloat(float64(s), 'f', prec, 64))
+	mustWriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64))
 	if st.Flag(' ') {
-		osw(" ")
+		mustWriteString(st, " ")
 	}
-	osw("%")
+	mustWriteString(st, "%")
 }
 
 // Percentage returns percentage decorator. It's a wrapper of NewPercentage.
diff --git a/decor/size_type.go b/decor/size_type.go
index 12879b8..09ecc23 100644
--- a/decor/size_type.go
+++ b/decor/size_type.go
@@ -49,12 +49,11 @@ func (self SizeB1024) Format(st fmt.State, verb rune) {
 		unit = _iTiB
 	}
 
-	osw := optimisticStringWriter(st)
-	osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
+	mustWriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
 	if st.Flag(' ') {
-		osw(" ")
+		mustWriteString(st, " ")
 	}
-	osw(unit.String())
+	mustWriteString(st, unit.String())
 }
 
 const (
@@ -98,10 +97,9 @@ func (self SizeB1000) Format(st fmt.State, verb rune) {
 		unit = _TB
 	}
 
-	osw := optimisticStringWriter(st)
-	osw(strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
+	mustWriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
 	if st.Flag(' ') {
-		osw(" ")
+		mustWriteString(st, " ")
 	}
-	osw(unit.String())
+	mustWriteString(st, unit.String())
 }
diff --git a/decor/speed.go b/decor/speed.go
index 99cfde2..f052352 100644
--- a/decor/speed.go
+++ b/decor/speed.go
@@ -23,7 +23,7 @@ type speedFormatter struct {
 
 func (self *speedFormatter) Format(st fmt.State, verb rune) {
 	self.Formatter.Format(st, verb)
-	optimisticStringWriter(st)("/s")
+	mustWriteString(st, "/s")
 }
 
 // EwmaSpeed exponential-weighted-moving-average based speed decorator.
diff --git a/export_test.go b/export_test.go
index fba0eaf..243429b 100644
--- a/export_test.go
+++ b/export_test.go
@@ -2,4 +2,4 @@ package mpb
 
 // make syncWidth func public in test
 var SyncWidth = syncWidth
-var MaxWidthDistributor = &maxWidthDistributor
+var MaxWidthDistributor = maxWidthDistributor
diff --git a/go.mod b/go.mod
index 8fa790d..db1457e 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ require (
 	github.com/VividCortex/ewma v1.2.0
 	github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
 	github.com/mattn/go-runewidth v0.0.13
-	golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
+	golang.org/x/sys v0.0.0-20220209214540-3681064d5158
 )
 
 go 1.14
diff --git a/go.sum b/go.sum
index aebe4d9..f36888b 100644
--- a/go.sum
+++ b/go.sum
@@ -6,5 +6,5 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/progress.go b/progress.go
index 123af17..1d9a53e 100644
--- a/progress.go
+++ b/progress.go
@@ -16,12 +16,10 @@ import (
 )
 
 const (
-	// default RefreshRate
-	prr = 150 * time.Millisecond
+	prr = 150 * time.Millisecond // default RefreshRate
 )
 
-// Progress represents a container that renders one or more progress
-// bars.
+// Progress represents a container that renders one or more progress bars.
 type Progress struct {
 	ctx          context.Context
 	uwg          *sync.WaitGroup
@@ -33,14 +31,12 @@ type Progress struct {
 	once         sync.Once
 }
 
-// pState holds bars in its priorityQueue. It gets passed to
-// *Progress.serve(...) monitor goroutine.
+// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
 type pState struct {
-	bHeap            priorityQueue
-	heapUpdated      bool
-	pMatrix          map[int][]chan int
-	aMatrix          map[int][]chan int
-	barShutdownQueue []*Bar
+	bHeap       priorityQueue
+	heapUpdated bool
+	pMatrix     map[int][]chan int
+	aMatrix     map[int][]chan int
 
 	// following are provided/overrided by user
 	idCount          int
@@ -52,26 +48,26 @@ type pState struct {
 	externalRefresh  <-chan interface{}
 	renderDelay      <-chan struct{}
 	shutdownNotifier chan struct{}
-	parkedBars       map[*Bar]*Bar
+	queueBars        map[*Bar]*Bar
 	output           io.Writer
 	debugOut         io.Writer
 }
 
 // New creates new Progress container instance. It's not possible to
-// reuse instance after *Progress.Wait() method has been called.
+// reuse instance after (*Progress).Wait method has been called.
 func New(options ...ContainerOption) *Progress {
 	return NewWithContext(context.Background(), options...)
 }
 
 // NewWithContext creates new Progress container instance with provided
-// context. It's not possible to reuse instance after *Progress.Wait()
+// context. It's not possible to reuse instance after (*Progress).Wait
 // method has been called.
 func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
 	s := &pState{
-		bHeap:      priorityQueue{},
-		rr:         prr,
-		parkedBars: make(map[*Bar]*Bar),
-		output:     os.Stdout,
+		bHeap:     priorityQueue{},
+		rr:        prr,
+		queueBars: make(map[*Bar]*Bar),
+		output:    os.Stdout,
 	}
 
 	for _, opt := range options {
@@ -110,8 +106,8 @@ func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOpti
 }
 
 // Add creates a bar which renders itself by provided filler.
-// If `total <= 0` trigger complete event is disabled until reset with *bar.SetTotal(int64, bool).
-// Panics if *Progress instance is done, i.e. called after *Progress.Wait().
+// If `total <= 0` triggering complete event by increment methods is disabled.
+// Panics if *Progress instance is done, i.e. called after (*Progress).Wait().
 func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
 	if filler == nil {
 		filler = NopStyle().Build()
@@ -122,9 +118,8 @@ func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar
 	case p.operateState <- func(ps *pState) {
 		bs := ps.makeBarState(total, filler, options...)
 		bar := newBar(p, bs)
-		if bs.runningBar != nil {
-			bs.runningBar.noPop = true
-			ps.parkedBars[bs.runningBar] = bar
+		if bs.afterBar != nil {
+			ps.queueBars[bs.afterBar] = bar
 		} else {
 			heap.Push(&ps.bHeap, bar)
 			ps.heapUpdated = true
@@ -133,7 +128,6 @@ func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar
 		result <- bar
 	}:
 		bar := <-result
-		bar.subscribeDecorators()
 		return bar
 	case <-p.done:
 		p.bwg.Done()
@@ -141,21 +135,8 @@ func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar
 	}
 }
 
-func (p *Progress) dropBar(b *Bar) {
-	select {
-	case p.operateState <- func(s *pState) {
-		if b.index < 0 {
-			return
-		}
-		heap.Remove(&s.bHeap, b.index)
-		s.heapUpdated = true
-	}:
-	case <-p.done:
-	}
-}
-
 func (p *Progress) traverseBars(cb func(b *Bar) bool) {
-	done := make(chan struct{})
+	sync := make(chan struct{})
 	select {
 	case p.operateState <- func(s *pState) {
 		for i := 0; i < s.bHeap.Len(); i++ {
@@ -164,9 +145,9 @@ func (p *Progress) traverseBars(cb func(b *Bar) bool) {
 				break
 			}
 		}
-		close(done)
+		close(sync)
 	}:
-		<-done
+		<-sync
 	case <-p.done:
 	}
 }
@@ -200,8 +181,8 @@ func (p *Progress) BarCount() int {
 // After this method has been called, there is no way to reuse *Progress
 // instance.
 func (p *Progress) Wait() {
+	// wait for user wg, if any
 	if p.uwg != nil {
-		// wait for user wg
 		p.uwg.Wait()
 	}
 
@@ -256,6 +237,64 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
 	}
 }
 
+func (s *pState) render(cw *cwriter.Writer) error {
+	if s.heapUpdated {
+		s.updateSyncMatrix()
+		s.heapUpdated = false
+	}
+	syncWidth(s.pMatrix)
+	syncWidth(s.aMatrix)
+
+	tw, err := cw.GetWidth()
+	if err != nil {
+		tw = s.reqWidth
+	}
+	for i := 0; i < s.bHeap.Len(); i++ {
+		bar := s.bHeap[i]
+		go bar.render(tw)
+	}
+
+	return s.flush(cw)
+}
+
+func (s *pState) flush(cw *cwriter.Writer) error {
+	var lines int
+	pool := make([]*Bar, 0, s.bHeap.Len())
+	for s.bHeap.Len() > 0 {
+		b := heap.Pop(&s.bHeap).(*Bar)
+		frame := <-b.frameCh
+		lines += frame.lines
+		_, err := cw.ReadFrom(frame.reader)
+		if err != nil {
+			return err
+		}
+		if frame.shutdown {
+			b.Wait() // waiting for b.done, so it's safe to read b.bs
+			var toDrop bool
+			if qb, ok := s.queueBars[b]; ok {
+				delete(s.queueBars, b)
+				qb.priority = b.priority
+				pool = append(pool, qb)
+				toDrop = true
+			} else if s.popCompleted && !b.bs.noPop {
+				lines -= frame.lines
+				toDrop = true
+			}
+			if toDrop || b.bs.dropOnComplete {
+				s.heapUpdated = true
+				continue
+			}
+		}
+		pool = append(pool, b)
+	}
+
+	for _, b := range pool {
+		heap.Push(&s.bHeap, b)
+	}
+
+	return cw.Flush(lines)
+}
+
 func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
 	ch := make(chan time.Time)
 	if s.shutdownNotifier == nil {
@@ -294,78 +333,6 @@ func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
 	return ch
 }
 
-func (s *pState) render(cw *cwriter.Writer) error {
-	if s.heapUpdated {
-		s.updateSyncMatrix()
-		s.heapUpdated = false
-	}
-	syncWidth(s.pMatrix)
-	syncWidth(s.aMatrix)
-
-	tw, err := cw.GetWidth()
-	if err != nil {
-		tw = s.reqWidth
-	}
-	for i := 0; i < s.bHeap.Len(); i++ {
-		bar := s.bHeap[i]
-		go bar.render(tw)
-	}
-
-	return s.flush(cw)
-}
-
-func (s *pState) flush(cw *cwriter.Writer) error {
-	var totalLines int
-	bm := make(map[*Bar]int, s.bHeap.Len())
-	for s.bHeap.Len() > 0 {
-		b := heap.Pop(&s.bHeap).(*Bar)
-		frame := <-b.frameCh
-		_, err := cw.ReadFrom(frame.reader)
-		if err != nil {
-			return err
-		}
-		if b.toShutdown {
-			if b.recoveredPanic != nil {
-				s.barShutdownQueue = append(s.barShutdownQueue, b)
-				b.toShutdown = false
-			} else {
-				// shutdown at next flush
-				// this ensures no bar ends up with less than 100% rendered
-				defer func() {
-					s.barShutdownQueue = append(s.barShutdownQueue, b)
-				}()
-			}
-		}
-		bm[b] = frame.lines
-		totalLines += frame.lines
-	}
-
-	for _, b := range s.barShutdownQueue {
-		if parkedBar := s.parkedBars[b]; parkedBar != nil {
-			parkedBar.priority = b.priority
-			heap.Push(&s.bHeap, parkedBar)
-			delete(s.parkedBars, b)
-			b.toDrop = true
-		}
-		if s.popCompleted && !b.noPop {
-			totalLines -= bm[b]
-			b.toDrop = true
-		}
-		if b.toDrop {
-			delete(bm, b)
-			s.heapUpdated = true
-		}
-		b.cancel()
-	}
-	s.barShutdownQueue = s.barShutdownQueue[0:0]
-
-	for b := range bm {
-		heap.Push(&s.bHeap, b)
-	}
-
-	return cw.Flush(totalLines)
-}
-
 func (s *pState) updateSyncMatrix() {
 	s.pMatrix = make(map[int][]chan int)
 	s.aMatrix = make(map[int][]chan int)
@@ -418,6 +385,8 @@ func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOptio
 		bs.buffers[i] = bytes.NewBuffer(make([]byte, 0, 512))
 	}
 
+	bs.subscribeDecorators()
+
 	return bs
 }
 
@@ -427,7 +396,7 @@ func syncWidth(matrix map[int][]chan int) {
 	}
 }
 
-var maxWidthDistributor = func(column []chan int) {
+func maxWidthDistributor(column []chan int) {
 	var maxWidth int
 	for _, ch := range column {
 		if w := <-ch; w > maxWidth {
diff --git a/progress_test.go b/progress_test.go
index 64e0fa6..2eaba5c 100644
--- a/progress_test.go
+++ b/progress_test.go
@@ -1,11 +1,9 @@
 package mpb_test
 
 import (
-	"bytes"
 	"context"
 	"io/ioutil"
 	"math/rand"
-	"sync"
 	"testing"
 	"time"
 
@@ -13,67 +11,87 @@ import (
 	"github.com/vbauerster/mpb/v7/decor"
 )
 
+const (
+	timeout = 200 * time.Millisecond
+)
+
 func init() {
 	rand.Seed(time.Now().UnixNano())
 }
 
 func TestBarCount(t *testing.T) {
-	p := mpb.New(mpb.WithOutput(ioutil.Discard))
+	shutdown := make(chan struct{})
+	p := mpb.New(mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(ioutil.Discard))
 
-	var wg sync.WaitGroup
-	wg.Add(1)
-	b := p.AddBar(100)
-	go func() {
-		rng := rand.New(rand.NewSource(time.Now().UnixNano()))
-		for i := 0; i < 100; i++ {
-			if i == 33 {
-				wg.Done()
-			}
-			b.Increment()
-			time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2)
-		}
-	}()
+	b := p.AddBar(0, mpb.BarRemoveOnComplete())
 
-	wg.Wait()
-	count := p.BarCount()
-	if count != 1 {
-		t.Errorf("BarCount want: %q, got: %q\n", 1, count)
+	if count := p.BarCount(); count != 1 {
+		t.Errorf("BarCount want: %d, got: %d\n", 1, count)
 	}
 
-	b.Abort(true)
-	p.Wait()
+	b.SetTotal(100, true)
+
+	b.Wait()
+
+	if count := p.BarCount(); count != 0 {
+		t.Errorf("BarCount want: %d, got: %d\n", 0, count)
+	}
+
+	go p.Wait()
+
+	select {
+	case <-shutdown:
+	case <-time.After(timeout):
+		t.Errorf("Progress didn't shutdown after %v", timeout)
+	}
 }
 
 func TestBarAbort(t *testing.T) {
-	p := mpb.New(mpb.WithOutput(ioutil.Discard))
-
-	var wg sync.WaitGroup
-	wg.Add(1)
-	bars := make([]*mpb.Bar, 3)
-	for i := 0; i < 3; i++ {
+	shutdown := make(chan struct{})
+	p := mpb.New(mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(ioutil.Discard))
+	n := 2
+	bars := make([]*mpb.Bar, n)
+	for i := 0; i < n; i++ {
 		b := p.AddBar(100)
-		rng := rand.New(rand.NewSource(time.Now().UnixNano()))
-		go func(n int) {
-			for i := 0; !b.Completed(); i++ {
-				if n == 0 && i >= 33 {
+		switch i {
+		case n - 1:
+			var abortCalledTimes int
+			for j := 0; !b.Aborted(); j++ {
+				if j >= 10 {
 					b.Abort(true)
-					wg.Done()
+					abortCalledTimes++
+				} else {
+					b.Increment()
 				}
-				b.Increment()
-				time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2)
 			}
-		}(i)
+			if abortCalledTimes != 1 {
+				t.Errorf("Expected abortCalledTimes: %d, got: %d\n", 1, abortCalledTimes)
+			}
+			b.Wait()
+			count := p.BarCount()
+			if count != 1 {
+				t.Errorf("BarCount want: %d, got: %d\n", 1, count)
+			}
+		default:
+			go func() {
+				for !b.Completed() {
+					b.Increment()
+					time.Sleep(randomDuration(100 * time.Millisecond))
+				}
+			}()
+		}
 		bars[i] = b
 	}
 
-	wg.Wait()
-	count := p.BarCount()
-	if count != 2 {
-		t.Errorf("BarCount want: %d, got: %d\n", 2, count)
+	bars[0].Abort(false)
+
+	go p.Wait()
+
+	select {
+	case <-shutdown:
+	case <-time.After(timeout):
+		t.Errorf("Progress didn't shutdown after %v", timeout)
 	}
-	bars[1].Abort(true)
-	bars[2].Abort(true)
-	p.Wait()
 }
 
 func TestWithContext(t *testing.T) {
@@ -81,34 +99,25 @@ func TestWithContext(t *testing.T) {
 	ctx, cancel := context.WithCancel(context.Background())
 	p := mpb.NewWithContext(ctx, mpb.WithShutdownNotifier(shutdown), mpb.WithOutput(ioutil.Discard))
 
-	start := make(chan struct{})
 	done := make(chan struct{})
-	fail := make(chan struct{})
 	bar := p.AddBar(0) // never complete bar
 	go func() {
-		close(start)
-		for !bar.Completed() {
-			bar.Increment()
+		for !bar.Aborted() {
 			time.Sleep(randomDuration(100 * time.Millisecond))
+			cancel()
 		}
 		close(done)
 	}()
 
 	go func() {
-		select {
-		case <-done:
-			p.Wait()
-		case <-time.After(150 * time.Millisecond):
-			close(fail)
-		}
+		<-done
+		p.Wait()
 	}()
 
-	<-start
-	cancel()
 	select {
 	case <-shutdown:
-	case <-fail:
-		t.Error("Progress didn't shutdown")
+	case <-time.After(timeout):
+		t.Errorf("Progress didn't shutdown after %v", timeout)
 	}
 }
 
@@ -126,31 +135,28 @@ func TestMaxWidthDistributor(t *testing.T) {
 	ready := make(chan struct{})
 	start := make(chan struct{})
 	end := make(chan struct{})
-	*mpb.MaxWidthDistributor = makeWrapper(*mpb.MaxWidthDistributor, start, end)
+	mpb.MaxWidthDistributor = makeWrapper(mpb.MaxWidthDistributor, start, end)
 
-	total := 80
+	total := 100
 	numBars := 6
 	p := mpb.New(mpb.WithOutput(ioutil.Discard))
 	for i := 0; i < numBars; i++ {
 		bar := p.AddBar(int64(total),
 			mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0),
-			mpb.PrependDecorators(
-				decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace),
-			),
+			mpb.PrependDecorators(decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace)),
 		)
 		go func() {
 			<-ready
-			rng := rand.New(rand.NewSource(time.Now().UnixNano()))
 			for i := 0; i < total; i++ {
 				start := time.Now()
-				if id := bar.ID(); id > 1 && i >= 42 {
+				if id := bar.ID(); id > 1 && i >= 32 {
 					if id&1 == 1 {
 						bar.Abort(true)
 					} else {
 						bar.Abort(false)
 					}
 				}
-				time.Sleep((time.Duration(rng.Intn(10)+1) * (50 * time.Millisecond)) / 2)
+				time.Sleep(randomDuration(100 * time.Millisecond))
 				bar.IncrInt64(rand.Int63n(5) + 1)
 				bar.DecoratorEwmaUpdate(time.Since(start))
 			}
@@ -181,11 +187,6 @@ func TestMaxWidthDistributor(t *testing.T) {
 	}
 }
 
-func getLastLine(bb []byte) []byte {
-	split := bytes.Split(bb, []byte("\n"))
-	return split[len(split)-2]
-}
-
 func randomDuration(max time.Duration) time.Duration {
 	return time.Duration(rand.Intn(10)+1) * max / 10
 }
diff --git a/proxyreader.go b/proxyreader.go
index 25f195b..b0dd89d 100644
--- a/proxyreader.go
+++ b/proxyreader.go
@@ -14,9 +14,6 @@ type proxyReader struct {
 func (x proxyReader) Read(p []byte) (int, error) {
 	n, err := x.ReadCloser.Read(p)
 	x.bar.IncrBy(n)
-	if err == io.EOF {
-		go x.bar.SetTotal(-1, true)
-	}
 	return n, err
 }
 
@@ -28,9 +25,6 @@ type proxyWriterTo struct {
 func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
 	n, err := x.wt.WriteTo(w)
 	x.bar.IncrInt64(n)
-	if err == io.EOF {
-		go x.bar.SetTotal(-1, true)
-	}
 	return n, err
 }
 
@@ -65,12 +59,12 @@ func (b *Bar) newProxyReader(r io.Reader) (rc io.ReadCloser) {
 	pr := proxyReader{toReadCloser(r), b}
 	if wt, ok := r.(io.WriterTo); ok {
 		pw := proxyWriterTo{pr, wt}
-		if b.hasEwmaDecorators {
+		if b.hasEwma {
 			rc = ewmaProxyWriterTo{ewmaProxyReader{pr}, pw}
 		} else {
 			rc = pw
 		}
-	} else if b.hasEwmaDecorators {
+	} else if b.hasEwma {
 		rc = ewmaProxyReader{pr}
 	} else {
 		rc = pr
diff --git a/proxyreader_test.go b/proxyreader_test.go
index 789695e..96892a3 100644
--- a/proxyreader_test.go
+++ b/proxyreader_test.go
@@ -28,12 +28,22 @@ func (r *testReader) Read(p []byte) (n int, err error) {
 	return r.Reader.Read(p)
 }
 
+type testWriterTo struct {
+	*testReader
+	wt io.WriterTo
+}
+
+func (wt testWriterTo) WriteTo(w io.Writer) (n int64, err error) {
+	wt.called = true
+	return wt.wt.WriteTo(w)
+}
+
 func TestProxyReader(t *testing.T) {
 	p := mpb.New(mpb.WithOutput(ioutil.Discard))
 
 	tReader := &testReader{strings.NewReader(content), false}
 
-	bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim())
+	bar := p.AddBar(int64(len(content)))
 
 	var buf bytes.Buffer
 	_, err := io.Copy(&buf, bar.ProxyReader(tReader))
@@ -52,34 +62,23 @@ func TestProxyReader(t *testing.T) {
 	}
 }
 
-type testWriterTo struct {
-	io.Reader
-	wt     io.WriterTo
-	called bool
-}
-
-func (wt *testWriterTo) WriteTo(w io.Writer) (n int64, err error) {
-	wt.called = true
-	return wt.wt.WriteTo(w)
-}
-
 func TestProxyWriterTo(t *testing.T) {
 	p := mpb.New(mpb.WithOutput(ioutil.Discard))
 
 	var reader io.Reader = strings.NewReader(content)
-	tReader := &testWriterTo{reader, reader.(io.WriterTo), false}
+	tWriterTo := testWriterTo{&testReader{reader, false}, reader.(io.WriterTo)}
 
-	bar := p.AddBar(int64(len(content)), mpb.BarFillerTrim())
+	bar := p.AddBar(int64(len(content)))
 
 	var buf bytes.Buffer
-	_, err := io.Copy(&buf, bar.ProxyReader(tReader))
+	_, err := io.Copy(&buf, bar.ProxyReader(tWriterTo))
 	if err != nil {
 		t.Errorf("Error copying from reader: %+v\n", err)
 	}
 
 	p.Wait()
 
-	if !tReader.called {
+	if !tWriterTo.called {
 		t.Error("WriteTo not called")
 	}