upd(vendor): upgrade sentimensrg ctx package

This commit is contained in:
Tit Petric
2018-09-02 20:13:57 +02:00
parent 2a55cc54c5
commit f859f9a044
42 changed files with 8142 additions and 123 deletions
+14
View File
@@ -0,0 +1,14 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
@@ -1,39 +1,6 @@
# DEPRECATION WARNING
MIT License
`sigctx` is now part of [SentimensRG/ctx](https://github.com/SentimensRG/ctx).
This repository will no longer receive updates.
# sigctx
Go contexts for graceful shutdown
## installation
```bash
go get -u github.com/SentimensRG/sigctx
```
## usage
```go
ctx := sigctx.New() // returns a regular context.Context
// With this simple pattern, your goroutines are guaranteed to terminate correctly
ctx, cancel := context.WithCancel(ctx)
go someBlockingFunction(ctx)
defer cancel()
<-ctx.Done() // will unblock on SIGINT and SIGTERM
```
## RFC
If you find this useful, please let me know: <l.thibault@sentimens.com>
## License
The MIT License
Copyright (c) 2017 Sentimens Research Group, LLC
Copyright (c) 2017 Sentimens Research Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -42,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+63
View File
@@ -0,0 +1,63 @@
# ctx
Composable utilities for Go contexts.
[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/SentimensRG/ctx)
[![Go Report Card](https://goreportcard.com/badge/github.com/SentimensRG/ctx?style=flat-square)](https://goreportcard.com/report/github.com/SentimensRG/ctx)
## Installation
```bash
go get -u github.com/SentimensRG/ctx
```
## Overview
The `ctx` package provides utilites for working with data structures satisfying
the `ctx.Doner` interface, most notably `context.Context`:
```go
type Doner interface {
Done() <-chan struct{}
}
```
The functions in `ctx` are appropriate for operations that do not preserve the
values in a context, e.g.: joining several contexts together.
## Subpackages
- [sigctx](https://github.com/SentimensRG/ctx/tree/master/sigctx): contexts for graceful shutdown
- [refctx](https://github.com/SentimensRG/ctx/tree/master/refctx): contexts linked to a reference-counter
- [mergectx](https://github.com/SentimensRG/ctx/tree/master/mergectx): utilities for merging `context.Context` instances while preserving values, errors and deadlines.
## RFC
If you find this useful please let me know: <l.thibault@sentimens.com>
Seriously, even if you just used it in your weekend project, I'd like to hear
about it :)
## License
The MIT License
Copyright (c) 2017 Sentimens Research Group, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+184
View File
@@ -0,0 +1,184 @@
package ctx
import (
"context"
"sync"
"time"
)
// Binder is the interface that wraps the basic Bind method.
// Bind executes logic until the Doner completes. Implementations of Bind must
// not return until the Doner has completed.
type Binder interface {
Bind(Doner)
}
// BindFunc is an adapter to allow the use of ordinary functions as Binders.
type BindFunc func(Doner)
// Bind executes logic until the Doner completes. It satisfies the Binder
// interface.
func (f BindFunc) Bind(d Doner) { f(d) }
// Doner can block until something is done
type Doner interface {
Done() <-chan struct{}
}
// C is a basic implementation of Doner
type C <-chan struct{}
// Background is the ctx analog to context.Background(). It never fires.
func Background() C {
return nil
}
// Done returns a channel that receives when an action is complete
func (dc C) Done() <-chan struct{} { return dc }
type ctx struct {
Doner
}
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
func (ctx) Deadline() (deadline time.Time, ok bool) {
return
}
func (c ctx) Err() error {
select {
case <-c.Done():
return context.Canceled
default:
return nil
}
}
func (c ctx) Value(interface{}) (v interface{}) {
return
}
// AsContext creates a context that fires when the Doner fires
func AsContext(d Doner) context.Context {
return ctx{d}
}
// After time time has elapsed, the Doner fires
func After(d time.Duration) C {
ch := make(chan struct{})
go func() {
<-time.After(d)
close(ch)
}()
return ch
}
// WithCancel returns a new Doner that can be cancelled via the associated
// function
func WithCancel(d Doner) (C, func()) {
var closer sync.Once
cq := make(chan struct{})
cancel := func() { closer.Do(func() { close(cq) }) }
go func() {
select {
case <-cq:
case <-d.Done():
cancel()
}
}()
return cq, cancel
}
// Tick returns a <-chan whose range ends when the underlying context cancels
func Tick(d Doner) <-chan struct{} {
c := make(chan struct{})
cq := d.Done()
go func() {
for {
select {
case <-cq:
close(c)
return
default:
select {
case c <- struct{}{}:
case <-cq:
}
}
}
}()
return c
}
// Defer guarantees that a function will be called after a context has cancelled
func Defer(d Doner, cb func()) {
go func() {
<-d.Done()
cb()
}()
}
// Link returns a channel that fires if ANY of the constituent Doners have fired
func Link(doners ...Doner) C {
c := make(chan struct{})
cancel := func() { close(c) }
var once sync.Once
for _, d := range doners {
Defer(d, func() { once.Do(cancel) })
}
return c
}
// Join returns a channel that receives when all constituent Doners have fired
func Join(doners ...Doner) C {
var wg sync.WaitGroup
wg.Add(len(doners))
for _, d := range doners {
Defer(d, wg.Done)
}
cq := make(chan struct{})
go func() {
wg.Wait()
close(cq)
}()
return cq
}
// FTick calls a function in a loop until the Doner has fired
func FTick(d Doner, f func()) {
for range Tick(d) {
f()
}
}
// FTickInterval calls a function repeatedly at a given internval, until the Doner
// has fired. Note that FTickInterval ignores the time spent executing a function,
// and instead guarantees an interval of `t` between of return of the previous
// function call and the invocation of the next function call.
func FTickInterval(d Doner, t time.Duration, f func()) {
for {
select {
case <-d.Done():
return
case <-time.After(t):
f()
}
}
}
// FDone returns a doner that fires when the function returns or panics
func FDone(f func()) C {
ch := make(chan struct{})
go func() {
defer close(ch)
f()
}()
return ch
}
+63
View File
@@ -0,0 +1,63 @@
# sigctx
Package `sigctx` provides contexts for graceful shutdown.
The `sigctx` package provides a context that terminates when it receives a
SIGINT or SIGTERM. This provides a convenient mechanism for triggering
graceful application shutdown.
`sigctx.New` returns a `ctx.C`, which implements the ubiquitous `ctx.Doner`
interface. It fires when either SIGINT or SIGTERM is caught.
## Examples
```go
import (
"log"
"github.com/SentimensRG/ctx/sigctx"
)
func main() {
ctx := sigctx.New() // returns a regular context.Context
<-ctx.Done() // will unblock on SIGINT and SIGTERM
log.Println("exiting.")
}
```
`sigctx.Tick` can be used to react to streams of signals. For example, you can
implement a graceful shutdown attempt, followed by a forced shutdown.
```go
import (
"log"
"github.com/SentimensRG/ctx/sigctx"
"github.com/SentimensRG/ctx"
)
func main() {
t := sigctx.Tick()
d, cancel := ctx.WithCancel(ctx.Background())
go func() {
defer cancel()
go func() {
// business logic goes here
}()
<-t
log.Println("attempting graceful shutdown - press Ctrl + c again to force quit")
go func() {
defer cancel()
// cleanup logic goes here
}()
<-t
log.Println("forcing close")
}()
<-d.Done()
}
```
+60
View File
@@ -0,0 +1,60 @@
// Package sigctx provides a context that expires when a SIGINT or SIGTERM is
// received.
package sigctx
import (
"os"
"os/signal"
"sync"
"syscall"
"github.com/SentimensRG/ctx"
)
var (
c ctx.C
sigCh chan os.Signal
initC, initSig sync.Once
)
func initSigCh() {
sigCh = make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
}
// New signal-bound ctx.C that terminates when either SIGINT or SIGTERM
// is caught.
func New() ctx.C {
initC.Do(func() {
initSig.Do(initSigCh)
dc := make(chan struct{})
c = dc
go func() {
select {
case <-sigCh:
close(dc)
case <-c.Done():
}
}()
})
return c
}
// Tick returns a channel that recvs each time a either SIGINT or SIGTERM are
// caught.
func Tick() <-chan struct{} {
initSig.Do(initSigCh)
dc := make(chan struct{})
go func() {
for {
<-sigCh
dc <- struct{}{}
}
}()
return dc
}
-31
View File
@@ -1,31 +0,0 @@
package sigctx
import (
"context"
"os"
"os/signal"
"syscall"
)
var ctx context.Context
func init() {
var cancel func()
ctx, cancel = context.WithCancel(context.Background())
go func() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
select {
case <-ch:
cancel()
case <-ctx.Done():
}
}()
}
// New signal-bound context
func New() context.Context {
return ctx
}
+7 -1
View File
@@ -4,6 +4,8 @@ go:
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
install:
- go get -u golang.org/x/tools/cmd/goimports
@@ -15,4 +17,8 @@ script:
- golint ./...
- go test ./...
- >
goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :
go_version=$(go version);
if [ ${go_version:13:4} = "1.11" ]; then
goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :;
fi
+1 -1
View File
@@ -232,7 +232,7 @@ type Router interface {
}
// Routes interface adds two methods for router traversal, which is also
// used by the `docgen` subpackage to generation documentation for Routers.
// used by the github.com/go-chi/docgen package to generate documentation for Routers.
type Routes interface {
// Routes returns the routing tree in an easily traversable structure.
Routes() []Route
+7 -7
View File
@@ -84,13 +84,13 @@ func (x *Context) URLParam(key string) string {
//
// For example,
//
// func Instrument(next http.Handler) http.Handler {
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// next.ServeHTTP(w, r)
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
// measure(w, r, routePattern)
// })
// }
// func Instrument(next http.Handler) http.Handler {
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// next.ServeHTTP(w, r)
// routePattern := chi.RouteContext(r.Context()).RoutePattern()
// measure(w, r, routePattern)
// })
// }
func (x *Context) RoutePattern() string {
routePattern := strings.Join(x.RoutePatterns, "")
return strings.Replace(routePattern, "/*/", "/", -1)
+22 -18
View File
@@ -16,7 +16,7 @@ var (
// DefaultLogger is called by the Logger middleware handler to log each request.
// Its made a package-level variable so that it can be reconfigured for custom
// logging configurations.
DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags)})
DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false})
)
// Logger is a middleware that logs the start and end of each request, along
@@ -81,29 +81,32 @@ type LoggerInterface interface {
// DefaultLogFormatter is a simple logger that implements a LogFormatter.
type DefaultLogFormatter struct {
Logger LoggerInterface
Logger LoggerInterface
NoColor bool
}
// NewLogEntry creates a new LogEntry for the request.
func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry {
useColor := !l.NoColor
entry := &defaultLogEntry{
DefaultLogFormatter: l,
request: r,
buf: &bytes.Buffer{},
useColor: useColor,
}
reqID := GetReqID(r.Context())
if reqID != "" {
cW(entry.buf, nYellow, "[%s] ", reqID)
cW(entry.buf, useColor, nYellow, "[%s] ", reqID)
}
cW(entry.buf, nCyan, "\"")
cW(entry.buf, bMagenta, "%s ", r.Method)
cW(entry.buf, useColor, nCyan, "\"")
cW(entry.buf, useColor, bMagenta, "%s ", r.Method)
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
cW(entry.buf, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto)
cW(entry.buf, useColor, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto)
entry.buf.WriteString("from ")
entry.buf.WriteString(r.RemoteAddr)
@@ -114,33 +117,34 @@ func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry {
type defaultLogEntry struct {
*DefaultLogFormatter
request *http.Request
buf *bytes.Buffer
request *http.Request
buf *bytes.Buffer
useColor bool
}
func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) {
switch {
case status < 200:
cW(l.buf, bBlue, "%03d", status)
cW(l.buf, l.useColor, bBlue, "%03d", status)
case status < 300:
cW(l.buf, bGreen, "%03d", status)
cW(l.buf, l.useColor, bGreen, "%03d", status)
case status < 400:
cW(l.buf, bCyan, "%03d", status)
cW(l.buf, l.useColor, bCyan, "%03d", status)
case status < 500:
cW(l.buf, bYellow, "%03d", status)
cW(l.buf, l.useColor, bYellow, "%03d", status)
default:
cW(l.buf, bRed, "%03d", status)
cW(l.buf, l.useColor, bRed, "%03d", status)
}
cW(l.buf, bBlue, " %dB", bytes)
cW(l.buf, l.useColor, bBlue, " %dB", bytes)
l.buf.WriteString(" in ")
if elapsed < 500*time.Millisecond {
cW(l.buf, nGreen, "%s", elapsed)
cW(l.buf, l.useColor, nGreen, "%s", elapsed)
} else if elapsed < 5*time.Second {
cW(l.buf, nYellow, "%s", elapsed)
cW(l.buf, l.useColor, nYellow, "%s", elapsed)
} else {
cW(l.buf, nRed, "%s", elapsed)
cW(l.buf, l.useColor, nRed, "%s", elapsed)
}
l.Logger.Print(l.buf.String())
@@ -148,7 +152,7 @@ func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) {
func (l *defaultLogEntry) Panic(v interface{}, stack []byte) {
panicEntry := l.NewLogEntry(l.request).(*defaultLogEntry)
cW(panicEntry.buf, bRed, "panic: %+v", v)
cW(panicEntry.buf, l.useColor, bRed, "panic: %+v", v)
l.Logger.Print(panicEntry.buf.String())
l.Logger.Print(string(stack))
}
+3 -3
View File
@@ -52,12 +52,12 @@ func init() {
}
// colorWrite
func cW(w io.Writer, color []byte, s string, args ...interface{}) {
if isTTY {
func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) {
if isTTY && useColor {
w.Write(color)
}
fmt.Fprintf(w, s, args...)
if isTTY {
if isTTY && useColor {
w.Write(reset)
}
}
+6
View File
@@ -83,6 +83,8 @@ type flushWriter struct {
}
func (f *flushWriter) Flush() {
f.wroteHeader = true
fl := f.basicWriter.ResponseWriter.(http.Flusher)
fl.Flush()
}
@@ -102,6 +104,8 @@ func (f *httpFancyWriter) CloseNotify() <-chan bool {
return cn.CloseNotify()
}
func (f *httpFancyWriter) Flush() {
f.wroteHeader = true
fl := f.basicWriter.ResponseWriter.(http.Flusher)
fl.Flush()
}
@@ -140,6 +144,8 @@ func (f *http2FancyWriter) CloseNotify() <-chan bool {
return cn.CloseNotify()
}
func (f *http2FancyWriter) Flush() {
f.wroteHeader = true
fl := f.basicWriter.ResponseWriter.(http.Flusher)
fl.Flush()
}