|
| 1 | +// journalCoordinator.js - Layer 2: Runtime과 저널 디렉터리 하나의 연산 순서와 캐시 수명. |
| 2 | +// |
| 3 | +// Reactive heap은 Runtime당 하나다. 같은 Runtime과 directory handle을 쓰는 여러 journal |
| 4 | +// facade가 독립 lock과 주소 cache를 가지면 한쪽 pack이 지운 주소를 다른 쪽 commit이 다시 |
| 5 | +// 단언하거나, recover가 commit의 lazy page 복사 중 heap을 바꿀 수 있다. 이 coordinator가 |
| 6 | +// heap, ref, CAS를 만지는 연산을 한 줄로 세우고 주소 cache와 storage epoch를 함께 소유한다. |
| 7 | + |
| 8 | +import { PyProcError } from "../../runtime/errors.js"; |
| 9 | + |
| 10 | +const RUNTIME_COORDINATORS = new WeakMap(); |
| 11 | + |
| 12 | +class JournalCoordinator { |
| 13 | + constructor(reactive) { |
| 14 | + this.reactive = reactive || null; |
| 15 | + this.addressCache = new Map(); |
| 16 | + this.storageEpoch = 0; |
| 17 | + this._tail = Promise.resolve(); |
| 18 | + this._pending = 0; |
| 19 | + } |
| 20 | + |
| 21 | + assertReactive(reactive) { |
| 22 | + if (!this.reactive) this.reactive = reactive || null; |
| 23 | + if (reactive && this.reactive !== reactive) { |
| 24 | + throw new PyProcError( |
| 25 | + "PYPROC_INPUT_INVALID", |
| 26 | + "journal: one Runtime and directory must share one ReactiveController", |
| 27 | + ); |
| 28 | + } |
| 29 | + } |
| 30 | + |
| 31 | + get busy() { return this._pending > 0; } |
| 32 | + |
| 33 | + run(operation) { |
| 34 | + this._pending++; |
| 35 | + const result = this._tail.then(async () => { |
| 36 | + try { return await operation(); } |
| 37 | + finally { this._pending--; } |
| 38 | + }); |
| 39 | + // 실패한 작업도 다음 작업의 줄을 끊지 않는다. 오류는 result를 받은 호출자에게만 전달한다. |
| 40 | + this._tail = result.then(() => undefined, () => undefined); |
| 41 | + return result; |
| 42 | + } |
| 43 | + |
| 44 | + async settle() { await this._tail; } |
| 45 | + |
| 46 | + invalidateAddresses() { this.addressCache.clear(); } |
| 47 | + |
| 48 | + resetStorage() { |
| 49 | + this.addressCache.clear(); |
| 50 | + this.storageEpoch++; |
| 51 | + return this.storageEpoch; |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +export function journalCoordinatorFor(rt, dir, reactive) { |
| 56 | + // cfg 검증은 MachineJournal.start가 소유한다. 불완전한 수동 구성은 공유할 key가 없으므로 |
| 57 | + // 독립 coordinator를 받고, 실제 연산 전에 기존과 같은 입력 오류가 난다. |
| 58 | + if (!rt || !dir || (typeof dir !== "object" && typeof dir !== "function")) { |
| 59 | + return new JournalCoordinator(reactive); |
| 60 | + } |
| 61 | + let byDirectory = RUNTIME_COORDINATORS.get(rt); |
| 62 | + if (!byDirectory) { |
| 63 | + byDirectory = new WeakMap(); |
| 64 | + RUNTIME_COORDINATORS.set(rt, byDirectory); |
| 65 | + } |
| 66 | + let coordinator = byDirectory.get(dir); |
| 67 | + if (!coordinator) { |
| 68 | + coordinator = new JournalCoordinator(reactive); |
| 69 | + byDirectory.set(dir, coordinator); |
| 70 | + } else { |
| 71 | + coordinator.assertReactive(reactive); |
| 72 | + } |
| 73 | + return coordinator; |
| 74 | +} |
0 commit comments