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_._