Run the parallel pipeline path: extract, registry, resolve, infra, k8s. */
| 1141 | |
| 1142 | /* Run the parallel pipeline path: extract, registry, resolve, infra, k8s. */ |
| 1143 | static int run_parallel_pipeline(cbm_pipeline_t *p, cbm_pipeline_ctx_t *ctx, |
| 1144 | const cbm_file_info_t *files, int file_count, int worker_count, |
| 1145 | struct timespec *t) { |
| 1146 | cbm_log_info("pipeline.mode", "mode", "parallel", "workers", itoa_buf(worker_count), "files", |
| 1147 | itoa_buf(file_count)); |
| 1148 | _Atomic int64_t shared_ids; |
| 1149 | atomic_init(&shared_ids, cbm_gbuf_next_id(p->gbuf)); |
| 1150 | CBMFileResult **cache = (CBMFileResult **)calloc(file_count, sizeof(CBMFileResult *)); |
| 1151 | if (!cache) { |
| 1152 | cbm_log_error("pipeline.err", "phase", "cache_alloc"); |
| 1153 | return CBM_NOT_FOUND; |
| 1154 | } |
| 1155 | cbm_clock_gettime(CLOCK_MONOTONIC, t); |
| 1156 | int rc = cbm_parallel_extract(ctx, files, file_count, cache, &shared_ids, worker_count); |
| 1157 | cbm_log_info("pass.timing", "pass", "parallel_extract", "elapsed_ms", |
| 1158 | itoa_buf((int)elapsed_ms(*t))); |
| 1159 | if (rc != 0 || check_cancel(p)) { |
| 1160 | for (int i = 0; i < file_count; i++) { |
| 1161 | cbm_free_result(cache[i]); |
| 1162 | } |
| 1163 | free(cache); |
| 1164 | return rc != 0 ? rc : CBM_NOT_FOUND; |
| 1165 | } |
| 1166 | cbm_gbuf_set_next_id(p->gbuf, atomic_load(&shared_ids)); |
| 1167 | /* extract -> registry handoff: return the extract phase's freed-but-retained |
| 1168 | * allocator pages to the OS before registry_build allocates. On a 2x Linux |
| 1169 | * index the extract peak holds ~13 GB of reclaimable pages (peak_mb 20.7 vs |
| 1170 | * live rss_mb 7); not returning them pushed the process over the system |
| 1171 | * memory-pressure threshold and got it SIGKILLed at registry entry. */ |
| 1172 | cbm_mem_collect(); |
| 1173 | cbm_log_info("mem.collect", "phase", "post_extract", "rss_mb", |
| 1174 | itoa_buf((int)(cbm_mem_rss() / (1024 * 1024)))); |
| 1175 | cbm_clock_gettime(CLOCK_MONOTONIC, t); |
| 1176 | rc = cbm_build_registry_from_cache(ctx, files, file_count, cache); |
| 1177 | cbm_log_info("pass.timing", "pass", "registry_build", "elapsed_ms", |
| 1178 | itoa_buf((int)elapsed_ms(*t))); |
| 1179 | log_phase_mem("registry_build"); |
| 1180 | if (rc != 0 || check_cancel(p)) { |
| 1181 | for (int i = 0; i < file_count; i++) { |
| 1182 | if (cache[i]) { |
| 1183 | cbm_free_result(cache[i]); |
| 1184 | } |
| 1185 | } |
| 1186 | free(cache); |
| 1187 | return rc != 0 ? rc : CBM_NOT_FOUND; |
| 1188 | } |
| 1189 | /* Registry consumers may materialize serial nodes (Channel, EnvVar, and |
| 1190 | * future carrier-derived resources) after parallel extraction established |
| 1191 | * the shared allocator watermark. Advance the atomic allocator before |
| 1192 | * resolve workers resume; otherwise their IDs and the later next-id reset |
| 1193 | * can collide with those nodes and orphan freshly inserted edges. */ |
| 1194 | int64_t registry_next_id = cbm_gbuf_next_id(p->gbuf); |
| 1195 | if (registry_next_id > atomic_load(&shared_ids)) { |
| 1196 | atomic_store(&shared_ids, registry_next_id); |
| 1197 | } |
| 1198 | /* Cross-file LSP precondition: build a project-wide CBMLSPDef[] |
| 1199 | * once. The fused resolve_worker invokes cbm_pxc_run_one(_ts) per |
| 1200 | * file using these defs + the file's IMPORTS map, so cross-file |
no test coverage detected