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  }