finished deadline pattern

This commit is contained in:
eamon 2020-06-05 17:36:30 +08:00
parent 9bb39b3fdf
commit a4a57a5556
4 changed files with 71 additions and 54 deletions

26
.vscode/settings.json vendored
View File

@ -1,15 +1,15 @@
{ {
"window.autoDetectColorScheme": false, "window.autoDetectColorScheme": false,
"editor.tabSize": 2, "editor.tabSize": 2,
"editor.semanticTokenColorCustomizations":{ "editor.semanticTokenColorCustomizations": {
"enabled": true, // enable semantic highlighting for all themes "enabled": true, // enable semantic highlighting for all themes
"rules": { "rules": {
"typeParameter": "#352cea", "typeParameter": "#352cea",
"type": { "type": {
"foreground": "#00aa00" "foreground": "#00aa00"
}, },
"parameter":"#4c70e7", "parameter": "#4c70e7",
} }
}, },
"extensions.showRecommendationsOnlyOnDemand": true, "extensions.showRecommendationsOnlyOnDemand": true,
"files.trimTrailingWhitespace": true, "files.trimTrailingWhitespace": true,
@ -47,9 +47,9 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.organizeImports": true "source.organizeImports": true
}, },
"fileheader.Author":"Edward", "fileheader.Author": "Edward",
"fileheader.tpl": "/*\r\n * @Description: https://github.com/crazybber\r\n * @Author: {author}\r\n * @Date: {createTime}\r\n * @Last Modified by: {lastModifiedBy}\r\n * @Last Modified time: {updateTime}\r\n */\r\n\r\n", "fileheader.tpl": "/*\r\n * @Description: https://github.com/crazybber\r\n * @Author: {author}\r\n * @Date: {createTime}\r\n * @Last Modified by: {lastModifiedBy}\r\n * @Last Modified time: {updateTime}\r\n */\r\n\r\n",
"fileheader.LastModifiedBy": "Edward", "fileheader.LastModifiedBy": "Edward",
"peacock.color": "#5e7959" "peacock.color": "#5e7959"
} }
//https://vscode.readthedocs.io/en/latest/getstarted/settings/ //https://vscode.readthedocs.io/en/latest/getstarted/settings/

View File

@ -2,4 +2,6 @@
do a thing ,until the deadline time point do a thing ,until the deadline time point
as known as timeout pattern
which act like time.after(), but time.after() only do once. which act like time.after(), but time.after() only do once.

View File

@ -3,10 +3,10 @@
* @Author: Edward * @Author: Edward
* @Date: 2020-06-05 12:43:39 * @Date: 2020-06-05 12:43:39
* @Last Modified by: Edward * @Last Modified by: Edward
* @Last Modified time: 2020-06-05 12:56:40 * @Last Modified time: 2020-06-05 17:34:37
*/ */
// Package deadline implements the deadline (also known as "timeout") resiliency pattern for Go. // Package deadline implements deadline (also known as "timeout") resiliency pattern for Go.
package deadline package deadline
import ( import (
@ -14,38 +14,42 @@ import (
"time" "time"
) )
// ErrTimedOut is the error returned from Run when the deadline expires. // ErrTimedOut is the error returned from Run when the Worker expires.
var ErrTimedOut = errors.New("timed out waiting for function to finish") var ErrTimedOut = errors.New("timed out waiting for function to finish")
// Deadline implements the deadline/timeout resiliency pattern. // Worker implements the Deadline/timeout resiliency pattern.
type Deadline struct { // worker do the target job
type Worker struct {
timeout time.Duration timeout time.Duration
action string action string
} }
// New constructs a new Deadline with the given timeout. // New create a new Worker with the given timeout.and tile
func New(timeout time.Duration, sometile string) *Deadline { func New(timeout time.Duration, someActionTitle string) *Worker {
return &Deadline{ return &Worker{
timeout: timeout, timeout: timeout,
action: someActionTitle,
} }
} }
// Run runs the given function, passing it a stopper channel. If the deadline passes before // Run runs the given function, passing it a stopper channel. If the Worker passes before
// the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper // the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper
// channel so that the work function can attempt to exit gracefully. It does not (and cannot) // channel so that the work function can attempt to exit gracefully. It does not (and cannot)
// simply kill the running function, so if it doesn't respect the stopper channel then it may // simply kill the running function, so if it doesn't respect the stopper channel then it may
// keep running after the deadline passes. If the function finishes before the deadline, then // keep running after the Worker passes. If the function finishes before the Worker, then
// the return value of the function is returned from Run. // the return value of the function is returned from Run.
func (d *Deadline) Run(work func(<-chan struct{}) error) error { func (d *Worker) Run(work func(stopperSignal chan error) error) error {
result := make(chan error)
stopper := make(chan struct{}) result := make(chan error)
//we can stop the work in advance
stopper := make(chan error, 1)
go func() { go func() {
value := work(stopper) value := work(stopper)
select { select {
case result <- value: case result <- value:
case <-stopper: case stopError := <-stopper:
result <- stopError
} }
}() }()

View File

@ -2,64 +2,75 @@ package deadline
import ( import (
"errors" "errors"
"fmt"
"testing" "testing"
"time" "time"
) )
func takes5ms(stopper <-chan struct{}) error { func workerTakes5ms(stopper chan error) error {
fmt.Println("i'm doing this job in 5ms")
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
return nil return nil
} }
func takes20ms(stopper <-chan struct{}) error { func workerTakes20ms(stopper chan error) error {
fmt.Println("i'm doing this job in 20ms,so work will timeout")
time.Sleep(20 * time.Millisecond) time.Sleep(20 * time.Millisecond)
return nil return nil
} }
func returnsError(stopper <-chan struct{}) error { func cancelWork(stopper chan error) error {
fmt.Println("i'm doing this job")
stopper <- errors.New("canceled job") //cancel job
time.Sleep(5 * time.Millisecond)
fmt.Println("job canceled")
return nil
}
func returnsError(stopper chan error) error {
fmt.Println("i'm doing this job but error occurred")
return errors.New("foo") return errors.New("foo")
} }
func TestMultiDeadline(t *testing.T) { func TestMultiDeadline(t *testing.T) {
dl := New(10*time.Millisecond, "test multi deadline case")
if err := dl.Run(takes5ms); err != nil { dl := New(15*time.Millisecond, "test multi deadline case")
if err := dl.Run(workerTakes5ms); err != nil {
t.Error(err) t.Error(err)
} }
if err := dl.Run(takes20ms); err != ErrTimedOut { err := dl.Run(cancelWork)
t.Log("cancelWork error:", err)
if err.Error() != "canceled job" {
t.Error(err) t.Error(err)
} }
if err := dl.Run(returnsError); err.Error() != "foo" { err = dl.Run(workerTakes20ms)
if err != ErrTimedOut {
t.Error(err) t.Error(err)
} }
}
done := make(chan struct{}) func TestDeadline(t *testing.T) {
err := dl.Run(func(stopper <-chan struct{}) error {
<-stopper dl := New(1*time.Second, "one time deadline case worker")
done := make(chan error)
err := dl.Run(func(stopper chan error) error {
fmt.Println("i am doing something here")
time.Sleep(time.Second * 2)
close(done) close(done)
return nil return nil
}) })
if err != ErrTimedOut { if err != ErrTimedOut {
t.Error(err) t.Error(err)
} }
<-done <-done
}
func TestDeadline(t *testing.T) {
dl := New(1*time.Second, "one dead line case")
err := dl.Run(func(stopper <-chan struct{}) error {
time.Sleep(time.Second * 10)
return nil
})
switch err {
case ErrTimedOut:
t.Error("execution took too long, oops")
default:
// some other error
t.Log("done")
}
} }