fix: guard Corrections against async read from compose
all() returns List.copyOf(entries), which iterates; add()/load() structurally modify. Once Task 10 wires all() into runTaskAsynchronously and Task 11 wires add() from the main-thread /ia corrigir command, the race throws ConcurrentModificationException. Guarded by entries' own monitor like Conversations: file parse and YAML save stay outside the lock, only fast in-memory work is under it.
This commit is contained in:
@@ -22,6 +22,15 @@ final class Corrections {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final File file;
|
private final File file;
|
||||||
|
/**
|
||||||
|
* Read by {@link #all()} from the async answer thread (Ai.compose) and
|
||||||
|
* mutated by {@link #add()}/{@link #load()} from the main thread (/ia
|
||||||
|
* corrigir). Guarded by its own monitor, like {@link Conversations}: only
|
||||||
|
* fast, in-memory work happens under the lock — file parsing and the YAML
|
||||||
|
* save stay outside it — so {@link #all()}'s {@code List.copyOf} never
|
||||||
|
* races a structural change and throws no
|
||||||
|
* {@code ConcurrentModificationException}.
|
||||||
|
*/
|
||||||
private final List<Entry> entries = new ArrayList<>();
|
private final List<Entry> entries = new ArrayList<>();
|
||||||
|
|
||||||
Corrections(File file) {
|
Corrections(File file) {
|
||||||
@@ -30,11 +39,12 @@ final class Corrections {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
|
YamlConfiguration yaml = file.exists() ? YamlConfiguration.loadConfiguration(file) : null;
|
||||||
|
synchronized (entries) {
|
||||||
entries.clear();
|
entries.clear();
|
||||||
if (!file.exists()) {
|
if (yaml == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
YamlConfiguration yaml = YamlConfiguration.loadConfiguration(file);
|
|
||||||
for (String key : yaml.getKeys(false)) {
|
for (String key : yaml.getKeys(false)) {
|
||||||
String question = yaml.getString(key + ".pergunta");
|
String question = yaml.getString(key + ".pergunta");
|
||||||
String answer = yaml.getString(key + ".resposta");
|
String answer = yaml.getString(key + ".resposta");
|
||||||
@@ -43,14 +53,17 @@ final class Corrections {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void add(String question, String answer) {
|
void add(String question, String answer) {
|
||||||
entries.add(new Entry(question, answer));
|
|
||||||
YamlConfiguration yaml = new YamlConfiguration();
|
YamlConfiguration yaml = new YamlConfiguration();
|
||||||
|
synchronized (entries) {
|
||||||
|
entries.add(new Entry(question, answer));
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
yaml.set("c" + i + ".pergunta", entries.get(i).question());
|
yaml.set("c" + i + ".pergunta", entries.get(i).question());
|
||||||
yaml.set("c" + i + ".resposta", entries.get(i).answer());
|
yaml.set("c" + i + ".resposta", entries.get(i).answer());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
yaml.save(file);
|
yaml.save(file);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -59,8 +72,10 @@ final class Corrections {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Entry> all() {
|
List<Entry> all() {
|
||||||
|
synchronized (entries) {
|
||||||
return List.copyOf(entries);
|
return List.copyOf(entries);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Corrections sharing at least one significant word with the question. */
|
/** Corrections sharing at least one significant word with the question. */
|
||||||
static List<Entry> matching(List<Entry> all, String question) {
|
static List<Entry> matching(List<Entry> all, String question) {
|
||||||
|
|||||||
Reference in New Issue
Block a user