github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/ext/fileio/coro.go (about) 1 package fileio 2 3 import ( 4 "fmt" 5 6 "github.com/ncruces/go-sqlite3/internal/util" 7 ) 8 9 // Adapted from: https://research.swtch.com/coro 10 11 const errCoroCanceled = util.ErrorString("coroutine canceled") 12 13 func coroNew[In, Out any](f func(In, func(Out) In) Out) (resume func(In) (Out, bool), cancel func()) { 14 type msg[T any] struct { 15 panic any 16 val T 17 } 18 19 cin := make(chan msg[In]) 20 cout := make(chan msg[Out]) 21 running := true 22 resume = func(in In) (out Out, ok bool) { 23 if !running { 24 return 25 } 26 cin <- msg[In]{val: in} 27 m := <-cout 28 if m.panic != nil { 29 panic(m.panic) 30 } 31 return m.val, running 32 } 33 cancel = func() { 34 if !running { 35 return 36 } 37 e := fmt.Errorf("%w", errCoroCanceled) 38 cin <- msg[In]{panic: e} 39 m := <-cout 40 if m.panic != nil && m.panic != e { 41 panic(m.panic) 42 } 43 } 44 yield := func(out Out) In { 45 cout <- msg[Out]{val: out} 46 m := <-cin 47 if m.panic != nil { 48 panic(m.panic) 49 } 50 return m.val 51 } 52 go func() { 53 defer func() { 54 if running { 55 running = false 56 cout <- msg[Out]{panic: recover()} 57 } 58 }() 59 var out Out 60 m := <-cin 61 if m.panic == nil { 62 out = f(m.val, yield) 63 } 64 running = false 65 cout <- msg[Out]{val: out} 66 }() 67 return resume, cancel 68 }