code.gitea.io/gitea@v1.22.3/modules/graceful/manager_common.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package graceful
     5  
     6  import (
     7  	"context"
     8  	"runtime/pprof"
     9  	"sync"
    10  	"time"
    11  )
    12  
    13  // FIXME: it seems that there is a bug when using systemd Type=notify: the "Install Page" (INSTALL_LOCK=false) doesn't notify properly.
    14  // At the moment, no idea whether it also affects Windows Service, or whether it's a regression bug. It needs to be investigated later.
    15  
    16  type systemdNotifyMsg string
    17  
    18  const (
    19  	readyMsg     systemdNotifyMsg = "READY=1"
    20  	stoppingMsg  systemdNotifyMsg = "STOPPING=1"
    21  	reloadingMsg systemdNotifyMsg = "RELOADING=1"
    22  	watchdogMsg  systemdNotifyMsg = "WATCHDOG=1"
    23  )
    24  
    25  func statusMsg(msg string) systemdNotifyMsg {
    26  	return systemdNotifyMsg("STATUS=" + msg)
    27  }
    28  
    29  // Manager manages the graceful shutdown process
    30  type Manager struct {
    31  	ctx                    context.Context
    32  	isChild                bool
    33  	forked                 bool
    34  	lock                   sync.RWMutex
    35  	state                  state
    36  	shutdownCtx            context.Context
    37  	hammerCtx              context.Context
    38  	terminateCtx           context.Context
    39  	managerCtx             context.Context
    40  	shutdownCtxCancel      context.CancelFunc
    41  	hammerCtxCancel        context.CancelFunc
    42  	terminateCtxCancel     context.CancelFunc
    43  	managerCtxCancel       context.CancelFunc
    44  	runningServerWaitGroup sync.WaitGroup
    45  	terminateWaitGroup     sync.WaitGroup
    46  	createServerCond       sync.Cond
    47  	createdServer          int
    48  	shutdownRequested      chan struct{}
    49  
    50  	toRunAtShutdown  []func()
    51  	toRunAtTerminate []func()
    52  }
    53  
    54  func newGracefulManager(ctx context.Context) *Manager {
    55  	manager := &Manager{ctx: ctx, shutdownRequested: make(chan struct{})}
    56  	manager.createServerCond.L = &sync.Mutex{}
    57  	manager.prepare(ctx)
    58  	manager.start()
    59  	return manager
    60  }
    61  
    62  func (g *Manager) prepare(ctx context.Context) {
    63  	g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx)
    64  	g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx)
    65  	g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx)
    66  	g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx)
    67  
    68  	g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate"))
    69  	g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown"))
    70  	g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer"))
    71  	g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager"))
    72  
    73  	if !g.setStateTransition(stateInit, stateRunning) {
    74  		panic("invalid graceful manager state: transition from init to running failed")
    75  	}
    76  }
    77  
    78  // DoImmediateHammer causes an immediate hammer
    79  func (g *Manager) DoImmediateHammer() {
    80  	g.notify(statusMsg("Sending immediate hammer"))
    81  	g.doHammerTime(0 * time.Second)
    82  }
    83  
    84  // DoGracefulShutdown causes a graceful shutdown
    85  func (g *Manager) DoGracefulShutdown() {
    86  	g.lock.Lock()
    87  	select {
    88  	case <-g.shutdownRequested:
    89  	default:
    90  		close(g.shutdownRequested)
    91  	}
    92  	forked := g.forked
    93  	g.lock.Unlock()
    94  
    95  	if !forked {
    96  		g.notify(stoppingMsg)
    97  	} else {
    98  		g.notify(statusMsg("Shutting down after fork"))
    99  	}
   100  	g.doShutdown()
   101  }
   102  
   103  // RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die.
   104  // Any call to RegisterServer must be matched by a call to ServerDone
   105  func (g *Manager) RegisterServer() {
   106  	KillParent()
   107  	g.runningServerWaitGroup.Add(1)
   108  }