aboutsummaryrefslogtreecommitdiffstats
path: root/ccp.go
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-04-15 20:56:06 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-04-15 21:32:07 -0700
commite550f85feee5d768f06c9737d321504f1cc3a31e (patch)
tree535119a3a798197887427167e55e73168136093d /ccp.go
parent92aa4003fb4b263242619bec211d39200c9618b3 (diff)
downloadccp-e550f85feee5d768f06c9737d321504f1cc3a31e.tar.zst
More perf improvements
Diffstat (limited to 'ccp.go')
-rw-r--r--ccp.go65
1 files changed, 39 insertions, 26 deletions
diff --git a/ccp.go b/ccp.go
index a7cfd02..81ee332 100644
--- a/ccp.go
+++ b/ccp.go
@@ -35,7 +35,7 @@ import (
"fmt"
"os"
"strings"
- "sync/atomic"
+ "sync"
"time"
"github.com/charmbracelet/bubbles/progress"
@@ -59,22 +59,28 @@ type model struct {
// max is the total bytes (plus fudge factor) to copy.
max int64
+ // done indicates whether the copy is done and we're just waiting for
+ // the progress bar to finish animating.
+ done bool
+ // errs are the errors encountered during operation.
+ errs []string
+
+ // Only current and copyingFile are protected by the mutex
+ // (for performance). The other fields are modified in Update according
+ // to the Elm architecture.
+ mu sync.Mutex
// current holds the current number of copied bytes.
- current atomic.Int64
- // Every 500 milliseconds, the current progress is appended to
- // measurements for calculating ETA.
- measurements deque.Deque[measurement]
+ current int64
// copyingFile is a file that is or was being copied that we're
// currently showing to the user.
copyingFile string
+
+ // Every 500 milliseconds, the current progress is appended to
+ // measurements for calculating ETA.
+ measurements deque.Deque[measurement]
// eta is the estimated time to completion, or -1 if we don't have
// enough samples.
eta time.Duration
- // errs are the errors encountered during operation.
- errs []string
- // done indicates whether the copy is done and we're just waiting for
- // the progress bar to finish animating.
- done bool
}
type (
@@ -85,12 +91,8 @@ type (
// during the program lifetime after we asynchronously calculate the
// number of bytes to copy.
maxMsg int64
- // fileStartMsg is sent whenever we start copying a file.
- fileStartMsg struct {
- from, to string
- }
- // fileDoneMsg is sent whenever we finish copying a file. err indicates
- // any error that was encountered during the copy.
+ // errorMsg is sent whenever we finish copying a file. err indicates any
+ // error that was encountered during the copy.
errorMsg struct{ error }
// doneMsg is sent when all files are finished copying and it's time
// to exit.
@@ -109,15 +111,16 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case maxMsg:
m.max = int64(msg)
- case fileStartMsg:
- m.copyingFile = msg.from + " -> " + msg.to
case errorMsg:
m.errs = append(m.errs, msg.Error())
case doneMsg:
m.done = true
var cmd tea.Cmd
if m.max > 0 {
- cmd = m.progress.SetPercent(float64(m.current.Load()) / float64(m.max))
+ m.mu.Lock()
+ current := m.current
+ m.mu.Unlock()
+ cmd = m.progress.SetPercent(float64(current) / float64(m.max))
}
if !m.progress.IsAnimating() {
return m, tea.Quit
@@ -125,7 +128,9 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd
case tickMsg:
- n := m.current.Load()
+ m.mu.Lock()
+ n := m.current
+ m.mu.Unlock()
now := time.Time(msg)
if m.measurements.Len() == 0 || now.Sub(m.measurements.At(m.measurements.Len()-1).t) > 500*time.Millisecond {
@@ -170,8 +175,11 @@ func (m *model) View() string {
if m.eta >= 0 {
etaStr = m.eta.Round(time.Second).String()
}
+ m.mu.Lock()
+ copyingFile := m.copyingFile
+ m.mu.Unlock()
return "\n" +
- " " + m.copyingFile + "\n" +
+ " " + copyingFile + "\n" +
" " + m.progress.View() + "\n" +
" " + "ETA: " + etaStr + "\n\n" +
warningStyle(strings.Join(m.errs, "\n")) + "\n"
@@ -179,8 +187,8 @@ func (m *model) View() string {
// progressUpdater implements the cp.Progress interface.
type progressUpdater struct {
- p *tea.Program
- current *atomic.Int64
+ p *tea.Program
+ m *model
}
func (pu *progressUpdater) Max(n int64) {
@@ -188,11 +196,16 @@ func (pu *progressUpdater) Max(n int64) {
}
func (pu *progressUpdater) Progress(n int64) {
- pu.current.Add(n)
+ pu.m.mu.Lock()
+ defer pu.m.mu.Unlock()
+ pu.m.current += n
}
func (pu *progressUpdater) FileStart(from, to string) {
- pu.p.Send(fileStartMsg{from, to})
+ s := from + " -> " + to
+ pu.m.mu.Lock()
+ defer pu.m.mu.Unlock()
+ pu.m.copyingFile = s
}
func (pu *progressUpdater) Error(err error) {
@@ -251,7 +264,7 @@ func run() error {
}
p := tea.NewProgram(m, tea.WithInput(nil), tea.WithOutput(os.Stderr))
go func() {
- cp.Copy(&progressUpdater{p, &m.current}, srcs, dst, *f) // Where the magic happens
+ cp.Copy(&progressUpdater{p, m}, srcs, dst, *f) // Where the magic happens
p.Send(doneMsg{})
}()
if _, err := p.Run(); err != nil {