#include "../config.h"
#include "../ort.h"
#include <stdlib.h>
#include <stdio.h>
#include "ee.h"
//创建任务的线程是master,协作执行的是worker
#define WORKER_YIELD 50
#define MASTER_YIELD 1000
//自旋等待宏,f为假时终止循环
#define WAIT_WHILE(f, trials) { \
int time = 0; \
for ( ; (f); time++) \
if (time == (trials)) { \
time = -1; \
othr_yield(); \
}; \
}
//线程池节点
typedef struct othr_pool_s {
void *arg;//线程函数的参数,是一个指向eecb的指针
void *info;//指向父线程当前eecb的info结构体,它是组内线程共享的
struct othr_pool_s *next;//下一个线程池节点
volatile int spin;//线程的自旋标志
int id;//用来存储组内id
char pad[32];//用于对齐的吧?我不确信,联系我QQ:2629608320 ,我期待你的解答
} othr_pool_t;
//共享信息结构体
typedef struct {
int level;//子线程所处的层次
volatile int *running;//指向一个整型数组,用来标记子线程是否在运行
othr_pool_t **tp;//指向线程组的各个线程池节点的指针
int team;//组的大小,不包括master线程
} othr_info;
static int pthread_lib_inited = 0;//标识线程库是否已经初始化,用来防止线程库重复初始化
static othr_pool_t *H = NULL;//指向线程池的头节点,初始为空
static volatile int plen;//存储线程池中当前可用的线程池节点数目(或线程数目)
static othr_lock_t plock;//这把锁在线程库初始化的时候创建,每次访问线程池的时候均要先获得这把锁
//threadlimit存储此程序中可存在的线程数目的最大值,是thread-limit-var的副本,不包括master线程
//threadscreated存储已经创建了的线程的数目
//levellimit用来存储此程序中允许的最大的活跃嵌套层次,是max-active-level-var的副本
//proc_bind存储是否可以在处理器内核之间移动OpenMP线程,是bind-var的副本
static int threadlimit, threadscreated, levellimit, proc_bind;
static pthread_attr_t *globalattr = NULL;//此线程属性用来设置线程栈的大小
static othr_pool_t *_new_bunch_of_threads(int n, othr_pool_t **tail)
{//新建n个线程,并将其正确的连接到线程池节点上,线程函数的参数为节点的指针,tail用来回传第n个节点的地址
pthread_t thr;
volatile othr_pool_t *block, *node;
int i, e;
void *threadjob(void *);
//int NP = ort_get_num_procs();
//创建n个线程池节点
if ((block=(othr_pool_t*)ort_alloc_aligned(n*sizeof(othr_pool_t),NULL))==NULL)
ort_error(5, "othr_init() failed; could not allocate memory\n");
for (i = 0, node = block; i < n; i++)
{
node->spin = 1;//打开线程自旋标志
FENCE;//将此标志从临时内存视图写到内存中去,以便线程函数能够即使发现spin的变化
node->next = (othr_pool_t *)(node+1);//构造链表
//创建线程,使用线程池节点的指针作为线程函数的参数
if ( (e = pthread_create(&thr, globalattr, threadjob, (void *) node)) )
ort_error(5, "pthread_create() failed with %d\n", e);
node++;//下一个线程池节点
}
(--node)->next = NULL;//将最后一个节点的next指针置为NULL
if (tail) *tail = (othr_pool_t *)node;//回传第n个线程池节点的地址
return ((othr_pool_t *)block);//返回新线程池的头指针
}
int othr_initialize(int *argc, char ***argv, ort_icvs_t *icv, ort_caps_t *caps)
{//线程库的初始化,完成全局内部控制变量和线程库性能参数的初始化,线程池的创建和初始化。此函数只会被调用一次
int nthr;
nthr = (icv->nthreads >= 0) ?//决定默认情况下并行域内除去master线程后的线程组的大小
icv->nthreads ://nthreads是除master线程外的线程数目
icv->ncpus - 1;//因为master线程已经占用了一个处理器,所以需要处理器内核的个数减1个
//当最大线程数目并非无限时,检查默认线程数目是否超出限制
if (icv->threadlimit != -1 && nthr > icv->threadlimit)
nthr = icv->threadlimit;//超出限制时,修改默认线程数
//初始化线程库性能参数
caps->supports_nested = 1;//线程库支持并行域嵌套
caps->supports_dynamic = 1;//线程库支持动态调整线程数目
caps->supports_nested_nondynamic = 1;//支持嵌套时不要求动态调整线程数的支持
caps->max_levels_supported = -1;//对活跃的嵌套层次没有要求,活跃的定义是该层的线程数目大于1
caps->default_numthreads = nthr;//设置默认的线程数目
caps->max_threads_supported = -1;//对程序中线程的数目没有限制
if ( pthread_lib_inited ) return (0);//误调用该函数,线程库已经初始化
threadlimit = icv->threadlimit;//初始化thread-limit-var的副本
threadscreated = 0;//当前已经创建0个线程
levellimit = icv->levellimit;//初始化max-active-level-var的副本
proc_bind = icv->proc_bind;//初始化bind-var的副本
if (icv->stacksize != -1)
{
//创建一个线程属性用于设置线程的栈的大小
if ((globalattr = (pthread_attr_t*)
ort_alloc_aligned(sizeof(pthread_attr_t), NULL)) == NULL)
ort_error(5, "othr_init() failed; could not create stack attributes\n");
//初始化线程属性
pthread_attr_init(globalattr);
//设置线程属性的栈大小的值,该值在创建线程时使用
if (pthread_attr_setstacksize(globalattr,icv->stacksize) != 0)
ort_error(5, "pthread_attr_setstacksize() failed.\n");
}
if (nthr > 0)
{
H = _new_bunch_of_threads(nthr, NULL);//新建nthr个线程,返回其头节点的指针
threadscreated = nthr + 1;//已经创建的线程数目,包含master线程
othr_init_lock(&plock, ORT_LOCK_SPIN);//初始化线程池访问锁
}
plen = nthr;//设置线程池当前可用的线程数目
pthread_lib_inited = 1;//设置线程库已经被初始化标志
return (0);
}
void othr_finalize(int exitvalue)
{
}
void *threadjob(void *env)
{//线程函数,线程池中的每个线程均执行此函数,参数为指向线程池节点的指针
volatile othr_info *myinfo;
volatile othr_pool_t *env_t = (othr_pool_t *)env;
while (1) {
//等待自旋结束,即spin变为0
WAIT_WHILE(env_t->spin, WORKER_YIELD);
env_t->spin = 1;//为下一次自旋做准备,将spin重置为1
ort_ee_dowork(env_t->id, env_t->arg);//使用给定参数让给定线程执行
myinfo = env_t->info;//指向共享的info
myinfo->running[env_t->id] = 0;//将给定线程设置为不在运行
}
}
int othr_request(int numthr, int level)
{//申请numthr个线程用于第level层的并行域,level为子线程的层次
int new, tmpplen;
othr_pool_t *bunch, *tail;
if (level == 1)
{//程序中只有init thread,无需加锁访问线程池。进程启动后创建的第1个线程为init thread,它的level为0
if (numthr <= plen)
plen -= numthr;//当前可用线程充足,获取numthr个线程,之后返回申请成功
else
{//当前可用线程不足,需要新创建线程
new = numthr - plen;//需要新创建的线程个数
if (threadlimit != -1 && new+threadscreated > threadlimit)
new = threadlimit - threadscreated;//当最大线程数目有所限制并且需要新建的和已有的线程总数超出了此限制,修改新建的数量
bunch = _new_bunch_of_threads(new, &tail);//新建new个线程池节点并让其自旋,返回头指针和尾指针
tail->next = H;//将新建的线程池链接到原有线程池的头部
H = bunch;
threadscreated += new;//修改已经创建了的线程数目
numthr = plen + new;//实际申请到的线程数目
plen=0;//当前可用线程数目为0,因为已经全部分配除去了
}
}
else
{//不是在第1层,有可能有多个线程同时申请,需要加锁保护
if (levellimit != -1 && level > levellimit)
return 0;//超出最大层次限制,申请失败
othr_set_lock(&plock);//访问线程池先上锁
if (numthr <= plen)
{//线程池有足够的线程可用
plen -= numthr;
othr_unset_lock(&plock);//解锁
}
else
{//线程池线程不足,需分配
new = numthr - plen;
if (threadlimit != -1 && new+threadscreated > threadlimit)
new = threadlimit - threadscreated;//超出最大线程数目限制
//这样写的目的是减小锁的长度,增强并行性,因为_new_bunch_of_threads函数花费时间较多
threadscreated += new;
tmpplen = plen;//实际申请到的线程数目
plen = 0;//当前可用线程数目为0
othr_unset_lock(&plock);//线程池访问解锁
if (new == 0) return (tmpplen);//不需要新建线程,返回实际申请到的线程数目
bunch = _new_bunch_of_threads(new, &tail);//创建新线程
othr_set_lock(&plock);//访问线程池先上锁
tail->next = H;//将新建的线程池链接到已有线程池的头部
H = bunch;
othr_unset_lock(&plock);//解锁
numthr = tmpplen + new;//修改实际申请到的线程数目
}
}
return (numthr);//返回实际申请到的线程数目
}
void othr_create(int numthr, int level, void *arg, void **info)
{//取下numthr个线程,设置线程函数和参数,解开自旋,其中level为父线程的level
volatile othr_pool_t *p;
volatile othr_info *t = (othr_info *) *info;
int i;
if (t == NULL)
{//线程第1次成为父线程,创建一个子线程共享的othr_info结构体
t = (othr_info *) ort_alloc_aligned(sizeof(othr_info),NULL);
//新建一个数组,用来记录子线程的运行状态
t->running = (volatile int *) ort_alloc_aligned(MAX_BAR_THREADS*sizeof(int),NULL);
//新建一个指针数组,用来指向各个线程池节点
t->tp = (othr_pool_t**) ort_alloc_aligned(MAX_BAR_THREADS*sizeof(othr_pool_t*),NULL);
}
t->team = numthr;//设置当前线程组的大小(除去master线程)
t->level = level;//设置子线程的层次
//唤醒自旋的线程
if(level == 0)//父线程为init thread,没有竞争所以不需要加锁
for (i = 1; i <= numthr; i++)
{
//取下一个线程
p = H;
H = H->next;
t->running[i] = 1;//将子线程对应的running标志设置为1
t->tp[i-1] = (othr_pool_t *)p;//将其指向相应的线程池节点
p->arg = arg;//线程函数的参数,为一个指向eecb的指针
p->info = (othr_info *)t;//各个线程池节点的info成员都指向同一个info
p->id = i;//设置子线程的组内id,它们是1-numthr,0号是master线程
p->spin = 0;//解开自旋锁
FENCE;//将自旋标志的改变写入内存中
}
else//可能有多个线程同时操作,需要上锁
for (i = 1; i <= numthr; i++)
{
othr_set_lock(&plock);//上线程池访问锁
p = H;//取下一个线程
H = H->next;
othr_unset_lock(&plock);//解锁
t->running[i] = 1;//设置对应子线程的运行状态为正在运行
t->tp[i-1] = (othr_pool_t *)p;//指向各个子线程的线程池节点
p->arg = arg;//设置线程函数的运行参数
p->info = (othr_info *)t;//各个线程池节点的info成员都指向同一个info
p->id = i;//设置线程的组内ID
p->spin = 0;//解开自旋
FENCE;//将自旋标志写入内存
}
*info = (othr_info *)t;//回传子线程的共享信息结构体指针,存在eecb的info中,这是eecb与线程池的接口
}
void othr_waitall(void **info)
{//此函数由master线程调用,用于等待组内的其他线程完成工作,info为组共享的信息
volatile othr_info *t = *info;
int i;
volatile int *x;
//等待所有组内所有线程完成工作,running[i]由threadjob设置为0
for(i=1; i<= t->team; i++)
{
x = &(t->running[i]);
WAIT_WHILE((*x == 1), MASTER_YIELD);
}
//将组内线程所在的线程池节点组合成链,准备放回线程池
for(i=0; i<(t->team)-1; i++)
t->tp[i]->next = t->tp[i+1];
if(t->level != 0)//不止一个线程操作,需上锁保护
othr_set_lock(&plock);
t->tp[(t->team)-1]->next = H;//将线程池节点链入线程池
H = t->tp[0];
plen += t->team;//修改线程池当前可用的线程数目
if(t->level != 0)//解开线程池访问锁
othr_unset_lock(&plock);
}
int othr_init_lock(othr_lock_t *lock, int type)
{//初始化给定锁为type类型
switch (lock->lock.type = type)
{
case ORT_LOCK_NEST:
{//初始化为嵌套锁
othr_nestlock_t *l = &(lock->lock.data.nest);//获取嵌套锁指针
pthread_mutex_init(&l->ilock, NULL);//初始化访问互斥量,用于访问此嵌套锁
pthread_mutex_init(&l->lock, NULL);//初始化互斥量
l->count = 0;//初始化加锁次数为0
pthread_cond_init(&l->cond, 0);//初始化条件变量
return (0);
}
case ORT_LOCK_SPIN:
{//初始化为自旋锁
lock->lock.data.spin.rndelay = 0;//设置初始延时时间,以后逐次增加延时时间
return pthread_mutex_init(&(lock->lock.data.spin.mutex), NULL);//初始化互斥量
}
default: //初始化为通用锁
return pthread_mutex_init(&(lock->lock.data.normal), NULL);//初始化互斥量
}
}
int othr_destroy_lock(othr_lock_t *lock)
{//销毁指定锁
switch (lock->lock.type)
{
case ORT_LOCK_NEST:
{//锁类型为嵌套锁
othr_nestlock_t *l = &(lock->lock.data.nest);
pthread_mutex_destroy(&l->lock);//销毁互斥量
pthread_cond_destroy(&l->cond);//销毁条件变量
pthread_mutex_destroy(&l->ilock);//销毁访问互斥量
return 0;
}
case ORT_LOCK_SPIN://锁类型为自旋锁
return pthread_mutex_destroy(&(lock->lock.data.spin.mutex));//销毁互斥量
default://锁类型为通用锁
return pthread_mutex_destroy(&(lock->lock.data.normal));//销毁互斥量
}
}
int othr_set_lock(othr_lock_t *lock)
{
switch (lock->lock.type)
{
case ORT_LOCK_NEST:
{//给嵌套锁上锁
othr_nestlock_t *l = &(lock->lock.data.nest);
void *me = ort_get_current_task();//指向当前线程的eecb的当前task,是一个指向ort_task_node_t的指针
pthread_mutex_lock(&l->ilock);//对访问互斥量上锁
if (pthread_mutex_trylock(&l->lock) == 0)
{//尝试上锁,若上锁成功,则修改锁的属主,和上锁的次数。此时此锁没有owner
l->owner = me;//若互斥量被其他task上锁,则尝试上锁会失败无法进入此语句
l->count++;
}
else
{//尝试上锁失败
if (l->owner == me)//判断是不是同一个task上锁,若是则上锁次数加1
l->count++;
else
{//被其他task上锁,则需要等待,等当前task释放锁后才可获得此锁
while ( pthread_mutex_trylock(&l->lock) )//尝试上锁失败则继续等待
pthread_cond_wait(&l->cond, &l->ilock);//等待条件变量
l->owner = me;//修改锁的属主
l->count++;//修改上锁次数
}
}
pthread_mutex_unlock(&l->ilock);//解开访问互斥量
return (0);
}
case ORT_LOCK_SPIN:
{//给自旋锁上锁
volatile int count, delay, dummy;
for (delay = lock->lock.data.spin.rndelay;
pthread_mutex_trylock(&(lock->lock.data.spin.mutex)); )
{
for (count = dummy = 0; count < delay; count++ )
dummy += count;//无用的加,只是用于消耗时间
if (delay == 0)
delay = 1;//当前延时为0,则设为1,为后面的加倍做准备
else
if (delay < 10000)
delay = delay << 1;//延时加倍
}
//此时尝试上锁成功
lock->lock.data.spin.rndelay++;//下次对此自旋锁上锁时,增加一点延时时间
return (0);
}
default://给普通锁上锁
return pthread_mutex_lock(&(lock->lock.data.normal));
}
}
int othr_unset_lock(othr_lock_t *lock)
{//解锁给定锁
switch (lock->lock.type)
{
case ORT_LOCK_NEST:
{//给嵌套锁解锁
othr_nestlock_t *l = &(lock->lock.data.nest);
pthread_mutex_lock(&l->ilock);//上锁访问锁
if (l->owner == ort_get_current_task() && l->count > 0)
{//只有锁的属主是当前任务并且上锁的次数大于0,此时才能解锁
l->count--;//减小上锁次数
if (l->count == 0)
{//当上锁次数见到0时,释放互斥量,发出条件变量通知,让等待此锁的其他任务获得此锁
pthread_mutex_unlock(&l->lock);
pthread_cond_signal(&l->cond);
}
}
pthread_mutex_unlock(&l->ilock);//释放访问互斥量
return 0;
}
case ORT_LOCK_SPIN://解锁自旋锁
lock->lock.data.spin.rndelay = 0;//将延时重新设置为0
return pthread_mutex_unlock(&(lock->lock.data.spin.mutex));//释放互斥量
default://解锁通用锁
return pthread_mutex_unlock(&(lock->lock.data.normal));//释放互斥量
}
}
int othr_test_lock(othr_lock_t *lock)
{//尝试上锁
switch (lock->lock.type)
{
case ORT_LOCK_NEST:
{//对嵌套锁尝试上锁
othr_nestlock_t *l = &(lock->lock.data.nest);
int res;
pthread_mutex_lock(&l->ilock);//上锁访问互斥量
if (pthread_mutex_trylock(&l->lock) == 0)
{//尝试上锁成功,修改属主和上锁次数
l->owner = ort_get_current_task();
res = ++l->count;
}
else//尝试上锁互斥量失败,此互斥量被某个task上锁了,此task可能是当前task也可能不是
if (l->owner == ort_get_current_task())
res = ++l->count;//是当前task,增加上锁次数
else
res = 0;//不是当前task,上锁失败
pthread_mutex_unlock(&l->ilock);//解开访问互斥量
return res;//此函数返回0表示尝试上锁失败,返回非0表示上锁成功。这与pthreads的尝试上锁互斥量不同
}
case ORT_LOCK_SPIN://尝试上锁自旋锁
return ( !pthread_mutex_trylock(&(lock->lock.data.spin.mutex)) );//尝试上锁成功则返回非0,否则返回0
default://尝试上锁通用锁
return ( !pthread_mutex_trylock(&(lock->lock.data.normal)) );//尝试上锁成功则返回非0,否则返回0
}
}
othr.c
最新推荐文章于 2022-11-22 23:40:29 发布