From 2c6429065b63d790508cf9cc82dd7f3db0bbb0b1 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Sun, 16 Nov 2025 06:59:52 +0300 Subject: [PATCH] wait with timeout + correct error processing --- example/golang/main.go | 5 ++-- example/ts/src/index.ts | 2 +- lib/golang/lib.go | 54 +++++++++++++++++++++++++++++------------ 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/example/golang/main.go b/example/golang/main.go index 97208b8..c2381bb 100644 --- a/example/golang/main.go +++ b/example/golang/main.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path" + "time" kittenipc "efprojects.com/kitten-ipc" ) @@ -59,9 +60,9 @@ func main() { if err != nil { 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) } } diff --git a/example/ts/src/index.ts b/example/ts/src/index.ts index 6852d53..7cfcf92 100644 --- a/example/ts/src/index.ts +++ b/example/ts/src/index.ts @@ -20,7 +20,7 @@ async function main() { 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(); } diff --git a/lib/golang/lib.go b/lib/golang/lib.go index 01dbe49..1a97cf9 100644 --- a/lib/golang/lib.go +++ b/lib/golang/lib.go @@ -350,34 +350,46 @@ func (p *ParentIPC) Stop() error { } p.stopRequested.Store(true) 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() } -func (p *ParentIPC) Wait() error { +func (p *ParentIPC) Wait(timeout ...time.Duration) (retErr error) { waitErrCh := make(chan error, 1) + const defaultTimeout = time.Duration(1<<63 - 1) // max duration in go + _timeout := variadicToOption(timeout).OrElse(defaultTimeout) + go func() { waitErrCh <- p.cmd.Wait() }() - var retErr error - select { - case err := <-p.errCh: - retErr = fmt.Errorf("ipc internal error: %w", err) - case err := <-waitErrCh: - if err != nil { - var exitErr *exec.ExitError - if ok := errors.As(err, &exitErr); ok { - if !exitErr.Success() { - ws, ok := exitErr.Sys().(syscall.WaitStatus) - if !(ok && ws.Signaled() && ws.Signal() == syscall.SIGINT && p.stopRequested.Load()) { - retErr = fmt.Errorf("cmd wait: %w", err) +loop: + for { + select { + case err := <-p.errCh: + retErr = mergeErr(retErr, fmt.Errorf("ipc internal error: %w", err)) + break loop + case err := <-waitErrCh: + if err != nil { + var exitErr *exec.ExitError + if ok := errors.As(err, &exitErr); ok { + if !exitErr.Success() { + 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 } + +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]) +}