summaryrefslogtreecommitdiff
path: root/xmake/modules/private/async/jobpool.lua
diff options
context:
space:
mode:
Diffstat (limited to 'xmake/modules/private/async/jobpool.lua')
-rw-r--r--xmake/modules/private/async/jobpool.lua127
1 files changed, 91 insertions, 36 deletions
diff --git a/xmake/modules/private/async/jobpool.lua b/xmake/modules/private/async/jobpool.lua
index 7f9067343..1358e0e12 100644
--- a/xmake/modules/private/async/jobpool.lua
+++ b/xmake/modules/private/async/jobpool.lua
@@ -20,10 +20,16 @@
-- imports
import("core.base.object")
+import("core.base.list")
import("core.base.hashset")
-- define module
-local jobpool = jobpool or object {_init = {"_size", "_rootjob", "_leafjobs", "_poprefs"}}
+local jobpool = jobpool or object {_init = {"_size", "_rootjob", "_leafjobs"}}
+
+-- the job status
+local JOB_STATUS_FREE = 1
+local JOB_STATUS_PENDING = 2
+local JOB_STATUS_FINISHED = 3
-- get jobs size
function jobpool:size()
@@ -45,7 +51,7 @@ end
--
function jobpool:newjob(name, run, opt)
opt = opt or {}
- return {name = name, run = run, distcc = opt.distcc}
+ return {name = name, run = run, distcc = opt.distcc, status = JOB_STATUS_FREE}
end
-- add run job to the given job node
@@ -60,7 +66,7 @@ end
--
function jobpool:addjob(name, run, opt)
opt = opt or {}
- return self:add({name = name, run = run, distcc = opt.distcc}, opt.rootjob)
+ return self:add({name = name, run = run, distcc = opt.distcc, status = JOB_STATUS_FREE}, opt.rootjob)
end
-- add job to the given job node
@@ -95,30 +101,73 @@ function jobpool:add(job, rootjob)
return job
end
--- pop job without deps at leaf node
-function jobpool:pop()
-
- -- no jobs?
+-- get a free job from the leaf jobs
+function jobpool:getfree()
if self:size() == 0 then
return
end
- -- init leaf jobs first
- local leafjobs = self._leafjobs
- if #leafjobs == 0 then
- local refs = {}
- self:_genleafjobs(self:rootjob(), leafjobs, refs)
+ -- get a free job from the leaf jobs
+ local leafjobs = self:_getleafjobs()
+ if not leafjobs:empty() then
+ -- try to get next free job fastly
+ if self._nextfree then
+ local job = self._nextfree
+ local nextfree = leafjobs:prev(job)
+ if nextfree ~= job and self:_isfree(nextfree) then
+ self._nextfree = nextfree
+ else
+ self._nextfree = nil
+ end
+ job.status = JOB_STATUS_PENDING
+ return job
+ end
+ -- find the next free job
+ local removed_jobs = {}
+ for job in leafjobs:ritems() do
+ if self:_isfree(job) then
+ local nextfree = leafjobs:prev(job)
+ if nextfree ~= job and self:_isfree(nextfree) then
+ self._nextfree = nextfree
+ end
+ job.status = JOB_STATUS_PENDING
+ return job
+ elseif job.group or job.status == JOB_STATUS_FINISHED then
+ table.insert(removed_jobs, job)
+ end
+ end
+ -- not found? if remove group and referenced node exist,
+ -- we try to remove them and find the next free job again
+ if #removed_jobs > 0 then
+ for _, job in ipairs(removed_jobs) do
+ self:remove(job)
+ end
+ for job in leafjobs:ritems() do
+ if self:_isfree(job) then
+ local nextfree = leafjobs:prev(job)
+ if nextfree ~= job and self:_isfree(nextfree) then
+ self._nextfree = nextfree
+ end
+ job.status = JOB_STATUS_PENDING
+ return job
+ end
+ end
+ end
end
+end
- -- pop a job from the leaf jobs
- if #leafjobs > 0 then
+-- remove the given job from the leaf jobs
+function jobpool:remove(job)
+ assert(self:size() > 0)
+ local leafjobs = self:_getleafjobs()
+ if not leafjobs:empty() then
+ assert(job ~= self._nextfree)
- -- get job
- local job = leafjobs[#leafjobs]
- table.remove(leafjobs, #leafjobs)
+ -- remove this job from leaf jobs
+ job.status = JOB_STATUS_FINISHED
+ leafjobs:remove(job)
- -- get priority and parents node
- local priority = job._priority or 0
+ -- get parents node
local parents = assert(job._parents, "invalid job without parents node!")
-- update all parents nodes
@@ -126,27 +175,13 @@ function jobpool:pop()
-- we need to avoid adding it to leafjobs repeatly, it will cause dead-loop when poping group job
-- @see https://github.com/xmake-io/xmake/issues/2740
if not p._leaf then
- p._priority = math.max(p._priority or 0, priority + 1)
p._deps:remove(job)
if p._deps:empty() and self._size > 0 then
p._leaf = true
- table.insert(leafjobs, 1, p)
+ leafjobs:insert_first(p)
end
end
end
-
- -- is group node or referenced node (it has been popped once) ?
- local poprefs = self._poprefs
- local jobkey = tostring(job)
- if job.group or poprefs[jobkey] then
- -- pop the next real job
- return self:pop()
- else
- -- pop this job
- self._size = self._size - 1
- poprefs[jobkey] = true
- return job, priority
- end
end
end
@@ -178,6 +213,26 @@ function jobpool:group_leave()
end
end
+-- is free job?
+-- we need to ignore group node (empty job) and referenced node (finished job)
+function jobpool:_isfree(job)
+ if job and job.status == JOB_STATUS_FREE and not job.group then
+ return true
+ end
+end
+
+-- get leaf jobs
+function jobpool:_getleafjobs()
+ local leafjobs = self._leafjobs
+ if leafjobs == nil then
+ leafjobs = list.new()
+ local refs = {}
+ self:_genleafjobs(self:rootjob(), leafjobs, refs)
+ self._leafjobs = leafjobs
+ end
+ return leafjobs
+end
+
-- generate all leaf jobs from the given job
function jobpool:_genleafjobs(job, leafjobs, refs)
local deps = job._deps
@@ -191,7 +246,7 @@ function jobpool:_genleafjobs(job, leafjobs, refs)
end
else
job._leaf = true
- table.insert(leafjobs, job)
+ leafjobs:insert_last(job)
end
end
@@ -232,5 +287,5 @@ end
-- new a jobpool
function new()
- return jobpool {0, {name = "root"}, {}, {}}
+ return jobpool {0, {name = "root"}, nil}
end