Global code ovehaul - remove CAS and other, add 15 minute greet check
- Remove CAS, as it no longer works for us - Remove other checks - Implement 15 minute greet timeout check - Fix chat logos - Ditch thirdparty logger, use stdlib log facility wrapper for logging
This commit is contained in:
97
internal/moon/shutdown.go
Normal file
97
internal/moon/shutdown.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package moon
|
||||
|
||||
import (
|
||||
"bunny-bot/internal/elog"
|
||||
"bunny-bot/internal/mylog"
|
||||
"fmt"
|
||||
|
||||
"github.com/akyoto/cache"
|
||||
)
|
||||
|
||||
// closeCaches shuts down every cache sweeper goroutine.
|
||||
//
|
||||
// Safe only once the update listener and the greeting sweeper have stopped:
|
||||
// cache.Close() reassigns the underlying sync.Map, which is a data race against
|
||||
// any concurrent reader. shutdown() enforces that order.
|
||||
func closeCaches() {
|
||||
closeOnce.Do(func() {
|
||||
for _, c := range []*cache.Cache{
|
||||
SquashedMembers,
|
||||
PendingGreetings.cache, GreetAnswers.cache,
|
||||
} {
|
||||
c.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Shutdown performs the ordered teardown, waits for it to finish, and reports
|
||||
// whether every stage of it succeeded.
|
||||
//
|
||||
// main calls it after Telegram returns, for two reasons. The listener returning is
|
||||
// not by itself a finished shutdown — a signal handler may be halfway through the
|
||||
// sequence at that moment, and main returning would end the process underneath it,
|
||||
// skipping stages and closing the log twice. And if the listener stopped for any
|
||||
// other reason, nobody would run the sequence at all.
|
||||
//
|
||||
// It is safe to call from both goroutines: sync.Once runs the body once and blocks
|
||||
// every other caller until that run completes, so both callers see the same result.
|
||||
func Shutdown() error {
|
||||
return shutdown()
|
||||
}
|
||||
|
||||
// shutdown stops everything in dependency order and waits for each stage.
|
||||
//
|
||||
// The order is not cosmetic. Every stage waits for the previous one to have
|
||||
// finished, so no goroutine is ever using a resource that the next stage is
|
||||
// closing:
|
||||
//
|
||||
// 1. the Telegram update listener — nothing else may add work;
|
||||
// 2. the greeting sweeper — the last periodic user of the caches;
|
||||
// 3. the caches — cache.Close() both signals the sweeper goroutines and
|
||||
// reassigns the underlying sync.Map, so it must run with nothing else
|
||||
// touching them;
|
||||
// 4. chat logging, then 5. the main log — so the last log lines, which
|
||||
// describe stages 1 to 3, still have somewhere to go.
|
||||
//
|
||||
// Stages 1 and 2 share one stop signal and one WaitGroup, so a worker that has
|
||||
// not started costs nothing and a worker that starts late is refused rather than
|
||||
// racing the wait.
|
||||
//
|
||||
// The main log's own failure is reported ahead of the chat log's, because it happened
|
||||
// last: a chat-log failure may no longer have been writable either.
|
||||
func shutdown() error {
|
||||
var result error
|
||||
|
||||
shutdownOnce.Do(func() {
|
||||
// Refuse new workers before signalling, so nothing can register between
|
||||
// the signal and the wait below.
|
||||
workersMu.Lock()
|
||||
stopping = true
|
||||
workersMu.Unlock()
|
||||
|
||||
// Ask every loop to return, then wait for all of them. Nothing may be
|
||||
// using a cache, a log or the API once the next stages start closing.
|
||||
close(stopRequested)
|
||||
workers.Wait()
|
||||
|
||||
closeCaches()
|
||||
|
||||
// Chat logging first: it writes to the main log, so it must be closed
|
||||
// before the main log is.
|
||||
chatErr := elog.Close()
|
||||
|
||||
// Since we shutting down, we have noting to do with log sync.
|
||||
logErr := mylog.Close()
|
||||
|
||||
switch {
|
||||
case logErr != nil:
|
||||
result = fmt.Errorf("main log: %w", logErr)
|
||||
case chatErr != nil:
|
||||
result = fmt.Errorf("chat log: %w", chatErr)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||
Reference in New Issue
Block a user