Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion library/modules/Job.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,14 @@ bool DFHack::Job::removeJob(df::job* job) {
// call the job cancel vmethod graciously provided by The Toady One.
// job_handler::cancel_job calls job::~job, and then deletes job (this has
// been confirmed by disassembly).
world->jobs.cancel_job(job);

// HACK: GCC (starting around GCC 10 targeting C++20 as of v50.09) optimizes
// out the vmethod call here regardless of optimization level, so we need to
// invoke the vmethod manually through a pointer, as the Lua wrapper does.
// `volatile` does not seem to be necessary but is included for good
// measure.
volatile auto cancel_job_method = &df::job_handler::cancel_job;
(world->jobs.*cancel_job_method)(job);

return true;
}
Expand Down
19 changes: 19 additions & 0 deletions test/modules/job.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
config.target = 'core'
config.mode = 'title' -- alters world state, not safe when a world is loaded

function test.removeJob()
-- removeJob() calls DF code, so ensure that that DF code is actually running

-- for an explanation of why this is necessary to check,
-- see https://github.com/DFHack/dfhack/pull/3713 and Job.cpp:removeJob()

expect.nil_(df.global.world.jobs.list.next, 'job list is not empty')

local job = df.job:new() -- will be deleted by removeJob() if the test passes
dfhack.job.linkIntoWorld(job)
expect.true_(df.global.world.jobs.list.next, 'job list is empty')
expect.eq(df.global.world.jobs.list.next.item, job, 'expected job not found in list')

expect.true_(dfhack.job.removeJob(job))
expect.nil_(df.global.world.jobs.list.next, 'job list is not empty after removeJob()')
end