diff options
| author | ruki <[email protected]> | 2019-11-29 22:39:51 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2019-11-29 09:59:12 +0800 |
| commit | 1084741560f0391301adae262269dd6c0e3e8ff0 (patch) | |
| tree | 3ee1caed54f9bde1e890057237838cd45da49156 | |
| parent | 3684e6e787a021555f33b5cc6731815507e897ee (diff) | |
impl co_suspend for scheduler
| -rw-r--r-- | xmake/core/base/scheduler.lua | 167 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/scheduler.lua | 5 |
2 files changed, 132 insertions, 40 deletions
diff --git a/xmake/core/base/scheduler.lua b/xmake/core/base/scheduler.lua index 2138caf1e..8d23e8ddb 100644 --- a/xmake/core/base/scheduler.lua +++ b/xmake/core/base/scheduler.lua @@ -92,6 +92,53 @@ function _coroutine:__gc() self._RAWCO = nil end +-- get the main loop coroutine +function scheduler:_co_mainloop() + return self._CO_MAINLOOP +end + +-- create the coroutine instance +function scheduler:_co_create(func) + local rawco = coroutine.create(func) + if not rawco then + return nil, "create coroutine failed!" + end + return _coroutine.new(rawco) +end + +-- suspend the current coroutine +function scheduler:_co_suspend(...) + + -- get the next ready coroutine first + local co_next = self:_co_next_ready() + + -- make the running coroutine as suspended + self:_co_mark_suspended(self:running()) + + -- switch to next coroutine + if (co_next ~= self:running()) then + return self:_co_switch(co_next, ...) + end + + -- no more coroutine? switch to mainloop coroutine + return self:_co_switch(self:_co_mainloop(), ...) +end + +-- TODO +-- resume the given coroutine +function scheduler:_co_resume() +end + +-- switch to the given coroutine +function scheduler:_co_switch(co, ...) + + -- mark it as the running coroutine + self._RUNNING = co + + -- switch to this coroutine + return co:resume(...) +end + -- TODO we need support socket/pipe io and processes as same time function scheduler:_poller_loop() print("poller loop") @@ -117,43 +164,50 @@ function scheduler:_poller_loop_ensure() return true end +-- the scheduler loop coroutine +function scheduler:_mainloop(opt) + + -- run loop + local co_list_ready = self:_co_list_ready() + while co_list_ready:size() > 0 do + + -- get the first ready coroutine + local co_ready = co_list_ready:first() + + -- switch to this coroutine + local ok, result_or_errors = self:_co_switch(co_ready) + if not ok then + os.raise(result_or_errors) + end + + -- this coroutine has been finished? we remove it from the ready queue + if co_ready:is_dead() then + self:_co_mark_dead(co_ready) + end + end +end + -- start scheduler loop function scheduler:_startloop() - self._RUNNING = true + self._RUNNING = nil + self._CO_MAINLOOP = nil self._POLLER_STARTED = false end -- stop scheduler loop function scheduler:_stoploop() - self._RUNNING = false + self._RUNNING = nil + self._CO_MAINLOOP = nil self._POLLER_STARTED = false end --- create the coroutine instance -function scheduler:_co_create(func) - local rawco = coroutine.create(func) - if not rawco then - return nil, "create coroutine failed!" - end - return _coroutine.new(rawco) -end - --- TODO --- suspend the current coroutine -function scheduler:_co_suspend() - coroutine.yield() -end - --- TODO --- resume the given coroutine -function scheduler:_co_resume() -end - -- get all ready coroutines list -- -- ready: ready -> ready -> .. -> running -> .. -> ready -> ..-> --- | | +-- | | <-> poller_loop -- ---------------------------<------------------------ +-- | | +-- mainloop -- function scheduler:_co_list_ready() local co_list_ready = self._CO_LIST_READY @@ -164,6 +218,44 @@ function scheduler:_co_list_ready() return co_list_ready end +-- get all suspended coroutines list +function scheduler:_co_list_suspended() + local co_list_suspended = self._CO_LIST_SUSPENDED + if not co_list_suspended then + co_list_suspended = dlist() + self._CO_LIST_SUSPENDED = co_list_suspended + end + return co_list_suspended +end + +-- get the next ready coroutine +function scheduler:_co_next_ready() + return self:_co_list_ready():next(self:running()) +end + +-- mark the given coroutine as suspended +function scheduler:_co_mark_suspended(co) + + -- cannot be mainloop coroutine + assert(co ~= self:_co_mainloop()) + + -- remove this coroutine from the ready coroutines + self:_co_list_ready():remove(co) + + -- append this coroutine to suspended coroutines + self:_co_list_suspended():push(co) +end + +-- mark the given coroutine as dead +function scheduler:_co_mark_dead(co) + + -- cannot be mainloop coroutine + assert(co ~= self:_co_mainloop()) + + -- remove this coroutine from the ready coroutines + self:_co_list_ready():remove(co) +end + -- run new coroutine function, it will insert to the pending queue function scheduler:run(func, ...) local argv = table.pack(...) @@ -177,7 +269,7 @@ end -- get the current running coroutine in scheduler function scheduler:running() - return self._RUNNING and coroutine.running() or nil + return self._RUNNING end -- wait socket events @@ -219,23 +311,18 @@ function scheduler:runloop(opt) -- start scheduler self:_startloop() - -- run loop - local co_list_ready = self:_co_list_ready() - while co_list_ready:size() > 0 do - - -- get the first ready coroutine - local co_ready = co_list_ready:first() + -- create the main loop + local co_mainloop, errors = self:_co_create(self._mainloop) + if not co_mainloop then + return false, errors + end + co_mainloop:name_set("mainloop") + self._CO_MAINLOOP = co_mainloop - -- switch to this coroutine - local ok, result_or_errors = co_ready:resume() - if not ok then - return false, result_or_errors - end - - -- this coroutine has been finished? we remove it from the ready queue - if co_ready:is_dead() then - co_list_ready:shift() - end + -- start the main loop + local ok, errors = self:_co_switch(co_mainloop, self, opt) + if not ok then + return false, errors end -- stop scheduler diff --git a/xmake/core/sandbox/modules/import/core/base/scheduler.lua b/xmake/core/sandbox/modules/import/core/base/scheduler.lua index 282f1490b..020199c7d 100644 --- a/xmake/core/sandbox/modules/import/core/base/scheduler.lua +++ b/xmake/core/sandbox/modules/import/core/base/scheduler.lua @@ -50,5 +50,10 @@ function sandbox_core_base_scheduler.runloop(opt) end end +-- get the current running coroutine instance +function sandbox_core_base_scheduler.running() + return scheduler:running() +end + -- return module return sandbox_core_base_scheduler |
