This is getting complex with the inline transition. Let me take a cleaner approach — use a single reader but manage the transition more explicitly by collecting way buffers.

Actually, this inline approach is messy. Let me take a simpler route: just change the Pass 2 reader to read ALL entity types, but only PROCESS node blocks. When it encounters a way block, it stashes it. Then Pass 3 processes the stashed way blocks plus continues reading from the same reader.

Actually the simplest approach: read the ENTIRE PBF into memory buffers in one pass, then process nodes and ways from the buffers. But 86GB doesn't fit in RAM.

Let me go with the cleaner approach: keep 2 separate readers but make Pass 2 read `node | way | relation`. It processes nodes, and when it hits ways, it hands the reader to Pass 3. But osmium's reader doesn't support "give me the rest" — once you read a block, it's consumed.

The cleanest approach: **single reader, two-phase processing within one loop**.


Edit

                // Single reader for nodes + ways + relations
                osmium::io::Reader combined_reader{input_file};

                // Phase 1: Feed node blocks to node workers
                {
                    while (auto buf = combined_reader.read()) {
                        // Check if this buffer contains nodes or ways
                        bool has_nodes = false, has_ways = false;
                        for (const auto& item : buf) {
                            if (item.type() == osmium::item_type::node) { has_nodes = true; break; }
                            if (item.type() == osmium::item_type::way) { has_ways = true; break; }
                        }

                        if (has_nodes) {
                            std::unique_lock<std::mutex> lock(queue_mutex);
                            queue_cv.wait(lock, [&]{ return block_queue.size() < MAX_QUEUE; });
                            block_queue.push_back(std::move(buf));
                            queue_cv.notify_one();
                        } else {
                            // First non-node block: signal node workers done, transition to ways
                            {
                                std::lock_guard<std::mutex> lock(queue_mutex);
                                reader_done = true;
                            }
                            queue_cv.notify_all();

                            // Wait for node workers to finish
                            for (auto& w : node_workers) w.join();

                            // Merge address points
                            uint64_t total_addrs = 0;
                            for (auto& local : ntld) {
                                for (size_t j = 0; j < local.addr_coords.size(); j++) {
                                    uint64_t dummy = 0;
                                    add_addr_point(data, local.addr_coords[j].first, local.addr_coords[j].second,
                                                   local.addr_strings[j].first.c_str(),
                                                   local.addr_strings[j].second.c_str(), dummy);
                                }
                                total_addrs += local.count;
                            }
                            std::cerr << "  Node processing complete: " << total_addrs
                                      << " address points collected." << std::endl;
                            log_phase("Pass 2: node processing", _pt);
                            std::cerr << "  Processing ways with " << num_threads << " threads..." << std::endl;

                            // Transition: this buffer is the first way/relation block
                            // Push it to the way queue (set up below) and continue reading
                            // We need to break out of this loop and start the way processing loop
                            // with this buffer as the first item.

                            // --- Way processing setup (was Pass 3) ---

                // Single reader for all entity types — processes nodes first,
                // then transitions to way processing when first way block arrives.
                osmium::io::Reader combined_reader{input_file};
                bool nodes_done = false;
                osmium::memory::Buffer first_way_buf; // stash the first way block

                // Phase 1: Feed node blocks to node workers
                while (auto buf = combined_reader.read()) {
                    // Check if this buffer contains nodes or ways/relations
                    bool has_node = false;
                    for (const auto& item : buf) {
                        if (item.type() == osmium::item_type::node) { has_node = true; break; }
                    }

                    if (has_node) {
                        std::unique_lock<std::mutex> lock(queue_mutex);
                        queue_cv.wait(lock, [&]{ return block_queue.size() < MAX_QUEUE; });
                        block_queue.push_back(std::move(buf));
                        queue_cv.notify_one();
                    } else {
                        // First non-node block — stash it and transition
                        first_way_buf = std::move(buf);
                        nodes_done = true;
                        break;
                    }
                }

                // Signal node workers done
                {
                    std::lock_guard<std::mutex> lock(queue_mutex);
                    reader_done = true;
                }
                queue_cv.notify_all();
                for (auto& w : node_workers) w.join();

                // Merge address points
                uint64_t total_addrs = 0;
                for (auto& local : ntld) {
                    for (size_t j = 0; j < local.addr_coords.size(); j++) {
                        uint64_t dummy = 0;
                        add_addr_point(data, local.addr_coords[j].first, local.addr_coords[j].second,
                                       local.addr_strings[j].first.c_str(),
                                       local.addr_strings[j].second.c_str(), dummy);
                    }
                    total_addrs += local.count;
                }
                std::cerr << "  Node processing complete: " << total_addrs
                          << " address points collected." << std::endl;
                log_phase("Pass 2: node processing", _pt);
                std::cerr << "  Processing ways with " << num_threads << " threads..." << std::endl;

                // --- Phase 2: Way processing using same reader ---