Top Banner
Coding in the Context Era Go Conference 2017 Spring Mar 25, 2017 Daisuke Maki @lestrrat
38

Coding in the context era

Apr 11, 2017

Download

Technology

lestrrat
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Coding in the context era

Coding in the Context Era

Go Conference 2017 Spring Mar 25, 2017

Daisuke Maki @lestrrat

Page 2: Coding in the context era

• @lestrrat • Perl/Go hacker, author, father • Author of github.com/peco/peco • Organizer for builderscon

Page 3: Coding in the context era

uildersconhttps://builderscon.io/tokyo/2017

Aug 3, 4, 5 2017

Page 4: Coding in the context era

(we run on Kubernetes)

Page 5: Coding in the context era

</advertisement>

Page 6: Coding in the context era

Web+DB Press vol 98 (Apr 2017)

Page 7: Coding in the context era

you DO use context.Context, right?

Page 8: Coding in the context era

obj.Start() obj.Stop()

Page 9: Coding in the context era

func (t *Obj) Start() { go func() { for { select { case <-t.done: … default: } … }() } } }

Page 10: Coding in the context era

func (t *Obj) Stop() { close(t.done) }

Page 11: Coding in the context era

THIS IS NOT SAFE

Page 12: Coding in the context era

obj.Start() obj.Start() // What?!

Page 13: Coding in the context era

API is ambiguous• Does it start a new goroutine every time Start() is called?

• Does it error from the second one?

Page 14: Coding in the context era

Only start one?• Maybe Start() can return an error if it has already been started

• Bonus points: can it be re-entrant? • Requires state synchronization

Page 15: Coding in the context era

func (obj *Obj) Start() error { obj.mu.Lock() if obj.started { obj.mu.Unlock() return errors.New(`already running`) } obj.started = true obj.mu.Unlock() … }

Page 16: Coding in the context era

func (obj *Obj) Start() error { … go func() { defer func() { obj.mu.Lock() obj.started = false obj.mu.Unlock() }() for { … } }() }

Page 17: Coding in the context era

func (obj *Obj) Start() error { … go func() { for { select { case <-obj.done: return default: } } … }() }

Page 18: Coding in the context era

func (obj *Obj) Stop() error { obj.mu.Lock() if obj.started { close(obj.done) obj.mu.Unlock() return errors.New(`already running`) } obj.started = true obj.Unlock() }

Page 19: Coding in the context era

This is still a simplified version.

Page 20: Coding in the context era

Life is too short for manual synchronization of concurrently executed code

人類に並行実行されている コードの手動同期は難しすぎる…!

Page 21: Coding in the context era

Nobody wants to do this

Page 22: Coding in the context era

Root cause: shared resource

• obj.started is shared • There is no way to escape manual locking

Page 23: Coding in the context era

TRUTHYOU WILL MAKE A MISTAKE

WHEN MANUALLY SYNCHRONIZING sync.Mutexes

Page 24: Coding in the context era

AVOID sync.Mutex(unless you know what you are doing)

Page 25: Coding in the context era

context.Contextcontext.Context

Page 26: Coding in the context era

context.Context• abstracts away shared state for cancellation

Page 27: Coding in the context era

context.Context• Explicit cancellation • Timeouts • Deadlines • It’s a pattern: you can use it anywhere!

Page 28: Coding in the context era

// To save some typing, please assume the // following for the rest of this talk: ctxbg := context.Background() delay := 5 * time.Second

Page 29: Coding in the context era

func (obj *Obj) Run(ctx context.Context) error { for { select { case <-ctx.Done(): return nil default: } … } }

Page 30: Coding in the context era

ctx, cancel := context.WithCancel(ctxbg) go obj.Run(ctx)

// later in your code cancel()

Page 31: Coding in the context era

No sync.Mutex!

Page 32: Coding in the context era

Clean, explicit grouting termination

Page 33: Coding in the context era

No ambiguous API

Page 34: Coding in the context era

ctx, cancel := context.WithTimeout(ctxbg, delay) defer cancel() go obj1.Run(ctx) // use a diff. ctx if need be go obj2.Run(ctx) // re-entrant! yay! go obj3.Run(ctx)

Explicit semantics

Page 35: Coding in the context era

YESSS!!!!

Page 36: Coding in the context era

Prediction/Recommendation• Almost everything that can (1) block or (2) spawn a goroutine will support context.Context very soon.

• You should use context.Context for anything that blocks!

Page 37: Coding in the context era

Learn to ♡ contexts

Page 38: Coding in the context era

Questions?