Use context.Context and Cancel function

This commit is contained in:
c-bata 2018-06-24 04:39:19 +09:00
parent 536e34532a
commit d7dd5f9548
3 changed files with 41 additions and 40 deletions

@ -2,6 +2,7 @@ package prompt
import ( import (
"bytes" "bytes"
"context"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
@ -29,6 +30,8 @@ type Prompt struct {
keyBindings []KeyBind keyBindings []KeyBind
ASCIICodeBindings []ASCIICodeBind ASCIICodeBindings []ASCIICodeBind
keyBindMode KeyBindMode keyBindMode KeyBindMode
ctx context.Context
cancel context.CancelFunc
} }
// Exec is the struct contains user input context. // Exec is the struct contains user input context.
@ -53,27 +56,23 @@ func (p *Prompt) Run() {
p.renderer.Render(p.buf, p.completion) p.renderer.Render(p.buf, p.completion)
bufCh := make(chan []byte, 128) bufchan := make(chan []byte, 128)
stopReadBufCh := make(chan struct{}) go p.readBuffer(p.ctx, bufchan)
go p.readBuffer(bufCh, stopReadBufCh)
exitCh := make(chan int) exitCh := make(chan int)
winSizeCh := make(chan *WinSize) winchan := make(chan *WinSize)
stopHandleSignalCh := make(chan struct{}) go p.handleSignals(p.ctx, p.cancel, winchan)
go p.handleSignals(exitCh, winSizeCh, stopHandleSignalCh)
for { for {
select { select {
case b := <-bufCh: case b := <-bufchan:
if shouldExit, e := p.feed(b); shouldExit { if shouldExit, e := p.feed(b); shouldExit {
p.renderer.BreakLine(p.buf) p.renderer.BreakLine(p.buf)
stopReadBufCh <- struct{}{} p.cancel()
stopHandleSignalCh <- struct{}{}
return return
} else if e != nil { } else if e != nil {
// Stop goroutine to run readBuffer function // Stop goroutine to run readBuffer function
stopReadBufCh <- struct{}{} p.cancel()
stopHandleSignalCh <- struct{}{}
// Unset raw mode // Unset raw mode
// Reset to Blocking mode because returned EAGAIN when still set non-blocking mode. // Reset to Blocking mode because returned EAGAIN when still set non-blocking mode.
@ -85,13 +84,16 @@ func (p *Prompt) Run() {
// Set raw mode // Set raw mode
p.in.Setup() p.in.Setup()
go p.readBuffer(bufCh, stopReadBufCh) ctx, cancel := context.WithCancel(context.Background())
go p.handleSignals(exitCh, winSizeCh, stopHandleSignalCh) p.ctx = ctx
p.cancel = cancel
go p.readBuffer(p.ctx, bufchan)
go p.handleSignals(p.ctx, p.cancel, winchan)
} else { } else {
p.completion.Update(*p.buf.Document()) p.completion.Update(*p.buf.Document())
p.renderer.Render(p.buf, p.completion) p.renderer.Render(p.buf, p.completion)
} }
case w := <-winSizeCh: case w := <-winchan:
p.renderer.UpdateWinSize(w) p.renderer.UpdateWinSize(w)
p.renderer.Render(p.buf, p.completion) p.renderer.Render(p.buf, p.completion)
case code := <-exitCh: case code := <-exitCh:
@ -233,20 +235,17 @@ func (p *Prompt) Input() string {
defer p.tearDown() defer p.tearDown()
p.renderer.Render(p.buf, p.completion) p.renderer.Render(p.buf, p.completion)
bufCh := make(chan []byte, 128) bufchan := make(chan []byte, 128)
stopReadBufCh := make(chan struct{}) go p.readBuffer(p.ctx, bufchan)
go p.readBuffer(bufCh, stopReadBufCh)
for { for {
select { select {
case b := <-bufCh: case b := <-bufchan:
if shouldExit, e := p.feed(b); shouldExit { if shouldExit, e := p.feed(b); shouldExit {
p.renderer.BreakLine(p.buf) p.renderer.BreakLine(p.buf)
stopReadBufCh <- struct{}{} p.cancel()
return "" return ""
} else if e != nil { } else if e != nil {
// Stop goroutine to run readBuffer function
stopReadBufCh <- struct{}{}
return e.input return e.input
} else { } else {
p.completion.Update(*p.buf.Document()) p.completion.Update(*p.buf.Document())
@ -258,11 +257,11 @@ func (p *Prompt) Input() string {
} }
} }
func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) { func (p *Prompt) readBuffer(ctx context.Context, bufCh chan []byte) {
log.Printf("[INFO] readBuffer start") log.Printf("[INFO] readBuffer start")
for { for {
select { select {
case <-stopCh: case <-ctx.Done():
log.Print("[INFO] stop readBuffer") log.Print("[INFO] stop readBuffer")
return return
default: default:
@ -278,9 +277,14 @@ func (p *Prompt) setUp() {
p.in.Setup() p.in.Setup()
p.renderer.Setup() p.renderer.Setup()
p.renderer.UpdateWinSize(p.in.GetWinSize()) p.renderer.UpdateWinSize(p.in.GetWinSize())
ctx, cancel := context.WithCancel(context.Background())
p.ctx = ctx
p.cancel = cancel
} }
func (p *Prompt) tearDown() { func (p *Prompt) tearDown() {
p.cancel()
p.in.TearDown() p.in.TearDown()
p.renderer.TearDown() p.renderer.TearDown()
} }

@ -3,17 +3,18 @@
package prompt package prompt
import ( import (
"context"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
) )
func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) { func (p *Prompt) handleSignals(ctx context.Context, cancel context.CancelFunc, winSizeCh chan *WinSize) {
in := p.in in := p.in
sigCh := make(chan os.Signal, 1) sigchan := make(chan os.Signal, 1)
signal.Notify( signal.Notify(
sigCh, sigchan,
syscall.SIGINT, syscall.SIGINT,
syscall.SIGTERM, syscall.SIGTERM,
syscall.SIGQUIT, syscall.SIGQUIT,
@ -22,22 +23,22 @@ func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop ch
for { for {
select { select {
case <-stop: case <-ctx.Done():
log.Println("[INFO] stop handleSignals") log.Println("[INFO] stop handleSignals")
return return
case s := <-sigCh: case s := <-sigchan:
switch s { switch s {
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
log.Println("[SIGNAL] Catch SIGINT") log.Println("[SIGNAL] Catch SIGINT")
exitCh <- 0 cancel()
case syscall.SIGTERM: // kill -SIGTERM XXXX case syscall.SIGTERM: // kill -SIGTERM XXXX
log.Println("[SIGNAL] Catch SIGTERM") log.Println("[SIGNAL] Catch SIGTERM")
exitCh <- 1 cancel()
case syscall.SIGQUIT: // kill -SIGQUIT XXXX case syscall.SIGQUIT: // kill -SIGQUIT XXXX
log.Println("[SIGNAL] Catch SIGQUIT") log.Println("[SIGNAL] Catch SIGQUIT")
exitCh <- 0 cancel()
case syscall.SIGWINCH: case syscall.SIGWINCH:
log.Println("[SIGNAL] Catch SIGWINCH") log.Println("[SIGNAL] Catch SIGWINCH")

@ -3,13 +3,14 @@
package prompt package prompt
import ( import (
"context"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
) )
func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) { func (p *Prompt) handleSignals(tx context.Context, cancel context.CancelFunc, winSizeCh chan *WinSize) {
sigCh := make(chan os.Signal, 1) sigCh := make(chan os.Signal, 1)
signal.Notify( signal.Notify(
sigCh, sigCh,
@ -20,23 +21,18 @@ func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop ch
for { for {
select { select {
case <-stop: case <-ctx.Done():
log.Println("[INFO] stop handleSignals")
return return
case s := <-sigCh: case s := <-sigCh:
switch s { switch s {
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
log.Println("[SIGNAL] Catch SIGINT") cancel()
exitCh <- 0
case syscall.SIGTERM: // kill -SIGTERM XXXX case syscall.SIGTERM: // kill -SIGTERM XXXX
log.Println("[SIGNAL] Catch SIGTERM") log.Println("[SIGNAL] Catch SIGTERM")
exitCh <- 1
case syscall.SIGQUIT: // kill -SIGQUIT XXXX case syscall.SIGQUIT: // kill -SIGQUIT XXXX
log.Println("[SIGNAL] Catch SIGQUIT") cancel()
exitCh <- 0
} }
} }
} }