2015-02-25

Exploring Go's Runtime - How a Process Bootstraps Itself - Part I

I’ve spent a considerable portion of my professional life dealing with Java's HotSpot virtual machine and decided after acquiring a strong affinity for Go that I should attempt to build as deep of an understanding of the Go runtime as well. Much to my surprise, there is little formal documentation—outside of the source, so the opportunity seems ripe to consolidate this in an accessible digest.


Let me speak briefly about the format of this post:

  • This will be a multi-part series with individual posts dedicated to discrete topics. It is unclear to me how many parts there will be, but I anticipate between two to three.
  • For the purposes of this series, I will stick to the source found in Go 1.4.1. The runtime variant under examination is GNU Linux on amd64/x86-64 virtual machine.
  • Where possible, I reference source code in Go's runtime as well as a custom-built sandbox to demonstrate the concepts. The sandbox lives here but is not strictly necessary to follow along. This first post does not use the sandbox heavily, but other posts may!

Without further ado, let’s begin Part I of this series: How a Process Bootstraps Itself. We’ll start out with the entry point of a Go binary. This task is critical for tracing the flow of execution to discover how a Go binary bootstraps itself. If you want, you can clone the sandbox and create a local copy of it for self-study:


$ git clone https://github.com/matttproud/golang_runtime_exploration
$ cd golang_runtime_exploration
$ make  # output elided

We turn ourselves to a newly-generated file called entrypoint/entrypoint.disassembled I generated from a vanilla Go program entrypoint/entrypoint.go using the objdump tool, which states


start address 0x0000000000421760

in the prologue. This is the entry point into the generated Go binary. What does this mean, and where does it come from? Since we're running on Linux, and Linux uses the ELF executable format these days, Go’s linker is obligated to define an entrypoint offset in its executable file emissions. Specifically, Go’s linker defines this offset in the entry member of the Elf32_Ehdr structure. A call inside the linker to entryvalue() populates this member from the static INITVALUE variable, which is set by interpolating a format string _rt0_%s_%s with GOARCH and GOOS constants. Now that we know where and how this entrypoint comes from, let’s return to what it means. Thusly the entrypoint.disassembled objdump at offset 421760 looks something like this (again: from objdump):


0000000000421760 <_rt0_amd64_linux>:
  421760:       48 8d 74 24 08          lea    0x8(%rsp),%rsi
  421765:       48 8b 3c 24             mov    (%rsp),%rdi
  421769:       b8 70 17 42 00          mov    $0x421770,%eax
  42176e:       ff e0                   jmpq   *%rax

This is the body of machine code that is executed when the execve call starts the binary.


Great, now how do I read this, you might ask? I won't pull your leg by saying that I'm a wonk of Assembler, but I will say that we’re in luck with mature, documented prior art: standardized process bootstrapping mechanisms, like the one that glibc defines (cf., i386 implementation). The Ksplice team at Oracle has even produced a nice writeup of their mechanics if you are so curious. The good news is instead of reading raw disassembled output, we can consult Go’s intermediate Assembler format and the canonical source in upstream since the format string above tells us to look at _rt0_amd64_linux. Bingo! The _rt0_amd64_linux symbol is defined in rt0_linux_amd64.s. Let’s trace out the bootstrapping process, focusing on the unique particulars of Go. Here’s that entrypoint again, but this time in Go’s Assembler format:


TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
 LEAQ 8(SP), SI // argv
 MOVQ 0(SP), DI // argc
 MOVQ $main(SB), AX
 JMP AX

What it is doing here is assigning argv and argc from the stack pointer to the local registers SI and DI respectively before invoking the main procedure. Note: this procedure is not the main function in package main we defined! We’ll get to why in a later post. Rather, this main is defined in the same file as follows:


TEXT main(SB),NOSPLIT,$-8
 MOVQ $runtime·rt0_go(SB), AX
 JMP AX

The definition is pretty straight forward: execute the rt0_go procedure found in package runtime. You may ask, why the boilerplate of having the entrypoint just invoke an simple shell of a pain procedure, which, in turn, just delegates itself to the rt0_go procedure? The answer is that each GOARCH and GOOS has different initial bootstrapping dependencies and requirements (cf., Linux on ARM). At this point, rt0_go is assumed to be safely generic for all GOOS variants since it is defined in asm_amd64.s. Let’s continue the tracing by looking at this procedure closely:


// copy arguments forward on an even stack
 MOVQ DI, AX  // argc
 MOVQ SI, BX  // argv
 SUBQ $(4*8+7), SP  // 2args 2auto
 ANDQ $~15, SP
 MOVQ AX, 16(SP)
 MOVQ BX, 24(SP)

This is responsible for setting up the stack, namely program arguments once again.


Before we look at the next segment, let’s take a quick diversion into terminology. In the world of Go’s scheduler, we have a cast of acronyms G, M, and P that require explanation. To borrow the words of Daniel Morsing (from his nice blog post on the Go scheduler ca. 1.1 release):


  • G corresponds to a Goroutine (struct G).
  • M corresponds to a Machine, which can be effectively substituted with a thread of execution within the operating system (struct M). G are executed on M.
  • P corresponds to a Processor, which can be thought of as a resource in which a M runs a G (struct P).

Now that we have that out of the way, we come to this gem:


// create istack out of the given (operating system) stack.
 // _cgo_init may update stackguard.
 MOVQ $runtime·g0(SB), DI
 LEAQ (-64*1024+104)(SP), BX
 MOVQ BX, g_stackguard0(DI)
 MOVQ BX, g_stackguard1(DI)
 MOVQ BX, (g_stack+stack_lo)(DI)
 MOVQ SP, (g_stack+stack_hi)(DI)

Using what we have learned above, we can infer that g0 is the 0th Goroutine of the system and possibly performs various runtime management functions. It is defined by the struct G and has some interesting fields, a few listed here:


Stack stack; // offset known to runtime/cgo 
uintptr stackguard0; // offset known to liblink
uintptr stackguard1; // offset known to liblink

Given the Go Assembler guide, the assembly listing, and the struct G excerpts, we can interpret the istack assembly to mean the following:


  • Assign the address of g0 to DI, which will be used to scope the subsequent references.
  • Assign the address literal of -0xff98 (-64*1024+104 in hex) from the stack pointer’s origin to BX. If someone has a good idea on where this value originates, clarification would be appreciated.
  • Assign BX to g0.stackguard0 and g0.stackguard1. In the context of Go, stackguard relates to the logic behind the stack resizing methodology and when more memory is allocated. This may become a topic for a future post but is described to some detail in the Contiguous Stacks Design Document.
  • Assign BX to g0.stack.lo and the stack pointer to g0.stack.hi.

… and there we have the geometry of g0’s stack. I presume a methodology similar to this is used in creation of additional Goroutines once the scheduler has started, but I haven’t verified yet.


Continuing, we hit the CPU type and capabilities detection procedure:


// find out information about the processor we're on
 MOVQ $0, AX
 CPUID
 CMPQ AX, $0
 JE nocpuinfo
 MOVQ $1, AX
 CPUID
 MOVL CX, runtime·cpuid_ecx(SB)
 MOVL DX, runtime·cpuid_edx(SB)

If the information is available, the feature set is recorded in cpuid_ecx and cpuid_edx. A quick survey reports that the ECX is used in selecting the fundamental hashing algorithm that the runtime uses internally:


if (cpuid_ecx&(1<<25)) != 0 && // aes (aesenc)
  (cpuid_ecx&(1<<9)) != 0 && // sse3 (pshufb)
  (cpuid_ecx&(1<<19)) != 0 { // sse4.1 (pinsr{d,q})
  useAeshash = true
  algarray[alg_MEM].hash = aeshash
  algarray[alg_MEM8].hash = aeshash
  algarray[alg_MEM16].hash = aeshash
  algarray[alg_MEM32].hash = aeshash32
  algarray[alg_MEM64].hash = aeshash64
  algarray[alg_MEM128].hash = aeshash
  algarray[alg_STRING].hash = aeshashstr

These are used, for instance, when indexing a map type, which is to say that the runtime attempts to select the most optimal hashing methodology given the detected architecture, which could be AES. As for EDX, its value informs whether SSE2 is present and thusly the mechanism used for the runtime’s memclr and memmove procedures.

You can see from the hashing algorithms discussion above that a number of Go’s core runtime features and behaviors are determined by function pointer. The same is the case with Cgo, which immediately follows the CPU capabilities detection.

_cgo_init is a function pointer, defined as such:

// Filled in by dynamic linker when Cgo is available.
void (*_cgo_init)(void);

If Cgo is bundled into the build artifact, the function pointer is set:

extern void x_cgo_init(G*);
void (*_cgo_init)(G*) = x_cgo_init;

x_cgo_init is defined as follows:

void
x_cgo_init(G* g, void (*setg)(void*))
{
 pthread_attr_t attr;
 size_t size;

 setg_gcc = setg;
 pthread_attr_init(&attr);
 pthread_attr_getstacksize(&attr, &size);
 g->stacklo = (uintptr)&attr - size + 4096;
 pthread_attr_destroy(&attr);
}

So that leaves us to an assembly block, which handles the Cgo bootstrapping:

// if there is an _cgo_init, call it.
 MOVQ _cgo_init(SB), AX
 TESTQ AX, AX
 JZ needtls
 // g0 already in DI
 MOVQ DI, CX // Win64 uses CX for first parameter
 MOVQ $setg_gcc<>(SB), SI
 CALL AX
 // update stackguard after _cgo_init
 MOVQ $runtime·g0(SB), CX
 MOVQ (g_stack+stack_lo)(CX), AX
 ADDQ $const_StackGuard, AX
 MOVQ AX, g_stackguard0(CX)
 MOVQ AX, g_stackguard1(CX)

So, if _cgo_init is non-null (TESTQ AX, AX), we prepare our function call, which means g0 as the first parameter and again another function pointer: setg_gcc. Remember: the Go assembler guide will help you read the peculiarities in here! setg_gcc on amd64 is defined as such:

// void setg_gcc(G*); set g called from gcc.
TEXT setg_gcc<>(SB),NOSPLIT,$0
 get_tls(AX)
 MOVQ DI, g(AX)
 RET

Let’s put this rat nest together. If _cgo_init is called, …

All of this is done for the system g0 Goroutine.

Irrespective of Cgo, the runtime now sets up thread local storage (TLS), which is used to scope stateful data to a given thread, or a M per the scheduler terminology. I will admit that this is entering murky territory for me.

LEAQ runtime·tls0(SB), DI
 CALL runtime·settls(SB)

tls0 refers to a byte array and is passed as the first argument to the settls procedure, which, in turn, invokes system call 158 (sys_arch_prctl) to instruct the kernel to set g0’s thread local storage to that array.

Assuming this has been successful, the runtime performs a blackbox test of TLS capability:

// store through it, to make sure it works
 get_tls(BX)
 MOVQ $0x123, g(BX)
 MOVQ runtime·tls0(SB), AX
 CMPQ AX, $0x123
 JEQ 2(PC)
 MOVL AX, 0 // abort

This merely passes the 0x123 integer literal into g0 TLS, fetches the value out of TLS, and then compares for end-to-end correctness. The Go distribution’s compiler bootstrapper (different from the process we are talking about) defines a set of macros related to TLS that are worth knowing:

"#define get_tls(r) MOVQ TLS, r\n"
  "#define g(r) 0(r)(TLS*1)\n"

If you have made it this far, I congratulate you! It’s been an arduous trek.

At this point, the runtime mutually binds g0 to m0 (m0 definition):

// set the per-goroutine and per-mach "registers"
 get_tls(BX)
 LEAQ runtime·g0(SB), CX
 MOVQ CX, g(BX)
 LEAQ runtime·m0(SB), AX
 // save m->g0 = g0
 MOVQ CX, m_g0(AX)
 // save m0 to g0->m
 MOVQ AX, g_m(CX)

One question that I have not answered yet is whether g0 and m0 are eternally bound. My assumption—to be explicitly validated later on—is whether this binding is done just to bootstrap the scheduler which, I presume to run on g0.

A set of mundane invariant checks follow, which are used to test environmental sanity:

Eventually argc and argv are persisted for later use in the process:

static int32 argc;

#pragma dataflag NOPTR /* argv not a heap pointer */
static uint8** argv;

void
runtime·args(int32 c, uint8 **v)
{
 argc = c;
 argv = v;
 if(runtime·sysargs != nil)
  runtime·sysargs(c, v);
}

The sysargs function pointer is responsible for platform-specific initializations given the arguments, and we’ll see just that. On AMD64 Linux, linux_setup_vdso is responsible for this:

void (*runtime·sysargs)(int32, uint8**) = runtime·linux_setup_vdso;

VDSO stands for Virtual Dynamic Shared Object. Go gets access to this by using the auxiliary vectors. My friend and colleague Manu Garg has a nice writeup about them; I encourage you to read it if you are unfamiliar.

The reason is to acquire initial random data samples:

if(elf_auxv[i].a_type == AT_RANDOM) {
          runtime·startup_random_data = (byte*)elf_auxv[i].a_un.a_val;
          runtime·startup_random_data_len = 16;
   continue;
  }

Who uses these samples, and where? If we trace this out, …

get_random_data piggybacks off of this body. It has only one use in the runtime, and that is in the runtime package’s init function if AES support is detected:

var rnd unsafe.Pointer
  var n int32
  get_random_data(&rnd, &n)
  if n > hashRandomBytes {
   n = hashRandomBytes
  }
  memmove(unsafe.Pointer(&aeskeysched[0]), rnd, uintptr(n))
  if n < hashRandomBytes {
   // Not very random, but better than nothing.
   for t := nanotime(); n < hashRandomBytes; n++ {
    aeskeysched[n] = byte(t >> uint(8*(n%8)))
   }
  }

The side-effects are left in aeskeysched, which is used in the AES hashing procedures aeshashbody, aeshash32, and aeshash64—the latter being provided as reference:

TEXT runtime·aeshash64(SB),NOSPLIT,$0-32
 MOVQ p+0(FP), AX // ptr to data
 // s+8(FP) is ignored, it is always sizeof(int64)
 MOVQ h+16(FP), X0 // seed
 PINSRQ $1, (AX), X0 // data
 AESENC runtime·aeskeysched+0(SB), X0
 AESENC runtime·aeskeysched+16(SB), X0
 AESENC runtime·aeskeysched+0(SB), X0
 MOVQ X0, ret+24(FP)
 RET

The AES instruction set reference may be helpful. But back to the original question of who uses this, let’s discuss that:

The answer lies in the index expressions specification for map types found in the Go Language Specification. Why? Because maps use hashes for bucketing KeyType-s. I had originally planned on writing a separate post on this topic—might still—but the cat is out of the bag now! One takeaway, as the blog post Go maps in action indicates, is that the user should not expect stable map iteration order! Even if the iteration orders appeared to be the same across runs on different processes on the same host, there is no guarantee that the order would ever be consistent on another host, especially if it possessed a different GOARCH or GOOS!

The next discussion is the last one, for the article has gotten long—and there is a dearth of more topics to cover!

The runtime then calls the operating system initialization routine. On Linux, it is trivial:

void
runtime·osinit(void)
{
 runtime·ncpu = getproccount();
}

This ncpu value is used in a few places:

I will now close out this part of the article here and save the remaining topics for a followup post or two. The big things to follow are the scheduler, memory manager, and the developer’s entrypoint of the Go programmer’s binary.

Don’t worry: I will link the posts together! Even if it takes a little bit of time for the following edition to come out (most of the research is done; just needs prosaic conversion), I expect the general ethos findings to remain true in spite of the partial rewrite of the Go runtime from C and .goc to Go. Stay tuned!

For extracurricular reading, my friend Jeremie Le Hen suggested a quick discussion on the following (included in the sandbox):

I think it would be worth showing at this point some differences between Go-generated binaries and standard binaries, which are kind of bloated nowadays:
$ ldd entrypoint
 not a dynamic executable
$ cat entrypoint.c 
int main(int argc, char *argv[])
{
  // do nothing
  return 0;
}
$ gcc -o entrypoint.c.bin -static entrypoint.c 
$ ls -l entrypoint.c.bin entrypoint
-rwxr-x--- 1 jlehen jlehen 581704 Feb 25 09:44 entrypoint
-rwxr-x--- 1 jlehen jlehen 876940 Feb 25 09:59 entrypoint.c.bin
Also the C binary has a plethora of ELF sections, whereas the Go executable has only 7 if you exclude debug sections, and 3 of them are Go-specific. This means the Go compiler is really independent from the classic C runtime, and doesn’t even use the C runtime objects (crtX.o stuff). The libc doesn’t seem to be used at all either, which is not surprising as there are not dependencies between it and the C runtime objects. This can be verified with objdump -t on the binaries:
  • on entrypoint.c.bin there is a whole lot of misc mostly-internal symbols, which originate in the standard lib;
  • on entrypoint, you see a nicely named collection of symbols, mainly divided in two big sections gc and runtime.

Now that we really are finished, I would like to say a big thank-you to Manu Garg and Jeremie Le Hen for their editorial review and contributions! Big round of applause!

follow us in feedly

2015-02-12

Go Runtime Exploration: Select Statements and Nondeterminism

The Go Programming Language Specification includes a nice blurb on select statements:


If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. …

How exactly does this work under the hood? I was curious, so perhaps you may be as well. Let's start with a simple executable that reads from two buffered and occupied channels in the confines of a select statement, which prints out which channel's read won:


package main

func main() {
 for i := 0; i < 5; i++ {
  var (
   first  = make(chan struct{}, 1)
   second = make(chan struct{}, 1)
  )
  first <- struct{}{}
  second <- struct{}{}
  select {
  case <-first:
   println("round 1: first")
  case <-second:
   println("round 1: second")
  }
  select {
  case <-first:
   println("round 2: first")
  case <-second:
   println("round 2: second")
  }
 }
}

The output may look similar to this:

$ go run example.go
round 1: first
round 2: second
round 1: first
round 2: second
round 1: first
round 2: second
round 1: second
round 2: first
round 1: first
round 2: second

What do we note here? In spite of the source ordering for the communication cases, we see that the first and the second channel both win. Let's take a closer look. To do this, we'll use the intermediate assembler form for Go:

$ go build -gcflags -S example.go
# command-line-arguments
"".main t=1 size=672 value=0 args=0x0 locals=0xc8
 0x0000 00000 (/tmp/example.go:3) TEXT "".main+0(SB),$200-0
 0x0000 00000 (/tmp/example.go:3) MOVQ (TLS),CX
 0x0009 00009 (/tmp/example.go:3) LEAQ -72(SP),AX
 0x000e 00014 (/tmp/example.go:3) CMPQ AX,16(CX)
 0x0012 00018 (/tmp/example.go:3) JHI ,27
 0x0014 00020 (/tmp/example.go:3) CALL ,runtime.morestack_noctxt(SB)
 0x0019 00025 (/tmp/example.go:3) JMP ,0
 0x001b 00027 (/tmp/example.go:3) SUBQ $200,SP
 0x0022 00034 (/tmp/example.go:3) FUNCDATA $0,gclocals·7c13896baab3273e10662a9a37b348ce+0(SB)
 0x0022 00034 (/tmp/example.go:3) FUNCDATA $1,gclocals·e4cbac1fb25db4dbafd4924af8fee3c8+0(SB)
 0x0022 00034 (/tmp/example.go:4) MOVQ $0,AX
 0x0024 00036 (/tmp/example.go:4) MOVQ AX,"".i+32(SP)
 0x0029 00041 (/tmp/example.go:4) CMPQ AX,$5
 0x002d 00045 (/tmp/example.go:4) JGE $0,464
 0x0033 00051 (/tmp/example.go:6) MOVQ $type.chan struct {}+0(SB),BX
 0x003a 00058 (/tmp/example.go:6) MOVQ BX,(SP)
 0x003e 00062 (/tmp/example.go:6) MOVQ $1,8(SP)
 0x0047 00071 (/tmp/example.go:6) PCDATA $0,$0
 0x0047 00071 (/tmp/example.go:6) CALL ,runtime.makechan(SB)
 0x004c 00076 (/tmp/example.go:6) MOVQ 16(SP),BX
 0x0051 00081 (/tmp/example.go:6) MOVQ BX,"".first+48(SP)
 0x0056 00086 (/tmp/example.go:6) NOP ,
 0x0056 00086 (/tmp/example.go:7) MOVQ $type.chan struct {}+0(SB),BX
 0x005d 00093 (/tmp/example.go:7) MOVQ BX,(SP)
 0x0061 00097 (/tmp/example.go:7) MOVQ $1,8(SP)
 0x006a 00106 (/tmp/example.go:7) PCDATA $0,$1
 0x006a 00106 (/tmp/example.go:7) CALL ,runtime.makechan(SB)
 0x006f 00111 (/tmp/example.go:7) MOVQ 16(SP),BX
 0x0074 00116 (/tmp/example.go:7) MOVQ BX,"".second+40(SP)
 0x0079 00121 (/tmp/example.go:7) NOP ,
 0x0079 00121 (/tmp/example.go:9) LEAQ "".autotmp_0002+32(SP),BX
 0x007e 00126 (/tmp/example.go:9) MOVQ $type.chan struct {}+0(SB),BX
 0x0085 00133 (/tmp/example.go:9) MOVQ BX,(SP)
 0x0089 00137 (/tmp/example.go:9) MOVQ "".first+48(SP),BX
 0x008e 00142 (/tmp/example.go:9) MOVQ BX,8(SP)
 0x0093 00147 (/tmp/example.go:9) LEAQ "".autotmp_0002+32(SP),BX
 0x0098 00152 (/tmp/example.go:9) MOVQ BX,16(SP)
 0x009d 00157 (/tmp/example.go:9) PCDATA $0,$2
 0x009d 00157 (/tmp/example.go:9) CALL ,runtime.chansend1(SB)
 0x00a2 00162 (/tmp/example.go:10) LEAQ "".autotmp_0003+32(SP),BX
 0x00a7 00167 (/tmp/example.go:10) MOVQ $type.chan struct {}+0(SB),BX
 0x00ae 00174 (/tmp/example.go:10) MOVQ BX,(SP)
 0x00b2 00178 (/tmp/example.go:10) MOVQ "".second+40(SP),BX
 0x00b7 00183 (/tmp/example.go:10) MOVQ BX,8(SP)
 0x00bc 00188 (/tmp/example.go:10) LEAQ "".autotmp_0003+32(SP),BX
 0x00c1 00193 (/tmp/example.go:10) MOVQ BX,16(SP)
 0x00c6 00198 (/tmp/example.go:10) PCDATA $0,$2
 0x00c6 00198 (/tmp/example.go:10) CALL ,runtime.chansend1(SB)
 0x00cb 00203 (/tmp/example.go:11) LEAQ "".autotmp_0005+56(SP),DI
 0x00d0 00208 (/tmp/example.go:11) MOVL $0,AX
 0x00d2 00210 (/tmp/example.go:11) DUFFZERO ,$
 0x00d7 00215 (/tmp/example.go:11) LEAQ "".autotmp_0005+56(SP),BX
 0x00dc 00220 (/tmp/example.go:11) MOVQ BX,(SP)
 0x00e0 00224 (/tmp/example.go:11) MOVQ $144,8(SP)
 0x00e9 00233 (/tmp/example.go:11) MOVL $2,16(SP)
 0x00f1 00241 (/tmp/example.go:11) PCDATA $0,$3
 0x00f1 00241 (/tmp/example.go:11) CALL ,runtime.newselect(SB)
 0x00f6 00246 (/tmp/example.go:12) LEAQ "".autotmp_0005+56(SP),BP
 0x00fb 00251 (/tmp/example.go:12) MOVQ BP,(SP)
 0x00ff 00255 (/tmp/example.go:12) MOVQ "".first+48(SP),BP
 0x0104 00260 (/tmp/example.go:12) MOVQ BP,8(SP)
 0x0109 00265 (/tmp/example.go:12) MOVQ $0,16(SP)
 0x0112 00274 (/tmp/example.go:12) PCDATA $0,$3
 0x0112 00274 (/tmp/example.go:12) CALL ,runtime.selectrecv(SB)
 0x0117 00279 (/tmp/example.go:12) MOVBQZX 24(SP),BX
 0x011c 00284 (/tmp/example.go:12) CMPB BL,$0
 0x011f 00287 (/tmp/example.go:12) JEQ ,564
 0x0125 00293 (/tmp/example.go:13) LEAQ go.string."round 1: first"+0(SB),BX
 0x012c 00300 (/tmp/example.go:13) LEAQ (SP),BP
 0x0130 00304 (/tmp/example.go:13) MOVQ BP,DI
 0x0133 00307 (/tmp/example.go:13) MOVQ BX,SI
 0x0136 00310 (/tmp/example.go:13) MOVSQ ,
 0x0138 00312 (/tmp/example.go:13) MOVSQ ,
 0x013a 00314 (/tmp/example.go:13) PCDATA $0,$2
 0x013a 00314 (/tmp/example.go:13) CALL ,runtime.printstring(SB)
 0x013f 00319 (/tmp/example.go:13) PCDATA $0,$2
 0x013f 00319 (/tmp/example.go:13) CALL ,runtime.printnl(SB)
 0x0144 00324 (/tmp/example.go:17) LEAQ "".autotmp_0006+56(SP),DI
 0x0149 00329 (/tmp/example.go:17) MOVL $0,AX
 0x014b 00331 (/tmp/example.go:17) DUFFZERO ,$
 0x0150 00336 (/tmp/example.go:17) LEAQ "".autotmp_0006+56(SP),BX
 0x0155 00341 (/tmp/example.go:17) MOVQ BX,(SP)
 0x0159 00345 (/tmp/example.go:17) MOVQ $144,8(SP)
 0x0162 00354 (/tmp/example.go:17) MOVL $2,16(SP)
 0x016a 00362 (/tmp/example.go:17) PCDATA $0,$3
 0x016a 00362 (/tmp/example.go:17) CALL ,runtime.newselect(SB)
 0x016f 00367 (/tmp/example.go:18) LEAQ "".autotmp_0006+56(SP),BP
 0x0174 00372 (/tmp/example.go:18) MOVQ BP,(SP)
 0x0178 00376 (/tmp/example.go:18) MOVQ "".first+48(SP),BP
 0x017d 00381 (/tmp/example.go:18) MOVQ BP,8(SP)
 0x0182 00386 (/tmp/example.go:18) MOVQ $0,16(SP)
 0x018b 00395 (/tmp/example.go:18) PCDATA $0,$4
 0x018b 00395 (/tmp/example.go:18) CALL ,runtime.selectrecv(SB)
 0x0190 00400 (/tmp/example.go:18) MOVBQZX 24(SP),BX
 0x0195 00405 (/tmp/example.go:18) CMPB BL,$0
 0x0198 00408 (/tmp/example.go:18) JEQ ,472
 0x019a 00410 (/tmp/example.go:19) LEAQ go.string."round 2: first"+0(SB),BX
 0x01a1 00417 (/tmp/example.go:19) LEAQ (SP),BP
 0x01a5 00421 (/tmp/example.go:19) MOVQ BP,DI
 0x01a8 00424 (/tmp/example.go:19) MOVQ BX,SI
 0x01ab 00427 (/tmp/example.go:19) MOVSQ ,
 0x01ad 00429 (/tmp/example.go:19) MOVSQ ,
 0x01af 00431 (/tmp/example.go:19) PCDATA $0,$0
 0x01af 00431 (/tmp/example.go:19) CALL ,runtime.printstring(SB)
 0x01b4 00436 (/tmp/example.go:19) PCDATA $0,$0
 0x01b4 00436 (/tmp/example.go:19) CALL ,runtime.printnl(SB)
 0x01b9 00441 (/tmp/example.go:4) MOVQ "".i+32(SP),AX
 0x01be 00446 (/tmp/example.go:4) INCQ ,AX
 0x01c1 00449 (/tmp/example.go:4) NOP ,
 0x01c1 00449 (/tmp/example.go:4) MOVQ AX,"".i+32(SP)
 0x01c6 00454 (/tmp/example.go:4) CMPQ AX,$5
 0x01ca 00458 (/tmp/example.go:4) JLT $0,51
 0x01d0 00464 (/tmp/example.go:24) ADDQ $200,SP
 0x01d7 00471 (/tmp/example.go:24) RET ,
 0x01d8 00472 (/tmp/example.go:20) LEAQ "".autotmp_0006+56(SP),BP
 0x01dd 00477 (/tmp/example.go:20) MOVQ BP,(SP)
 0x01e1 00481 (/tmp/example.go:20) MOVQ "".second+40(SP),BP
 0x01e6 00486 (/tmp/example.go:20) MOVQ BP,8(SP)
 0x01eb 00491 (/tmp/example.go:20) MOVQ $0,16(SP)
 0x01f4 00500 (/tmp/example.go:20) PCDATA $0,$4
 0x01f4 00500 (/tmp/example.go:20) CALL ,runtime.selectrecv(SB)
 0x01f9 00505 (/tmp/example.go:20) MOVBQZX 24(SP),BX
 0x01fe 00510 (/tmp/example.go:20) CMPB BL,$0
 0x0201 00513 (/tmp/example.go:20) JEQ ,548
 0x0203 00515 (/tmp/example.go:21) LEAQ go.string."round 2: second"+0(SB),BX
 0x020a 00522 (/tmp/example.go:21) LEAQ (SP),BP
 0x020e 00526 (/tmp/example.go:21) MOVQ BP,DI
 0x0211 00529 (/tmp/example.go:21) MOVQ BX,SI
 0x0214 00532 (/tmp/example.go:21) MOVSQ ,
 0x0216 00534 (/tmp/example.go:21) MOVSQ ,
 0x0218 00536 (/tmp/example.go:21) PCDATA $0,$0
 0x0218 00536 (/tmp/example.go:21) CALL ,runtime.printstring(SB)
 0x021d 00541 (/tmp/example.go:21) PCDATA $0,$0
 0x021d 00541 (/tmp/example.go:21) CALL ,runtime.printnl(SB)
 0x0222 00546 (/tmp/example.go:4) JMP ,441
 0x0224 00548 (/tmp/example.go:17) LEAQ "".autotmp_0006+56(SP),BX
 0x0229 00553 (/tmp/example.go:17) MOVQ BX,(SP)
 0x022d 00557 (/tmp/example.go:17) PCDATA $0,$4
 0x022d 00557 (/tmp/example.go:17) CALL ,runtime.selectgo(SB)
 0x0232 00562 (/tmp/example.go:17) UNDEF ,
 0x0234 00564 (/tmp/example.go:14) LEAQ "".autotmp_0005+56(SP),BP
 0x0239 00569 (/tmp/example.go:14) MOVQ BP,(SP)
 0x023d 00573 (/tmp/example.go:14) MOVQ "".second+40(SP),BP
 0x0242 00578 (/tmp/example.go:14) MOVQ BP,8(SP)
 0x0247 00583 (/tmp/example.go:14) MOVQ $0,16(SP)
 0x0250 00592 (/tmp/example.go:14) PCDATA $0,$3
 0x0250 00592 (/tmp/example.go:14) CALL ,runtime.selectrecv(SB)
 0x0255 00597 (/tmp/example.go:14) MOVBQZX 24(SP),BX
 0x025a 00602 (/tmp/example.go:14) CMPB BL,$0
 0x025d 00605 (/tmp/example.go:14) JEQ ,643
 0x025f 00607 (/tmp/example.go:15) LEAQ go.string."round 1: second"+0(SB),BX
 0x0266 00614 (/tmp/example.go:15) LEAQ (SP),BP
 0x026a 00618 (/tmp/example.go:15) MOVQ BP,DI
 0x026d 00621 (/tmp/example.go:15) MOVQ BX,SI
 0x0270 00624 (/tmp/example.go:15) MOVSQ ,
 0x0272 00626 (/tmp/example.go:15) MOVSQ ,
 0x0274 00628 (/tmp/example.go:15) PCDATA $0,$2
 0x0274 00628 (/tmp/example.go:15) CALL ,runtime.printstring(SB)
 0x0279 00633 (/tmp/example.go:15) PCDATA $0,$2
 0x0279 00633 (/tmp/example.go:15) CALL ,runtime.printnl(SB)
 0x027e 00638 (/tmp/example.go:17) JMP ,324
 0x0283 00643 (/tmp/example.go:11) LEAQ "".autotmp_0005+56(SP),BX
 0x0288 00648 (/tmp/example.go:11) MOVQ BX,(SP)
 0x028c 00652 (/tmp/example.go:11) PCDATA $0,$3
 0x028c 00652 (/tmp/example.go:11) CALL ,runtime.selectgo(SB)
 0x0291 00657 (/tmp/example.go:11) UNDEF ,
 0x0000 65 48 8b 0c 25 00 00 00 00 48 8d 44 24 b8 48 3b  eH..%....H.D$.H;
 0x0010 41 10 77 07 e8 00 00 00 00 eb e5 48 81 ec c8 00  A.w........H....
 0x0020 00 00 31 c0 48 89 44 24 20 48 83 f8 05 0f 8d 9d  ..1.H.D$ H......
 0x0030 01 00 00 48 8d 1d 00 00 00 00 48 89 1c 24 48 c7  ...H......H..$H.
 0x0040 44 24 08 01 00 00 00 e8 00 00 00 00 48 8b 5c 24  D$..........H.\$
 0x0050 10 48 89 5c 24 30 48 8d 1d 00 00 00 00 48 89 1c  .H.\$0H......H..
 0x0060 24 48 c7 44 24 08 01 00 00 00 e8 00 00 00 00 48  $H.D$..........H
 0x0070 8b 5c 24 10 48 89 5c 24 28 48 8d 5c 24 20 48 8d  .\$.H.\$(H.\$ H.
 0x0080 1d 00 00 00 00 48 89 1c 24 48 8b 5c 24 30 48 89  .....H..$H.\$0H.
 0x0090 5c 24 08 48 8d 5c 24 20 48 89 5c 24 10 e8 00 00  \$.H.\$ H.\$....
 0x00a0 00 00 48 8d 5c 24 20 48 8d 1d 00 00 00 00 48 89  ..H.\$ H......H.
 0x00b0 1c 24 48 8b 5c 24 28 48 89 5c 24 08 48 8d 5c 24  .$H.\$(H.\$.H.\$
 0x00c0 20 48 89 5c 24 10 e8 00 00 00 00 48 8d 7c 24 38   H.\$......H.|$8
 0x00d0 31 c0 e8 00 00 00 00 48 8d 5c 24 38 48 89 1c 24  1......H.\$8H..$
 0x00e0 48 c7 44 24 08 90 00 00 00 c7 44 24 10 02 00 00  H.D$......D$....
 0x00f0 00 e8 00 00 00 00 48 8d 6c 24 38 48 89 2c 24 48  ......H.l$8H.,$H
 0x0100 8b 6c 24 30 48 89 6c 24 08 48 c7 44 24 10 00 00  .l$0H.l$.H.D$...
 0x0110 00 00 e8 00 00 00 00 0f b6 5c 24 18 80 fb 00 0f  .........\$.....
 0x0120 84 0f 01 00 00 48 8d 1d 00 00 00 00 48 8d 2c 24  .....H......H.,$
 0x0130 48 89 ef 48 89 de 48 a5 48 a5 e8 00 00 00 00 e8  H..H..H.H.......
 0x0140 00 00 00 00 48 8d 7c 24 38 31 c0 e8 00 00 00 00  ....H.|$81......
 0x0150 48 8d 5c 24 38 48 89 1c 24 48 c7 44 24 08 90 00  H.\$8H..$H.D$...
 0x0160 00 00 c7 44 24 10 02 00 00 00 e8 00 00 00 00 48  ...D$..........H
 0x0170 8d 6c 24 38 48 89 2c 24 48 8b 6c 24 30 48 89 6c  .l$8H.,$H.l$0H.l
 0x0180 24 08 48 c7 44 24 10 00 00 00 00 e8 00 00 00 00  $.H.D$..........
 0x0190 0f b6 5c 24 18 80 fb 00 74 3e 48 8d 1d 00 00 00  ..\$....t>H.....
 0x01a0 00 48 8d 2c 24 48 89 ef 48 89 de 48 a5 48 a5 e8  .H.,$H..H..H.H..
 0x01b0 00 00 00 00 e8 00 00 00 00 48 8b 44 24 20 48 ff  .........H.D$ H.
 0x01c0 c0 48 89 44 24 20 48 83 f8 05 0f 8c 63 fe ff ff  .H.D$ H.....c...
 0x01d0 48 81 c4 c8 00 00 00 c3 48 8d 6c 24 38 48 89 2c  H.......H.l$8H.,
 0x01e0 24 48 8b 6c 24 28 48 89 6c 24 08 48 c7 44 24 10  $H.l$(H.l$.H.D$.
 0x01f0 00 00 00 00 e8 00 00 00 00 0f b6 5c 24 18 80 fb  ...........\$...
 0x0200 00 74 21 48 8d 1d 00 00 00 00 48 8d 2c 24 48 89  .t!H......H.,$H.
 0x0210 ef 48 89 de 48 a5 48 a5 e8 00 00 00 00 e8 00 00  .H..H.H.........
 0x0220 00 00 eb 95 48 8d 5c 24 38 48 89 1c 24 e8 00 00  ....H.\$8H..$...
 0x0230 00 00 0f 0b 48 8d 6c 24 38 48 89 2c 24 48 8b 6c  ....H.l$8H.,$H.l
 0x0240 24 28 48 89 6c 24 08 48 c7 44 24 10 00 00 00 00  $(H.l$.H.D$.....
 0x0250 e8 00 00 00 00 0f b6 5c 24 18 80 fb 00 74 24 48  .......\$....t$H
 0x0260 8d 1d 00 00 00 00 48 8d 2c 24 48 89 ef 48 89 de  ......H.,$H..H..
 0x0270 48 a5 48 a5 e8 00 00 00 00 e8 00 00 00 00 e9 c1  H.H.............
 0x0280 fe ff ff 48 8d 5c 24 38 48 89 1c 24 e8 00 00 00  ...H.\$8H..$....
 0x0290 00 0f 0b                                         ...
 rel 5+4 t=9 +0
 rel 21+4 t=3 runtime.morestack_noctxt+0
 rel 54+4 t=7 type.chan struct {}+0
 rel 72+4 t=3 runtime.makechan+0
 rel 89+4 t=7 type.chan struct {}+0
 rel 107+4 t=3 runtime.makechan+0
 rel 129+4 t=7 type.chan struct {}+0
 rel 158+4 t=3 runtime.chansend1+0
 rel 170+4 t=7 type.chan struct {}+0
 rel 199+4 t=3 runtime.chansend1+0
 rel 211+4 t=3 runtime.duffzero+220
 rel 242+4 t=3 runtime.newselect+0
 rel 275+4 t=3 runtime.selectrecv+0
 rel 296+4 t=7 go.string."round 1: first"+0
 rel 315+4 t=3 runtime.printstring+0
 rel 320+4 t=3 runtime.printnl+0
 rel 332+4 t=3 runtime.duffzero+220
 rel 363+4 t=3 runtime.newselect+0
 rel 396+4 t=3 runtime.selectrecv+0
 rel 413+4 t=7 go.string."round 2: first"+0
 rel 432+4 t=3 runtime.printstring+0
 rel 437+4 t=3 runtime.printnl+0
 rel 501+4 t=3 runtime.selectrecv+0
 rel 518+4 t=7 go.string."round 2: second"+0
 rel 537+4 t=3 runtime.printstring+0
 rel 542+4 t=3 runtime.printnl+0
 rel 558+4 t=3 runtime.selectgo+0
 rel 593+4 t=3 runtime.selectrecv+0
 rel 610+4 t=7 go.string."round 1: second"+0
 rel 629+4 t=3 runtime.printstring+0
 rel 634+4 t=3 runtime.printnl+0
 rel 653+4 t=3 runtime.selectgo+0
"".init t=1 size=80 value=0 args=0x0 locals=0x0
 0x0000 00000 (/tmp/example.go:24) TEXT "".init+0(SB),$0-0
 0x0000 00000 (/tmp/example.go:24) MOVQ (TLS),CX
 0x0009 00009 (/tmp/example.go:24) CMPQ SP,16(CX)
 0x000d 00013 (/tmp/example.go:24) JHI ,22
 0x000f 00015 (/tmp/example.go:24) CALL ,runtime.morestack_noctxt(SB)
 0x0014 00020 (/tmp/example.go:24) JMP ,0
 0x0016 00022 (/tmp/example.go:24) NOP ,
 0x0016 00022 (/tmp/example.go:24) NOP ,
 0x0016 00022 (/tmp/example.go:24) FUNCDATA $0,gclocals·3280bececceccd33cb74587feedb1f9f+0(SB)
 0x0016 00022 (/tmp/example.go:24) FUNCDATA $1,gclocals·3280bececceccd33cb74587feedb1f9f+0(SB)
 0x0016 00022 (/tmp/example.go:24) MOVBQZX "".initdone·+0(SB),BX
 0x001d 00029 (/tmp/example.go:24) CMPB BL,$0
 0x0020 00032 (/tmp/example.go:24) JEQ ,54
 0x0022 00034 (/tmp/example.go:24) MOVBQZX "".initdone·+0(SB),BX
 0x0029 00041 (/tmp/example.go:24) CMPB BL,$2
 0x002c 00044 (/tmp/example.go:24) JNE ,47
 0x002e 00046 (/tmp/example.go:24) RET ,
 0x002f 00047 (/tmp/example.go:24) PCDATA $0,$0
 0x002f 00047 (/tmp/example.go:24) CALL ,runtime.throwinit(SB)
 0x0034 00052 (/tmp/example.go:24) UNDEF ,
 0x0036 00054 (/tmp/example.go:24) MOVB $1,"".initdone·+0(SB)
 0x003d 00061 (/tmp/example.go:24) MOVB $2,"".initdone·+0(SB)
 0x0044 00068 (/tmp/example.go:24) RET ,
 0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 77 07 e8  eH..%....H;a.w..
 0x0010 00 00 00 00 eb ea 0f b6 1d 00 00 00 00 80 fb 00  ................
 0x0020 74 14 0f b6 1d 00 00 00 00 80 fb 02 75 01 c3 e8  t...........u...
 0x0030 00 00 00 00 0f 0b c6 05 00 00 00 00 01 c6 05 00  ................
 0x0040 00 00 00 02 c3                                   .....
 rel 5+4 t=9 +0
 rel 16+4 t=3 runtime.morestack_noctxt+0
 rel 25+4 t=7 "".initdone·+0
 rel 37+4 t=7 "".initdone·+0
 rel 48+4 t=3 runtime.throwinit+0
 rel 56+4 t=7 "".initdone·+-1
 rel 63+4 t=7 "".initdone·+-1
go.string."round 1: first" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0e 00 00 00 00 00 00 00  ................
 0x0010 72 6f 75 6e 64 20 31 3a 20 66 69 72 73 74 00     round 1: first.
 rel 0+8 t=1 go.string."round 1: first"+16
go.string."round 1: second" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0f 00 00 00 00 00 00 00  ................
 0x0010 72 6f 75 6e 64 20 31 3a 20 73 65 63 6f 6e 64 00  round 1: second.
 rel 0+8 t=1 go.string."round 1: second"+16
go.string."round 2: first" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0e 00 00 00 00 00 00 00  ................
 0x0010 72 6f 75 6e 64 20 32 3a 20 66 69 72 73 74 00     round 2: first.
 rel 0+8 t=1 go.string."round 2: first"+16
go.string."round 2: second" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0f 00 00 00 00 00 00 00  ................
 0x0010 72 6f 75 6e 64 20 32 3a 20 73 65 63 6f 6e 64 00  round 2: second.
 rel 0+8 t=1 go.string."round 2: second"+16
gclocals·e4cbac1fb25db4dbafd4924af8fee3c8 t=7 dupok size=48 value=0
 0x0000 05 00 00 00 28 00 00 00 00 00 00 00 00 00 00 00  ....(...........
 0x0010 08 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00  ................
 0x0020 9a 6a 99 96 69 00 00 00 92 6a 99 96 69 00 00 00  .j..i....j..i...
gclocals·7c13896baab3273e10662a9a37b348ce t=7 dupok size=8 value=0
 0x0000 05 00 00 00 00 00 00 00                          ........
gclocals·3280bececceccd33cb74587feedb1f9f t=7 dupok size=8 value=0
 0x0000 01 00 00 00 00 00 00 00                          ........
gclocals·3280bececceccd33cb74587feedb1f9f t=7 dupok size=8 value=0
 0x0000 01 00 00 00 00 00 00 00                          ........
"".initdone· t=22 size=1 value=0
"".main·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 "".main+0
runtime.makechan·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.makechan+0
runtime.chansend1·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.chansend1+0
runtime.newselect·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.newselect+0
runtime.selectrecv·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.selectrecv+0
runtime.printstring·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.printstring+0
runtime.printnl·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.printnl+0
runtime.selectgo·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.selectgo+0
"".init·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 "".init+0
runtime.throwinit·f t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 runtime.throwinit+0
runtime.gcbits.0x000000000000000000000000000000 t=7 dupok size=16 value=0
 0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
go.string."struct {}" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00  ................
 0x0010 73 74 72 75 63 74 20 7b 7d 00                    struct {}.
 rel 0+8 t=1 go.string."struct {}"+16
type.struct {} t=7 dupok size=96 value=0
 0x0000 00 00 00 00 00 00 00 00 1b ac f6 27 00 01 01 99  ...........'....
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 rel 16+8 t=1 runtime.algarray+16
 rel 24+8 t=1 runtime.gcbits.0x000000000000000000000000000000+0
 rel 40+8 t=1 go.string."struct {}"+0
 rel 56+8 t=1 go.weak.type.*struct {}+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.struct {}+96
runtime.gcbits.0x88000000000000000000000000000000 t=7 dupok size=16 value=0
 0x0000 88 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
go.string."chan struct {}" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0e 00 00 00 00 00 00 00  ................
 0x0010 63 68 61 6e 20 73 74 72 75 63 74 20 7b 7d 00     chan struct {}.
 rel 0+8 t=1 go.string."chan struct {}"+16
type.chan struct {} t=7 dupok size=88 value=0
 0x0000 08 00 00 00 00 00 00 00 53 e5 5e 5c 00 08 08 32  ........S.^\...2
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0050 03 00 00 00 00 00 00 00                          ........
 rel 16+8 t=1 runtime.algarray+80
 rel 24+8 t=1 runtime.gcbits.0x88000000000000000000000000000000+0
 rel 40+8 t=1 go.string."chan struct {}"+0
 rel 56+8 t=1 go.weak.type.*chan struct {}+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.struct {}+0
go.typelink.chan struct {}/chan struct {} t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 type.chan struct {}+0
runtime.gcbits.0x88444800000000000000000000000000 t=7 dupok size=16 value=0
 0x0000 88 44 48 00 00 00 00 00 00 00 00 00 00 00 00 00  .DH.............
""..gostring.1 t=7 dupok size=128 value=0
 0x0000 00 00 00 00 00 00 00 00 6d 00 00 00 00 00 00 00  ........m.......
 0x0010 73 74 72 75 63 74 20 7b 20 65 6c 65 6d 20 2a 75  struct { elem *u
 0x0020 69 6e 74 38 3b 20 63 68 61 6e 20 2a 75 69 6e 74  int8; chan *uint
 0x0030 38 3b 20 70 63 20 75 69 6e 74 70 74 72 3b 20 6b  8; pc uintptr; k
 0x0040 69 6e 64 20 75 69 6e 74 31 36 3b 20 73 6f 20 75  ind uint16; so u
 0x0050 69 6e 74 31 36 3b 20 72 65 63 65 69 76 65 64 70  int16; receivedp
 0x0060 20 2a 75 69 6e 74 38 3b 20 72 65 6c 65 61 73 65   *uint8; release
 0x0070 74 69 6d 65 20 75 69 6e 74 36 34 20 7d 00        time uint64 }.
 rel 0+8 t=1 ""..gostring.1+16
go.string."elem" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ................
 0x0010 65 6c 65 6d 00                                   elem.
 rel 0+8 t=1 go.string."elem"+16
go.string."chan" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ................
 0x0010 63 68 61 6e 00                                   chan.
 rel 0+8 t=1 go.string."chan"+16
go.string."pc" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
 0x0010 70 63 00                                         pc.
 rel 0+8 t=1 go.string."pc"+16
go.string."kind" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ................
 0x0010 6b 69 6e 64 00                                   kind.
 rel 0+8 t=1 go.string."kind"+16
go.string."so" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
 0x0010 73 6f 00                                         so.
 rel 0+8 t=1 go.string."so"+16
go.string."receivedp" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00  ................
 0x0010 72 65 63 65 69 76 65 64 70 00                    receivedp.
 rel 0+8 t=1 go.string."receivedp"+16
go.string."releasetime" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00  ................
 0x0010 72 65 6c 65 61 73 65 74 69 6d 65 00              releasetime.
 rel 0+8 t=1 go.string."releasetime"+16
type.struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 } t=7 dupok size=376 value=0
 0x0000 30 00 00 00 00 00 00 00 a6 46 8d 8f 00 08 08 19  0........F......
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0050 07 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00  ................
 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00a0 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
 0x00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00d0 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00f0 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00  ................
 0x0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0120 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0140 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00  ........ .......
 0x0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0170 28 00 00 00 00 00 00 00                          (.......
 rel 16+8 t=1 runtime.algarray+112
 rel 24+8 t=1 runtime.gcbits.0x88444800000000000000000000000000+0
 rel 40+8 t=1 ""..gostring.1+0
 rel 56+8 t=1 go.weak.type.*struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+96
 rel 96+8 t=1 go.string."elem"+0
 rel 104+8 t=1 go.importpath."".+0
 rel 112+8 t=1 type.*uint8+0
 rel 136+8 t=1 go.string."chan"+0
 rel 144+8 t=1 go.importpath."".+0
 rel 152+8 t=1 type.*uint8+0
 rel 176+8 t=1 go.string."pc"+0
 rel 184+8 t=1 go.importpath."".+0
 rel 192+8 t=1 type.uintptr+0
 rel 216+8 t=1 go.string."kind"+0
 rel 224+8 t=1 go.importpath."".+0
 rel 232+8 t=1 type.uint16+0
 rel 256+8 t=1 go.string."so"+0
 rel 264+8 t=1 go.importpath."".+0
 rel 272+8 t=1 type.uint16+0
 rel 296+8 t=1 go.string."receivedp"+0
 rel 304+8 t=1 go.importpath."".+0
 rel 312+8 t=1 type.*uint8+0
 rel 336+8 t=1 go.string."releasetime"+0
 rel 344+8 t=1 go.importpath."".+0
 rel 352+8 t=1 type.uint64+0
runtime.gcbits.0x48844400000000000000000000000000 t=7 dupok size=16 value=0
 0x0000 48 84 44 00 00 00 00 00 00 00 00 00 00 00 00 00  H.D.............
""..gostring.2 t=7 dupok size=128 value=0
 0x0000 00 00 00 00 00 00 00 00 6f 00 00 00 00 00 00 00  ........o.......
 0x0010 5b 5d 73 74 72 75 63 74 20 7b 20 65 6c 65 6d 20  []struct { elem
 0x0020 2a 75 69 6e 74 38 3b 20 63 68 61 6e 20 2a 75 69  *uint8; chan *ui
 0x0030 6e 74 38 3b 20 70 63 20 75 69 6e 74 70 74 72 3b  nt8; pc uintptr;
 0x0040 20 6b 69 6e 64 20 75 69 6e 74 31 36 3b 20 73 6f   kind uint16; so
 0x0050 20 75 69 6e 74 31 36 3b 20 72 65 63 65 69 76 65   uint16; receive
 0x0060 64 70 20 2a 75 69 6e 74 38 3b 20 72 65 6c 65 61  dp *uint8; relea
 0x0070 73 65 74 69 6d 65 20 75 69 6e 74 36 34 20 7d 00  setime uint64 }.
 rel 0+8 t=1 ""..gostring.2+16
type.[]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 } t=7 dupok size=80 value=0
 0x0000 18 00 00 00 00 00 00 00 a7 a2 25 dc 00 08 08 17  ..........%.....
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 rel 16+8 t=1 runtime.algarray+272
 rel 24+8 t=1 runtime.gcbits.0x48844400000000000000000000000000+0
 rel 40+8 t=1 ""..gostring.2+0
 rel 56+8 t=1 go.weak.type.*[]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
go.typelink.[]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }/[]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 } t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 type.[]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
runtime.gcbits.0x88444888444800000000000000000000 t=7 dupok size=16 value=0
 0x0000 88 44 48 88 44 48 00 00 00 00 00 00 00 00 00 00  .DH.DH..........
""..gostring.3 t=7 dupok size=136 value=0
 0x0000 00 00 00 00 00 00 00 00 70 00 00 00 00 00 00 00  ........p.......
 0x0010 5b 32 5d 73 74 72 75 63 74 20 7b 20 65 6c 65 6d  [2]struct { elem
 0x0020 20 2a 75 69 6e 74 38 3b 20 63 68 61 6e 20 2a 75   *uint8; chan *u
 0x0030 69 6e 74 38 3b 20 70 63 20 75 69 6e 74 70 74 72  int8; pc uintptr
 0x0040 3b 20 6b 69 6e 64 20 75 69 6e 74 31 36 3b 20 73  ; kind uint16; s
 0x0050 6f 20 75 69 6e 74 31 36 3b 20 72 65 63 65 69 76  o uint16; receiv
 0x0060 65 64 70 20 2a 75 69 6e 74 38 3b 20 72 65 6c 65  edp *uint8; rele
 0x0070 61 73 65 74 69 6d 65 20 75 69 6e 74 36 34 20 7d  asetime uint64 }
 0x0080 00                                               .
 rel 0+8 t=1 ""..gostring.3+16
type.[2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 } t=7 dupok size=96 value=0
 0x0000 60 00 00 00 00 00 00 00 ff e3 2a 83 00 08 08 11  `.........*.....
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0050 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
 rel 16+8 t=1 runtime.algarray+112
 rel 24+8 t=1 runtime.gcbits.0x88444888444800000000000000000000+0
 rel 40+8 t=1 ""..gostring.3+0
 rel 56+8 t=1 go.weak.type.*[2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
 rel 80+8 t=1 type.[]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
go.typelink.[2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }/[2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 } t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 type.[2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
go.string."[]*uint8" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
 0x0010 5b 5d 2a 75 69 6e 74 38 00                       []*uint8.
 rel 0+8 t=1 go.string."[]*uint8"+16
type.[]*uint8 t=7 dupok size=80 value=0
 0x0000 18 00 00 00 00 00 00 00 85 cd 8f fc 00 08 08 17  ................
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 rel 16+8 t=1 runtime.algarray+272
 rel 24+8 t=1 runtime.gcbits.0x48844400000000000000000000000000+0
 rel 40+8 t=1 go.string."[]*uint8"+0
 rel 56+8 t=1 go.weak.type.*[]*uint8+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.*uint8+0
go.typelink.[]*uint8/[]*uint8 t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 type.[]*uint8+0
go.string."[2]*uint8" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00  ................
 0x0010 5b 32 5d 2a 75 69 6e 74 38 00                    [2]*uint8.
 rel 0+8 t=1 go.string."[2]*uint8"+16
type.[2]*uint8 t=7 dupok size=96 value=0
 0x0000 10 00 00 00 00 00 00 00 a1 f0 1d 56 00 08 08 11  ...........V....
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0050 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
 rel 16+8 t=1 runtime.algarray+96
 rel 24+8 t=1 runtime.gcbits.0x88000000000000000000000000000000+0
 rel 40+8 t=1 go.string."[2]*uint8"+0
 rel 56+8 t=1 go.weak.type.*[2]*uint8+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.*uint8+0
 rel 80+8 t=1 type.[]*uint8+0
go.typelink.[2]*uint8/[2]*uint8 t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 type.[2]*uint8+0
go.string."[]uint16" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
 0x0010 5b 5d 75 69 6e 74 31 36 00                       []uint16.
 rel 0+8 t=1 go.string."[]uint16"+16
type.[]uint16 t=7 dupok size=80 value=0
 0x0000 18 00 00 00 00 00 00 00 e7 8e e3 20 00 08 08 17  ........... ....
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 rel 16+8 t=1 runtime.algarray+272
 rel 24+8 t=1 runtime.gcbits.0x48844400000000000000000000000000+0
 rel 40+8 t=1 go.string."[]uint16"+0
 rel 56+8 t=1 go.weak.type.*[]uint16+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.uint16+0
go.typelink.[]uint16/[]uint16 t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 type.[]uint16+0
go.string."[2]uint16" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00  ................
 0x0010 5b 32 5d 75 69 6e 74 31 36 00                    [2]uint16.
 rel 0+8 t=1 go.string."[2]uint16"+16
type.[2]uint16 t=7 dupok size=96 value=0
 0x0000 04 00 00 00 00 00 00 00 20 ce 55 49 00 02 02 91  ........ .UI....
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0050 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
 rel 16+8 t=1 runtime.algarray+64
 rel 24+8 t=1 runtime.gcbits.0x000000000000000000000000000000+0
 rel 40+8 t=1 go.string."[2]uint16"+0
 rel 56+8 t=1 go.weak.type.*[2]uint16+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.uint16+0
 rel 80+8 t=1 type.[]uint16+0
go.typelink.[2]uint16/[2]uint16 t=7 dupok size=8 value=0
 0x0000 00 00 00 00 00 00 00 00                          ........
 rel 0+8 t=1 type.[2]uint16+0
runtime.gcbits.0x84884884844884844800000000000000 t=7 dupok size=16 value=0
 0x0000 84 88 48 84 84 48 84 84 48 00 00 00 00 00 00 00  ..H..H..H.......
""..gostring.4 t=7 dupok size=264 value=0
 0x0000 00 00 00 00 00 00 00 00 f1 00 00 00 00 00 00 00  ................
 0x0010 73 74 72 75 63 74 20 7b 20 74 63 61 73 65 20 75  struct { tcase u
 0x0020 69 6e 74 31 36 3b 20 6e 63 61 73 65 20 75 69 6e  int16; ncase uin
 0x0030 74 31 36 3b 20 70 6f 6c 6c 6f 72 64 65 72 20 2a  t16; pollorder *
 0x0040 75 69 6e 74 38 3b 20 6c 6f 63 6b 6f 72 64 65 72  uint8; lockorder
 0x0050 20 2a 75 69 6e 74 38 3b 20 73 63 61 73 65 20 5b   *uint8; scase [
 0x0060 32 5d 73 74 72 75 63 74 20 7b 20 65 6c 65 6d 20  2]struct { elem
 0x0070 2a 75 69 6e 74 38 3b 20 63 68 61 6e 20 2a 75 69  *uint8; chan *ui
 0x0080 6e 74 38 3b 20 70 63 20 75 69 6e 74 70 74 72 3b  nt8; pc uintptr;
 0x0090 20 6b 69 6e 64 20 75 69 6e 74 31 36 3b 20 73 6f   kind uint16; so
 0x00a0 20 75 69 6e 74 31 36 3b 20 72 65 63 65 69 76 65   uint16; receive
 0x00b0 64 70 20 2a 75 69 6e 74 38 3b 20 72 65 6c 65 61  dp *uint8; relea
 0x00c0 73 65 74 69 6d 65 20 75 69 6e 74 36 34 20 7d 3b  setime uint64 };
 0x00d0 20 6c 6f 63 6b 6f 72 64 65 72 61 72 72 20 5b 32   lockorderarr [2
 0x00e0 5d 2a 75 69 6e 74 38 3b 20 70 6f 6c 6c 6f 72 64  ]*uint8; pollord
 0x00f0 65 72 61 72 72 20 5b 32 5d 75 69 6e 74 31 36 20  erarr [2]uint16
 0x0100 7d 00                                            }.
 rel 0+8 t=1 ""..gostring.4+16
go.string."tcase" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00  ................
 0x0010 74 63 61 73 65 00                                tcase.
 rel 0+8 t=1 go.string."tcase"+16
go.string."ncase" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00  ................
 0x0010 6e 63 61 73 65 00                                ncase.
 rel 0+8 t=1 go.string."ncase"+16
go.string."pollorder" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00  ................
 0x0010 70 6f 6c 6c 6f 72 64 65 72 00                    pollorder.
 rel 0+8 t=1 go.string."pollorder"+16
go.string."lockorder" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00  ................
 0x0010 6c 6f 63 6b 6f 72 64 65 72 00                    lockorder.
 rel 0+8 t=1 go.string."lockorder"+16
go.string."scase" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00  ................
 0x0010 73 63 61 73 65 00                                scase.
 rel 0+8 t=1 go.string."scase"+16
go.string."lockorderarr" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00  ................
 0x0010 6c 6f 63 6b 6f 72 64 65 72 61 72 72 00           lockorderarr.
 rel 0+8 t=1 go.string."lockorderarr"+16
go.string."pollorderarr" t=7 dupok size=32 value=0
 0x0000 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00  ................
 0x0010 70 6f 6c 6c 6f 72 64 65 72 61 72 72 00           pollorderarr.
 rel 0+8 t=1 go.string."pollorderarr"+16
type.struct { tcase uint16; ncase uint16; pollorder *uint8; lockorder *uint8; scase [2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }; lockorderarr [2]*uint8; pollorderarr [2]uint16 } t=7 dupok size=376 value=0
 0x0000 90 00 00 00 00 00 00 00 a9 db 78 e1 00 08 08 19  ..........x.....
 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0050 07 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00  ................
 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00a0 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00  ................
 0x00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00d0 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x00f0 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
 0x0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0120 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0140 00 00 00 00 00 00 00 00 78 00 00 00 00 00 00 00  ........x.......
 0x0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 0x0170 88 00 00 00 00 00 00 00                          ........
 rel 16+8 t=1 runtime.algarray+112
 rel 24+8 t=1 runtime.gcbits.0x84884884844884844800000000000000+0
 rel 40+8 t=1 ""..gostring.4+0
 rel 56+8 t=1 go.weak.type.*struct { tcase uint16; ncase uint16; pollorder *uint8; lockorder *uint8; scase [2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }; lockorderarr [2]*uint8; pollorderarr [2]uint16 }+0
 rel 64+8 t=1 runtime.zerovalue+0
 rel 72+8 t=1 type.struct { tcase uint16; ncase uint16; pollorder *uint8; lockorder *uint8; scase [2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }; lockorderarr [2]*uint8; pollorderarr [2]uint16 }+96
 rel 96+8 t=1 go.string."tcase"+0
 rel 104+8 t=1 go.importpath."".+0
 rel 112+8 t=1 type.uint16+0
 rel 136+8 t=1 go.string."ncase"+0
 rel 144+8 t=1 go.importpath."".+0
 rel 152+8 t=1 type.uint16+0
 rel 176+8 t=1 go.string."pollorder"+0
 rel 184+8 t=1 go.importpath."".+0
 rel 192+8 t=1 type.*uint8+0
 rel 216+8 t=1 go.string."lockorder"+0
 rel 224+8 t=1 go.importpath."".+0
 rel 232+8 t=1 type.*uint8+0
 rel 256+8 t=1 go.string."scase"+0
 rel 264+8 t=1 go.importpath."".+0
 rel 272+8 t=1 type.[2]struct { elem *uint8; chan *uint8; pc uintptr; kind uint16; so uint16; receivedp *uint8; releasetime uint64 }+0
 rel 296+8 t=1 go.string."lockorderarr"+0
 rel 304+8 t=1 go.importpath."".+0
 rel 312+8 t=1 type.[2]*uint8+0
 rel 336+8 t=1 go.string."pollorderarr"+0
 rel 344+8 t=1 go.importpath."".+0
 rel 352+8 t=1 type.[2]uint16+0
go.string."runtime" t=7 dupok size=24 value=0
 0x0000 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00  ................
 0x0010 72 75 6e 74 69 6d 65 00                          runtime.
 rel 0+8 t=1 go.string."runtime"+16
go.importpath.runtime. t=7 dupok size=16 value=0
 0x0000 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00  ................
 rel 0+8 t=1 go.string."runtime"+16
runtime.zerovalue t=7 dupok size=0 value=0

Take a moment to study this listing above. You'll find it quite inviting. So, what's the kernel we are looking for?

0x022d 00557 (/tmp/example.go:17) CALL ,runtime.selectgo(SB)

Namely the call to runtime.selectgo. What is interesting to note about it is that each time a select statement is created, runtime.selectgo is subsequently invoked. Here's the core for runtime.selectgo, but this excerpt will interest you the most:

for i := 1; i < int(sel.ncase); i++ {
 o := pollorder[i]
 j := int(fastrand1()) % (i + 1)
 pollorder[i] = pollorder[j]
 pollorder[j] = o
}

There you have it: the communication case's Fisher-Yates-based shuffling algorithm. What's to note: it will be invoked each time a select statement's body is encountered, meaning there is no sense of fairness with queued channel operations, since the statements are executed sequentially from a randomized collection.

I hope you enjoyed this adventure as much as me. I find the Go runtime incredibly accessible and want to share as much of it as I can.

follow us in feedly
 

None of the content contained herein represents the views of my employer nor should it be construed in such a manner. . All content is copyright Matt T. Proud and may not be reproduced in whole or part without expressed permission.