Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

main: move InterruptHandler to util #5872

Merged
merged 1 commit into from
Dec 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 2 additions & 69 deletions cmd/ipfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@ import (
"context"
"errors"
"fmt"
"io"
"math/rand"
"os"
"os/signal"
"path/filepath"
"runtime/pprof"
"strings"
"sync"
"syscall"
"time"

util "github.com/ipfs/go-ipfs/cmd/ipfs/util"
oldcmds "github.com/ipfs/go-ipfs/commands"
core "github.com/ipfs/go-ipfs/core"
corecmds "github.com/ipfs/go-ipfs/core/commands"
Expand Down Expand Up @@ -106,7 +103,7 @@ func mainRet() int {
}
defer stopFunc() // to be executed as late as possible

intrh, ctx := setupInterruptHandler(ctx)
intrh, ctx := util.SetupInterruptHandler(ctx)
defer intrh.Close()

// Handle `ipfs version` or `ipfs help`
Expand Down Expand Up @@ -355,70 +352,6 @@ func writeHeapProfileToFile() error {
return pprof.WriteHeapProfile(mprof)
}

// IntrHandler helps set up an interrupt handler that can
// be cleanly shut down through the io.Closer interface.
type IntrHandler struct {
sig chan os.Signal
wg sync.WaitGroup
}

func NewIntrHandler() *IntrHandler {
ih := &IntrHandler{}
ih.sig = make(chan os.Signal, 1)
return ih
}

func (ih *IntrHandler) Close() error {
close(ih.sig)
ih.wg.Wait()
return nil
}

// Handle starts handling the given signals, and will call the handler
// callback function each time a signal is catched. The function is passed
// the number of times the handler has been triggered in total, as
// well as the handler itself, so that the handling logic can use the
// handler's wait group to ensure clean shutdown when Close() is called.
func (ih *IntrHandler) Handle(handler func(count int, ih *IntrHandler), sigs ...os.Signal) {
signal.Notify(ih.sig, sigs...)
ih.wg.Add(1)
go func() {
defer ih.wg.Done()
count := 0
for range ih.sig {
count++
handler(count, ih)
}
signal.Stop(ih.sig)
}()
}

func setupInterruptHandler(ctx context.Context) (io.Closer, context.Context) {
intrh := NewIntrHandler()
ctx, cancelFunc := context.WithCancel(ctx)

handlerFunc := func(count int, ih *IntrHandler) {
switch count {
case 1:
fmt.Println() // Prevent un-terminated ^C character in terminal

ih.wg.Add(1)
go func() {
defer ih.wg.Done()
cancelFunc()
}()

default:
fmt.Println("Received another interrupt before graceful shutdown, terminating...")
os.Exit(-1)
}
}

intrh.Handle(handlerFunc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)

return intrh, ctx
}

func profileIfEnabled() (func(), error) {
// FIXME this is a temporary hack so profiling of asynchronous operations
// works as intended.
Expand Down
77 changes: 77 additions & 0 deletions cmd/ipfs/util/signal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// +build !wasm

package util

import (
"context"
"fmt"
"io"
"os"
"os/signal"
"sync"
"syscall"
)

// IntrHandler helps set up an interrupt handler that can
// be cleanly shut down through the io.Closer interface.
type IntrHandler struct {
sig chan os.Signal
wg sync.WaitGroup
}

func NewIntrHandler() *IntrHandler {
ih := &IntrHandler{}
ih.sig = make(chan os.Signal, 1)
return ih
}

func (ih *IntrHandler) Close() error {
close(ih.sig)
ih.wg.Wait()
return nil
}

// Handle starts handling the given signals, and will call the handler
// callback function each time a signal is catched. The function is passed
// the number of times the handler has been triggered in total, as
// well as the handler itself, so that the handling logic can use the
// handler's wait group to ensure clean shutdown when Close() is called.
func (ih *IntrHandler) Handle(handler func(count int, ih *IntrHandler), sigs ...os.Signal) {
signal.Notify(ih.sig, sigs...)
ih.wg.Add(1)
go func() {
defer ih.wg.Done()
count := 0
for range ih.sig {
count++
handler(count, ih)
}
signal.Stop(ih.sig)
}()
}

func SetupInterruptHandler(ctx context.Context) (io.Closer, context.Context) {
intrh := NewIntrHandler()
ctx, cancelFunc := context.WithCancel(ctx)

handlerFunc := func(count int, ih *IntrHandler) {
switch count {
case 1:
fmt.Println() // Prevent un-terminated ^C character in terminal

ih.wg.Add(1)
go func() {
defer ih.wg.Done()
cancelFunc()
}()

default:
fmt.Println("Received another interrupt before graceful shutdown, terminating...")
os.Exit(-1)
}
}

intrh.Handle(handlerFunc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)

return intrh, ctx
}
18 changes: 18 additions & 0 deletions cmd/ipfs/util/signal_wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package util

import (
"context"
"io"
)

type ctxCloser context.CancelFunc

func (c ctxCloser) Close() error {
c()
return nil
}

func SetupInterruptHandler(ctx context.Context) (io.Closer, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return ctxCloser(cancel), ctx
}