Compare commits
1 Commits
b7a7f2a82f
...
c822404bf7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c822404bf7 |
15
go.mod
15
go.mod
@@ -1,18 +1,3 @@
|
|||||||
module show-rss
|
module show-rss
|
||||||
|
|
||||||
go 1.23.3
|
go 1.23.3
|
||||||
|
|
||||||
require modernc.org/sqlite v1.37.0
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
|
||||||
modernc.org/libc v1.62.1 // indirect
|
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
|
||||||
modernc.org/memory v1.9.1 // indirect
|
|
||||||
)
|
|
||||||
|
|||||||
47
go.sum
47
go.sum
@@ -1,47 +0,0 @@
|
|||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
|
||||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
|
||||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
|
||||||
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
|
|
||||||
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
|
||||||
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=
|
|
||||||
modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw=
|
|
||||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
|
||||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
|
||||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
|
||||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
|
||||||
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
|
|
||||||
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
|
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
|
||||||
modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
|
|
||||||
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
|
||||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
|
||||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
|
||||||
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
|
|
||||||
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
|
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
|
||||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
|
||||||
12
main.go
12
main.go
@@ -8,14 +8,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := Main(context.Background()); err != nil {
|
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
|
||||||
|
defer can()
|
||||||
|
|
||||||
|
if err := cmd.Main(ctx); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main(ctx context.Context) error {
|
|
||||||
ctx, can := signal.NotifyContext(ctx, syscall.SIGINT)
|
|
||||||
defer can()
|
|
||||||
|
|
||||||
return cmd.Main(ctx)
|
|
||||||
}
|
|
||||||
|
|||||||
11
main_test.go
11
main_test.go
@@ -2,16 +2,17 @@ package main_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
main "show-rss"
|
"os/signal"
|
||||||
|
"show-rss/src/cmd"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(t *testing.T) {
|
func TestCmdMain(t *testing.T) {
|
||||||
ctx, can := context.WithTimeout(context.Background(), 2*time.Second)
|
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
|
||||||
defer can()
|
defer can()
|
||||||
|
|
||||||
if err := main.Main(ctx); err != nil && ctx.Err() == nil {
|
if err := cmd.Main(ctx); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package cleanup
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
const ctxKey = "__cleanup"
|
|
||||||
|
|
||||||
func Inject(ctx context.Context, foo func()) context.Context {
|
|
||||||
before := Extract(ctx)
|
|
||||||
after := func() {
|
|
||||||
foo()
|
|
||||||
before()
|
|
||||||
}
|
|
||||||
return context.WithValue(ctx, ctxKey, after)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Extract(ctx context.Context) func() {
|
|
||||||
v := ctx.Value(ctxKey)
|
|
||||||
if v == nil {
|
|
||||||
return func() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
v2, _ := v.(func())
|
|
||||||
if v2 == nil {
|
|
||||||
return func() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package cleanup_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"show-rss/src/cleanup"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCleanup(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
called := make([]bool, 100)
|
|
||||||
for i := range called {
|
|
||||||
i := i
|
|
||||||
ctx = cleanup.Inject(ctx, func() {
|
|
||||||
t.Logf("cleaning %d", i)
|
|
||||||
if i < len(called)-1 && !called[i+1] {
|
|
||||||
t.Errorf("cleaning %d before %d", i, i+1)
|
|
||||||
}
|
|
||||||
called[i] = true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("cleaning")
|
|
||||||
cleanup.Extract(ctx)()
|
|
||||||
t.Logf("cleaned")
|
|
||||||
|
|
||||||
for i := range called {
|
|
||||||
if !called[i] {
|
|
||||||
t.Fatalf("missing called[%d]", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"show-rss/src/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Config(ctx context.Context) (context.Context, error) {
|
|
||||||
ctx, err := db.Inject(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return ctx, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx, nil
|
|
||||||
}
|
|
||||||
10
src/cmd/cron.go
Normal file
10
src/cmd/cron.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func cron(ctx context.Context) error {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package cron
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Main(ctx context.Context) error {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
@@ -3,52 +3,26 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"show-rss/src/cmd/cron"
|
|
||||||
"show-rss/src/cmd/server"
|
|
||||||
"show-rss/src/pool"
|
"show-rss/src/pool"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Main(ctx context.Context) error {
|
func Main(ctx context.Context) error {
|
||||||
ctx, can := context.WithCancel(ctx)
|
ctx, can := context.WithCancel(ctx)
|
||||||
defer can()
|
defer can()
|
||||||
|
|
||||||
ctx, err := Config(ctx)
|
foos := map[string]func() error{
|
||||||
if err != nil {
|
"server": func() error { return server(ctx) },
|
||||||
return fmt.Errorf("failed to inject: %w", err)
|
"cron": func() error { return cron(ctx) },
|
||||||
}
|
|
||||||
|
|
||||||
foos := map[string]func(context.Context) error{
|
|
||||||
"server": server.Main,
|
|
||||||
"cron": cron.Main,
|
|
||||||
}
|
}
|
||||||
p := pool.New(len(foos))
|
p := pool.New(len(foos))
|
||||||
defer p.Wait(ctx)
|
defer p.Wait(ctx)
|
||||||
|
|
||||||
for k, foo := range foos {
|
for k, foo := range foos {
|
||||||
if err := p.Go(ctx, k, runner(ctx, k, foo)); err != nil {
|
foo := foo
|
||||||
|
if err := p.Go(ctx, k, foo); err != nil {
|
||||||
return fmt.Errorf("failed to go %s: %v", k, err)
|
return fmt.Errorf("failed to go %s: %v", k, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.Wait(ctx)
|
return p.Wait(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runner(ctx context.Context, k string, foo func(context.Context) error) func() error {
|
|
||||||
return func() error {
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
err = foo(ctx)
|
|
||||||
if ctx.Err() == nil {
|
|
||||||
log.Printf("%s failed; restarting: %v", k, err)
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
break
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
10
src/cmd/server.go
Normal file
10
src/cmd/server.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func server(ctx context.Context) error {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Main(ctx context.Context) error {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"database/sql"
|
|
||||||
|
|
||||||
"show-rss/src/cleanup"
|
|
||||||
|
|
||||||
_ "modernc.org/sqlite"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ctxKey = "__db"
|
|
||||||
|
|
||||||
func Inject(ctx context.Context, conn string) (context.Context, error) {
|
|
||||||
connctx, can := context.WithTimeout(ctx, 15*time.Second)
|
|
||||||
defer can()
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite", conn)
|
|
||||||
if err != nil {
|
|
||||||
return ctx, err
|
|
||||||
}
|
|
||||||
ctx = cleanup.Inject(ctx, func() {
|
|
||||||
db.Close()
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := func() error {
|
|
||||||
c := time.NewTicker(100 * time.Millisecond)
|
|
||||||
defer c.Stop()
|
|
||||||
|
|
||||||
var err error
|
|
||||||
for connctx.Err() == nil {
|
|
||||||
if err = db.PingContext(connctx); err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-connctx.Done():
|
|
||||||
case <-c.C:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}(); err != nil {
|
|
||||||
return ctx, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.WithValue(ctx, ctxKey, db), ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func extract(ctx context.Context) (*sql.DB, error) {
|
|
||||||
db := ctx.Value(ctxKey)
|
|
||||||
if db == nil {
|
|
||||||
return nil, fmt.Errorf("db not injected")
|
|
||||||
}
|
|
||||||
return db.(*sql.DB), nil
|
|
||||||
}
|
|
||||||
29
src/db/db.go
29
src/db/db.go
@@ -1,29 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func QueryOne(ctx context.Context, q string, args ...any) error {
|
|
||||||
return with(ctx, func(db *sql.DB) error {
|
|
||||||
row := db.QueryRowContext(ctx, q, args...)
|
|
||||||
TODO generic and return value
|
|
||||||
return row.Err()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Exec(ctx context.Context, q string, args ...any) error {
|
|
||||||
return with(ctx, func(db *sql.DB) error {
|
|
||||||
_, err := db.ExecContext(ctx, q, args...)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func with(ctx context.Context, foo func(*sql.DB) error) error {
|
|
||||||
db, err := extract(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return foo(db)
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package db_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"path"
|
|
||||||
"show-rss/src/cleanup"
|
|
||||||
"show-rss/src/db"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDB(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
ctx, err := db.Inject(ctx, path.Join(t.TempDir(), "db"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
cleanup.Extract(ctx)()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := db.Exec(ctx, `
|
|
||||||
CREATE TABLE IF NOT EXISTS test (k TEXT);
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS test_idx ON test (k);
|
|
||||||
INSERT INTO test (k) SELECT 'a';
|
|
||||||
INSERT INTO test (k) SELECT 'b';
|
|
||||||
`); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result struct {
|
|
||||||
K string
|
|
||||||
}
|
|
||||||
if got, err := db.QueryOne[result](ctx, `SELECT k FROM test WHERE k='a'`); err != nil {
|
|
||||||
t.Errorf("failed query one: %w", err)
|
|
||||||
} else if got.K != "a" {
|
|
||||||
t.Errorf("bad query one: %+v", got)
|
|
||||||
}
|
|
||||||
|
|
||||||
if gots, err := db.Query[result](ctx, `SELECT k FROM test`); err != nil {
|
|
||||||
t.Errorf("failed query: %w", err)
|
|
||||||
} else if len(gots) != 2 {
|
|
||||||
t.Errorf("expected 2 but got %d gots", len(gots))
|
|
||||||
} else if gots[0].K != "a" {
|
|
||||||
t.Errorf("expected [0]='a' but got %q", gots[0].K)
|
|
||||||
} else if gots[1].K != "b" {
|
|
||||||
t.Errorf("expected [1]='b' but got %q", gots[1].K)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,14 +39,36 @@ func (p *Pool) Go(ctx context.Context, name string, foo func() error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pool) Wait(ctx context.Context) error {
|
func (p *Pool) Wait(ctx context.Context) error {
|
||||||
if err := p.close(ctx); err != nil {
|
waited := make(chan bool)
|
||||||
return err
|
defer close(waited)
|
||||||
|
go func() {
|
||||||
|
c := time.NewTicker(100 * time.Millisecond)
|
||||||
|
defer c.Stop()
|
||||||
|
|
||||||
|
if p.jobs != nil {
|
||||||
|
for len(p.jobs) > 0 && ctx.Err() == nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-c.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(p.jobs)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.Err()
|
p.wg.Wait()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case waited <- true:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-waited:
|
||||||
|
p.jobs = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pool) Err() error {
|
|
||||||
if len(p.errs) == 0 {
|
if len(p.errs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -120,47 +142,3 @@ func (p *Pool) withLock(foo func()) {
|
|||||||
defer p.lock.Unlock()
|
defer p.lock.Unlock()
|
||||||
foo()
|
foo()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pool) close(ctx context.Context) error {
|
|
||||||
waited := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
defer close(waited)
|
|
||||||
|
|
||||||
p.withLock(func() {
|
|
||||||
p._close(ctx)
|
|
||||||
})
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
case waited <- true:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
case <-waited:
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pool) _close(ctx context.Context) {
|
|
||||||
if p.jobs != nil {
|
|
||||||
c := time.NewTicker(100 * time.Millisecond)
|
|
||||||
defer c.Stop()
|
|
||||||
|
|
||||||
for len(p.jobs) > 0 && ctx.Err() == nil {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
case <-c.C:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func() {
|
|
||||||
defer func() { recover() }()
|
|
||||||
close(p.jobs)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
p.jobs = nil
|
|
||||||
|
|
||||||
p.wg.Wait()
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user