CS520
Spring 2019
Laboratory 7 addendum
Friday March 22


This addendum provides more details on the steps to follow to complete Lab 7.

The goal for Lab 7 is to complete the createGoroutine, yieldGoroutine and the cleanupGouroutine functions:

  1. Define a C struct for the Goroutine Control Block (GCB), which contains the following members:

  2. Create a "ready list", a linked list of all the GCBs for goroutines that are ready to run. The first GCB on the list should be the currently running goroutine. I recommend implementing the ready list by using two global static pointers to GCBs, one to point to the head of the list and one to point to the tail of the list. Initialize these two pointers to NULL.

  3. Write an Intel 64 assembly language routine to be used by yieldGoroutine in order to save the state of the current goroutine and to restore the state of the goroutine to run next:
    1. The function should begin with the two instructions that all "good" Intel 64 functions begin with: push the current frame pointer (rbp) onto the top of stack, and establish the new frame pointer to point to the top of the stack.
    2. Next check if the function's first parameter (which will be in rdi) is null (zero). If so, branch over the next several instructions that save the state of the current goroutine.
    3. Save the state (rsp, rbx, r12, r13, r14, r15) of the current goroutine into the GCB pointed to by the first parameter (rdi). This will be done by a sequence of "movq" instructions, e.g. movq %rsp, 0(%rdi). Note that each value will be stored at a particular offset into the struct. In the "movq" instruction given, it is assumed that the rsp register is stored first in the GCB struct, which will be at offset 0. Be sure that the offsets you use in assembly language align with how you declared the C struct.
    4. Restore the state of the goroutine to run next from the GCB pointed to by the second parameter (rsi). This will be done by a sequence of "movq" instructions, e.g. movq 0(%rsi), %rsp. Also move values from the rdi and rsi GCB slots into rdi and rsi. Be sure you set the rsi register last.
    5. The function should end with the two instructions that all "good" Intel 64 functions end with: set the frame pointer (rbp) by popping a value from the top of the stack, and then a return instruction.

  4. Write the panic C function. The exact C code is: void panic(char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); exit(-1); } To support this code, you need to include stdarg.h at the top of goroutines.c.

  5. Write the gStart C function. The exact C code is: static void gStart(void (*func)(void *), void *arg) { func(arg); gCleanup(); }

  6. Write the yieldGoroutine C function:
    1. If the ready list is empty, malloc a GCB for the main goroutine and insert it into the ready list. If malloc fails, call the panic function with an appropriate error message.
    2. Rotate GCBs on the ready list: first (current running goroutine) becomes last and second (next to run) becomes first.
    3. Call the assembly language routine to save the state of the current running goroutine into its GCB and to restore the state of the goroutine to run next from its GCB.

  7. Write the createGoroutine C function:
    1. If the first argument is NULL, call the panic function with an appropriate error message.
    2. If the ready list is empty, malloc a GCB for the main goroutine and insert it into the ready list.
    3. Use malloc to allocate a GCB and put it on the end of the ready list.
    4. Use malloc to allocate a stack. Save the base address of the stack in the GCB so it can be freed later.
    5. If any of these malloc calls fail, call the panic function with an appropriate error message.
    6. Initialize the GCB and the stack so that the goroutine, when it is yielded to, will execute the gStart function:
      • put the createGoroutine's arguments into the rdi and rsi slots of the GCB.
      • put the address of the third-to-last quadword in the stack into the rsp slot of the GCB.
      • put the address of the gStart function into the second-to-last quadword in the stack.

  8. Write the gCleanup C function:
    1. Remove the GCB from the front of the ready list.
    2. Free the stack for the last goroutine that called gCleanup.
    3. Remember the pointer to the base of the stack for the current goroutine so that it can be freed at the next call to gCleanup.
    4. Free the GCB that was removed from the front of the ready list.
    5. Call the assembly language routine that saves/restores goroutine state, passing NULL for its first parameter, and passing as the second parameter the address of the GCB now on the front of the ready list.

  9. Write the cleanupGoroutines C function:
    1. Iterate through the ready list and free the GCB and stack for each goroutine. (Of course, do not free the stack for the main goroutine.)


Last modified on January 15, 2019.

Comments and questions should be directed to pjh@cs.unh.edu