changed 6 years ago
Published Linked with GitHub

something you don’t know about
Golang context

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
object vs context

郭學聰 Hsueh-Tsung KuoSun, 18 Aug 2019


who am I?

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →


  • programmer from Rayark, a game company in Taiwan
  • backend (and temporary frontend) engineer, focus on common service
  • usually develop something related to my work in Python, Ruby, ECMAScript, Golang, C#
  • ECMAScript hater since Netscape is dead
  • built CDN-aware game asset update system
  • built almost entire VOEZ game server by myself only
  • supported Sdorica backend development

outline

ʕ◔ϖ◔ʔ


  1. process/thread/fiber
    • process
    • thread
    • fiber
  2. goroutine
    • M:N threading
  3. control process/thread/fiber/goroutine
    • id
    • object
    • context

  1. context
    • map (goroutine scope variable)
    • control
  2. issues
    • join
  3. conclusion
    • principle
    • comment
    • reference
  4. commercial
  5. Q&A

process/thread/fiber


common

  • independent program counter
  • independent stack

process

  • independent user memory space
  • almost preemptive multitasking
    • exclude classic MacOS ~9 & Windows ~3.x
  • could run in parallel on different CPU core

thread

  • shared memory space
  • may be preemptive or cooperative multitasking
  • could run in parallel on different CPU core

fiber

  • shared memory space
  • cooperative multitasking
  • yield (context switch) manually

goroutine


goroutine?

  • M:N threading
    • G-P-M model

M:N threading

digraph M_N_threading {
    nodesep=1.0 // increases the separation between nodes

    node [color="red" shape=box]
    edge [color="blue" style=dashed]

    cpu1 [label="CPU core 1"]
    cpu2 [label="CPU core 2"]
    thread1 [label="thread M1" shape="oval"]
    thread2 [label="thread M2" shape="oval"]
    thread3 [label="thread M3" shape="oval"]
    thread4 [label="thread M4" shape="oval"]
    processor1 [label="logical processor P1" shape="oval"]
    processor2 [label="logical processor P2" shape="oval"]
    goroutine1 [label="goroutine G1" shape="oval"]
    goroutine2 [label="goroutine G2" shape="oval"]
    goroutine3 [label="goroutine G3" shape="oval"]
    goroutine4 [label="goroutine G4" shape="oval"]
    goroutine5 [label="goroutine G5" shape="oval"]

    cpu1->thread1 [style=solid]
    cpu2->thread2 [style=solid]

    thread1->processor1 [style=solid]
    thread2->processor2 [style=solid]

    processor1->goroutine1 [style=solid]
    processor1->goroutine2 [style=solid]

    processor2->goroutine3 [style=solid]
    processor2->goroutine4 [style=solid]
    processor2->goroutine5 [style=solid]
}

control process/thread/fiber/goroutine


how to trace process/thread/fiber?

  • id
  • object

id

  • POSIX
    • pid = fork();
    • pthread_create(pthread_t *thread, );
      • typedef unsigned long int pthread_t;
  • Windows
    • pid = spawn(P_NOWAIT, );

object

Ruby

t = Thread.new { (1..5).each { puts('hello') sleep(1.0) } } t.terminate() t.join()

C#

Thread t = new Thread(new ThreadStart(object.Method)); t.Start(); t.Abort(); t.Join();

goroutine

  • Go
    • start?
      • go function()
    • terminate? join?
      • use the channels, Luke!

communicating sequential processes newbie:
manually assign channels for every goroutine!?


somehow idiomatic solution

import "context" parentCtx := context.Background() childCtx, cancel := context.WithCancel(parentCtx) go function(ctx context.Context, param1 int, param2 string) error { for { select { case <-ctx.Done(): return ctx.Err() default: doSomething() } } }(childCtx, param1, param2) cancel()

digraph goroutine_tree {
    nodesep=1.0 // increases the separation between nodes

    node [color="green" shape=box]
    edge [color="blue"]

    goroutine1 [label="goroutine 1"]
    goroutine2 [label="goroutine 2"]
    goroutine3 [label="goroutine 3"]
    goroutine4 [label="goroutine 4"]
    goroutine5 [label="goroutine 5"]
    goroutine6 [label="goroutine 6"]
    goroutine7 [label="goroutine 7"]

    goroutine1->goroutine2 [label = "context"]
    goroutine2->goroutine3 [label = "context"]
    goroutine2->goroutine4 [label = "context"]
    goroutine2->goroutine5 [label = "context"]
    goroutine5->goroutine6 [label = "context"]
    goroutine5->goroutine7 [label = "context"]
}

digraph goroutine_tree {
    nodesep=1.0 // increases the separation between nodes

    node [color="gray" shape=box]
    edge [color="gray" style=dashed]

    goroutine1 [color="green" label="goroutine 1"]
    goroutine2 [color="red" label="goroutine 2"]
    goroutine3 [label="goroutine 3"]
    goroutine4 [label="goroutine 4"]
    goroutine5 [label="goroutine 5"]
    goroutine6 [label="goroutine 6"]
    goroutine7 [label="goroutine 7"]

    goroutine1->goroutine2 [color="blue" label = "context" style=solid]
    goroutine2->goroutine3 [label = "context"]
    goroutine2->goroutine4 [label = "context"]
    goroutine2->goroutine5 [label = "context"]
    goroutine5->goroutine6 [label = "context"]
    goroutine5->goroutine7 [label = "context"]
}

that is all?


讓我們看下去

https://store.line.me/stickershop/product/8601/zh-Hant


context


context?

  • map (goroutine scope variable)
  • control

// init parentCtx := context.Background() // map (goroutine scope variable) // store goroutine scope variable childCtx := context.WithValue(parentCtx, key, value) value, ok := childCtx.Value(key).(ValueType) // control // get cancel button childCtx, cancel := context.WithCancel(parentCtx) // get cancel button and set deadline childCtx, cancel := context.WithDeadline(parentCtx, deadline) // get cancel button and set timeout childCtx, cancel := context.WithTimeout(parentCtx, timeout)

every context.With*() string context as linked list


map (goroutine scope variable)


childCtx := context.WithValue(parentCtx, key, value) value, ok := childCtx.Value(key).(ValueType)

warning

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

The provided key must be comparable and should not be of type string or any other built-in type to avoid
collisions between packages using context. Users of WithValue should define their own types for keys. To avoid
allocating when assigning to an interface{}, context keys often have concrete type struct{}. Alternatively, exported
context key variables' static type should be a pointer or interface.

https://golang.org/pkg/context/


warning

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

type contextKey int const ( contextKeySessionData = contextKey(1) ) contextWithSessionData := context.WithValue(r.Context(), contextKeySessionData, sessionData) sessionData, ok := r.Context().Value(contextKeySessionData).(*AuthSessionData)

https://github.com/rayark/osecure/blob/master/osecure.go


control


// get cancel button childCtx, cancel := context.WithCancel(parentCtx)

// get cancel button and set deadline childCtx, cancel := context.WithDeadline(parentCtx, deadline)

// get cancel button and set timeout childCtx, cancel := context.WithTimeout(parentCtx, timeout)

issues


start/terminate/join

  • start
    • go function()
  • terminate
    • childCtx, cancel := context.WithCancel(parentCtx)
    • cancel()
  • join
    • context: this is not on my business

[大事不妙啊] ʕOϖOʔ

マジやばくね

https://dic.nicovideo.jp/a/マジやばくね


join

import "sync" var wg sync.WaitGroup wg.Add(1) go function() { defer wg.Done() doSomething() }() wg.Wait()

the elegant(?) solution

note left of parent: wg.Add()
parent->child: go function()
note right of child: start
parent->child: cancel()
note right of child: case <-ctx.Done():
note right of child: stop
parent->child: call wg.Wait()
note right of child: wg.Done()
child->parent: wg.Wait() return

note

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • we can replace all of them with channels

this is it
( >ω•)b


this is it
( O_o) ?


what about deeply nested goroutine?
( @д@) !


forget it
¯\_(ツ)_/¯


or more elegant way:


conclusion


context =
(thread object) - (join) + (thread scope variable)

郭學聰 Hsueh-Tsung Kuo2019_08_18


principle

Incoming requests to a server should create a Context,
and outgoing calls to servers should accept a Context.

https://golang.org/pkg/context/


elegant

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

func HandlerFunc(w http.ResponseWriter, r *http.Request) { ctx := r.Context() resource.Connect(ctx, param1, param2) ... }

comment

  • comment on context
    • community
      • good/bad design = the concurrent war
    • me
      • not perfect, but practical

reference


commercial


Sdorica -mirage-


Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →


Rayark Wants You !

Rayark Careers | Make A Difference


Q&A


Select a repo