stario/fn.go
starainrt c8facb5a03
stario: 提升 Go 1.20 基线与交互/队列稳定性
- 提升 go.mod 基线到 Go 1.20,并补齐对应测试
  - 修正 Passwd / PasswdResponseSignal 语义,Ctrl+C 默认退出当前流程
  - 优化 raw terminal redraw、Restore 与 StopUntil 的边界行为
  - 新增 StarPipe、FrameReader/FrameWriter、ReadFullContext/WriteFullContext/CopyContext、IsTerminal/ReadPasswordContext
  - 收口 StarQueue / StarBuffer 语义,删除 EndWrite,统一 Close / Abort 行为
  - 补齐 signal、timeout、queue、terminal、pipe、buffer 的回归测试与 race 覆盖
2026-04-15 14:35:19 +08:00

112 lines
3.1 KiB
Go

package stario
import (
"context"
"errors"
"time"
)
// ERR_TIMEOUT is the legacy timeout sentinel used by WaitUntilTimeout*.
var ERR_TIMEOUT = errors.New("TIME OUT")
// WaitUntilContext runs fn and returns either its result or the context error,
// whichever happens first.
func WaitUntilContext(ctx context.Context, fn func(context.Context) error) error {
if ctx == nil {
ctx = context.Background()
}
finished := make(chan error, 1)
go func() {
finished <- fn(ctx)
}()
select {
case err := <-finished:
return err
case <-ctx.Done():
return ctx.Err()
}
}
// WaitUntilContextFinished is the asynchronous form of WaitUntilContext.
func WaitUntilContextFinished(ctx context.Context, fn func(context.Context) error) <-chan error {
result := make(chan error, 1)
go func() {
result <- WaitUntilContext(ctx, fn)
close(result)
}()
return result
}
// WaitUntilContextDone adapts a done-channel worker to a context-based wait.
func WaitUntilContextDone(ctx context.Context, fn func(<-chan struct{}) error) error {
if ctx == nil {
ctx = context.Background()
}
return WaitUntilContext(ctx, func(context.Context) error {
return fn(ctx.Done())
})
}
// WaitUntilContextDoneFinished is the asynchronous form of WaitUntilContextDone.
func WaitUntilContextDoneFinished(ctx context.Context, fn func(<-chan struct{}) error) <-chan error {
return WaitUntilContextFinished(ctx, func(ctx context.Context) error {
return fn(ctx.Done())
})
}
// WaitUntilTimeout is a legacy timeout helper kept for compatibility.
//
// The provided stop channel must be treated as receive-only by callers. New
// code should prefer WaitUntilContext or WaitUntilContextDone.
func WaitUntilTimeout(tm time.Duration, fn func(chan struct{}) error) error {
ctx, cancel := context.WithTimeout(context.Background(), tm)
defer cancel()
err := WaitUntilContextDone(ctx, func(done <-chan struct{}) error {
return fn(bridgeDoneChan(done))
})
if errors.Is(err, context.DeadlineExceeded) {
return ERR_TIMEOUT
}
return err
}
// WaitUntilFinished runs fn asynchronously and returns its eventual result.
func WaitUntilFinished(fn func() error) <-chan error {
return WaitUntilContextFinished(context.Background(), func(ctx context.Context) error {
return fn()
})
}
// WaitUntilTimeoutFinished is the asynchronous form of WaitUntilTimeout.
//
// The provided stop channel must be treated as receive-only by callers. New
// code should prefer WaitUntilContextFinished or WaitUntilContextDoneFinished.
func WaitUntilTimeoutFinished(tm time.Duration, fn func(chan struct{}) error) <-chan error {
result := make(chan error, 1)
go func() {
ctx, cancel := context.WithTimeout(context.Background(), tm)
defer cancel()
err := WaitUntilContextDone(ctx, func(done <-chan struct{}) error {
return fn(bridgeDoneChan(done))
})
if errors.Is(err, context.DeadlineExceeded) {
err = ERR_TIMEOUT
}
result <- err
close(result)
}()
return result
}
func bridgeDoneChan(done <-chan struct{}) chan struct{} {
stop := make(chan struct{})
if done == nil {
return stop
}
go func() {
<-done
close(stop)
}()
return stop
}