wait with timeout + correct error processing

This commit is contained in:
Egor Aristov 2025-11-16 06:59:52 +03:00
parent 5b52490495
commit 2c6429065b
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
3 changed files with 42 additions and 19 deletions

View File

@ -6,6 +6,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"time"
kittenipc "efprojects.com/kitten-ipc" kittenipc "efprojects.com/kitten-ipc"
) )
@ -59,9 +60,9 @@ func main() {
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
log.Printf("remote call result = %v", res) log.Printf("remote call result from ts = %v", res)
if err := ipc.Wait(); err != nil { if err := ipc.Wait(1 * time.Second); err != nil {
log.Panic(err) log.Panic(err)
} }
} }

View File

@ -20,7 +20,7 @@ async function main() {
await ipc.start(); await ipc.start();
console.log(`remote call result = ${await remoteApi.Div(10, 2)}`); console.log(`remote call result from go = ${await remoteApi.Div(10, 2)}`);
await ipc.wait(); await ipc.wait();
} }

View File

@ -350,34 +350,46 @@ func (p *ParentIPC) Stop() error {
} }
p.stopRequested.Store(true) p.stopRequested.Store(true)
if err := p.cmd.Process.Signal(syscall.SIGINT); err != nil { if err := p.cmd.Process.Signal(syscall.SIGINT); err != nil {
return fmt.Errorf("send SIGTERM: %w", err) return fmt.Errorf("send SIGINT: %w", err)
} }
return p.Wait() return p.Wait()
} }
func (p *ParentIPC) Wait() error { func (p *ParentIPC) Wait(timeout ...time.Duration) (retErr error) {
waitErrCh := make(chan error, 1) waitErrCh := make(chan error, 1)
const defaultTimeout = time.Duration(1<<63 - 1) // max duration in go
_timeout := variadicToOption(timeout).OrElse(defaultTimeout)
go func() { go func() {
waitErrCh <- p.cmd.Wait() waitErrCh <- p.cmd.Wait()
}() }()
var retErr error loop:
select { for {
case err := <-p.errCh: select {
retErr = fmt.Errorf("ipc internal error: %w", err) case err := <-p.errCh:
case err := <-waitErrCh: retErr = mergeErr(retErr, fmt.Errorf("ipc internal error: %w", err))
if err != nil { break loop
var exitErr *exec.ExitError case err := <-waitErrCh:
if ok := errors.As(err, &exitErr); ok { if err != nil {
if !exitErr.Success() { var exitErr *exec.ExitError
ws, ok := exitErr.Sys().(syscall.WaitStatus) if ok := errors.As(err, &exitErr); ok {
if !(ok && ws.Signaled() && ws.Signal() == syscall.SIGINT && p.stopRequested.Load()) { if !exitErr.Success() {
retErr = fmt.Errorf("cmd wait: %w", err) ws, ok := exitErr.Sys().(syscall.WaitStatus)
if !(ok && ws.Signaled() && ws.Signal() == syscall.SIGINT && p.stopRequested.Load()) {
retErr = mergeErr(retErr, fmt.Errorf("cmd wait: %w", err))
}
} }
} else {
retErr = mergeErr(retErr, fmt.Errorf("cmd wait: %w", err))
} }
} else { }
retErr = fmt.Errorf("cmd wait: %w", err) break loop
case <-time.After(_timeout):
p.stopRequested.Store(true)
if err := p.cmd.Process.Signal(syscall.SIGINT); err != nil {
retErr = mergeErr(retErr, fmt.Errorf("send SIGINT: %w", err))
} }
} }
} }
@ -450,3 +462,13 @@ func mapTypeNames(types []any) map[string]any {
} }
return result return result
} }
func variadicToOption[T any](variadic []T) mo.Option[T] {
if len(variadic) >= 2 {
panic("variadic param count must be 0 or 1")
}
if len(variadic) == 0 {
return mo.None[T]()
}
return mo.Some(variadic[0])
}