1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package gitcli
4
5import (
6 "bufio"
7 "context"
8 "io"
9 "sort"
10
11 "bnema.dev/ermite/internal/domain"
12)
13
14// CatFilePIDs returns the process ids of the live long-lived cat-file
15// processes, for the tests of the pool.
16func (g *Git) CatFilePIDs() []int {
17 g.cat.mu.Lock()
18 defer g.cat.mu.Unlock()
19 var pids []int
20 for _, procs := range g.cat.procs {
21 for _, cp := range procs {
22 pids = append(pids, cp.cmd.Process.Pid)
23 }
24 }
25 sort.Ints(pids)
26 return pids
27}
28
29// BlobStreams returns the number of blob streams that are open.
30func (g *Git) BlobStreams() int {
31 g.cat.mu.Lock()
32 defer g.cat.mu.Unlock()
33 return len(g.cat.streams)
34}
35
36// BlobStreamPIDs returns the process ids of the open blob streams.
37func (g *Git) BlobStreamPIDs() []int {
38 g.cat.mu.Lock()
39 defer g.cat.mu.Unlock()
40 var pids []int
41 for s := range g.cat.streams {
42 pids = append(pids, s.cmd.Process.Pid)
43 }
44 return pids
45}
46
47// FireIdleTimers runs, for every idle cat-file process, what its idle timer
48// would do now, so that tests do not wait for real time.
49func (g *Git) FireIdleTimers() {
50 g.cat.mu.Lock()
51 type armed struct {
52 cp *catProc
53 gen uint64
54 }
55 var all []armed
56 for _, procs := range g.cat.procs {
57 for _, cp := range procs {
58 all = append(all, armed{cp, cp.gen})
59 }
60 }
61 g.cat.mu.Unlock()
62 for _, a := range all {
63 g.cat.expire(a.cp, a.gen)
64 }
65}
66
67// FireStaleIdleTimers runs what the idle timers armed before the last use of
68// each process would do: nothing, since the process was used since.
69func (g *Git) FireStaleIdleTimers() {
70 g.cat.mu.Lock()
71 type armed struct {
72 cp *catProc
73 gen uint64
74 }
75 var all []armed
76 for _, procs := range g.cat.procs {
77 for _, cp := range procs {
78 all = append(all, armed{cp, cp.gen - 1})
79 }
80 }
81 g.cat.mu.Unlock()
82 for _, a := range all {
83 g.cat.expire(a.cp, a.gen)
84 }
85}
86
87// ExecSlots returns the size of the one-shot process semaphore and how many
88// slots are taken.
89func (g *Git) ExecSlots() (size, taken int) { return cap(g.exec), len(g.exec) }
90
91// HoldCatFile runs fn while holding a cat-file process of the repository, as
92// a request in flight would.
93func (g *Git) HoldCatFile(ctx context.Context, repo domain.RepoName, fn func()) error {
94 dir, err := g.repoDir(repo)
95 if err != nil {
96 return err
97 }
98 return g.cat.do(ctx, dir, func(*catConn) error { fn(); return nil })
99}
100
101// CatFileWaiting returns the number of requests that wait for a cat-file
102// process.
103func (g *Git) CatFileWaiting() int {
104 g.cat.mu.Lock()
105 defer g.cat.mu.Unlock()
106 n := 0
107 for _, q := range g.cat.waiters {
108 n += len(q)
109 }
110 return n
111}
112
113// MaxPushedCommits is the bound of the commits of a push, for the tests.
114const MaxPushedCommits = maxPushedCommits
115
116// ParseRevList reads rev-list output with the bounds of the guard replaced, and
117// returns the number of commits kept.
118func ParseRevList(r io.Reader, maxCommits, maxIDs int) (int, error) {
119 c, err := parseRevList(bufio.NewReaderSize(r, 64*1024), maxCommits, maxIDs)
120 return len(c), err
121}
122
123// MaxPushedObjects is the bound of the ids of the commits of a push.
124const MaxPushedObjects = maxPushedObjects