#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "gtest/gtest.h"
#include "coroutine.h"
#include "asyncio.h"
static int g_run_sums = 0;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static void co(void *args)
{
int num = *((int *)args);
pthread_mutex_lock(&g_lock);
g_run_sums += num; // 每个协程分别递增全局变量
pthread_mutex_unlock(&g_lock);
printf("coroutine:%d begin...\r\n", num);
coroutine_yield();
printf("coroutine:%d ended...\r\n", num);
}
TEST(coroutine_create, three_cos_run_success)
{
int a = 1, b = 2, c = 3; //a, b, c三个协程依次对全局变量g_run_sums增加1,2,3
coroutine_init();
coroutine_create(co, (void*)&a);
coroutine_create(co, (void*)&b);
coroutine_create(co, (void*)&c);
coroutine_loop();
EXPECT_EQ(g_run_sums, 6); // 最终全局变量为6
}
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "gtest/gtest.h"
#include "coroutine.h"
#include "asyncio.h"
static int seq = 0;
static int co_seq[2] = {0};
static void co_sleep(void *args)
{
printf("co sleep begin.\r\n");
asyncio_sleep(100); // 调用asyncio api睡眠100ms.
co_seq[seq++] = 100;
printf("co sleep end.\r\n");
}
static void co_nosleep(void *args)
{
printf("co no sleep begin.\r\n");
co_seq[seq++] = 200;
printf("co no sleep end.\r\n");
}
TEST(coroutine_run, co_sleep)
{
coroutine_init();
coroutine_create(co_sleep, NULL);
coroutine_create(co_nosleep, NULL);
coroutine_loop();
EXPECT_EQ(co_seq[0], 200); //验证未睡眠协程先完成了执行
EXPECT_EQ(co_seq[1], 100);
}
corouting有3种状态, RUNNALBE, RUNNING, WAITING.
当没有任何协程存在时,则退出主进程.
模拟实现了Golang中的waitGroup, 用于等待所有协程退出.新协程创建会调用waitGroup_add,协程结束会调用waitGroup_del,当waitGroup空闲时
则说明所有协程都已经退出.
corouting在用户态进行上下文切换,上下文主要包括:堆栈,寄存器(IP, SP等).
上下文切换主要通过<ucontext.h>中定义的getcontext, setcontext, makecontext, swapcontext实现.
这样每个协程最大都可以有1M的堆栈空间,且堆栈空间能够按需分配,每个processor_t上堆栈的消耗为所有协程
实际使用的堆栈内存+1M.
如果不这样实现,每个协程都需要初始分配1M空间,消耗为协程个数*1M.
cmake -H. -Bbuild
cmake --build ./build
cd build
ctest --verbose