decred.org/dcrdex@v1.0.5/server/cmd/dcrdex/signal.go (about)

     1  // Copyright (c) 2018-2020, The Decred developers
     2  // Copyright (c) 2013-2014, The btcsuite developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package main
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"os"
    12  	"os/signal"
    13  )
    14  
    15  // shutdownRequested checks if the Done channel of the given context has been
    16  // closed. This could indicate cancellation, expiration, or deadline expiry. But
    17  // when called for the context provided by withShutdownCancel, it indicates if
    18  // shutdown has been requested (i.e. via os.Interrupt or requestShutdown).
    19  func shutdownRequested(ctx context.Context) bool {
    20  	select {
    21  	case <-ctx.Done():
    22  		return true
    23  	default:
    24  		return false
    25  	}
    26  }
    27  
    28  var (
    29  	// shutdownRequest is used to initiate shutdown from one of the subsystems
    30  	// using the same code paths as when an interrupt signal is received.
    31  	shutdownRequest = make(chan struct{})
    32  	// shutdownSignal is closed whenever shutdown is invoked through an
    33  	// interrupt signal or via requestShutdown. Any contexts created using
    34  	// withShutdownChannel are canceled when this is closed.
    35  	shutdownSignal = make(chan struct{})
    36  )
    37  
    38  // withShutdownCancel creates a copy of a context that is canceled whenever
    39  // shutdown is invoked through an interrupt signal or from an JSON-RPC stop
    40  // request.
    41  func withShutdownCancel(ctx context.Context) context.Context {
    42  	ctx, cancel := context.WithCancel(ctx)
    43  	go func() {
    44  		<-shutdownSignal
    45  		cancel()
    46  	}()
    47  	return ctx
    48  }
    49  
    50  // requestShutdown signals for starting the clean shutdown of the process
    51  // through an internal component.
    52  func requestShutdown() {
    53  	shutdownRequest <- struct{}{}
    54  }
    55  
    56  // shutdownListener listens for shutdown requests and cancels all contexts
    57  // created from withShutdownCancel. This function never returns and is intended
    58  // to be spawned in a new goroutine.
    59  func shutdownListener() {
    60  	interruptChannel := make(chan os.Signal, 1)
    61  	signal.Notify(interruptChannel, os.Interrupt)
    62  
    63  	// Listen for the initial shutdown signal.
    64  	select {
    65  	case sig := <-interruptChannel:
    66  		fmt.Printf("Received signal (%s). Shutting down...\n", sig)
    67  	case <-shutdownRequest:
    68  		fmt.Println("Shutdown requested. Shutting down...")
    69  	}
    70  
    71  	// Cancel all contexts created from withShutdownCancel.
    72  	close(shutdownSignal)
    73  
    74  	// Listen for any more shutdown signals and log that shutdown has already
    75  	// been signaled.
    76  	for {
    77  		select {
    78  		case <-interruptChannel:
    79  		case <-shutdownRequest:
    80  		}
    81  		log.Info("Shutdown signaled. Already shutting down...")
    82  	}
    83  }