Mercurial > hg > Game > Cerium
view TaskManager/Cell/CellTaskManagerImpl.cc @ 897:6bd218d3f643 draft
add return address in SimpleTask for debugging.
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 16 Jul 2010 19:00:22 +0900 |
parents | 5f5301028e29 |
children | 382a48afb061 |
line wrap: on
line source
#define DEBUG #include "error.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include "CellTaskManagerImpl.h" #include "HTaskInfo.h" #include "SchedTask.h" #include "MainScheduler.h" #include "types.h" #include "SysFunc.h" static void send_alloc_reply(CellTaskManagerImpl *tm, int id, SpeThreads *speThreads); CellTaskManagerImpl::~CellTaskManagerImpl() { delete speThreads; delete [] speTaskList; delete ppeManager; } void CellTaskManagerImpl::init() { spe_running = 0; // 実行可能な HTask のリスト。 FifoTaskManager と共有される activeTaskQueue = new HTaskInfo(); // HTask の factory。 HTaskInfo ならなんでもいい。 htaskImpl = activeTaskQueue ; // any HTaskInfo speThreads = new SpeThreads(machineNum); speThreads->init(); // 実行される Task 用の パイプライン用のダブルバッファ speTaskList = new TaskListInfoPtr[machineNum]; // spe上の走っている Task の配列 taskListInfo = new TaskListInfoPtr[machineNum]; // 次に走る Task の配列 for (int i = 0; i < machineNum; i++) { taskListInfo[i] = new TaskListInfo(); speTaskList[i] = new TaskListInfo(); } // PPE 側の管理をする Manager ppeManager = new FifoTaskManagerImpl(machineNum); // 大半のTaskQueueInfoは、共有される MainScheduler *mscheduler = new MainScheduler; set_scheduler(mscheduler); ppeManager->init(mscheduler, this); // ここで HTaskInfo が共有される。 ppeManager->get_scheduler()->set_manager(this); // Task 内からManager->task_create() とかするときに必要なTaskManager。 // 現状では ppe 側からしか動かない // spe 側から Task create できない schedTaskManager = new SchedTask(); schedTaskManager->init(0,0,0,ppeManager->get_scheduler()); } void CellTaskManagerImpl::append_activeTask(HTaskPtr task) { if (task->cpu_type == CPU_PPE) { ppeManager->append_activeTask(task); } else { activeTaskQueue->addLast(task); } } // SPE_ANY が指定されていた時に // これをインクリメントしつつ呼ぶことにする。 // 乱数使ってもいいけどさ。 int cur_anySpeid = 0; /** * ActiveTaskQueue から Task を * 各 SPE に渡す TaskList に入れる * * ここの activeTaskQueue は FifoTaskManagerImpl のと意味が違い、 * spe に渡される Task だけ入っている */ void CellTaskManagerImpl::set_runTaskList() { int speid; while (HTaskPtr htask = activeTaskQueue->poll()) { if (htask->cpu_type == SPE_ANY) { speid = cur_anySpeid++; cur_anySpeid = (cur_anySpeid < machineNum) ? cur_anySpeid : 0; } else { // -1 してるのは // htask->cpu_type - CPU_SPE で // SPE0 = 1, SPE1 = 2, ... SPE5 = 6 ってなってるので // 配列的 (SPE0 = arr[0], SPE1 = arr[1]) にするため speid = htask->cpu_type - CPU_SPE - 1; // SPU の数以上が指定されていれば // とりあえず MAX_USE_SPE_NUM (実際に動く SPE の最大数) で // あまり求めてそれを使うことにする。 // ここで判定するもんでもないか? if (speid >= machineNum) { speid %= machineNum; } } set_taskList(htask, taskListInfo[speid]); } } void CellTaskManagerImpl::sendTaskList() { for (int i = 0; i < machineNum; i++) { if ( taskListInfo[i]->length() > 0 ) { send_taskList(i); spe_running++; } } } void CellTaskManagerImpl::poll() { mail_check(); // SPE に送る TaskList の準備 set_runTaskList(); // TaskList 待ちの SPE に TaskList を送る sendTaskList(); } void CellTaskManagerImpl::run() { do { // PPE side ppeManager->poll(); // SPE side do { poll(); } while (ppeManager->activeTaskQueue->empty() && spe_running >0 ); } while (!ppeManager->activeTaskQueue->empty() || spe_running >0); if (!waitTaskQueue->empty()) { get_scheduler()->printf("Dead lock detected\n"); } } /** * SPE からのメールをチェックする */ void CellTaskManagerImpl::mail_check() { memaddr data; // SPE Scheduler からの mail check for (int id = 0; id < machineNum; id++) { while (speThreads->has_mail(id, 1, &data)) { if (data == (memaddr)MY_SPE_STATUS_READY) { // MY_SPE_STATUS_READY: SPE が持ってた Task 全て終了 // freeAll する前に循環リストに戻す speTaskList[id]->getLast()->next = speTaskList[id]; speTaskList[id]->freeAll(); spe_running--; } else if (data == (memaddr)MY_SPE_COMMAND_MALLOC) { // MY_SPE_COMMAND_MALLOC SPE からのmain memory request send_alloc_reply(this, id, speThreads); } else if (data > (memaddr)MY_SPE_NOP) { #ifdef TASK_LIST_MAIL TaskListPtr list = (TaskListPtr)data; check_task_list_finish(schedTaskManager, list, waitTaskQueue); #else // 終了したタスク(PPEにあるのでアドレス) HTaskPtr task = (HTaskPtr)data; task->post_func(schedTaskManager, task->post_arg1, task->post_arg2); check_task_finish(task, waitTaskQueue); #endif } // MY_SPE_NOP: 特に意味のないコマンド } } } void CellTaskManagerImpl::polling() { // may call recursively check_task_list_finish() // we need fifo here mail_check(); } static void send_alloc_reply(CellTaskManagerImpl *tm, int id, SpeThreads *speThreads) { /** * info[0] = alloc_id; (CellScheduler::mainMem_alloc 参照) * info[1] = alloc_addr; */ memaddr alloc_info[2]; long alloc_size; long command; speThreads->get_mail(id, 2, alloc_info); command = (long)alloc_info[0]; alloc_size = (long)alloc_info[1]; alloc_info[1] = (memaddr)tm->allocate(alloc_size); //__debug_ppe("[PPE] MALLOCED 0x%lx from [SPE %d]\n", alloc_info[1],id); // 今のところ何もしてない。どうも、この allocate を free // するのは、SPE task が返した値を見て行うらしい。それは、 // 忘れやすいのではないか? speThreads->add_output_tasklist(command, alloc_info[1], alloc_size); speThreads->send_mail(id, 2, alloc_info); } /** * 条件を満たしたら SPE に TaskList を送信する * 条件1. SPE が持ってた TaskList を終了して、次の TaskList を待ってる * 条件2. SPE に送る TaskList に Task がある * * SPE で実行終了した speTaskList と * これから実行する taskListInfo のバッファを入れ替える */ void CellTaskManagerImpl::send_taskList(int id) { if (taskListInfo[id]->empty()) return; // speTaskList は走り終わった ppe の Task の List. // taskListInfo はこれから走る Task の List. // 交換して実行する TaskListInfoPtr tmp = taskListInfo[id]; taskListInfo[id] = speTaskList[id]; speTaskList[id] = tmp; // speTaskList は本来は循環リストなのだけど、実行中は線形リストである。 // spe の Task が終了した時点でなおす。 tmp->getLast()->next = 0; TaskListPtr p = tmp->getFirst(); speThreads->send_mail(id, 1, (memaddr *)&p); } void CellTaskManagerImpl::show_profile() { for (int id = 0; id < machineNum; id++) { HTaskPtr t = create_task(ShowTime,__builtin_return_address(0)); t->set_cpu((CPU_TYPE)(id+2)); t->spawn(); } } void CellTaskManagerImpl::start_profile() { for (int id = 0; id < machineNum; id++) { HTaskPtr t = create_task(StartProfile,__builtin_return_address(0)); t->set_cpu((CPU_TYPE)(id+2)); t->spawn(); } } #ifdef __CERIUM_CELL__ TaskManagerImpl* create_impl(int num) { return new CellTaskManagerImpl(num); } #endif // __CERIUM_CELL