Skip to content

Commit fc49161

Browse files
author
andy
committed
Merge branch 'main' into wip/openssh-backend
# Conflicts: # CMakeLists.txt
2 parents 1307b7d + 2d2594a commit fc49161

13 files changed

Lines changed: 445 additions & 34 deletions

File tree

‎CLAUDE.md‎

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,18 @@ subagent flags, directly.
2727
- Andy runs Debian ("trixie") on his machines — **beryllium** and
2828
**niobium**. Default to Debian conventions, not Ubuntu, for anything
2929
environment/package related, unless told otherwise.
30-
- Releases are tagged (e.g. `v8.0.0-alpha.1`) on `main`. The exception
31-
is a bugfix release made after `main` has moved on to the next minor
32-
version: it goes on a `release/X.Y.Z` branch off the previous tag,
33-
with the fixes cherry-picked from `main` (8.16.2 and 8.16.3 were made
34-
this way while `main` was 8.17.0-dev). One long-lived branch exists by
35-
design: `v9` (the code-name for the revamped ECCE; worktree `../ECCE-native`), the **9.x
36-
experimental line** (plan on #133). Risky or structural work lands
37-
there first: the libssh transport, the server/client package split,
38-
STOMP broker access, the Coin3D viewer, retiring csh. `main` is the
39-
**8.x stable line**: bug fixes go straight to it, and proven 9.x
40-
changes are **backported** as small cherry-picks ("cherry picked
41-
from ..."). "Proven" means its test passes on 9.x, it has run there
42-
a while, and Andy has seen anything user-visible. Keep 9.x commits
43-
backport-sized (one change plus its test), and bring `main` into 9.x
44-
regularly so backports stay conflict-free. "v9" is a code-word for
45-
the big future features, not a gate: anything from it that is useful
46-
now and opt-in on 8.x can ship in an 8.x release, as the split
47-
packages did in 8.16.6 (Andy, 2026-09-29). Old branches
48-
(`develop`, `modernize-build`, `stable`, `master`, `make`) were
49-
consolidated into `main` and renamed to `archive/*` — no reason to
50-
branch from or compare against them.
30+
- Releases are tagged on `main`, which is the **9.x line** (9.0.0-dev
31+
from 2026-10-01; the breaking changes — split packages, sh job
32+
scripts, no site-defined shells, "killed" only on a user cancel —
33+
made it 9.0.0). **8.x gets bug fixes only** (Andy, 2026-10-01): each
34+
8.x patch release goes on its own `release/X.Y.Z` branch off the
35+
previous tag, with the fix cherry-picked from `main` (`-x`), as
36+
8.18.1 and 8.18.2 were. Before tagging, run `tests/teaching` on the
37+
release branch. The `v9` branch was merged into `main` and retired
38+
(`archive/v9`); work that needs isolation goes on short-lived `wip/*`
39+
branches. Old branches (`develop`, `modernize-build`, `stable`,
40+
`master`, `make`) were consolidated into `main` and renamed to
41+
`archive/*` — no reason to branch from or compare against them.
5142
- Build directory is `build-cmake` (not `cmake-build`) — `ninja` or
5243
`cmake --build .` from inside it.
5344
- **The Gateway window no longer appears** (#93, 2026-09-22). `ecce`

‎CMakeLists.txt‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,17 @@ if(PYTHON3_EXECUTABLE)
17321732
${CMAKE_CURRENT_SOURCE_DIR}/tests/launch/jobwait_test.py --control
17331733
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/launch)
17341734
set_tests_properties(launch_jobwait PROPERTIES TIMEOUT 120 SKIP_RETURN_CODE 77)
1735+
add_test(NAME launch_cancel
1736+
COMMAND ${PYTHON3_EXECUTABLE}
1737+
${CMAKE_CURRENT_SOURCE_DIR}/tests/launch/cancel_test.py
1738+
--build ${CMAKE_BINARY_DIR}
1739+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/launch)
1740+
set_tests_properties(launch_cancel PROPERTIES TIMEOUT 900 SKIP_RETURN_CODE 77)
1741+
add_test(NAME launch_filewait
1742+
COMMAND ${PYTHON3_EXECUTABLE}
1743+
${CMAKE_CURRENT_SOURCE_DIR}/tests/launch/filewait_test.py
1744+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/launch)
1745+
set_tests_properties(launch_filewait PROPERTIES TIMEOUT 120 SKIP_RETURN_CODE 77)
17351746

17361747
# tests/teaching -- introductory chemistry teaching calculations run end
17371748
# to end through Launch, with NWChem, and checked against known answers.

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ releases, and why each fix was made, are in `docs/HISTORY.md`.
251251
releases; drop the oldest minor when a new one is added. Everything
252252
older is on the releases page. -->
253253

254+
- **v8.18.2** — a job that finishes while its monitor is restarting is no
255+
longer stored as killed.
256+
- **v8.18.1** — a fresh calculation's output file is no longer sometimes
257+
named "Outputs", which lost MOPAC's energies and geometries (#207).
254258
- **v8.18.0** — experimental built-in ssh (Edit > Preferences): commands,
255259
file copies and job monitoring without a shell session, a host-key
256260
dialog (#204); telnet, Globus and rsh removed; Machine Registration

‎data/client/config/Version‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8.18.0
1+
9.0.0

‎include/dsm/TaskJob.H‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ class TaskJob : public Job,
121121
virtual bool launchdata(const Launchdata& launchinfo);
122122
virtual Launchdata launchdata(const bool &useInputProvider = false) const;
123123

124+
/** The user asked for this job to be cancelled. eccejobstore reads it
125+
to tell a cancelled job from one that vanished. Cleared by
126+
launch and by any reset to created/ready. */
127+
virtual bool killRequested(bool requested);
128+
virtual bool killRequested() const;
129+
124130
virtual bool joblog(const char* log);
125131
virtual string joblog(void) const;
126132

‎scripts/eccejobmonitor‎

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,10 @@ sub FileWait
15031503

15041504
if ($status == $TRY_TIMEOUT)
15051505
{
1506+
# The alarm can cut the last pause short just after the file
1507+
# appeared; look once more before giving up.
1508+
return undef if (-e $file);
1509+
15061510
$errMsg = "$what '$file' "
15071511
. "does not exist and have nonzero size within "
15081512
. "$timeLimit seconds";
@@ -1567,6 +1571,10 @@ sub FileWait2
15671571
}
15681572
elsif ($status == $TRY_TIMEOUT)
15691573
{
1574+
# Same last look as FileWait: the alarm can cut the final pause short.
1575+
$result = &$function();
1576+
return ($TRY_OK, $result) if ($result);
1577+
15701578
$errMsg = "neither $what1 '$file1' nor $what2 '$file2'"
15711579
. "exist and have nonzero size within "
15721580
. "$timeLimit seconds";
@@ -2037,15 +2045,32 @@ sub JobCheck
20372045
}
20382046
elsif ($q eq 'moab')
20392047
{
2040-
$cmd = "squeue -h -a -j $id -o %T 2>&1";
2048+
# checkjob, not squeue: a Moab site need not run Slurm, and an
2049+
# unknown id would read as a finished job at once (#209).
2050+
$cmd = "checkjob $id 2>&1";
20412051
if (open(QUERY, "$cmd |"))
20422052
{
20432053
while (<QUERY>)
20442054
{
20452055
LogMsg "JobCheck Moab status: $_";
20462056

2047-
my $state = $_;
2048-
chomp $state;
2057+
my $state;
2058+
if (/^State:\s*(\S+)/)
2059+
{
2060+
$state = uc $1;
2061+
}
2062+
elsif (/cannot locate job/)
2063+
{
2064+
if ($gJobCheckState != $JOB_STATE_NONE)
2065+
{
2066+
$status = $JOB_STATE_DONE;
2067+
}
2068+
last;
2069+
}
2070+
else
2071+
{
2072+
next;
2073+
}
20492074

20502075
my $capq = uc $q;
20512076
&MsgSendUp("$capq job id '$id' in state '$state'");

‎src/comm/commtools/RunMgmt.C‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,18 @@ string RunMgmt::terminate(const TaskJob *calc)
138138
// shell config enabled it, there's no reason this internal,
139139
// ECCE-controlled status write should ever want to fail because
140140
// of it.
141+
// Before the status file: the monitor may report 302 the moment
142+
// it appears, and eccejobstore calls that a cancel only with this
143+
// set. Withdrawn below if the request could not be made.
144+
const_cast<TaskJob*>(calc)->killRequested(true);
141145
string fixStatus = "echo 302 > ";
142146
fixStatus += job.jobpath + "/.ecce.status";
143147
if (rcmd.exec(fixStatus)) {
144148
string outstr;
145-
if (rcmd.execout(command, outstr))
149+
if (rcmd.execout(command, outstr)) {
146150
ret = "Request to terminate job " + id + " on " +
147151
launch.machine + " has been issued";
152+
}
148153
else {
149154
// If the job has already completed then the kill command
150155
// will return "No such process" and no $status
@@ -166,6 +171,13 @@ string RunMgmt::terminate(const TaskJob *calc)
166171
} else
167172
ret = rcmd.commError();
168173

174+
// The Shell cancel command ends with a second kill that fails once the
175+
// first has worked, so "process not found" still means the user's
176+
// request went through.
177+
if (ret.find("has been issued") == string::npos &&
178+
ret.find("process not found") == string::npos)
179+
const_cast<TaskJob*>(calc)->killRequested(false);
180+
169181
delete refMachine;
170182
} else
171183
ret = "Request to terminate job not issued--unable to lookup job id";

‎src/comm/commxt/JobStore.C‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2391,13 +2391,11 @@ void interactStatus(char* statusData)
23912391
static int lastStatus = -1;
23922392

23932393
int status = 0;
2394+
bool emptyStatus = (statusData==NULL || *statusData=='\0');
23942395

2395-
if (statusData==NULL || *statusData=='\0') {
2396+
if (emptyStatus)
23962397
status = 302;
2397-
logErr("eccejobmonitor",
2398-
"Status message contained no value--setting calculation to killed "
2399-
"(possible file system problem on compute server, disk full?)");
2400-
} else
2398+
else
24012399
status = atoi(statusData);
24022400

24032401
if (lastStatus != status) {
@@ -2420,7 +2418,21 @@ void interactStatus(char* statusData)
24202418
}
24212419
} else if (status == 302) {
24222420
gUnsuccessfulStatus = 0;
2423-
calcUpdateState(ResourceDescriptor::STATE_KILLED);
2421+
// 302 only means the job is gone. It is a cancel only if the user
2422+
// asked for one (RunMgmt::terminate); read it fresh, the request was
2423+
// made by another process.
2424+
if (calculation && calculation->killRequested()) {
2425+
calcUpdateState(ResourceDescriptor::STATE_KILLED);
2426+
} else {
2427+
gFailReason = emptyStatus
2428+
? "The job monitor sent an empty status (possible file system "
2429+
"problem on the compute server, disk full?) and the job was "
2430+
"not cancelled by the user"
2431+
: "The job ended with neither an output file nor a status file, "
2432+
"and was not cancelled by the user";
2433+
logErr("eccejobmonitor", gFailReason);
2434+
calcUpdateState(ResourceDescriptor::STATE_SYSTEM_FAILURE);
2435+
}
24242436
} else if (status == 303) {
24252437
gUnsuccessfulStatus = 0;
24262438
calcUpdateState(ResourceDescriptor::STATE_LOADED);

‎src/comm/commxt/Launch.C‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2142,8 +2142,10 @@ void Launch::updateState(void)
21422142
// Also switch "created" calcs because that's what they are for imports
21432143
// Lisong 07/24/06: Not sure if setState sending notification will
21442144
// cause problem or not.
2145-
if (currentState <= ResourceDescriptor::STATE_READY)
2145+
if (currentState <= ResourceDescriptor::STATE_READY) {
2146+
p_taskjob->killRequested(false);
21462147
p_taskjob->setState(ResourceDescriptor::STATE_SUBMITTED);
2148+
}
21472149

21482150
// special STTR reaction rate study logic to reset the state of all
21492151
// follow-on tasks to force input file regeneration because it's likely

‎src/dsm/edsiimpl/TaskJob.C‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,24 @@ Jobdata TaskJob::jobdata() const
358358
}
359359

360360

361+
bool TaskJob::killRequested(bool requested)
362+
{
363+
vector<MetaDataResult> results(1);
364+
results[0].name = VDoc::getEcceNamespace() + ":killrequested";
365+
results[0].value = requested ? "true" : "";
366+
return addProps(results);
367+
}
368+
369+
370+
/** Reads from the server, not the property cache: the request is made by
371+
another process. */
372+
bool TaskJob::killRequested() const
373+
{
374+
const_cast<TaskJob*>(this)->updateProps();
375+
return getProp(VDoc::getEcceNamespace() + ":killrequested") == "true";
376+
}
377+
378+
361379
/**
362380
* Sets launch data associated with the task.
363381
*
@@ -1769,7 +1787,7 @@ bool TaskJob::canChangeState(string & message) const
17691787
bool TaskJob::setState(const ResourceDescriptor::RUNSTATE& state)
17701788
{
17711789
string stateName = ResourceUtils::stateToString(state);
1772-
vector<MetaDataResult> results((state<=ResourceDescriptor::STATE_READY)?6:2);
1790+
vector<MetaDataResult> results((state<=ResourceDescriptor::STATE_READY)?7:2);
17731791
results[0].name = VDoc::getEcceNamespace() + ":state";
17741792
results[0].value = stateName;
17751793
results[1].name = VDoc::getEcceNamespace() + ":rerun";
@@ -1783,6 +1801,8 @@ bool TaskJob::setState(const ResourceDescriptor::RUNSTATE& state)
17831801
results[4].value = "false";
17841802
results[5].name = VDoc::getEcceNamespace() + ":reviewedDate";
17851803
results[5].value = "";
1804+
results[6].name = VDoc::getEcceNamespace() + ":killrequested";
1805+
results[6].value = "";
17861806
}
17871807

17881808
bool ret = addProps(results);

0 commit comments

Comments
 (0)