aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRalph Amissah <ralph.amissah@gmail.com>2026-09-09 16:12:37 -0400
committerRalph Amissah <ralph.amissah@gmail.com>2026-09-09 20:26:33 -0400
commita87c7dbef6968f496168f6d2cfe249bb150e0e27 (patch)
tree360fce31917c1ed89e6c4ffd129b9e48d9cf9f0a /src
parentsqlite: schema version, & fail run on writes fail (diff)
ssp: abstraction directory cleared once
abstraction directory cleared once, by whichever language is first The .ssp writer clears stale files out of pod/<doc>/media/abstraction/, and that one directory is shared by every language of a document. The clearing is now done once per directory per run, by whichever language reaches it first, with the lock held across it. A language that finds the directory already prepared has passed through that same lock before writing, so the clearing it skipped had completed before its own write began: no .ssp produced on a given run can be removed during it. (removes possibility of a race condition on parallelisation) (assisted by Claude-Code)
Diffstat (limited to 'src')
-rw-r--r--src/sisudoc/ocda/abstraction/ssp.d46
1 files changed, 33 insertions, 13 deletions
diff --git a/src/sisudoc/ocda/abstraction/ssp.d b/src/sisudoc/ocda/abstraction/ssp.d
index 2501add..05931b1 100644
--- a/src/sisudoc/ocda/abstraction/ssp.d
+++ b/src/sisudoc/ocda/abstraction/ssp.d
@@ -406,6 +406,12 @@ template sspObjectRecord() {
}
}
+/+ ↓ abstraction directories whose stale .ssp have been cleared this run, one
+ entry per document. __gshared, and read and written only inside the
+ synchronized block in the writer below: a document's languages share one
+ abstraction directory and may be written in parallel
++/
+private __gshared bool[string] _abstraction_dir_prepared;
template spineAbstractionTxt() {
import std.conv : to;
import std.digest : toHexString;
@@ -579,21 +585,35 @@ template spineAbstractionTxt() {
auto pths_pod = spinePathsPods!()(doc_matters);
string base_pth
= pths_pod.abstraction_root(doc_matters.src.filename).filesystem_open_zpod.to!string;
- try {
- if (!exists(base_pth)) {
- base_pth.mkdirRecurse;
- } else if (doc_matters.src.language
- == doc_matters.pod.manifest_list_of_languages[0]
- ) {
- /+ ↓ this directory is the one part of the pod written before the pod
- is built, so the pod builder leaves it alone. It therefore clears
- itself here, on the first language of a run, or a .ssp for a
- language the document no longer has would linger and be bundled +/
- foreach (string _f; dirEntries(base_pth, "*.ssp", SpanMode.shallow)) {
- _f.remove;
+ /+ ↓ this directory is the one part of the pod written before the pod is
+ built, so the pod builder leaves it alone. It therefore clears itself
+ here, or a .ssp for a language the document no longer has would linger
+ and be bundled.
+ .
+ Every language of a document writes into this one directory, and the
+ languages may be running at once, so the clearing is done once per
+ directory per run, by whichever language reaches it first, with the
+ lock held across it. A language that finds the directory already
+ prepared passed through this lock before writing, so the clearing it
+ skipped had already finished, and no .ssp written by this run can be
+ removed by it. Keying the clearing on the manifest's first language
+ instead makes it wait for a thread that may be scheduled last, and
+ take the .ssp already written by the other languages with it.
+ +/
+ synchronized {
+ if (base_pth !in _abstraction_dir_prepared) {
+ _abstraction_dir_prepared[base_pth] = true;
+ try {
+ if (!exists(base_pth)) {
+ base_pth.mkdirRecurse;
+ } else {
+ foreach (string _f; dirEntries(base_pth, "*.ssp", SpanMode.shallow)) {
+ _f.remove;
+ }
+ }
+ } catch (Exception ex) {
}
}
- } catch (Exception ex) {
}
string out_file = ((base_pth.chainPath(
doc_matters.src.doc_uid_out ~ ".ssp")).asNormalizedPath).array;