diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..efe500e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +debug +release \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..14aff19 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.4.1) +include_directories(libstack/include) +add_subdirectory(libstack) + +include_directories(libtimer/include) +add_subdirectory(libtimer) + +include_directories(libthread/include) +add_subdirectory(libthread) \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9127015 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +debug_dir = debug +debug_dir_target = $(debug_dir)-$(wildcard $(debug_dir)) +debug_dir_present = $(debug_dir)-$(debug_dir) +debug_dir_absent = $(debug_dir)- +release_dir = release +release_dir_target = $(release_dir)-$(wildcard $(release_dir)) +release_dir_present = $(release_dir)-$(release_dir) +release_dir_absent = $(release_dir)- + +all: debug release + +debug: | $(debug_dir_target) build_debug + +release: | $(release_dir_target) build_release + +$(release_dir_present): + +$(release_dir_absent): + mkdir $(release_dir) + +$(debug_dir_present): + +$(debug_dir_absent): + mkdir $(debug_dir) + +build_debug: + cd ${debug_dir} ; cmake -DCMAKE_BUILD_TYPE=Debug .. ; make || exit +build_release: + cd ${release_dir} ; cmake -DCMAKE_BUILD_TYPE=Release .. ; make || exit + +.PHONY: all + +clean: + rm -rf $(debug_dir) + rm -rf $(release_dir) diff --git a/README.md b/README.md index 79e9856..72a1a16 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # README # -Routines that will allow a program to run, with multiple threads, using NON-preemptive scheduling on most Unix/Linux operating systems. Contains implementation of custom semaphores, and sample programs to test threads and semaphores. +Routines that will allow a program to run, with multiple threads, using preemptive scheduling on most Unix/Linux operating systems. Contains implementation of custom semaphores, and sample programs to test threads and semaphores. ### Requirements ### * Ubuntu 14.04 -* gcc compiler +* g++ compiler +* gnu make +* cmake ### To test threads library ### -1. gcc thread_test.c -2. ./a.out | head -40 \ No newline at end of file +1. make \ No newline at end of file diff --git a/TCB.h b/TCB.h deleted file mode 100644 index ed7ce23..0000000 --- a/TCB.h +++ /dev/null @@ -1,26 +0,0 @@ -/************************************************************* -* - Author: Denny Abraham Cheriyan, Adrin Peter Fernandes - Contains TCB structure definition and code to initialize TCB - -*/ - -#include -#include - -typedef struct TCB_t{ - struct TCB_t *next; - ucontext_t context; - struct TCB_t *prev; -}TCB_t; - -void init_TCB (TCB_t *tcb, void *function, void *stackP, int stack_size) -{ - memset(tcb,'\0', sizeof(TCB_t)); //wash rinse - getcontext(&tcb->context); //have to get parent context, else snow forms on hell - tcb->context.uc_stack.ss_sp = stackP; - tcb->context.uc_stack.ss_size = (size_t) stack_size; - makecontext(&tcb->context,function,0); //context cooked -} - - diff --git a/libstack/CMakeLists.txt b/libstack/CMakeLists.txt new file mode 100644 index 0000000..8376cfe --- /dev/null +++ b/libstack/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.4.1) + +include_directories(include) +add_library(stack SHARED direction.cpp new.cpp) +add_subdirectory(tests) diff --git a/libstack/direction.cpp b/libstack/direction.cpp new file mode 100644 index 0000000..ccdd427 --- /dev/null +++ b/libstack/direction.cpp @@ -0,0 +1,22 @@ +#include "include/stack.h" + +int Stack::internal::getStackDirection(int *addr) { + int fun_local; + if (addr < &fun_local) { + printf("Stack grows upward\n"); + return SDU; + } + printf("Stack grows downward\n"); + return SDD; +} + +int Stack::getStackDirection() { + int main_local; + return internal().getStackDirection(&main_local); +} + +char * Stack::getStackDirectionAsString() { + if (direction == SDU) return "Stack grows upwards"; + else if (direction == SDD) return "Stack grows downwards"; + else return "Stack grows in an unknown direction"; +} \ No newline at end of file diff --git a/libstack/include/internal/direction.h b/libstack/include/internal/direction.h new file mode 100644 index 0000000..3a18b03 --- /dev/null +++ b/libstack/include/internal/direction.h @@ -0,0 +1,12 @@ +#ifndef STACK_DIRECTION +#define STACK_DIRECTION + +// C program to check whether stack grows // downward or upward. +#include + +#define STACK_DIRECTION_UP 11 +#define SDU STACK_DIRECTION_UP +#define STACK_DIRECTION_DOWN 12 +#define SDD STACK_DIRECTION_DOWN + +#endif \ No newline at end of file diff --git a/libstack/include/stack.h b/libstack/include/stack.h new file mode 100644 index 0000000..dd9bd99 --- /dev/null +++ b/libstack/include/stack.h @@ -0,0 +1,23 @@ +#ifndef STACK_STACK +#define STACK_STACK + +#include "internal/direction.h" +#include + +class Stack { + public: + char * stack = nullptr; + char * top = nullptr; // points to top of stack + size_t size = 0; + int direction = 0; + void alloc(size_t size); + void free(); + class internal { + public: + int getStackDirection(int *addr); + }; + int getStackDirection(); + char * getStackDirectionAsString(); +}; + +#endif \ No newline at end of file diff --git a/libstack/new.cpp b/libstack/new.cpp new file mode 100644 index 0000000..7c69721 --- /dev/null +++ b/libstack/new.cpp @@ -0,0 +1,18 @@ +#include "include/stack.h" + +void Stack::alloc(size_t size) { + stack = new char[size]; + size = size; + direction = getStackDirection(); + if (direction == SDU) top = stack; + else top = stack + (size*sizeof(char));; +} + +void Stack::free() { + if (stack == nullptr) return; + delete[] stack; + stack = nullptr; + top = nullptr; + size = 0; + direction = 0; +} diff --git a/libstack/tests/CMakeLists.txt b/libstack/tests/CMakeLists.txt new file mode 100644 index 0000000..0e8486c --- /dev/null +++ b/libstack/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.4.1) + +add_executable(test_stack stack.cpp) +target_link_libraries(test_stack stack) +add_custom_command( TARGET test_stack POST_BUILD COMMAND test_stack) diff --git a/libstack/tests/stack.cpp b/libstack/tests/stack.cpp new file mode 100644 index 0000000..29a4d0c --- /dev/null +++ b/libstack/tests/stack.cpp @@ -0,0 +1,8 @@ +#include + +int main(void) { + Stack s = Stack(); + s.alloc(100); + s.free(); + return 0; +} diff --git a/libthread/CMakeLists.txt b/libthread/CMakeLists.txt new file mode 100644 index 0000000..caddf51 --- /dev/null +++ b/libthread/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.4.1) + +add_library(thread SHARED TBC.cpp q.cpp thread.cpp) +target_link_libraries(thread stack timer) +add_subdirectory(tests) diff --git a/libthread/TBC.cpp b/libthread/TBC.cpp new file mode 100644 index 0000000..0ab2d1b --- /dev/null +++ b/libthread/TBC.cpp @@ -0,0 +1,9 @@ +#include "include/internal/TCB.h" + +void init_TCB (TCB_t *tcb, void (* function)(void), void *stackP, int stack_size) { + memset(tcb, '\0', sizeof(TCB_t)); //wash rinse + getcontext(&tcb->context); //have to get parent context, else snow forms on hell + tcb->context.uc_stack.ss_sp = stackP; + tcb->context.uc_stack.ss_size = (size_t) stack_size; + makecontext(&tcb->context, function, 0); //context cooked +} \ No newline at end of file diff --git a/libthread/include/internal/TCB.h b/libthread/include/internal/TCB.h new file mode 100644 index 0000000..1de0037 --- /dev/null +++ b/libthread/include/internal/TCB.h @@ -0,0 +1,17 @@ +/************************************************************* +* + Author: Denny Abraham Cheriyan, Adrin Peter Fernandes + Contains TCB structure definition and code to initialize TCB + +*/ + +#include +#include + +typedef struct TCB_t { + struct TCB_t *next; + ucontext_t context; + struct TCB_t *prev; +} TCB_t; + +void init_TCB (TCB_t *tcb, void (* function)(void), void *stackP, int stack_size); \ No newline at end of file diff --git a/libthread/include/internal/q.h b/libthread/include/internal/q.h new file mode 100644 index 0000000..40cb783 --- /dev/null +++ b/libthread/include/internal/q.h @@ -0,0 +1,20 @@ +/************************************************************* +* + Author: Denny Abraham Cheriyan, Adrin Peter Fernandes + Contains functions to initialize, add, delete and rotate + a doubly linked circular queue + +*/ + +#include "TCB.h" +#include +#include + + +void InitQ(TCB_t **head); + +void AddQ(TCB_t **head, TCB_t *item); + +TCB_t * DelQ(TCB_t **head); + +void RotateQ(TCB_t **head); \ No newline at end of file diff --git a/libthread/include/thread.h b/libthread/include/thread.h new file mode 100644 index 0000000..a00d0ab --- /dev/null +++ b/libthread/include/thread.h @@ -0,0 +1,12 @@ +/************************************************************* +* + Author: Denny Abraham Cheriyan, Adrin Peter Fernandes + Contains functions to Start, Run and Yield to other threads + +*/ + +#include "internal/q.h" + +void start_thread(void (* function)(void)); +void run(); +void yield(); \ No newline at end of file diff --git a/libthread/q.cpp b/libthread/q.cpp new file mode 100644 index 0000000..07e96b3 --- /dev/null +++ b/libthread/q.cpp @@ -0,0 +1,41 @@ +#include "include/internal/q.h" + +void InitQ(TCB_t **head) { + *head = 0; +} + +void AddQ(TCB_t **head, TCB_t *item) { + if (*head == 0) { + //When the head pointer is null + *head = item; + (*head)->next = *head; + (*head)->prev = *head; + } else { + //When there is one or more nodes + (*head)->prev->next = item; + item->prev = (*head)->prev; + (*head)->prev = item; + item->next = *head; + } +} + +TCB_t * DelQ(TCB_t **head) { + if ((*head) == 0) + return 0; + TCB_t *temp = *head; + if ((*head)->next == *head) { + //When there is one node + *head = 0; + } else { + //When there are two or more nodes + *head = (*head)->next; + temp->prev->next = *head; + (*head)->prev = temp->prev; + } + return temp; +} + +void RotateQ(TCB_t **head) { + if (*head != 0) + (*head) = (*head)->next; +} \ No newline at end of file diff --git a/libthread/tests/CMakeLists.txt b/libthread/tests/CMakeLists.txt new file mode 100644 index 0000000..c0cd382 --- /dev/null +++ b/libthread/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.4.1) + +add_executable(test_thread thread.cpp) +target_link_libraries(test_thread thread) +add_custom_command( TARGET test_thread POST_BUILD COMMAND test_thread) diff --git a/libthread/tests/thread.cpp b/libthread/tests/thread.cpp new file mode 100644 index 0000000..798c882 --- /dev/null +++ b/libthread/tests/thread.cpp @@ -0,0 +1,76 @@ +/************************************************************* +* + Author: Denny Abraham Cheriyan, Adrin Peter Fernandes + Testing threads.h using infinite functions + +*/ + +#include +#include // std::this_thread +#include // std::chrono_literals + +#include +#include + +using namespace std::chrono_literals; + +void thr1() { + while(1) { + printf("thr1 sleep for 2 seconds!\n"); + // sleep(2) resumes on receiving a caught signal + std::this_thread::sleep_for(2s); + printf("slept!\n"); + } +} + +void thr2() { + while(1) { + printf("thr2 sleep for 2 seconds!\n"); + // sleep(2) resumes on receiving a caught signal + std::this_thread::sleep_for(2s); + printf("slept!\n"); + } +} + +bool firstrun = false; + +Timer timer = Timer(); + +// declare a custom timer handler + +void Timer::timer_handler(int signum) { + static int count = 1; + if (!firstrun) { + fprintf (stderr, "> FIRST RUN timer ms = %d, count = %03d\n", timer.ms_, count++); + firstrun = true; + run(); + } else { + fprintf (stderr, "> YIELDING timer ms = %d, count = %03d\n", timer.ms_, count++); + yield(); + } +} + +// declare a custom schedule yeilder + +int Sched::yield(void * arg) { + // timer is per process, the process it is set in must exist in order to recieve a signal + timer.set_timer(timer.Types.ITIMER_REAL, Timer::timer_handler, 1000, false); + pause(); + return 0; +} + +int main(){ + /* Flush each printf() as it happens. */ + setvbuf(stdout, 0, _IOLBF, 0); + setvbuf(stderr, 0, _IOLBF, 0); + + Sched foo = Sched(); // scoped initialization and de-initialization of the preemptive scheduler + + start_thread(thr1); + start_thread(thr2); + + // let threads to do something for 8 seconds + sleep(8); + puts("exiting"); + return 0; +} diff --git a/libthread/thread.cpp b/libthread/thread.cpp new file mode 100644 index 0000000..9991be7 --- /dev/null +++ b/libthread/thread.cpp @@ -0,0 +1,23 @@ +#include "include/thread.h" + +const int STACK_SIZE = 8192; +TCB_t *RunQ = 0; + +void start_thread(void (* function)(void)) { + void *stack = (void *) malloc(STACK_SIZE); + TCB_t *tcb = (TCB_t *) malloc(sizeof(TCB_t)); + init_TCB(tcb, function, stack, STACK_SIZE); + AddQ(&RunQ, tcb); +} + +void run() { + ucontext_t parent; + getcontext(&parent); + swapcontext(&parent, &(RunQ->context)); +} + +void yield() { + ucontext_t *current = &(RunQ->context); + RotateQ(&RunQ); + swapcontext(current, &(RunQ->context)); +} \ No newline at end of file diff --git a/libtimer/CMakeLists.txt b/libtimer/CMakeLists.txt new file mode 100644 index 0000000..c8db561 --- /dev/null +++ b/libtimer/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.4.1) + +add_library(timer SHARED timer.cpp) +#add_subdirectory(tests) diff --git a/libtimer/include/internal/sched.h b/libtimer/include/internal/sched.h new file mode 100644 index 0000000..ec80352 --- /dev/null +++ b/libtimer/include/internal/sched.h @@ -0,0 +1,45 @@ +// +// Created by brothercomplex on 2/11/19. +// + +#ifndef custom_threads_libtimer_sched +#define custom_threads_libtimer_sched + +#include +#include // sleep +#include // SIGCHLD +#include // clone +#include // wait + +class Sched { + long int pid; + public: + static int yield(void * arg); + Stack x = Stack(); + Sched() { + puts("starting Scheduler"); + x.alloc(4096); + // start in a new process to prevent its signal handler from being modified once set + pid = clone(yield, x.top, CLONE_VM|SIGCHLD, nullptr); + if (pid == -1) perror("YIELD_THREAD clone"); + puts("successfully started Scheduler"); + } + ~Sched() { + if (pid != -1) { + puts("stopping Scheduler"); + kill(pid, SIGTERM); + siginfo_t info; + if (waitid(P_PID, pid, &info, WEXITED | WSTOPPED) == -1) { + perror("waitid"); + puts("failed to stop Scheduler"); + return; + } + if (info.si_code == CLD_KILLED && info.si_status == SIGTERM) + puts("successfully stopped Scheduler"); + else puts("failed to stop Scheduler"); + x.free(); + } + } +}; + +#endif //custom_threads_libtimer_sched diff --git a/libtimer/include/timer.h b/libtimer/include/timer.h new file mode 100644 index 0000000..0701938 --- /dev/null +++ b/libtimer/include/timer.h @@ -0,0 +1,37 @@ +// +// Created by brothercomplex on 2/11/19. +// + +#ifndef custom_threads_libtimer +#define custom_threads_libtimer + +#include // exit(), EXIT_FAILURE, EXIT_SUCCESS +#include // sigaction() +#include // printf(), fprintf(), stdout, stderr, perror(), _IOLBF +#include // memset() +#include // ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF, struct itimerval, setitimer() +#include // true, false +#include // INT_MAX + +#include "internal/sched.h" + +class Timer { +public: + int type_ = 0; + void (*handler_) (int) = nullptr; + int ms_ = 0; + bool oneShot_ = true; + class Types { + public: + enum { + ITIMER_REAL, + ITIMER_VIRTUAL, + ITIMER_PROF + }; + } Types; + int timer_signal(int timer_type); + void set_timer(int type, void (*handler) (int), int ms, bool oneShot); + static void timer_handler (int signum); +}; + +#endif //custom_threads_libtimer \ No newline at end of file diff --git a/libtimer/timer.cpp b/libtimer/timer.cpp new file mode 100644 index 0000000..9c54d72 --- /dev/null +++ b/libtimer/timer.cpp @@ -0,0 +1,72 @@ +// +// Created by brothercomplex on 2/11/19. +// + +#include "include/timer.h" + +int Timer::timer_signal(int timer_type) { + int sig; + + switch (timer_type) { + case ITIMER_REAL: + sig = SIGALRM; + break; + case ITIMER_VIRTUAL: + sig = SIGVTALRM; + break; + case ITIMER_PROF: + sig = SIGPROF; + break; + default: + fprintf(stderr, "ERROR: unknown timer type %d!\n", timer_type); + exit(EXIT_FAILURE); + } + + return sig; +} + +void Timer::set_timer(int type, void (*handler)(int), int ms, bool oneShot) { + type_ = type; + handler_ = handler; + ms_ = ms; + oneShot_ = oneShot; + printf("oneshot: %s\n", oneShot ? "true" : "false"); + struct itimerval timer; + struct sigaction sa; + + /* Install signal handler for the timer. */ + memset (&sa, 0, sizeof (sa)); + sa.sa_handler = handler; + sigaction (timer_signal(type), &sa, NULL); + + /* Configure the timer to expire after ms msec... */ + // convert ms to seconds and ms + + int milliseconds = ms; + int seconds = milliseconds; + if (seconds > 999) { + seconds /= 1000; + milliseconds -= seconds * 1000; + } else seconds = 0; + printf("ms = %d\nmilliseconds = %d\nseconds = %d\n", ms, milliseconds, seconds); + + if (seconds > 60) { + puts("error: seconds must not be greater than 60 seconds"); + abort(); + } + + timer.it_value.tv_sec = seconds; + timer.it_value.tv_usec = milliseconds; + + if (oneShot) { + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 0; + } else { + timer.it_interval.tv_sec = seconds; + timer.it_interval.tv_usec = milliseconds; + } + if (setitimer (type, &timer, NULL) < 0) { + perror("Setting timer"); + exit(EXIT_FAILURE); + }; +} \ No newline at end of file diff --git a/cl-srv.c b/others/cl-srv.c similarity index 100% rename from cl-srv.c rename to others/cl-srv.c diff --git a/msgs.h b/others/msgs.h similarity index 100% rename from msgs.h rename to others/msgs.h diff --git a/msgs_test.c b/others/msgs_test.c similarity index 97% rename from msgs_test.c rename to others/msgs_test.c index 5a56d50..d2b3d36 100644 --- a/msgs_test.c +++ b/others/msgs_test.c @@ -41,9 +41,8 @@ void server(void){ printf("Server %d: Sending message on port %d\n",id,msg[0]); Send(&p[msg[0]],msg); printf("Server %d: Sent message on port %d\n",id,msg[0]); - sleep(1); - } + } } void client(void){ @@ -98,7 +97,10 @@ int main() for(i=10;i<100;i++) start_thread(client); run(); - while (1) sleep(1); + while (1) { + printf("SLEEPING"); + sleep(1); + } } diff --git a/prod_cons.c b/others/prod_cons.c similarity index 100% rename from prod_cons.c rename to others/prod_cons.c diff --git a/q.c b/others/q.c similarity index 97% rename from q.c rename to others/q.c index 25c7f56..d2bb4ee 100644 --- a/q.c +++ b/others/q.c @@ -1,6 +1,6 @@ #include #include -#include +#include "libthread/include/internal/TCB.h" struct Node { int data; diff --git a/q_degug.c b/others/q_degug.c similarity index 100% rename from q_degug.c rename to others/q_degug.c diff --git a/read_write.c b/others/read_write.c similarity index 100% rename from read_write.c rename to others/read_write.c diff --git a/sem.h b/others/sem.h similarity index 95% rename from sem.h rename to others/sem.h index 0265fc9..1e77172 100644 --- a/sem.h +++ b/others/sem.h @@ -6,7 +6,7 @@ have to ensure that the P(sem) and V(sem) are atomic operations */ -#include "threads.h" +#include "libthread/include/thread.h" typedef struct Semaphore_t { int count; diff --git a/q.h b/q.h deleted file mode 100644 index e4ea14d..0000000 --- a/q.h +++ /dev/null @@ -1,54 +0,0 @@ -/************************************************************* -* - Author: Denny Abraham Cheriyan, Adrin Peter Fernandes - Contains functions to initialize, add, delete and rotate - a doubly linked circular queue - -*/ - -#include "TCB.h" -#include -#include - - -void InitQ(TCB_t **head){ - *head = 0; -} - -void AddQ(TCB_t **head, TCB_t *item){ - if(*head == 0){ - //When the head pointer is null - *head = item; - (*head)->next = *head; - (*head)->prev = *head; - } - else{ - //When there is one or more nodes - (*head)->prev->next = item; - item->prev = (*head)->prev; - (*head)->prev = item; - item->next = *head; - } -} - -TCB_t * DelQ(TCB_t **head){ - if((*head) == 0) - return 0; - TCB_t *temp = *head; - if((*head)->next == *head){ - //When there is one node - *head = 0; - } - else { - //When there are two or more nodes - *head = (*head)->next; - temp->prev->next = *head; - (*head)->prev = temp->prev; - } - return temp; -} - -void RotateQ(TCB_t **head){ - if(*head != 0) - (*head) = (*head)->next; -} \ No newline at end of file diff --git a/thread_test.c b/thread_test.c deleted file mode 100644 index a0cf59d..0000000 --- a/thread_test.c +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************* -* - Author: Denny Abraham Cheriyan, Adrin Peter Fernandes - Testing threads.h using infinite functions - -*/ - -#include "threads.h" - -void func1(){ - int i=0; - while(1){ - printf("Executing within func1\n"); - i+=1; - yield(); - printf("Func1 Value - %d\n", i); - } -} - -void func2(){ - int i=0; - while(1){ - printf("Executing within func2\n"); - i+=2; - yield(); - printf("Func2 Value - %d\n", i); - } -} - -void func3(){ - int i=0; - while(1){ - printf("Executing within func3\n"); - i+=3; - yield(); - printf("Func3 Value - %d\n", i); - } -} - -int main(){ - start_thread(func1); - start_thread(func2); - start_thread(func3); - run(); - return 0; -} \ No newline at end of file diff --git a/threads.h b/threads.h deleted file mode 100644 index 975577c..0000000 --- a/threads.h +++ /dev/null @@ -1,31 +0,0 @@ -/************************************************************* -* - Author: Denny Abraham Cheriyan, Adrin Peter Fernandes - Contains functions to Start, Run and Yield to other threads - -*/ - -#include "q.h" - -const int STACK_SIZE = 8192; -TCB_t *RunQ = 0; - -void start_thread(void (* function)(void)){ - void *stack = (void *) malloc (STACK_SIZE); - TCB_t *tcb = (TCB_t *) malloc (sizeof (TCB_t)); - init_TCB(tcb,function,stack,STACK_SIZE); - AddQ(&RunQ,tcb); -} - -void run(){ - ucontext_t parent; - getcontext(&parent); - swapcontext(&parent, &(RunQ->context)); -} - -void yield(){ - ucontext_t *current = &(RunQ->context); - RotateQ(&RunQ); - swapcontext(current,&(RunQ->context)); -} -