PVFS2 源代码分析之输入输出src/io/job/job-time-mgr任务时间管理器

PVFS2发布的某些类型的任务(job)设置了超时,需要任务时间管理器(job time manager)统一记录和管理这些任务。

  • 数据结构

任务时间管理器维护一个时间散列,时间元(time bucket)由链表实现,按时刻由早到晚排列;每个时间元又维护一个任务描述符(job descriptor)链表,表示在该时刻应完成的任务,其中每个任务描述符对一个任务。这里的链表使用quicklist 。

 

时间元的结构体如下:

struct time_bucket { long expire_time_sec; struct qlist_head bucket_link; struct qlist_head jd_queue; };

第3行expire_time_sec记录超时时间,由当日到该时间的秒数表示,是该时间元的时刻。

第4行bucket_link表示时间散列中的一个项。

第5行jd_queue表示该时间元维护的任务描述符链表,记录的是链表头。

qlist_head类型参见quicklist 。

 

还有重要声明就是时间散列:

static QLIST_HEAD(bucket_queue);

时间元结构体的成员bucket_link就是链接在这个链表中。QLIST_HEAD定义参见quicklist 。

  • 功能函数

带有“__”前缀的函数不是线程安全的,通常只被接口函数调用;接口函数供其他组件调用,且都是线程安全的。

  1. 初始化
    int job_time_mgr_init(void) { INIT_QLIST_HEAD(&bucket_queue); return(0); }
    初始化了时间散列,INIT_QLIST_HEAD定义参见quicklist 。
  2. 添加任务
    接口函数是job_time_mgr_add,它只是以加锁的方式线程安全地调用了如下__job_time_mgr_add函数:
    static int __job_time_mgr_add(struct job_desc* jd, int timeout_sec) { struct timeval tv; long expire_time_sec; struct qlist_head* tmp_link; struct time_bucket* tmp_bucket = NULL; struct time_bucket* prev_bucket = NULL; struct time_bucket* new_bucket = NULL; struct qlist_head* prev; struct qlist_head* next; // ...... gettimeofday(&tv, NULL); expire_time_sec = tv.tv_sec + timeout_sec; // ...... /* look for a bucket matching the desired seconds value */ qlist_for_each(tmp_link, &bucket_queue) { tmp_bucket = qlist_entry( tmp_link, struct time_bucket, bucket_link); assert(tmp_bucket); if(tmp_bucket->expire_time_sec >= expire_time_sec) { break; } prev_bucket = tmp_bucket; tmp_bucket = NULL; } if(!tmp_bucket || tmp_bucket->expire_time_sec != expire_time_sec) { /* make a new bucket, we didn't find an exact match */ new_bucket = (struct time_bucket*) malloc(sizeof(struct time_bucket)); assert(new_bucket); new_bucket->expire_time_sec = expire_time_sec; INIT_QLIST_HEAD(&new_bucket->bucket_link); INIT_QLIST_HEAD(&new_bucket->jd_queue); if(tmp_bucket) next = &tmp_bucket->bucket_link; else next = &bucket_queue; if(prev_bucket) prev = &prev_bucket->bucket_link; else prev = &bucket_queue; __qlist_add(&new_bucket->bucket_link, prev, next); tmp_bucket = new_bucket; } // ...... /* add the job descriptor onto the correct bucket */ qlist_add_tail(&jd->job_time_link, &tmp_bucket->jd_queue); jd->time_bucket = tmp_bucket; return(0); }
    第12~13行计算任务超时的时刻,是由当前时刻加上任务等待时间(timeout)。

    第16~27行遍历时间散列bucket_queue查找任务添加的合适时间元。如果当前时间元tmp_bucket的截止时刻已经大于或等于任务超时时刻,那么就终止遍历(第21~24行);此时,之前遍历过的时间元一定早于任务超时时刻,之后未遍历的时间元一定晚于任务超时时刻,或者说与任务超时时刻最接近的就是当前时间元。

    如果当前时间元截止时间恰好是任务超时时间,那么会跳过第18~47行,直接将该任务描述符添加到当前时间元(第50行)。

    但是,当前时间元的截止时间可能不是恰好等于任务超时时间,这就需要创建新的时间元(第31~33行),设置它的时刻为该任务超时时刻(第34行)和其他成员,并插入到时间散列bucket_queue中。
  3. 检查超时
    int job_time_mgr_expire(void) { struct timeval tv; struct qlist_head* iterator = NULL; struct qlist_head* scratch = NULL; struct qlist_head* iterator2 = NULL; struct qlist_head* scratch2 = NULL; struct time_bucket* tmp_bucket = NULL; struct job_desc* jd = NULL; int ret = -1; PVFS_size tmp_size = 0; gettimeofday(&tv, NULL); gen_mutex_lock(&bucket_mutex); qlist_for_each_safe(iterator, scratch, &bucket_queue) { tmp_bucket = qlist_entry(iterator, struct time_bucket, bucket_link); assert(tmp_bucket); /* stop when we see the first bucket that has not expired */ if(tmp_bucket->expire_time_sec > tv.tv_sec) { break; } /* cancel the associated jobs and remove the bucket */ qlist_for_each_safe(iterator2, scratch2, &tmp_bucket->jd_queue) { jd = qlist_entry(iterator2, struct job_desc, job_time_link); qlist_del(&jd->job_time_link); switch(jd->type) { case JOB_BMI: gossip_err("%s: job time out: cancelling bmi operation, job_id: %llu./n", __func__, llu(jd->job_id)); ret = job_bmi_cancel(jd->job_id, jd->context_id); jd->time_bucket = NULL; break; case JOB_FLOW: // ...... break; case JOB_TROVE: // ...... break; default: ret = 0; jd->time_bucket = NULL; break; } } qlist_del(&tmp_bucket->bucket_link); INIT_QLIST_HEAD(&tmp_bucket->jd_queue); free(tmp_bucket); } gen_mutex_unlock(&bucket_mutex); return(0); }
    检查有无已经超时的任务。第14行对应的外循环遍历时间散列,对于已经超时的时间元执行第24行对应的内循环。内循环中,主要就是处理该时间元记录的任务,它们已经超时。处理这些超时任务的办法根据任务类型不同而不同,这里只列出了JOB_BMI类型的代码(第31~33行),主要就是进行记录并取消任务。
  4. 撤销任务和清理时间管理器
    可以通过函数job_time_mgr_rem取消对某个任务的超时监控;时间管理器的清理任务由job_time_mgr_expire负责。它们主要操作都是删除相关项并释放空间,此处从略。

你可能感兴趣的:(struct,null,iterator,任务,Descriptor,代码分析)