CS计算机代考程序代写 assembly compiler prolog CPSC 213, Summer 2017, Term 2 — Final Exam Solution Date: August 2017; Instructor: Anthony Estey

CPSC 213, Summer 2017, Term 2 — Final Exam Solution Date: August 2017; Instructor: Anthony Estey

THE UNIVERSITY OF BRITISH COLUMBIA CPSC 213: MIDTERM 1 – October 10, 2017
FullName: ______________________ ExamID: Signature: ______________________ UBC Student #:
Important notes about this examination
1. Copy the last 3 digits of your exam’s serial number from the upper right‐hand corner into the Exam ID box on this page.
2. You have 60 minutes to write this examination. An empty page is provided for you to answer any questions for which you need
extra space. Clearly indicate which question the answer is related to.
3. No notes, books, or any type of electronic equipment is allowed. You can have pen or pencil, eraser, your ID and a bottle of
water on your desk. All other material should be stored in your bag under your desk.
4. The marks allocated to each question are in square brackets alongside the question. Use this information to help you determine
how much time you should spend on each question.
5. No questions are permitted during the exam. If you are unsure about the meaning of a question, write your assumptions.
Student Conduct during Examinations
1. Each examination candidate must be prepared to produce, upon the request of the invigilator or examiner, his or her UBCcard for identification.
2. Examination candidates are not permitted to ask questions of the examiners or invigilators, except in cases of supposed errors or ambiguities in examination questions, illegible or missing material, or the like.
3. No examination candidate shall be permitted to enter the examination room after the expiration of one‐half hour from the scheduled starting time, or to leave during the first half hour of the examination. Should the examination run forty‐ five (45) minutes or less, no examination candidate shall be permitted to enter the examination room once the examination has begun.
4. Examination candidates must conduct themselves honestly and in accordance with established rules for a given examination, which will be articulated by the examiner or invigilator prior to the examination commencing. Should dishonest behaviour be observed by the examiner(s) or invigilator(s), pleas of accident or forgetfulness shall not be received.
5. Examination candidates suspected of any of the following, or any other similar practices, may be immediately dismissed from the examination by the examiner/invigilator, and may be subject to disciplinary action:
i. speaking or communicating with other examination candidates, unless
otherwise authorized;
ii. purposely exposing written papers to the view of other examination
candidates or imaging devices;
iii. purposely viewing the written papers of other examination candidates;
iv. using or having visible at the place of writing any books, papers or other
memory aid devices other than those authorized by the examiner(s); and,
v. using or operating electronic devices including but not limited to telephones, calculators, computers, or similar devices other than those authorized by the
examiner(s)—(electronic devices other than those authorized by the examiner(s) must be completely powered down if present at the place of writing).
6. Examination candidates must not destroy or damage any examination material, must hand in all examination papers, and must not take any examination material from the examination room without permission of the examiner or invigilator.
7. Notwithstanding the above, for any mode of examination that does not fall into the traditional, paper‐based method, examination candidates shall adhere to any special rules for conduct as established and articulated by the examiner.
8. Examination candidates must follow any additional examination rules or directions communicated by the examiner(s) or invigilator(s).
Please do not write in this space:
Question 1: Question 2:
Question 3: Question 4: Question 5: Question 6:
Question 7: Question 8: Question 9:
1 236413 048929

1 [4 marks] Variables and Memory. Consider the following C code containing global variables a, b, c, and d that is executed on a little endian, 32-bit processor. Assume that the address of a is 0x1000 and that the compiler allocates the variables contiguously, in the order they appear, wasting no memory between them other than what is required to ensure that they are properly aligned (and assuming that int’s and pointers are 4-bytes long). With this information, you can determine the value of certain bytes of memory following the execution of foo().
int a;
char b;
int c;
int *d;
void foo() {
a = 0x22446688;
Listtheaddressandvalueofeverymemorylocationwhoseaddressandvalueyouknow.Usetheform“address: value”. List every byte on a separate line and list all numbers in hex.
b = (char)a;
c = (int)b;
d = &a;
*d = (b & 0xff) << 4; } 1000: 0x80 1001: 0x08 1002: 0x00 1003: 0x00 1004: 0x88 1008: 0x88 1009: 0xff 100a: 0xff 100b: 0xff 100c: 0x00 100d: 0x10 100e: 0x00 100f: 0x00 3 2 [4 marks] Global Variables. Give the SM213 assembly code for the following statements, given the global variables a, b, and c. Treat each sub-question separately (i.e., do not use values computed in prior sub-questions). Comments are not required. int* a; int b; int* c; 2a b = *a; 2b a[b] = c[b]; 2c Howmanymemoryreadsinthefollowingoperation: a[b] = a[c[2]]; ld $a, r0 ld (r0), r0 ld (r0), r0 ld $b, r1 st r0, (r1) # r0 = &a # r0 = a # r0 = a[0] # r0 = &b # b = &a ld $a, r0 ld (r0), r0 ld $b, r1 ld (r1), r1 ld $c, r2 ld (r2), r2 ld (r2, r1, 4), r2 st r2, (r0, r1, 4) #r0=&a #r0=&a[0] #r1=&b #r1=b #r2=&c #r2=&c[0] # r2 = c[b] # a[b] = c[b] 5 4 3 [4 marks] Instance and Local Variables. Assume two global variables root and other, have been declared. struct B { int data[2]; struct A* parent; int id; }; struct A* root; struct B other; struct A { struct A* left; struct A* right; struct B node; }; Treat each sub-question separately (i.e., do not use values computed in prior sub-questions). Comments are not required, but they will probably help you. Assume all pointers are 4 bytes long. 3a WhatistheoffsetofidinstructA?(Hint:structAhasastructB“inside”it) 20 3b GiveassemblycodefortheCstatement: other.data[1] = 5; 3c GiveassemblycodefortheCstatement: root->left->node.id = other.id;
ld $other, r0
ld $5, r1
st 4(r0), r1
# r0 = &other
# r1 = 5
# other.data[i] = 5
ld $root, r0
ld (r0), r0
ld (r0), r0
ld $other, r1
ld 12(r1), r1
st r1, 20(r0)
# r0 = &root
# r0 = root
# r0 = root->left
# r1 = other
# r1 = other.id
# root->left->node.id = other.id
5

4 [10 marks] C Pointers and Functions Determine the output for the following C Code. Assume that the address
of a
is 0x1000, x is 0x2000, and y is 0x3000. Note: there is space on the next page to write down the output.
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10] void sub(int *x,
[11] *x=*x-y;
[12] }
int a;
int *x;
int y;
int **z;
void add(int *x,
x = x + y;
inty){
inty){
}
[13]
[14] void mul(int x, int y) {
[15] x = x * y;
[16] }
[17]
[18] int main (void) {
[19] a = 5;
[20] x = &a;
[21] y = 3;
[22] z = &x;
[23]
[24] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[25]
[26] add(x, y);
[27] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[28]
[29] sub(x, y);
[30] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[31]
[32] **z = 7;
[33] mul(**z, y);
[34] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[35]
[36] sub(*z, y);
[37] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[38]
[39] add(&a, y);
[40] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[41]
[42] sub(&a, y);
[43] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[44]
[45] x = &y;
[46] add(x, y);
[47] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[48]
[49] sub(x, y);
[50] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[51]
[52] a = &y;
[53] z = &a;
[54] printf(“a:%d x:0x%x *x:%d y:%d z:0x%x **z:%d”, a, x, *x, y, z, **z);
[55] }
6

This page is for Question 6. Please write the output of each printf statement. Line numbers are given in square brackets ([24] means the printf statements on line 24 of previous page).
4a [24] a: x:
a:5 x:0x1000 *x:5
4b [27] a: x:
a:5 x:0x1000 *x:5
4c [30] a: x:
a:2 x:0x1000 *x:2
4d [34] a: x:
a:7 x:0x1000 *x:7
4e [37] a: x:
a:4 x:0x1000 *x:4
4f [40] a: x:
a:4 x:0x1000 *x:4
4g [43] a: x:
a:1 x:0x1000 *x:1
4h [47] a: x:
a:1 x:0x3000 *x:3
4i [50] a: x:
a:1 x:0x3000 *x:0
4j [54] a: x: a:0x3000 x:0x3000
*x:
*x:
*x:
*x:
*x:
*x:
*x:
*x:
y:3
*x:
*x:
*x:0
y:3
y:3
y:3
y:3
y:3
y:3
y:3
y:0
y: z: **z:
z:0x2000 **z:5
y: z: **z:
z:0x2000 **z:5
y: z: **z:
z:0x2000 **z:2
y: z: **z:
z:0x2000 **z:7
y: z: **z:
z:0x2000 **z:4
y: z: **z:
z:0x2000 **z:4
y: z: **z:
z:0x2000 **z:1
y: z:
z:0x2000 **z:3
y: z:
z:0x2000 **z:0
**z:
**z:
**z:
**z:0
y:
z:
y:0 z:0x1000
7

5 [8 marks] C Memory Errors. There are four memory related errors in the program below. For each error, explain what type of error it is, and indicate how to fix it.
[1] struct Transcript {
[2] int cs_id;
[3] int gpa;
[4] int refcount;
[5] };
[6]
[7] struct Transcript *top;
[8]
[9] void maybe_top_student(struct Transcript *cur) {
[10] if ((top == NULL) ll (top && cur->gpa > top->gpa)) {
[11] if (top != NULL) {
[12] dec_ref(top);
[13] }
[14] top = cur;
[15] }
[16] }
[17]
[18] int convert_to_gpa(int grade) { /*assume this correctly returns gpa* }
[19]
[20] void init(int *a, int n, struct Transcript* t) {
[21] int sum=0;
[22] int high[5];
[23] for
[24]
[25]
[26] }
[27] t->gpa = sum/n;
[28] }
(inti=0;irefcount = 0;
[33] t->cs_id = cs_id;
[34] init(nums, size, t);
[35] return t;
[36] }
[37]
[38] void print_info(struct Transcript* t) {
[39] printf(“student id: %d, gpa: %d”, t->cs_id, t->gpa);
[40] free(t);
[41] }
[42]
[43] int main() {
[44] // ASSUME all variables have been correctly initialized here
[45] // nums is a 2d-array of integers; size is the number of grade
[46] // entries at each index of nums; and cs_ids is the associated id.
[47]
[48] for (int i = 0; i < NUM_STUDENTS; i++) { [49] struct Transcript* s1 = create_transcript(nums[i], size[i], cs_ids[i]); [50] maybe_top_student(s1); [51] print_info(s1); [52] } [53] printf("The top student in the class is:"); [54] print_info(top); [55] } 8 5a Error1: explanation: fix: 5b Error2: explanation: fix: 5c Error3: explanation: fix: 5d Error4: explanation: fix: 1. line 15, ref count value incorrect, add inc ref(top); 2. line 23, array bounds problem, make sure n is never greater than 5 3. line 32, ref count value incorrect, refcount = 1 4. line 40, free will cause dangling pointer, change to dec ref(t); 9 6 [8 marks] Reading Assembly Code. The following procedures use the call/return conventions described in class, where we use r0 for return values, r5 for the stack pointer, and r6 for the return address. foo: deca r5 deca r5 st r6, 4(r5) ld $0, r1 st r1, 0(r5) ld 8(r5), r2 # allocate callee portion of stack frame # int result # save return address on stack #r1=0=i’ #result=0 #r2=a; #r3=n #r3=-n #r4=i’ #r4=i’ -n ld 12(r5), not r3 inc r3 L0: mov r1, r4 add r3, r4 beq r4, L1 bgt r4, L1 r3 ld(r2,r1,4),r4 deca r5 st r4, 0(r5) gpc $6, r6 j bar inca r5 ld 0(r5), r4 add r0, r4 st r4, 0(r5) inc r1 br L0 L1: ld 0(r5), r0 ld 4(r5), r6 inca r5 inca r5 j (r6) bar: ld 0(r5), r7 ld $1, ld $0, and r7, beq r4, mov r7, 6b ConverttheassemblyintoC. L2: j r4 r0 r4 L2 r0 (r6) # goto L1 # goto L1 #r4=a[i’] # allocation caller portion of stack # save a[i’] on stack as first arg # compute ra # r’ = bar(a[i]) # deallocate caller portion of stack # r4 = result # result’ += result’ + r’ # result = result’ + r’ # i’++; # goto L0 # r0 = result # get ra from stack # deallocate callee portion of stack # return result #x=arg1 #r4=1 #r0=0 #r4=arg1&1 #gotoL2if(arg1&1)==0 #r0=arg1 # return r0 (arg1 or 0 depending on cond) 6a Carefullycommenteverylineofcodeabove. if i’==n if i’>n
int bar(int arg1) {
if (arg1 & 1)
return arg1;
return 0;
}
int foo(int *a, int n) {
int result = 0;
for (int i = 0; i < n; i++) { result += bar(a[i]); } return result; } 6c Whatisthepurposeofthecodefrompreviouspage?Givethesimplest,plainEnglishdescriptionyoucan. foo returns the sum of all odd numbers in the array a. 10 7 [8 marks] Procedure Calls. Give the SM213 assembly for the foo procedure below. Assume the caller of foo’s prologue was implemented correctly (so r5 is pointing to the right place). Similar to in lecture this semester, assume that we use r0 for return values, r5 for the stack pointer, and r6 for the return address. Comments are not required. int foo(int a, int b) { int x = a+1; int y = a+b; bar(); if (x > y) {
return x*2;
} else
return y; }
}
foo:
ld $0xfffffff4, r1
add r1, r5
st r6, 0x8(r5)
ld 0xc(r5), r1
ld 0x10(r5), r2
add r1, r2
inc r1
st r1, 0x0(r5)
st r2, 0x4(r5)
gpc $0x6, r6
j bar
ld 0x0(r5), r1
ld 0x4(r5), r2
mov r2, r3
not r3
inc r3
add r1, r3
bgt r3, ret
mov r2, r0
br end
shl $1, r1
mov r1, r0
ld 0x8(r5), r6
ld $12, r1
add r1, r5
j 0x0(r6)
# r1 = -12
# allocate callee part of stack frame # store ra
#r1=a
# r2 = b
#r2=a+b
#r1=a+1
#savex =a+1intostackframe #savey =a+bintostackframe #putra into r6
# bar()
#r1=x
#r2=y
#r3=y
#r3=-y #r3=x-y #gotoretifx>y #r0=y
# goto end #r1=2*x #r0=2*x
# load ra into r6 #r1=12
# callee epilogue
# return r0
ret: end:
11

8 [8 marks] Switch Statements Translate the following assembly code into two different C implementations. One implementation should use a switch statement, the other should use a jump table.
There is space on the next page for you to write your solutions.
You do not need to write comments, but it may award you part marks if you are unable to correctly write the corre- sponding C code.
.pos 0x100
foo: ld
ld $-2, r1
add r0, r1
bgt r1, L0
br L4
L0: ld $-6, r1
add r0, r1
bgt r1, L4
ld $-3, r1
add r1, r0
ld $jt, r1
j *(r1, r0, 4)
L6: halt
.pos 0x140
L1: ld
L2: ld
br L5
L3: ld
br L5
L4: ld
br L5
L5: ld
st r1, 0x0(r0)
.pos 0x800
jt: .long L1
.long L2
.long L4
.long L3
.pos 0x1000
i: .long 0x4
j: .long 0x0
$i, r0
ld 0x0(r0), r0
$0, r1 br L5
$1, r1
$2, r1
$3, r1
$j, r0 j L6
12

8a Solutionusingswitchstatements:
//assume i and j have been declared and initialized above
switch(i) {
case 3: j=0; break;
case 4: j=1; break;
case 6: j=2; break;
default: j=3; break;
}
8b Solutionusingajumptable:
//assume i and j have been declared and initialized above
static const void* jt[] = {&&L0, &&L1, &&DEFAULT, &&L2};
if (i < 3 ll i > 6)
goto DEFAULT;
goto *jt [i – 3];
L0: j=0; goto CONT;
L1: j=1; goto CONT;
L2: j=2; goto CONT;
DEFAULT: j=3; goto CONT;
CONT: //end of question
13

9 [10 marks] Using Function Pointers. Complete the C procedure named apply that returns the sum of an operation repeatedly performed on the elements of two arrays for which a certain condition is met. Fill in the blanks!
int fn1(int a, int b) { return a+b; }
int fn2(int a, int b) { return a-b; }
int fn3(int a, int b) { return a*b; }
int fn4(int a, int b) { return a/b; }
int fn5(int a, int b) { return a>b; }
int fn6(int a, int b) { return (a-b) == 0; }
int fn7(int a, int b) { return (a-b) <= 0; } int fn8(int a, int b) { return (a%b) == 0; } int fn9(int a, int b) { return (a/b) == 0; } int fn0(int a, int b) { return (a&b) == 0; } int apply(int(*fn)(int,int),int(*cond)(int,int),int *a, int *b, int size) { int result = 0; for (int i = 0; i < size; i++) { if (cond(a[i], b[i]) _________________________________________________________ } result += fn(a[i], b[i]); _____________________________________________________ return result; } 9a Usingapplyandthesuppliedproceduresabove,finishtheprocedurecallsothatresultissettothesumof differences between values of the value-pairs in the array where the value in a[i] is greater than b[i]. For example: a: {8, 2, 6, 1, 3} b: {3, 5, 1, 4, 2} difference: 5 -3 5 -4 1 a bigger? Y N Y N Y result: 5 + 5 + 1 = 11 fn2, fn5 int result = apply(________________,________________, a, b, size); 9b Usingapplyandthesuppliedproceduresabove,finishtheprocedurecallsothatresultissettothesumof products between values of the value-pairs in the array where the value in a[i] is equal to b[i]. For example: a: {8, 2, 6, 1, 3} b: {5, 2, 7, 1, 3} equal?: N Y N Y Y product: 40 4 42 1 9 result: 4+1+9=14 fn3, fn6 int result = apply(________________,________________, a, b, size); 9c Usingapplyandthesuppliedproceduresabove,finishtheprocedurecallsothatresultissettothenumber of value-pairs in the array where the value in a[i] is a multiple of b[i].For example: a: {8, 8, 6, 1, 6} b: {8, 2, 7, 4, 3} multiple?: Y Y N N Y result: 3 (number of Ys) fn8, fn8 int result = apply(________________,________________, a, b, size); 14 10 [4 marks] IO Devices. For each of the following, (a) explain what it is and (b) state whether the CPU, IO controller, or memory determines when it occurs (i.e., initiates it). I have completed a) for you. 10a ProgrammedIO(PIO): Example: The CPU uses PIO to read or write to an IO device one word at a time. It is initiated by the CPU. 10b DMA: 10c Interrupts: DMA allows information needed from an I/O device to be written to memory directly, without involving the CPU. Initiated by I/O controller Interrupts allow a device controller to send a signal to the CPU. This “interrupts” the CPU’s current execution. Initiated by I/O controller. 15 11 [8 marks] IO Devices. The image below is taken from the Unit 2a slides. Note there are 6 arrows labeled 1 through 6. Each arrow begins at either the CPU, Memory, or an I/O controller and ends at a different component. Assume you need to load a buffer with data from a disk device, similar to in A9, and you choose to use interrupts in your implementation. Briefly explain each step of the process and which arrow is ”followed” during that process, up to and including the step where the data is loaded into a CPU register. I have completed a) for you 11a Step1:Arrow#__2___ Example:PIO to request a read from a device. 11b Step2:Arrow#__________ Description: 11c Step3:Arrow#__________ Description: 11d Step4:Arrow#__________ Description: 11e Step5:Arrow#__________ Description: Step 2: Arrow 3 Description: I/O controller getting data from device... Step 3: Arrow 5 Description: DMA (I/O controller writes data to memory) Step 4: Arrow 1 Description: Interrupt (I/O controller signals to CPU) Step 5: Arrow 4 Description: CPU accesses memory 16 12 [4 marks] Threads and Scheduling. Answer the following question about the code using threads below. uthread_t t1, t2; int i = 0; void* ping (void* x) { printf("ping1"); uthread_yield(); printf("ping2"); uthread_unblock(t2); printf("ping3"); } void* pong (void* x) { printf("PONG1"); uthread_block(); printf("PONG2"); } int main (int argc, char** argv) { uthread_init (2); //assume thread t1 is created to run ping, //t2 is created to run pong, but you do not //know which order they are initially scheduled uthread_join (t1, 0); uthread_join (t2, 0); } Which of the following outputs could occur given the code above? (Note: ...(error!) is there to denote a deadlock or some other kind of problem where the program does not finish execution as normal. CIRCLE ALL THAT APPLY. A. ping1 PONG1 ping2 ping3 PONG2 B. ping1 ping2 ping3 PONG1 PONG2 C. PONG1 ping1 ping2 PONG2 ping3 D. PONG1 ping1 PONG2 ping2 ping3 E. PONG1 ping1 ping2 ping3 PONG2 F. ping1 ping2 ping3 PONG1 (error!) G. ping1 ping2 PONG1 ...(error!) H. PONG1 ping1 ...(error!) I. ping1 ...(error!) ACEF 17 13 [8 marks] Spinlocks Examine the following code that is used to protect a critical section in some code. 13a Drawadiagramthatshowstheexecutionofmultiplethreads,andillustratehowmorethanonethreadcould ”think” it holds the lock based on when interrupts occur. int lock = 0; void lock (int* lock) { while (*lock==1) {} *lock = 1; } void unlock (int* lock) { *lock = 0; } 13b Writetheassemblycodetocorrectlyimplementaspinlockusingthexchginstructionshownbelow. (Hint: even if you don‘t remember this example in class, think about how spinlocks work. What is the problem in the example above, and how can we solve it with this atomic xchg operation which allows us to read and write at the same time. To receive full marks, your solution should minimize the number of times xchg is executed while “spinning”.) loop: ld $lock, r1 # r1 = lock (comments not mandatory) ld (r1), r0 beq r0, try br loop try: ld $1, r0 xchg (r1), r0 beq r0, held # r0 = lock # goto try if lock==0 (available) # goto loop if lock!=0 (held) # r0 = 1 # atomically swap r0 and lock # goto held lock was 0 before swap # try again if another thread holds lock # we now hold the lock held: br loop 18 14 [8 marks] Semaphores. The code shown below uses semaphores to ensure the program always prints ”do...re...me”. On the next page, you will provide a solution that ensures the same output using mutexes. I have started the code for you, but feel free to cross out what I have put down and write it from scratch, or use different variables. uthread_sem_t lockab, lockbc; void* do(void* p) { printf("do..."); uthread_sem_signal(lockab); return NULL; } void* re(void* p) { uthread_sem_wait(lockab); printf("re..."); uthread_sem_signal(lockbc); return NULL; } void* me(void* p) { uthread_sem_wait(lockbc); printf("me..."); return NULL; } int main (int argc, char** argv) { uthread_t at,bt,ct; uthread_init(3); lockab = uthread_sem_create (0); lockbc = uthread_sem_create (0); at = uthread_create (do,NULL); bt = uthread_create (re,NULL); ct = uthread_create (me,NULL); uthread_join(at,0); uthread_join(bt,0); uthread_join(ct,0); printf("Done!\n"); uthread_sem_destroy(lockab); uthread_sem_destroy(lockbc); } 19 uthread_mutex_t mx; uthread_cond_t first; uthread_cond_t second; uthread_cond_t third; int ready = 0; void* doe() { return NULL; } void* re() { return NULL; } void* me() { return NULL; } int main (int argc, char** argv) { uthread_init (1); mx = uthread_mutex_create(); first = uthread_cond_create(mx); second = uthread_cond_create(mx); third = uthread_cond_create(mx); uthread_t t1, t2, t3; t1 = uthread_create (doe,NULL); t2 = uthread_create (re,NULL); t3 = uthread_create (me,NULL); uthread_join(t1,0); uthread_join(t2,0); uthread_join(t3,0); printf("Done!\n"); } 20 void* do() { uthread_mutex_lock(mutex); while (ready < 2) { uthread_cond_wait(first); } printf("do..."); uthread_cond_signal(second); uthread_mutex_unlock(mutex); return NULL; } void* re() { uthread_mutex_lock(mutex); ready++; uthread_cond_signal(first); uthread_cond_wait(second); printf("re..."); uthread_cond_signal(third); uthread_mutex_unlock(mutex); return NULL; } void* me() { uthread_mutex_lock(mutex); ready++; uthread_cond_signal(first); uthread_cond_wait(third); printf("me..."); uthread_mutex_unlock(mutex); return NULL; } 21