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