talks: State of Go 2017

Change-Id: If5900ebc7b9872314ae75160489a66d0fd65cc81
Reviewed-on: https://go-review.googlesource.com/36053
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/2017/state-of-go.slide b/2017/state-of-go.slide
new file mode 100644
index 0000000..634804a
--- /dev/null
+++ b/2017/state-of-go.slide
@@ -0,0 +1,522 @@
+The State of Go
+Where we are in February 2017
+
+Francesc Campoy
+Google Developer Advocate
+@francesc
+campoy@golang.org
+
+* Time flies
+
+Go 1.6 is one year old (happy birthday!)
+
+Go 1.7 is already 6 months old!
+
+Go 1.8 to be released sometime in February.
+
+Go 1.8 Candidate Release 3 was released on January 26th.
+
+.image state-of-go/img/flying.png
+
+* Notes
+
+The slides are available on [[https://talks.golang.org/2017/state-of-go.slide]]
+
+Most of the code examples won't run except locally and using Go 1.8.
+
+The playground still runs Go 1.7.
+
+* Agenda
+
+Changes since Go 1.7:
+
+- The Language
+- The Standard Library
+- The Runtime
+- The Tooling
+- The Community
+
+* Changes to the language
+
+* Conversion rules
+
+How many times have you found yourself with two types that were *almost* equal?
+
+Let's say you define `Person`:
+
+.code state-of-go/stdlib/json_old.go /type Person/,/^}/
+
+And that for some reason, like JSON you also have:
+
+.code state-of-go/stdlib/json_old.go /var aux/,/}/
+
+* Conversion rules
+
+In order to convert `aux` to type `Person` you needed to do:
+
+.code state-of-go/stdlib/json_old.go /type Person/,/}/
+
+    return Person{
+        Name:     aux.Name,
+        AgeYears: aux.AgeYears,
+        SSN:      aux.SSN
+    }
+
+* Conversion rules
+
+Since Go 1.8 you can simply do:
+
+    return Person(aux)
+
+Both types still need to have:
+
+- same *sequence* of fields (the order matters)
+- corresponding fields with same type.
+
+* Conversion rules
+
+A non-constant value x can be converted to type T in any of these cases:
+
+- x is assignable to T.
+- x's type and T have identical underlying types.
+- x's type and T are unnamed pointer types and their pointer base types have identical underlying types.
+- ...
+
+* Conversion rules
+
+A non-constant value x can be converted to type T in any of these cases:
+
+- x is assignable to T.
+- *ignoring*struct*tags*, x's type and T have identical underlying types.
+- *ignoring*struct*tags*, x's type and T are unnamed pointer types and their pointer base types have identical underlying types.
+- ...
+
+* Ports to other platforms
+
+* Ports to other platforms
+
+32-bit MIPS
+
+- big-endian (`linux/mips`)
+- little-endian (`linux/mipsle`) - requires Floating Point Unit
+
+Go on DragonFly BSD now requires DragonFly 4.4.4+.
+
+Go on OpenBSD now requires OpenBSD 5.9+.
+
+Plan 9 is now better!
+
+* Ports to other platforms
+
+Go 1.8 supports OS X 10.8+. Likely last time we support 10.8.
+
+ARM:
+
+- Go 1.8 is the last version to support ARMv5E and ARMv6 processors.
+
+- Go 1.9 will require ARMv6K. Will it work on my platform?
+
+    go tool dist -check-armv6k
+
+* Tools
+
+* Fix
+
+Fixes the import path `"golang.org/x/net/context"` to `"context"`.
+
+.code state-of-go/tools/gofix.go
+
+Simply run the command below:
+
+.play state-of-go/tools/gofix.sh /go tool/
+
+Drop the `-diff` flag to rewrite the files.
+
+* Vet
+
+"Vet is stricter in some ways and looser where it previously caused false positives."
+
+Example of extra check:
+
+.play -edit state-of-go/tools/govet.go /func main/,
+
+`govet` detects the problem statically:
+
+.play state-of-go/tools/govet.sh /go vet/
+
+* SSA everywhere!
+
+The SSA backend:
+
+- generates more compact and efficient code
+- is a better platform for optimizations
+
+For 32-bit ARM systems this means 20-30% speed up!
+
+For others (where SSA was used already) gains are 0-10%.
+
+* SSA everywhere
+
+.image state-of-go/img/benchmark.png _ 800
+
+* Default GOPATH
+
+Yay!
+
+When `GOPATH` is not defined, the tool will use:
+
+- `$HOME/go` on Unix
+- `%USERPROFILE%\go` on Windows
+
+* go bug
+
+Easier way to create bugs including all relevant information.
+
+Example:
+
+.play state-of-go/tools/gobug.sh /go bug/
+
+* Runtime
+
+* Detection of concurrent map accesses
+
+Improvement on Go 1.6.
+
+.play state-of-go/runtime/mapcrash.go /const/,/Wait\(\)/
+
+Outputs:
+
+	fatal error: concurrent map read and map write
+	fatal error: concurrent map writes
+
+* Mutext Contention Profiling
+
+Profile your benchmarks and the contention on your mutexes.
+
+    go test bench=. -mutexprofile=mutex.out
+
+
+Alternatively, activate contention profiling with this new method.
+
+    runtime.SetMutexProfileFraction
+
+_Note_: For now `sync.RWMutex` is not profiled.
+
+* Mutex Contention Profiling
+
+Let's write a program to count how many times each factor appears from 2 to N.
+
+Example N = 10:
+
+    Factorizations:
+
+        2:  2
+        3:  3
+        4:  2 2
+        5:  5
+        6:  2 3
+        7:  7
+        8:  2 2 2
+        9:  3 3
+        10: 2 5
+
+    Count:
+
+        2: 8
+        3: 4
+        5: 2
+        7: 1
+
+* Mutex Contention Profiling
+
+Which option is better?
+
+Wide protected region:
+
+.play state-of-go/runtime/mutex/main.go /WIDE/,/Unlock/
+
+Narrow protected region:
+
+.play state-of-go/runtime/mutex/main.go /NARROW/,/}/
+
+* Benchmark
+
+    $ go test -bench=.
+
+#    BenchmarkNarrowSection/10-8         	  300000	      5085 ns/op
+#    BenchmarkNarrowSection/100-8        	   20000	     77458 ns/op
+#    BenchmarkNarrowSection/1000-8       	    2000	    909658 ns/op
+#    BenchmarkNarrowSection/10000-8      	     100	  21230633 ns/op
+#
+#    BenchmarkWideSection/10-8           	  200000	      5323 ns/op
+#    BenchmarkWideSection/100-8          	   10000	    103228 ns/op
+#    BenchmarkWideSection/1000-8         	    1000	   2131861 ns/op
+#    BenchmarkWideSection/10000-8        	      10	 103575793 ns/op
+
+.image state-of-go/img/mutex_noprofile.png _ 1000
+
+* Benchmarking with Mutex Contention
+
+    $ go test -bench=. -mutexprofile=mutex.out
+
+#    BenchmarkNarrowSection/10-8         	  300000	      5464 ns/op
+#    BenchmarkNarrowSection/100-8        	   10000	    108583 ns/op
+#    BenchmarkNarrowSection/1000-8       	    1000	   1378379 ns/op
+#    BenchmarkNarrowSection/10000-8      	     100	  32828827 ns/op
+#
+#    BenchmarkWideSection/10-8           	  200000	      7155 ns/op
+#    BenchmarkWideSection/100-8          	   10000	    197001 ns/op
+#    BenchmarkWideSection/1000-8         	     300	   4339571 ns/op
+#    BenchmarkWideSection/10000-8        	       5	 303559562 ns/op
+
+.image state-of-go/img/mutex_profile.png _ 1000
+
+* Analyzing the Mutex Contention Profile
+
+    $ go tool pprof runtime.test mutex.out
+    Entering interactive mode (type "help" for commands)
+    (pprof) list
+
+    0      5.38s (flat, cum) 43.97% of Total
+    .          .     34:				mu.Lock()
+    .          .     35:				m[f]++
+    .      5.38s     36:				mu.Unlock()
+
+    0      6.86s (flat, cum) 56.03% of Total
+    .          .     53:			mu.Lock()
+    .          .     54:			for _, f := range factors(i) {
+    .          .     55:				m[f]++
+    .          .     56:			}
+    .      6.86s     57:			mu.Unlock()
+
+* So much contention ...
+
+* Contention by CPU
+
+.image state-of-go/img/mutex_procs.png _ 1000
+
+* Comparing it to sequential algorithm
+
+.image state-of-go/img/mutex_all.png _ 1000
+
+* Comparing it to sequential algorithm (zoom)
+
+.image state-of-go/img/mutex_all_zoom.png _ 1000
+
+* Performance
+
+* GC history in tweets
+
+* go 1.5
+
+.image state-of-go/img/gct1.png _ 900
+
+* go 1.6
+
+.image state-of-go/img/gct2.png _ 900
+
+* go 1.7
+
+.image state-of-go/img/gct4.png _ 900
+
+* go 1.8 (beta 1)
+
+.image state-of-go/img/gct5.png _ 700
+
+* go 1.8 (beta 1) CPU
+
+.image state-of-go/img/gctcpu.png _ 800
+
+* defer is faster
+
+    name         old time/op  new time/op  delta
+    Defer-4       101ns ± 1%    66ns ± 0%  -34.73%  (p=0.000 n=20+20)
+    Defer10-4    93.2ns ± 1%  62.5ns ± 8%  -33.02%  (p=0.000 n=20+20)
+    DeferMany-4   148ns ± 3%   131ns ± 3%  -11.42%  (p=0.000 n=19+19)
+
+.image state-of-go/img/defer.png _ 500
+
+* cgo is also faster!
+
+    name       old time/op  new time/op  delta
+    CgoNoop-8  93.5ns ± 0%  51.1ns ± 1%  -45.34%  (p=0.016 n=4+5)
+
+.image state-of-go/img/cgo.png _ 500
+
+Source: [[https://dave.cheney.net/2016/11/19/go-1-8-toolchain-improvements][dave.cheney.net]]
+
+* Changes to the standard library
+
+* Sorting
+
+Exercise:
+
+Given a slice of `Person`
+
+    var p []Person
+
+Print the slice sorted by name, age, and SSN.
+
+.code state-of-go/stdlib/sort/sort_test.go /sort.Sort/,/bySSN/
+
+Easy, right?
+
+* Sorting
+
+Well, you forgot about this part.
+
+.code state-of-go/stdlib/sort/sort_test.go /byName/,/bySSN\) Swap/
+
+* sort.Slice
+
+Since Go 1.8 you can simply write this:
+
+.code state-of-go/stdlib/sort/sort_test.go /sort\.Slice/,/SSN/
+
+Also new `SliceStable` and `SliceIsSorted`.
+
+* Benchmark
+
+    N=1 go test -bench=.
+        BenchmarkSortSort-8     10000000               145 ns/op
+        BenchmarkSortSlice-8    10000000               190 ns/op
+    N=10 go test -bench=.
+        BenchmarkSortSort-8      2000000               918 ns/op
+        BenchmarkSortSlice-8     1000000              1776 ns/op
+    N=100 go test -bench=.
+        BenchmarkSortSort-8       100000             16588 ns/op
+        BenchmarkSortSlice-8       50000             39035 ns/op
+    N=1000 go test -bench=.
+        BenchmarkSortSort-8         5000            320951 ns/op
+        BenchmarkSortSlice-8        3000            446677 ns/op
+    N=10000 go test -bench=.
+        BenchmarkSortSort-8          500           3644480 ns/op
+        BenchmarkSortSlice-8         300           4962263 ns/op
+    N=100000 go test -bench=.
+        BenchmarkSortSort-8           30          43573572 ns/op
+        BenchmarkSortSlice-8          20          60861706 ns/op
+
+.caption Benchmark ran on my MacBook Pro (8 cores), simply indicative.
+
+* Benchmark
+
+.image state-of-go/img/bench.png _ 800
+
+* Benchmark (log/log)
+
+.image state-of-go/img/bench_log.png _ 800
+
+
+* Plugins
+
+Define a plugin:
+
+.code state-of-go/stdlib/plugin/plugin.go
+
+Then build it:
+
+    go build -buildmode=plugin
+
+* Plugins
+
+.code state-of-go/stdlib/plugin/main.go /plugin.Open/,/Hello/
+
+* Plugins demo
+
+Demo video: [[https://twitter.com/francesc/status/827851085943566336][twitter.com/francesc]]
+
+Source code: [[https://github.com/campoy/golang-plugins][github.com/campoy/golang-plugins]]
+
+* HTTP shutdown
+
+Added `Shutdown` method to `http.Server`.
+
+Example:
+
+Call `Shutdown` when a signal is received:
+
+.code state-of-go/stdlib/shutdown.go /subscribe/,/}\(\)/
+
+* HTTP shutdown
+
+Check why the server stopped.
+
+.code state-of-go/stdlib/shutdown.go /HandleFunc/,/gracefully/
+
+
+* HTTP/2
+
+`http.Response` now satisfies the `http.Pusher` interface.
+
+    type Pusher interface {
+        Push(target string, opts *PushOptions) error
+    }
+
+A simple example:
+
+.code state-of-go/stdlib/http2/http2.go /func rootHandler/,/^}/
+
+* HTTP/2
+
+.play state-of-go/stdlib/http2/http2.go /func main/,/^}/
+
+HTTP: [[http://localhost:8080]]
+HTTP/2: [[https://localhost:8081]]
+
+* HTTP/2
+
+HTTP
+
+.image state-of-go/img/http.png _ 800
+
+HTTP/2
+
+.image state-of-go/img/http2.png _ 800
+
+* More context support
+
+Since Go 1.7:
+
+- net
+- net/http
+- os/exec
+
+Since Go 1.8:
+
+- http.Server.Shutdown
+- database/sql
+- net.Resolver
+
+* A couple more changes too
+
+.image state-of-go/img/more.png _ 1000
+.caption [[https://beta.golang.org/doc/go1.8][Go 1.8 release notes]]
+
+* The community
+
+* Women Who Go
+
+.image state-of-go/img/wwg.png _ 800
+.caption 16 chapters already! [[http://www.womenwhogo.org]]
+
+* Go meetups
+
+.image state-of-go/img/meetups.png _ 900
+.caption Gophers all around the world! [[http://go-meetups.appspot.com]]
+
+* Conferences:
+
+- [[https://fosdem.org/2017/][FOSDEM]], right here right now!
+- [[http://www.gophercon.in/][GopherCon India]], Feb 25-25th
+- [[https://gophercon.com/][Gophercon Denver]], Jul 12-15th
+- [[http://golanguk.com/][Golang UK]], August 16th-18th
+- [[http://2017.dotgo.eu/][dotGo]], Nov 6th
+
+* Go 1.8 release party, February 16th
+
+Go 1.8 ships soon!
+
+Go meetups are organising to hold a [[https://github.com/golang/go/wiki/Go-1.8-release-party][release party]] on the 16th of February.
+
+.image state-of-go/img/party-gopher.png _ 300
+.caption Join the party!!!
diff --git a/2017/state-of-go/img/bench.png b/2017/state-of-go/img/bench.png
new file mode 100644
index 0000000..76d3112
--- /dev/null
+++ b/2017/state-of-go/img/bench.png
Binary files differ
diff --git a/2017/state-of-go/img/bench_log.png b/2017/state-of-go/img/bench_log.png
new file mode 100644
index 0000000..b1e2d9c
--- /dev/null
+++ b/2017/state-of-go/img/bench_log.png
Binary files differ
diff --git a/2017/state-of-go/img/benchmark.png b/2017/state-of-go/img/benchmark.png
new file mode 100644
index 0000000..18f7163
--- /dev/null
+++ b/2017/state-of-go/img/benchmark.png
Binary files differ
diff --git a/2017/state-of-go/img/cgo.png b/2017/state-of-go/img/cgo.png
new file mode 100644
index 0000000..b05fc8d
--- /dev/null
+++ b/2017/state-of-go/img/cgo.png
Binary files differ
diff --git a/2017/state-of-go/img/defer.png b/2017/state-of-go/img/defer.png
new file mode 100644
index 0000000..cf96e09
--- /dev/null
+++ b/2017/state-of-go/img/defer.png
Binary files differ
diff --git a/2017/state-of-go/img/flying.png b/2017/state-of-go/img/flying.png
new file mode 100644
index 0000000..7157563
--- /dev/null
+++ b/2017/state-of-go/img/flying.png
Binary files differ
diff --git a/2017/state-of-go/img/gct1.png b/2017/state-of-go/img/gct1.png
new file mode 100644
index 0000000..22dbb67
--- /dev/null
+++ b/2017/state-of-go/img/gct1.png
Binary files differ
diff --git a/2017/state-of-go/img/gct2.png b/2017/state-of-go/img/gct2.png
new file mode 100644
index 0000000..a3f8a29
--- /dev/null
+++ b/2017/state-of-go/img/gct2.png
Binary files differ
diff --git a/2017/state-of-go/img/gct3.png b/2017/state-of-go/img/gct3.png
new file mode 100644
index 0000000..e1364f9
--- /dev/null
+++ b/2017/state-of-go/img/gct3.png
Binary files differ
diff --git a/2017/state-of-go/img/gct4.png b/2017/state-of-go/img/gct4.png
new file mode 100644
index 0000000..d8f0668
--- /dev/null
+++ b/2017/state-of-go/img/gct4.png
Binary files differ
diff --git a/2017/state-of-go/img/gct5.png b/2017/state-of-go/img/gct5.png
new file mode 100644
index 0000000..f34d6ea
--- /dev/null
+++ b/2017/state-of-go/img/gct5.png
Binary files differ
diff --git a/2017/state-of-go/img/gctcpu.png b/2017/state-of-go/img/gctcpu.png
new file mode 100644
index 0000000..9d57649
--- /dev/null
+++ b/2017/state-of-go/img/gctcpu.png
Binary files differ
diff --git a/2017/state-of-go/img/http.png b/2017/state-of-go/img/http.png
new file mode 100644
index 0000000..e89d9bd
--- /dev/null
+++ b/2017/state-of-go/img/http.png
Binary files differ
diff --git a/2017/state-of-go/img/http2.png b/2017/state-of-go/img/http2.png
new file mode 100644
index 0000000..552eb91
--- /dev/null
+++ b/2017/state-of-go/img/http2.png
Binary files differ
diff --git a/2017/state-of-go/img/meetups.png b/2017/state-of-go/img/meetups.png
new file mode 100644
index 0000000..85a508c
--- /dev/null
+++ b/2017/state-of-go/img/meetups.png
Binary files differ
diff --git a/2017/state-of-go/img/more.png b/2017/state-of-go/img/more.png
new file mode 100644
index 0000000..3160004
--- /dev/null
+++ b/2017/state-of-go/img/more.png
Binary files differ
diff --git a/2017/state-of-go/img/mutex_all.png b/2017/state-of-go/img/mutex_all.png
new file mode 100644
index 0000000..8429e77
--- /dev/null
+++ b/2017/state-of-go/img/mutex_all.png
Binary files differ
diff --git a/2017/state-of-go/img/mutex_all_zoom.png b/2017/state-of-go/img/mutex_all_zoom.png
new file mode 100644
index 0000000..7a1246c
--- /dev/null
+++ b/2017/state-of-go/img/mutex_all_zoom.png
Binary files differ
diff --git a/2017/state-of-go/img/mutex_noprofile.png b/2017/state-of-go/img/mutex_noprofile.png
new file mode 100644
index 0000000..42a579e
--- /dev/null
+++ b/2017/state-of-go/img/mutex_noprofile.png
Binary files differ
diff --git a/2017/state-of-go/img/mutex_procs.png b/2017/state-of-go/img/mutex_procs.png
new file mode 100644
index 0000000..340b9c0
--- /dev/null
+++ b/2017/state-of-go/img/mutex_procs.png
Binary files differ
diff --git a/2017/state-of-go/img/mutex_profile.png b/2017/state-of-go/img/mutex_profile.png
new file mode 100644
index 0000000..ff7daad
--- /dev/null
+++ b/2017/state-of-go/img/mutex_profile.png
Binary files differ
diff --git a/2017/state-of-go/img/party-gopher.png b/2017/state-of-go/img/party-gopher.png
new file mode 100644
index 0000000..d18690f
--- /dev/null
+++ b/2017/state-of-go/img/party-gopher.png
Binary files differ
diff --git a/2017/state-of-go/img/wwg.png b/2017/state-of-go/img/wwg.png
new file mode 100644
index 0000000..a86fbd2
--- /dev/null
+++ b/2017/state-of-go/img/wwg.png
Binary files differ
diff --git a/2017/state-of-go/runtime/mapcrash.go b/2017/state-of-go/runtime/mapcrash.go
new file mode 100644
index 0000000..60a07bb
--- /dev/null
+++ b/2017/state-of-go/runtime/mapcrash.go
@@ -0,0 +1,26 @@
+/// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"sync"
+)
+
+func main() {
+	const workers = 100 // what if we have 1, 2, 25?
+
+	var wg sync.WaitGroup
+	wg.Add(workers)
+	m := map[int]int{}
+	for i := 1; i <= workers; i++ {
+		go func(i int) {
+			for j := 0; j < i; j++ {
+				m[i]++ // HL
+			}
+			wg.Done()
+		}(i)
+	}
+	wg.Wait()
+	fmt.Println(m)
+}
diff --git a/2017/state-of-go/runtime/mutex/main.go b/2017/state-of-go/runtime/mutex/main.go
new file mode 100644
index 0000000..3ff277e
--- /dev/null
+++ b/2017/state-of-go/runtime/mutex/main.go
@@ -0,0 +1,89 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"sort"
+	"sync"
+)
+
+func main() {
+	n := flag.Int("n", 10, "maximum number to consider")
+	flag.Parse()
+
+	type pair struct{ n, c int }
+	var pairs []pair
+	for n, c := range countFactorsWideSection(*n) {
+		pairs = append(pairs, pair{n, c})
+	}
+	sort.Slice(pairs, func(i, j int) bool { return pairs[i].n < pairs[j].n })
+	for _, p := range pairs {
+		fmt.Printf("%3d: %3d\n", p.n, p.c)
+	}
+}
+
+func countFactorsNarrowSection(n int) map[int]int {
+	m := map[int]int{}
+	var mu sync.Mutex
+	var wg sync.WaitGroup
+
+	wg.Add(n - 1)
+	for i := 2; i <= n; i++ {
+		go func(i int) {
+			// NARROW OMIT
+			for _, f := range factors(i) {
+				mu.Lock() // HL
+				m[f]++
+				mu.Unlock() // HL
+			}
+			wg.Done()
+		}(i)
+	}
+	wg.Wait()
+	return m
+}
+
+func countFactorsWideSection(n int) map[int]int {
+	m := map[int]int{}
+	var mu sync.Mutex
+	var wg sync.WaitGroup
+
+	wg.Add(n - 1)
+	for i := 2; i <= n; i++ {
+		go func(i int) {
+			// WIDE OMIT
+			mu.Lock() // HL
+			for _, f := range factors(i) {
+				m[f]++
+			}
+			mu.Unlock() // HL
+			wg.Done()
+		}(i)
+	}
+	wg.Wait()
+	return m
+}
+
+func countFactorsSeq(n int) map[int]int {
+	m := map[int]int{}
+	for i := 2; i <= n; i++ {
+		for _, f := range factors(i) { // HL
+			m[f]++ // HL
+		} // HL
+	}
+	return m
+}
+
+func factors(v int) []int {
+	var fs []int
+	for v > 1 {
+		for f := 2; f <= v; f++ {
+			if v%f == 0 {
+				v = v / f
+				fs = append(fs, f)
+				break
+			}
+		}
+	}
+	return fs
+}
diff --git a/2017/state-of-go/runtime/mutex/main_test.go b/2017/state-of-go/runtime/mutex/main_test.go
new file mode 100644
index 0000000..be80382
--- /dev/null
+++ b/2017/state-of-go/runtime/mutex/main_test.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+	"fmt"
+	"testing"
+)
+
+func benchFunc(b *testing.B, f func(int) map[int]int) {
+	for n := 10; n <= 10000; n *= 10 {
+		b.Run(fmt.Sprint(n), func(b *testing.B) {
+			for i := 0; i < b.N; i++ {
+				f(n)
+			}
+		})
+	}
+}
+
+func BenchmarkNarrowSection(b *testing.B) { benchFunc(b, countFactorsNarrowSection) }
+func BenchmarkWideSection(b *testing.B)   { benchFunc(b, countFactorsWideSection) }
+func BenchmarkSq(b *testing.B)            { benchFunc(b, countFactorsSeq) }
diff --git a/2017/state-of-go/runtime/mutex/mutex.out b/2017/state-of-go/runtime/mutex/mutex.out
new file mode 100644
index 0000000..59ea5b6
--- /dev/null
+++ b/2017/state-of-go/runtime/mutex/mutex.out
@@ -0,0 +1,5 @@
+--- mutex:
+cycles/second=2793570419
+sampling period=1
+19155477440 1345723 @ 0x10575f1 0x10f0266 0x1051c61
+15032825888 1091734 @ 0x10575f1 0x10f0106 0x1051c61
diff --git a/2017/state-of-go/runtime/mutex/mutex.test b/2017/state-of-go/runtime/mutex/mutex.test
new file mode 100755
index 0000000..12ef088
--- /dev/null
+++ b/2017/state-of-go/runtime/mutex/mutex.test
Binary files differ
diff --git a/2017/state-of-go/stdlib/http2/cert.pem b/2017/state-of-go/stdlib/http2/cert.pem
new file mode 100644
index 0000000..6612f86
--- /dev/null
+++ b/2017/state-of-go/stdlib/http2/cert.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/jCCAeagAwIBAgIQJmT9thxoUtA2lXTupgK7cDANBgkqhkiG9w0BAQsFADAS
+MRAwDgYDVQQKEwdBY21lIENvMB4XDTE3MDEyOTIwNTUxNVoXDTE4MDEyOTIwNTUx
+NVowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAL4TpHOAUq54uyrGj06xE7TZ0+SPVXWiydYP4hg1QKkhFcwc2NHwnzIf
+wgFTBGXz7TrrWpLHNJLvWKjXeFDQo35BKfWd4u2TKMU43HUdPSgFT8skhbP3FYbJ
+WIYoD2J2BaMxylMYfGGm2azqnuTPc0/Qn98RLDFe9TAykDuel3YWvdlQJGYo+cth
+bUURzTzvQzmnMpQ+CxpU+TdBz4Ngh+e29CEfZGOWXtd/7ydWyK3BznzgPLBTuU9N
+3f0mC40sDayTTBaUiI+GkSEeqK0Ft/gwozM1KOK8l5lfqtOYzqhLjAAzcD5Hf8ZK
+GsnC5ojJkuypLY/uAq/BX+2oL4wZi+UCAwEAAaNQME4wDgYDVR0PAQH/BAQDAgWg
+MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwGQYDVR0RBBIwEIIO
+bG9jYWxob3N0OjgwODAwDQYJKoZIhvcNAQELBQADggEBACqdIJJFcow4WJ0YW6l2
+bjYj0fAf7GZdLP3tRYzxcj1ORoJf9FIN2jwlraEgtXN2hhEK15nVgrxbrWzDMkrx
+e9VhGOSWnixo3rnN2i2vUEaKqZubawhzcU2/5ZnP6Q1JH00WYqavNYhfQIOdNI6e
+s6xqz+MUhYSw3ZhjgFGMAeed0fRIksWRlbioTAOk1TxoChTaa+N+tTf9Niwr1Eaz
+Cy6ygwmlJYhi/iutknG2Fp+MjDPX2hmemgoJYbn8i1C0DQuNlKxeNwY3zfEBxazu
+nLFbDwbyCIO+2q1+eervE1TF5Vw6ODNqRWKgsl9VnGmL369p8H1plU//SnQh4TIW
+nKc=
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/2017/state-of-go/stdlib/http2/http2.go b/2017/state-of-go/stdlib/http2/http2.go
new file mode 100644
index 0000000..e370961
--- /dev/null
+++ b/2017/state-of-go/stdlib/http2/http2.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+	"fmt"
+	"go/build"
+	"log"
+	"net/http"
+	"path/filepath"
+)
+
+var cert, key string
+
+func init() {
+	pkg, err := build.Import("golang.org/x/talks/2017/state-of-go/http2", ".", build.FindOnly)
+	if err != nil {
+		log.Fatal(err)
+	}
+	cert = filepath.Join(pkg.Dir, "cert.pem")
+	key = filepath.Join(pkg.Dir, "key.pem")
+}
+
+func main() {
+	http.HandleFunc("/", rootHandler)
+	http.HandleFunc("/style.css", cssHandler)
+
+	go func() {
+		log.Fatal(http.ListenAndServeTLS("127.0.0.1:8081", cert, key, nil))
+	}()
+	log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
+}
+
+func rootHandler(w http.ResponseWriter, r *http.Request) {
+	if p, ok := w.(http.Pusher); ok { // HL
+		err := p.Push("/style.css", nil) // HL
+		if err != nil {
+			log.Printf("could not push: %v", err)
+		}
+	}
+
+	fmt.Fprintln(w, html)
+}
+
+func cssHandler(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprintln(w, css)
+}
+
+const (
+	html = `
+<html>
+<head>
+	<link rel="stylesheet" href="/style.css">
+	<title>HTTP2 push test</title>
+</head>
+<body>
+	<h1>Hello</h1>
+</body>
+</html>
+`
+	css = `
+h1 {
+    color: red;
+    text-align: center;
+    text-shadow: green 0 0 40px;
+    font-size: 10em;
+}
+`
+)
diff --git a/2017/state-of-go/stdlib/http2/key.pem b/2017/state-of-go/stdlib/http2/key.pem
new file mode 100644
index 0000000..cd458b0
--- /dev/null
+++ b/2017/state-of-go/stdlib/http2/key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAvhOkc4BSrni7KsaPTrETtNnT5I9VdaLJ1g/iGDVAqSEVzBzY
+0fCfMh/CAVMEZfPtOutaksc0ku9YqNd4UNCjfkEp9Z3i7ZMoxTjcdR09KAVPyySF
+s/cVhslYhigPYnYFozHKUxh8YabZrOqe5M9zT9Cf3xEsMV71MDKQO56Xdha92VAk
+Zij5y2FtRRHNPO9DOacylD4LGlT5N0HPg2CH57b0IR9kY5Ze13/vJ1bIrcHOfOA8
+sFO5T03d/SYLjSwNrJNMFpSIj4aRIR6orQW3+DCjMzUo4ryXmV+q05jOqEuMADNw
+Pkd/xkoaycLmiMmS7Kktj+4Cr8Ff7agvjBmL5QIDAQABAoIBAFUAe76bWF5l523N
+tjC+x81MzJGd993Pmut71uR0jCIWhaTuEZhxPwAva5ckBQeC+kgrECoro7tCBigb
+k9awNy0y/wy0OtN7x/PK5ewJ01ueZHy4LIK6sInCaTA1oguqNAMzNQPMI3OYJihK
+FBzHGQ5MgfJDv7ukd4nCUvYWii1oYNoNsc/A2uYPonLRJfAnPLAfZ+ikiPdydLum
+4/nLxBKD5Yke/Y8lpxh1RzZz28G2YX9WKTwAZuBo9AJ+4U7bkjgSof5nUOlwvbcg
+Z9d/DMdWkI7yQxhaQzXaFWNmvpt6NvqHUSPYnqQnaCDPCRbUR8u3YU8vwCRN60ss
+dzac+4ECgYEA2+jYj3pllRMaifnAEVg4MS3mpQ9o+XCzQb4H7ecFztXh16Uom0e/
+gnI1cqutMdhcyAoy++RxTXLCca9xd5e8tC3N2kZG9+3vUV0kn43LbVY4sCK150wM
+UHy7TMdoAqg36W3AhTunIZ1sgDM/qc5LcesXeHVDUQF2NmteZnga9m0CgYEA3UVq
+y42mBXMuCqY8GZbTQlZqowqBjNLddhVxXjuuAHcxCTlLmD63L7UWx24dOAeVbEfq
+Aev6tsOiaNB+JPEGYfQf9ESRxbt3ZFArYP6+be94NmETzNVtekwCJqfp7Cjyzj1a
+C00r5L0qOSP0P8lvSrBOwT2Yxq3JVNL3EDj1YFkCgYAG7dpNAw1KWjS+3ji4EzCK
+FCktUEP9gBiV3LgBPgNH1iNsmQ6jfepB4PlgKJqndGrP/spGd5c+WnxX+rA3lXdj
+sgHHe+lmjH+675Vk1aHwSwQ0QJO8uv+0VYnNTIcxdj10xHmJeSy1+XDexT6fShnE
+eCTgLcm2NraT1mQ+FFC9LQKBgC3rtyMxbXAaHEcHgteIrqVIy+60QniYVm+oOZPl
+7NGZ6upQIrkg4uGawnR3DMdRA5iqQA1QDQMbDLyV8Gf4QWvYvzzxchNIOZnu7WG+
+3IRyO0+FzBcpgAPufE/Lb0eco+9bWjGYPXDGNVoQdSM7ycYFWwLNpsQs0uiws6eB
+OqNZAoGBALM48r7mA/3KuFH6CcRcsCGfcCoXkzjGogsxIfN2yKmNUu33koYkzUxc
+ihgXBGwQ5Sqdrqz9BYh1CArXQvVkb+Q0wEAVS1ikbMozTQQelI8FRiToVJF37zoS
+UgA+CE3BSuHa+zMBue+guh/+ssMlKPnpzTgWjkk50GXUAMy/NVf5
+-----END RSA PRIVATE KEY-----
diff --git a/2017/state-of-go/stdlib/json_old.go b/2017/state-of-go/stdlib/json_old.go
new file mode 100644
index 0000000..acb7041
--- /dev/null
+++ b/2017/state-of-go/stdlib/json_old.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+)
+
+func main() {
+	in := []byte(`
+    {
+        "full_name": "Gopher",
+        "age": 7,
+        "social_security": 1234
+    }`)
+
+	var p Person
+	if err := json.Unmarshal(in, &p); err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf("%+v\n", p)
+}
+
+type Person struct {
+	Name     string
+	AgeYears int
+	SSN      int
+}
+
+func (p *Person) UnmarshalJSON(data []byte) error {
+	var aux struct {
+		Name     string `json:"full_name"`
+		AgeYears int    `json:"age"`
+		SSN      int    `json:"social_security"`
+	}
+	if err := json.Unmarshal(data, &aux); err != nil {
+		return err
+	}
+	*p = Person{
+		Name:     aux.Name,
+		AgeYears: aux.AgeYears,
+		SSN:      aux.SSN,
+	}
+	*p = Person(aux)
+	return nil
+}
diff --git a/2017/state-of-go/stdlib/plugin/main.go b/2017/state-of-go/stdlib/plugin/main.go
new file mode 100644
index 0000000..5d5482f
--- /dev/null
+++ b/2017/state-of-go/stdlib/plugin/main.go
@@ -0,0 +1,23 @@
+package main
+
+import "plugin"
+
+func main() {
+	p, err := plugin.Open("plugin_name.so")
+	if err != nil {
+		panic(err)
+	}
+
+	v, err := p.Lookup("V")
+	if err != nil {
+		panic(err)
+	}
+
+	f, err := p.Lookup("F")
+	if err != nil {
+		panic(err)
+	}
+
+	*v.(*int) = 7
+	f.(func())() // prints "Hello, number 7"
+}
diff --git a/2017/state-of-go/stdlib/plugin/plugin.go b/2017/state-of-go/stdlib/plugin/plugin.go
new file mode 100644
index 0000000..99bb60d
--- /dev/null
+++ b/2017/state-of-go/stdlib/plugin/plugin.go
@@ -0,0 +1,10 @@
+package main
+
+// No C code needed.
+import "C"
+
+import "fmt"
+
+var V int
+
+func F() { fmt.Printf("Hello, number %d\n", V) }
diff --git a/2017/state-of-go/stdlib/shutdown.go b/2017/state-of-go/stdlib/shutdown.go
new file mode 100644
index 0000000..4dfb915
--- /dev/null
+++ b/2017/state-of-go/stdlib/shutdown.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+	"os/signal"
+)
+
+func main() {
+	// subscribe to SIGINT signals
+	quit := make(chan os.Signal) // HL
+	signal.Notify(quit, os.Interrupt)
+
+	srv := &http.Server{Addr: ":8080", Handler: http.DefaultServeMux}
+	go func() { // HL
+		<-quit // HL
+		log.Println("Shutting down server...")
+		if err := srv.Shutdown(context.Background()); err != nil { // HL
+			log.Fatalf("could not shutdown: %v", err)
+		}
+	}()
+
+	http.HandleFunc("/", handler)
+	err := srv.ListenAndServe()
+	if err != http.ErrServerClosed { // HL
+		log.Fatalf("listen: %s\n", err)
+	}
+	log.Println("Server gracefully stopped")
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprintln(w, "hello")
+}
diff --git a/2017/state-of-go/stdlib/sort/sort.go b/2017/state-of-go/stdlib/sort/sort.go
new file mode 100644
index 0000000..d3d75c0
--- /dev/null
+++ b/2017/state-of-go/stdlib/sort/sort.go
@@ -0,0 +1,50 @@
+package main
+
+/*
+
+import (
+	"fmt"
+	"sort"
+)
+
+type Person struct {
+	Name     string
+	AgeYears int
+	SSN      int
+}
+
+type byName []Person
+
+func (b byName) Len() int           { return len(b) }
+func (b byName) Less(i, j int) bool { return b[i].Name < b[j].Name }
+func (b byName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+type byAge []Person
+
+func (b byAge) Len() int           { return len(b) }
+func (b byAge) Less(i, j int) bool { return b[i].AgeYears < b[j].AgeYears }
+func (b byAge) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+type bySSN []Person
+
+func (b bySSN) Len() int           { return len(b) }
+func (b bySSN) Less(i, j int) bool { return b[i].SSN < b[j].SSN }
+func (b bySSN) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+func main() {
+	p := []Person{
+		{"Alice", 20, 1234},
+		{"Bob", 10, 2345},
+		{"Carla", 15, 3456},
+	}
+
+	sort.Sort(byName(p))
+	fmt.Printf("sorted by name: %v\n", p)
+
+	sort.Sort(byAge(p))
+	fmt.Printf("sorted by age: %v\n", p)
+
+	sort.Sort(bySSN(p))
+	fmt.Printf("sorted by SSN: %v\n", p)
+}
+*/
diff --git a/2017/state-of-go/stdlib/sort/sort_new.go b/2017/state-of-go/stdlib/sort/sort_new.go
new file mode 100644
index 0000000..2b9e76c
--- /dev/null
+++ b/2017/state-of-go/stdlib/sort/sort_new.go
@@ -0,0 +1,31 @@
+package main
+
+/*
+import (
+	"fmt"
+	"sort"
+)
+
+type Person struct {
+	Name     string
+	AgeYears int
+	SSN      int
+}
+
+func main() {
+	p := []Person{
+		{"Alice", 20, 1234},
+		{"Bob", 10, 2345},
+		{"Carla", 15, 3456},
+	}
+
+	sort.Slice(p, func(i, j int) bool { return p[i].Name < p[j].Name })
+	fmt.Printf("sorted by name: %v\n", p)
+
+	sort.Slice(p, func(i, j int) bool { return p[i].AgeYears < p[j].AgeYears })
+	fmt.Printf("sorted by age: %v\n", p)
+
+	sort.Slice(p, func(i, j int) bool { return p[i].SSN < p[j].SSN })
+	fmt.Printf("sorted by SSN: %v\n", p)
+}
+*/
diff --git a/2017/state-of-go/stdlib/sort/sort_test.go b/2017/state-of-go/stdlib/sort/sort_test.go
new file mode 100644
index 0000000..0c50905
--- /dev/null
+++ b/2017/state-of-go/stdlib/sort/sort_test.go
@@ -0,0 +1,66 @@
+package main
+
+import (
+	"fmt"
+	"math/rand"
+	"os"
+	"sort"
+	"strconv"
+	"testing"
+)
+
+type Person struct {
+	Name     string
+	AgeYears int
+	SSN      int
+}
+
+type byName []Person
+
+func (b byName) Len() int           { return len(b) }
+func (b byName) Less(i, j int) bool { return b[i].Name < b[j].Name }
+func (b byName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+type byAge []Person
+
+func (b byAge) Len() int           { return len(b) }
+func (b byAge) Less(i, j int) bool { return b[i].AgeYears < b[j].AgeYears }
+func (b byAge) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+type bySSN []Person
+
+func (b bySSN) Len() int           { return len(b) }
+func (b bySSN) Less(i, j int) bool { return b[i].SSN < b[j].SSN }
+func (b bySSN) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+func BenchmarkSortSort(b *testing.B) {
+	p := manyPeople()
+	for i := 0; i < b.N; i++ {
+		sort.Sort(byName(p))
+		sort.Sort(byAge(p))
+		sort.Sort(bySSN(p))
+	}
+}
+
+func BenchmarkSortSlice(b *testing.B) {
+	p := manyPeople()
+	for i := 0; i < b.N; i++ {
+		sort.Slice(p, func(i, j int) bool { return p[i].Name < p[j].Name })
+		sort.Slice(p, func(i, j int) bool { return p[i].AgeYears < p[j].AgeYears })
+		sort.Slice(p, func(i, j int) bool { return p[i].SSN < p[j].SSN })
+	}
+}
+
+func manyPeople() []Person {
+	n, err := strconv.Atoi(os.Getenv("N"))
+	if err != nil {
+		panic(err)
+	}
+	p := make([]Person, n)
+	for i := range p {
+		p[i].AgeYears = rand.Intn(100)
+		p[i].SSN = rand.Intn(10000000000)
+		p[i].Name = fmt.Sprintf("Mr or Ms %d", p[i].AgeYears)
+	}
+	return p
+}
diff --git a/2017/state-of-go/tools/gobug.sh b/2017/state-of-go/tools/gobug.sh
new file mode 100644
index 0000000..5100fea
--- /dev/null
+++ b/2017/state-of-go/tools/gobug.sh
@@ -0,0 +1,2 @@
+#! /bin/bash
+go bug
\ No newline at end of file
diff --git a/2017/state-of-go/tools/gofix.go b/2017/state-of-go/tools/gofix.go
new file mode 100644
index 0000000..9f62814
--- /dev/null
+++ b/2017/state-of-go/tools/gofix.go
@@ -0,0 +1,12 @@
+package main
+
+import "golang.org/x/net/context" // HL
+
+func main() {
+	ctx := context.Background()
+	doSomething(ctx)
+}
+
+func doSomething(ctx context.Context) {
+	// doing something
+}
diff --git a/2017/state-of-go/tools/gofix.sh b/2017/state-of-go/tools/gofix.sh
new file mode 100644
index 0000000..f0096e6
--- /dev/null
+++ b/2017/state-of-go/tools/gofix.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+go tool fix -diff -force=context state-of-go/tools/gofix.go
\ No newline at end of file
diff --git a/2017/state-of-go/tools/govet.go b/2017/state-of-go/tools/govet.go
new file mode 100644
index 0000000..520e8a7
--- /dev/null
+++ b/2017/state-of-go/tools/govet.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+	"io"
+	"log"
+	"net/http"
+	"os"
+)
+
+func main() {
+	res, err := http.Get("https://golang.org")
+	defer res.Body.Close()
+	if err != nil {
+		log.Fatal(err)
+	}
+	io.Copy(os.Stdout, res.Body)
+}
diff --git a/2017/state-of-go/tools/govet.sh b/2017/state-of-go/tools/govet.sh
new file mode 100644
index 0000000..fd3f847
--- /dev/null
+++ b/2017/state-of-go/tools/govet.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+go vet state-of-go/tools/govet.go
\ No newline at end of file