github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/blog/content/playground.article (about) 1 Inside the Go Playground 2 12 Dec 2013 3 Tags: playground 4 5 Andrew Gerrand 6 7 * Introduction 8 9 In September 2010 we [[http://blog.golang.org/introducing-go-playground][introduced the Go Playground]], 10 a web service that compiles and executes arbitrary Go code and returns the 11 program output. 12 13 If you're a Go programmer then you have probably already used the playground 14 by using the [[http://play.golang.org][Go Playground]] directly, 15 taking the [[http://tour.golang.org][Go Tour]], 16 or running [[http://golang.org/pkg/strings/#pkg-examples][executable examples]] 17 from the Go documentation. 18 19 You may also have used it by clicking one of the "Run" buttons in a slide 20 deck on [[http://talks.golang.org/][talks.golang.org]] or a post on this 21 very blog 22 (such as the [[http://blog.golang.org/strings][recent article on Strings]]). 23 24 In this article we will take a look at how the playground is implemented 25 and integrated with these services. 26 The implementation involves a variant operating system environment and runtime 27 and our description here assumes you have some familiarity with systems 28 programming using Go. 29 30 31 * Overview 32 33 .image playground/overview.png 34 35 The playground service has three parts: 36 37 - A back end that runs on Google's servers. It receives RPC requests, compiles the user program using the gc tool chain, executes the user program, and returns the program output (or compilation errors) as the RPC response. 38 - A front end that runs on [[https://developers.google.com/appengine/][Google App Engine]]. It receives HTTP requests from the client and makes corresponding RPC requests to the back end. It also does some caching. 39 - A JavaScript client that implements the user interface and makes HTTP requests to the front end. 40 41 42 * The back end 43 44 The back end program itself is trivial, so we won't discuss its implementation 45 here. The interesting part is how we safely execute arbitrary user code in a 46 secure environment while still providing core functionality such as time, the 47 network, and the file system. 48 49 To isolate user programs from Google's infrastructure, the back end runs 50 them under [[https://developers.google.com/native-client/][Native Client]] 51 (or "NaCl"), a technology developed by Google to permit the safe execution of 52 x86 programs inside web browsers. The back end uses a special version of the gc 53 tool chain that generates NaCl executables. 54 55 (This special tool chain will be merged into the core for Go 1.3. 56 To learn more, read the [[http://golang.org/s/go13nacl][design document]]. 57 If you want to play with NaCl before then, you can 58 [[https://code.google.com/r/rsc-go13nacl/source/checkout][check out a fork]] 59 that has all the changes.) 60 61 NaCl limits the amount of CPU and RAM a program may consume, and it prevents 62 programs from accessing the network or file system. 63 This presents a problem, however. 64 Go's concurrency and networking support are among its key strengths, 65 and access to the file system is vital for many programs. 66 To demonstrate concurrency effectively we need time, and to demonstrate 67 networking and the file system we obviously need a network and a file system. 68 69 Although all these things are supported today, the first version of the 70 playground, launched in 2010, had none of them. 71 The current time was fixed at 10 November 2009, `time.Sleep` had no effect, 72 and most functions of the `os` and `net` packages were stubbed out to 73 return an `EINVALID` error. 74 75 A year ago we 76 [[https://groups.google.com/d/msg/golang-nuts/JBsCrDEVyVE/30MaQsiQcWoJ][implemented fake time]] 77 in the playground, so that programs that sleep would behave correctly. 78 A more recent update to the playground introduced a fake network stack and a 79 fake file system, making the playground's tool chain similar to a normal 80 Go tool chain. 81 These facilities are described in the following sections. 82 83 84 ** Faking time 85 86 Playground programs are limited in the amount of CPU time and memory they can 87 use, but they are also restricted in how much real time they can use. 88 This is because each running program consumes resources on the back end 89 and any stateful infrastructure between it and the client. 90 Limiting the run time of each playground program makes our service more 91 predictable and defends us against denial of service attacks. 92 93 But these restrictions become stifling when running code that uses time. 94 The [[http://talks.golang.org/2012/concurrency.slide][Go Concurrency Patterns]] 95 talk demonstrates concurrency with examples that use timing functions like 96 [[http://golang.org/pkg/time/#Sleep][`time.Sleep`]] and 97 [[http://golang.org/pkg/time/#After][`time.After`]]. 98 When run under early versions of the playground, these programs' sleeps would 99 have no effect and their behavior would be strange (and sometimes wrong). 100 101 By using a clever trick we can make a Go program _think_ that it is sleeping, 102 when really the sleeps take no time at all. 103 To explain the trick we first need to understand how the scheduler manages 104 sleeping goroutines. 105 106 When a goroutine calls `time.Sleep` (or similar) the scheduler adds a timer to 107 a heap of pending timers and puts the goroutine to sleep. 108 Meanwhile, a special timer goroutine manages that heap. 109 When the timer goroutine starts it tells the scheduler to wake 110 it when the next pending timer is ready to fire and then sleeps. 111 When it wakes up it checks which timers have expired, wakes the appropriate 112 goroutines, and goes back to sleep. 113 114 The trick is to change the condition that wakes the timer goroutine. 115 Instead of waking it after a specific time period, we modify the scheduler to 116 wait for a deadlock; the state where all goroutines are blocked. 117 118 The playground version of the runtime maintains its own internal clock. When 119 the modified scheduler detects a deadlock it checks whether any timers are 120 pending. If so, it advances the internal clock to the trigger time of the 121 earliest timer and then wakes the timer goroutine. Execution continues and the 122 program believes that time has passed, when in fact the sleep was nearly 123 instantaneous. 124 125 These changes to the scheduler can be found in [[https://code.google.com/r/rsc-go13nacl/source/diff?spec=svnc9a5be0fa2db5edcf8f8788da9f7eed323df6c07&r=c9a5be0fa2db5edcf8f8788da9f7eed323df6c07&format=side&path=/src/pkg/runtime/proc.c#sc_svn35d5bae6aac826e6db1851ea14fe9f8da95088a8_2314][`proc.c`]] and [[https://code.google.com/r/rsc-go13nacl/source/diff?spec=svnc9a5be0fa2db5edcf8f8788da9f7eed323df6c07&r=c9a5be0fa2db5edcf8f8788da9f7eed323df6c07&format=side&path=/src/pkg/runtime/time.goc#sc_svnc9a5be0fa2db5edcf8f8788da9f7eed323df6c07_178][`time.goc`]]. 126 127 Fake time fixes the issue of resource exhaustion on the back end, but what 128 about the program output? It would be odd to see a program that sleeps run to 129 completion correctly without taking any time. 130 131 The following program prints the current time each second and then exits after 132 three seconds. Try running it. 133 134 .play -edit playground/time.go /^func main/,$ 135 136 How does this work? It is a collaboration between the back end, front end, and client. 137 138 We capture the timing of each write to standard output and standard error and 139 provide it to the client. Then the client can "play back" the writes with the 140 correct timing, so that the output appears just as if the program were running 141 locally. 142 143 The playground's `runtime` package provides a special 144 [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/runtime/sys_nacl_amd64p32.s?r=1f01be1a1dc2#54][`write` function]] 145 that includes a small "playback header" before each write. 146 The playback header comprises a magic string, the current time, and the 147 length of the write data. A write with a playback header has this structure: 148 149 0 0 P B <8-byte time> <4-byte data length> <data> 150 151 The raw output of the program above looks like this: 152 153 \x00\x00PB\x11\x74\xef\xed\xe6\xb3\x2a\x00\x00\x00\x00\x1e2009-11-10 23:00:01 +0000 UTC 154 \x00\x00PB\x11\x74\xef\xee\x22\x4d\xf4\x00\x00\x00\x00\x1e2009-11-10 23:00:02 +0000 UTC 155 \x00\x00PB\x11\x74\xef\xee\x5d\xe8\xbe\x00\x00\x00\x00\x1e2009-11-10 23:00:03 +0000 UTC 156 157 The front end parses this output as a series of events 158 and returns a list of events to the client as a JSON object: 159 160 { 161 "Errors": "", 162 "Events": [ 163 { 164 "Delay": 1000000000, 165 "Message": "2009-11-10 23:00:01 +0000 UTC\n" 166 }, 167 { 168 "Delay": 1000000000, 169 "Message": "2009-11-10 23:00:02 +0000 UTC\n" 170 }, 171 { 172 "Delay": 1000000000, 173 "Message": "2009-11-10 23:00:03 +0000 UTC\n" 174 } 175 ] 176 } 177 178 The JavaScript client (running in the user's web browser) then plays back the 179 events using the provided delay intervals. 180 To the user it appears that the program is running in real time. 181 182 183 ** Faking the file system 184 185 Programs built with the Go's NaCl tool chain cannot access the local machine's 186 file system. Instead, the `syscall` package's file-related functions 187 (`Open`, `Read`, `Write`, and so on) operate on an in-memory file system 188 that is implemented by the `syscall` package itself. 189 Since package `syscall` is the interface between the Go code and the operating 190 system kernel, user programs see the file system exactly the same way as they 191 would a real one. 192 193 The following example program writes data to a file, and then copies 194 its contents to standard output. Try running it. (You can edit it, too!) 195 196 .play -edit playground/os.go /^func main/,$ 197 198 When a process starts, the file system is populated with some devices under 199 `/dev` and an empty `/tmp` directory. The program can manipulate the file 200 system as usual, but when the process exits any changes to the file system are 201 lost. 202 203 There is also a provision to load a zip file into the file system at init time 204 (see [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/unzip_nacl.go][`unzip_nacl.go`]]). 205 So far we have only used the unzip facility to provide the data files required 206 to run the standard library tests, but we intend to provide playground programs 207 with a set of files that can be used in documentation examples, blog posts, and 208 the Go Tour. 209 210 The implementation can be found in the 211 [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fs_nacl.go][`fs_nacl.go`]] and 212 [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fd_nacl.go][`fd_nacl.go`]] files 213 (which, by virtue of their `_nacl` suffix, are built into package `syscall` only 214 when `GOOS` is set to `nacl`). 215 216 The file system itself is represented by the 217 [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fs_nacl.go?r=5317d308abe4078f68aecc14fd5fb95303d62a06#25][`fsys` struct]], 218 of which a global instance (named `fs`) is created during init time. 219 The various file-related functions then operate on `fs` instead of making the 220 actual system call. 221 For instance, here is the [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fs_nacl.go?r=1f01be1a1dc2#467][`syscall.Open`]] function: 222 223 func Open(path string, openmode int, perm uint32) (fd int, err error) { 224 fs.mu.Lock() 225 defer fs.mu.Unlock() 226 f, err := fs.open(path, openmode, perm&0777|S_IFREG) 227 if err != nil { 228 return -1, err 229 } 230 return newFD(f), nil 231 } 232 233 File descriptors are tracked by a global slice named 234 [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fd_nacl.go?r=1f01be1a1dc2#16][`files`]]. 235 Each file descriptor corresponds to a [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fd_nacl.go?r=1f01be1a1dc2#22][`file`]] 236 and each `file` provides a value that implements the [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fd_nacl.go?r=1f01be1a1dc2#29][`fileImpl`]] interface. 237 There are several implementations of the interface: 238 239 - regular files and devices (such as `/dev/random`) are represented by [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fs_nacl.go?r=1f01be1a1dc2#58][`fsysFile`]], 240 - standard input, output, and error are instances of [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/fd_nacl.go?r=1f01be1a1dc2#209][`naclFile`]], which uses system calls to interact with the actual files (these are a playground program's only way to interact with the outside world), 241 - network sockets have their own implementation, discussed in the next section. 242 243 244 ** Faking the network 245 246 Like the file system, the playground's network stack is an in-process fake 247 implemented by the `syscall` package. It permits playground projects to use 248 the loopback interface (`127.0.0.1`). Requests to other hosts will fail. 249 250 For an executable example, run the following program. It listens on a TCP port, 251 waits for an incoming connection, copies the data from that connection to 252 standard output, and exits. In another goroutine, it makes a connection to the 253 listening port, writes a string to the connection, and closes it. 254 255 .play -edit playground/net.go /^func main/,$ 256 257 The interface to the network is more complex than the one for files, so the 258 implementation of the fake network is larger and more complex than the fake 259 file system. It must simulate read and write timeouts, different address types 260 and protocols, and so on. 261 262 The implementation can be found in [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/net_nacl.go?r=1f01be1a1dc2][`net_nacl.go`]]. 263 A good place to start reading is [[https://code.google.com/r/rsc-go13nacl/source/browse/src/pkg/syscall/net_nacl.go?r=1f01be1a1dc2#419][`netFile`]], the network socket implementation of the `fileImpl` interface. 264 265 266 * The front end 267 268 The playground front end is another simple program (shorter than 100 lines). 269 It receives HTTP requests from the client, makes RPC requests to the back end, 270 and does some caching. 271 272 The front end serves an HTTP handler at `http://golang.org/compile`. 273 The handler expects a POST request with a `body` field 274 (the Go program to run) and an optional `version` field 275 (for most clients this should be `"2"`). 276 277 When the front end receives a compilation request it first checks 278 [[https://developers.google.com/appengine/docs/memcache/][memcache]] 279 to see if it has cached the results of a previous compilation of that source. 280 If found, it returns the cached response. 281 The cache prevents popular programs such as those on the 282 [[http://golang.org/][Go home page]] from overloading the back ends. 283 If there is no cached response, the front end makes an RPC request to the back 284 end, stores the response in memcache, parses the playback events, and returns 285 a JSON object to the client as the HTTP response (as described above). 286 287 288 * The client 289 290 The various sites that use the playground each share some common JavaScript 291 code for setting up the user interface (the code and output boxes, the run 292 button, and so on) and communicating with the playground front end. 293 294 This implementation is in the file 295 [[https://code.google.com/p/go/source/browse/godoc/static/playground.js?repo=tools][`playground.js`]] 296 in the `go.tools` repository, which can be imported from the 297 [[http://godoc.org/code.google.com/p/go.tools/godoc/static][`go.tools/godoc/static`]] package. 298 Some of it is clean and some is a bit crufty, as it is the result of 299 consolidating several divergent implementations of the client code. 300 301 The [[https://code.google.com/p/go/source/browse/godoc/static/playground.js?repo=tools#226][`playground`]] 302 function takes some HTML elements and turns them into an interactive 303 playground widget. You should use this function if you want to put the 304 playground on your own site (see 'Other clients' below). 305 306 The [[https://code.google.com/p/go/source/browse/godoc/static/playground.js?repo=tools#6][`Transport`]] 307 interface (not formally defined, this being JavaScript) 308 abstracts the user interface from the means of talking to the web front end. 309 [[https://code.google.com/p/go/source/browse/godoc/static/playground.js?repo=tools#43][`HTTPTransport`]] 310 is an implementation of `Transport` that speaks the HTTP-based protocol 311 described earlier. 312 [[https://code.google.com/p/go/source/browse/godoc/static/playground.js?repo=tools#115][`SocketTransport`]] 313 is another implementation that speaks WebSocket (see 'Playing offline' below). 314 315 To comply with the [[http://en.wikipedia.org/wiki/Same-origin_policy][same-origin policy]], 316 the various web servers (godoc, for instance) proxy requests to 317 `/compile` through to the playground service at `http://golang.org/compile`. 318 The common [[http://godoc.org/code.google.com/p/go.tools/playground][`go.tools/playground`]] 319 package does this proxying. 320 321 322 * Playing offline 323 324 Both the [[http://tour.golang.org][Go Tour]] and the 325 [[http://godoc.org/code.google.com/p/go.talks/present][Present Tool]] can be 326 run offline. This is great for people with limited internet connectivity 327 or presenters at conferences who cannot (and _should_ not) rely on a working 328 internet connection. 329 330 To run offline, the tools run their own version of the playground back end on 331 the local machine. The back end uses a regular Go tool chain with none of the 332 aforementioned modifications and uses a WebSocket to communicate with the 333 client. 334 335 The WebSocket back end implementation can be found in the 336 [[http://godoc.org/code.google.com/p/go.tools/playground/socket][`go.tools/playground/socket`]] package. 337 The [[http://talks.golang.org/2012/insidepresent.slide#1][Inside Present]] talk discusses this code in detail. 338 339 340 * Other clients 341 342 The playground service is used by more than just the official Go project 343 ([[https://gobyexample.com/][Go by Example]] is one other instance) 344 and we are happy for you to use it on your own site. All we ask is that 345 you [[mailto:golang-dev@googlegroups.com][contact us first]], 346 use a unique user agent in your requests (so we can identify you), and that 347 your service is of benefit to the Go community. 348 349 350 * Conclusion 351 352 From godoc to the tour to this very blog, the playground has become an 353 essential part of our Go documentation story. With the recent additions 354 of the fake file system and network stack we are excited to expand 355 our learning materials to cover those areas. 356 357 But, ultimately, the playground is just the tip of the iceberg. 358 With Native Client support scheduled for Go 1.3, 359 we look forward to seeing what the community can do with it. 360 361 _This_article_is_part_12_of_the_ 362 [[http://blog.gopheracademy.com/go-advent-2013][Go Advent Calendar]], 363 _a_series_of_daily_blog_posts_throughout_December_._