something you don’t know about
Golang context
===
<!-- .slide: data-background="pink" -->
<!-- .slide: data-transition="zoom" -->
> :hash: object vs context
>
> [name=郭學聰 Hsueh-Tsung Kuo] [time=Sun, 18 Aug 2019] [color=red]
---
<!-- .slide: data-transition="convex" -->
## who am I?
![fieliapm](https://pbs.twimg.com/profile_images/591670980021387264/aZAYLRUe_400x400.png)
----
<!-- .slide: data-transition="convex" -->
* 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
---
<!-- .slide: data-transition="convex" -->
## outline
ʕ◔ϖ◔ʔ
----
<!-- .slide: data-transition="convex" -->
4. process/thread/fiber
* process
* thread
* fiber
5. goroutine
* M:N threading
6. control process/thread/fiber/goroutine
* id
* object
* context
----
<!-- .slide: data-transition="convex" -->
7. context
* map (goroutine scope variable)
* control
8. issues
* join
9. conclusion
* principle
* comment
* reference
10. commercial
11. Q&A
---
<!-- .slide: data-transition="convex" -->
## process/thread/fiber
----
<!-- .slide: data-transition="convex" -->
### common
* independent program counter
* independent stack
----
<!-- .slide: data-transition="convex" -->
### process
* independent user memory space
* almost preemptive multitasking
* exclude classic MacOS ~9 & Windows ~3.x
* could run in parallel on different CPU core
----
<!-- .slide: data-transition="convex" -->
### thread
* shared memory space
* may be preemptive or cooperative multitasking
* could run in parallel on different CPU core
----
<!-- .slide: data-transition="convex" -->
### fiber
* shared memory space
* cooperative multitasking
* yield (context switch) manually
---
<!-- .slide: data-transition="convex" -->
## goroutine
----
<!-- .slide: data-transition="convex" -->
### goroutine?
* M:N threading
* G-P-M model
----
<!-- .slide: data-transition="convex" -->
### M:N threading
```graphviz
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]
}
```
---
<!-- .slide: data-transition="convex" -->
## control process/thread/fiber/goroutine
----
<!-- .slide: data-transition="convex" -->
how to trace process/thread/fiber?
* id
* object
----
<!-- .slide: data-transition="convex" -->
### id
* POSIX
* pid = fork();
* pthread_create(pthread_t *thread, ...);
* typedef unsigned long int pthread_t;
* Windows
* pid = spawn(P_NOWAIT, ...);
----
<!-- .slide: data-transition="convex" -->
### object
Ruby
```ruby=
t = Thread.new {
(1..5).each {
puts('hello')
sleep(1.0)
}
}
t.terminate()
t.join()
```
C#
```clike=
Thread t = new Thread(new ThreadStart(object.Method));
t.Start();
t.Abort();
t.Join();
```
----
<!-- .slide: data-transition="convex" -->
### goroutine
* Go
* start?
* go function() <!-- .element: class="fragment" data-fragment-index="1" -->
* terminate? join?
* use the channels, Luke! <!-- .element: class="fragment" data-fragment-index="2" -->
----
<!-- .slide: data-transition="convex" -->
communicating sequential processes newbie:
manually assign channels for every goroutine!?
----
<!-- .slide: data-transition="convex" -->
### somehow idiomatic solution
```go=
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()
```
----
<!-- .slide: data-transition="convex" -->
```graphviz
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"]
}
```
----
<!-- .slide: data-transition="convex" -->
```graphviz
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"]
}
```
----
<!-- .slide: data-transition="convex" -->
that is all?
----
<!-- .slide: data-transition="convex" -->
![讓我們看下去](https://stickershop.line-scdn.net/stickershop/v1/sticker/16846578/iPhone/sticker@2x.png =531x495)
<small>https://store.line.me/stickershop/product/8601/zh-Hant</small>
---
<!-- .slide: data-transition="convex" -->
## context
----
<!-- .slide: data-transition="convex" -->
### context?
* map (goroutine scope variable)
* control
----
<!-- .slide: data-transition="convex" -->
```go=
// 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 <!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="convex" -->
### map (goroutine scope variable)
----
<!-- .slide: data-transition="convex" -->
```go=
childCtx := context.WithValue(parentCtx, key, value)
value, ok := childCtx.Value(key).(ValueType)
```
----
<!-- .slide: data-transition="convex" -->
warning :warning:
```
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.
```
<small>https://golang.org/pkg/context/</small>
----
<!-- .slide: data-transition="convex" -->
warning :warning:
```go=
type contextKey int
const (
contextKeySessionData = contextKey(1)
)
contextWithSessionData := context.WithValue(r.Context(), contextKeySessionData, sessionData)
sessionData, ok := r.Context().Value(contextKeySessionData).(*AuthSessionData)
```
<small>https://github.com/rayark/osecure/blob/master/osecure.go</small>
----
<!-- .slide: data-transition="convex" -->
### control
----
<!-- .slide: data-transition="convex" -->
```go=
// get cancel button
childCtx, cancel := context.WithCancel(parentCtx)
```
----
<!-- .slide: data-transition="convex" -->
```go=
// get cancel button and set deadline
childCtx, cancel := context.WithDeadline(parentCtx, deadline)
```
----
<!-- .slide: data-transition="convex" -->
```go=
// get cancel button and set timeout
childCtx, cancel := context.WithTimeout(parentCtx, timeout)
```
---
<!-- .slide: data-transition="convex" -->
## issues
----
<!-- .slide: data-transition="convex" -->
### start/terminate/join
* start
* go function()
* terminate
* childCtx, cancel := context.WithCancel(parentCtx) <!-- .element: class="fragment" data-fragment-index="1" -->
* cancel() <!-- .element: class="fragment" data-fragment-index="2" -->
* join
* context: this is not on my business <!-- .element: class="fragment" data-fragment-index="3" -->
----
<!-- .slide: data-transition="convex" -->
[大事不妙啊] ʕOϖOʔ
![マジやばくね](https://tn.smilevideo.jp/smile?i=30565927.L =540x405)
<small>https://dic.nicovideo.jp/a/マジやばくね</small>
----
<!-- .slide: data-transition="convex" -->
### join
```go=
import "sync"
var wg sync.WaitGroup
wg.Add(1)
go function() {
defer wg.Done()
doSomething()
}()
wg.Wait()
```
----
<!-- .slide: data-transition="convex" -->
### the elegant(?) solution
```sequence
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
```
----
<!-- .slide: data-transition="convex" -->
note :notebook:
* we can replace all of them with channels
----
<!-- .slide: data-transition="zoom" -->
this is it
( >ω•)b
----
<!-- .slide: data-transition="zoom" -->
~~this is it~~
( O\_o) ?
----
<!-- .slide: data-transition="zoom" -->
what about deeply nested goroutine?
( @д@) !
----
<!-- .slide: data-transition="zoom" -->
forget it
¯\\\_\(ツ\)\_/¯
----
<!-- .slide: data-transition="convex" -->
or more elegant way:
* Package tomb
* https://gopkg.in/tomb.v2
---
<!-- .slide: data-transition="convex" -->
## conclusion
----
<!-- .slide: data-transition="convex" -->
context =
(thread object) - (join) + (thread scope variable)
> [name=郭學聰 Hsueh-Tsung Kuo] [time=2019_08_18] [color=red]
----
<!-- .slide: data-transition="convex" -->
### principle
```
Incoming requests to a server should create a Context,
and outgoing calls to servers should accept a Context.
```
<small>https://golang.org/pkg/context/</small>
----
<!-- .slide: data-transition="convex" -->
elegant :dizzy:
```go=
func HandlerFunc(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
resource.Connect(ctx, param1, param2)
...
}
```
----
<!-- .slide: data-transition="convex" -->
### comment
* comment on context
* community
* good/bad design = the **con**current war
* me
* not perfect, but **practical**
----
<!-- .slide: data-transition="convex" -->
### reference
* Go Package context
* <small>https://golang.org/pkg/context/</small>
* Package tomb
* <small>https://gopkg.in/tomb.v2</small>
* Context isn’t for cancellation - Dave Cheney
* <small>https://dave.cheney.net/2017/08/20/context-isnt-for-cancellation</small>
* Context should go away for Go 2 - Michal Štrba
* <small>https://faiface.github.io/post/context-should-go-away-go2/</small>
---
<!-- .slide: data-transition="zoom" -->
## commercial
----
<!-- .slide: data-transition="zoom" -->
Sdorica -mirage-
* <small>OP Animation https://www.youtube.com/watch?v=ggrrxYZT5yU</small>
----
<!-- .slide: data-transition="zoom" -->
{%youtube ggrrxYZT5yU %}
----
<!-- .slide: data-transition="zoom" -->
# Rayark Wants You !
Rayark Careers | Make A Difference
* EN https://careers.rayark.com/jobs/
* ZH https://careers.rayark.com/zh/jobs/
---
<!-- .slide: data-transition="zoom" -->
## Q&A
---
<style>
.reveal {
background: #FFDFEF;
color: black;
}
.reveal h2,
.reveal h3,
.reveal h4 {
color: black;
}
.reveal code {
font-size: 18px !important;
line-height: 1.2;
}
.rightpart{
float:right;
width:50%;
}
.leftpart{
margin-right: 50% !important;
height:50%;
}
.reveal section img { background:none; border:none; box-shadow:none; }
p.blo {
font-size: 50px !important;
background:#B6BDBB;
border:1px solid silver;
display:inline-block;
padding:0.5em 0.75em;
border-radius: 10px;
box-shadow: 5px 5px 5px #666;
}
p.blo1 {
background: #c7c2bb;
}
p.blo2 {
background: #b8c0c8;
}
p.blo3 {
background: #c7cedd;
}
p.bloT {
font-size: 60px !important;
background:#B6BDD3;
border:1px solid silver;
display:inline-block;
padding:0.5em 0.75em;
border-radius: 8px;
box-shadow: 1px 2px 5px #333;
}
p.bloA {
background: #B6BDE3;
}
p.bloB {
background: #E3BDB3;
}
.slide-number{
margin-bottom:10px !important;
width:100%;
text-align:center;
font-size:25px !important;
background-color:transparent !important;
}
iframe.myclass{
width:100px;
height:100px;
bottom:0;
left:0;
position:fixed;
border:none;
z-index:99999;
}
h1.raw {
color: #fff;
background-image: linear-gradient(90deg,#f35626,#feab3a);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: hue 5s infinite linear;
}
@keyframes hue {
from {
filter: hue-rotate(0deg);
}
to {
filter: hue-rotate(360deg);
}
}
.progress{
height:14px !important;
}
.progress span{
height:14px !important;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAMCAIAAAAs6UAAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUNCQzIyREQ0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUNCQzIyREU0QjdEMTFFMzlEMDM4Qzc3MEY0NzdGMDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQ0JDMjJEQjRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQ0JDMjJEQzRCN0QxMUUzOUQwMzhDNzcwRjQ3N0YwOCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PovDFgYAAAAmSURBVHjaYvjPwMAAxjMZmBhA9H8INv4P4TPM/A+m04zBNECAAQBCWQv9SUQpVgAAAABJRU5ErkJggg==") repeat-x !important;
}
.progress span:after,
.progress span.nyancat{
content: "";
background: url('data:image/gif;base64,R0lGODlhIgAVAKIHAL3/9/+Zmf8zmf/MmZmZmf+Z/wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpDMkJBNjY5RTU1NEJFMzExOUM4QUM2MDAwNDQzRERBQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREIzOEIzMzRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREIzOEIzMjRCN0IxMUUzODhEQjgwOTYzMTgyNTE0QiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChXaW5kb3dzKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkM1QkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMyQkE2NjlFNTU0QkUzMTE5QzhBQzYwMDA0NDNEREFDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8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') !important;
width: 34px !important;
height: 21px !important;
border: none !important;
float:right;
margin-top:-7px;
margin-right:-10px;
}
</style>
{"metaMigratedAt":"2023-06-14T23:07:00.305Z","metaMigratedFrom":"Content","title":"something you don’t know about\nGolang context","breaks":true,"contributors":"[{\"id\":\"ea27dcd7-a3f2-47c2-b25e-6760e7936c38\",\"add\":60498,\"del\":39974}]"}