Back to Tesla questions
CodingAutopilot - C++

Width-Limited Queue

Role: Autopilot - C++

Frequency: Reported


From a shared Tesla interviewer question bank (code question 5 of 6; the original was shared as a video plus README).

Problem Description (from README)

Implement a work queue class that takes jobs as input and schedules them to run in parallel on multiple threads. Assume that the jobs are arbitrary, and that each job can require an unpredictable amount of time to run.

The work queue class must have a constraint option: the "width" of the queue called w.

  • When w = 1, the job queue is equivalent to a serial queue: each job runs sequentially in FIFO order, with nothing running in parallel.
  • When w = 3 for example, a maximum of three jobs may run concurrently, but no more. If the user schedules three or fewer jobs to run, then those may run concurrently.

API Requirements: The API is flexible. Design whatever interface would be the easiest to use for the programmer. At a minimum, the programmer should not be aware of threads and how/when work is scheduled to run.

Areas of Concern (interviewer rubric):

  • Threading primitives: Candidates should have a solid understanding of threading primitives (std::thread, std::mutex, etc.).
  • Abstraction: Candidates must invent an adequate abstraction for a job (e.g., std::function).
  • Signal handling: Candidates asking for OS APIs to check if a job is done is a red flag. A job is finished when the function returns.
  • Resources: The queue should minimize impact on system resources when idle (no busy-looping).

Follow-Up Questions:

  • What should happen if w is changed after the work queue is initialized?
  • How do we unit test this code in a deterministic manner?
  • What should happen when the job queue class is destroyed, or if the program terminates?

Example Solution

cpp
#include <atomic>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <optional>
#include <queue>
#include <thread>
#include <vector>

class JobQueue {
public:
    using Job = std::function<void()>;

    JobQueue(size_t max_width) : running_(true) {
        // Create a thread pool with "max_width" number of worker threads.
        // Cap the maximum number of threads to the hardware limit if it is
        // available, otherwise use a hardcoded limit of 128 to avoid resource
        // exhaustion.
        size_t threads = std::thread::hardware_concurrency();
        if (threads == 0) {
            threads = 128;
        }

        for (size_t i = 0; i < std::min(max_width, threads); ++i) {
            thread_pool_.emplace_back([this] { thread_worker(); });
        }
    }

    ~JobQueue() {
        // Flip running flag so that waiting threads will terminate.
        running_ = false;

        // Wake up any waiting threads.
        cv_.notify_all();

        // Join all threads.
        for (std::thread& t : thread_pool_) {
            t.join();
        }
    }

    void add_job(const Job& job) {
        {
            std::unique_lock<std::mutex> lock(mutex_);
            pending_jobs_.push(job);
        }
        // Wake up a worker thread to start running job if there is one
        // available.
        cv_.notify_one();
    }

private:
    void thread_worker() {
        while (running_) {
            // Check if there is a job waiting in the queue.
            auto next_job = [this]() -> std::optional<Job> {
                std::unique_lock<std::mutex> lock(mutex_);
                cv_.wait(lock, [this] { return !pending_jobs_.empty() || !running_; });

                if (!running_ && pending_jobs_.empty()) {
                    return std::nullopt;
                }

                // Although wait handles this, double check safely
                if (pending_jobs_.empty()) {
                    return std::nullopt;
                }

                Job next_job = pending_jobs_.front();
                pending_jobs_.pop();
                return next_job;
            }();

            if (!next_job.has_value()) {
                return;
            }

            // Run the next job if available.
            (*next_job)();
        }
    }

    std::atomic<bool> running_;
    std::vector<std::thread> thread_pool_;
    std::queue<Job> pending_jobs_;
    std::mutex mutex_;
    std::condition_variable cv_;
};

// ==========================================
// Test Harness (from video footer)
// ==========================================

void test(const char* name, size_t width) {
    using namespace std::chrono_literals;

    JobQueue q(width);

    std::atomic<int> max_running_jobs = 0;
    std::atomic<int> running_jobs = 0;

    // Declare the function that will run as the job on the work queue.
    const auto job_func = [&]() {
        ++running_jobs;

        // Sleep for a few milliseconds so that multiple jobs have the
        // opportunity to run concurrently.
        std::this_thread::sleep_for(1ms);

        // Update the maximum number of running jobs based on how many are
        // currently running.
        int old_max_jobs = max_running_jobs;
        while (old_max_jobs < running_jobs &&
               !max_running_jobs.compare_exchange_weak(old_max_jobs, running_jobs)) {
            // max_running_jobs updated in another thread. Try again.
        }

        --running_jobs;
    };

    // Initialize job queue with specified width.
    // Schedule a number of jobs to run on the work queue.
    // Ensure we schedule enough jobs to saturate the width.
    int jobs_to_schedule = std::min((size_t)100, width * 10);

    for (int i = 0; i < jobs_to_schedule; ++i) {
        q.add_job(job_func);
    }

    // Wait for all the jobs to finish running.
    while (running_jobs > 0) {
        std::this_thread::sleep_for(1ms);
    }

    // Check conditions.
    if (max_running_jobs <= width) {
         std::cout << "PASS: " << name << " (Max: " << max_running_jobs << ", Limit: " << width << ")" << std::endl;
    } else {
         std::cerr << "FAIL: " << name << " (Max: " << max_running_jobs << ", Limit: " << width << ")" << std::endl;
    }
}

int main() {
    test("Serial Queue", 1);
    test("Parallel Queue", 4);
    return 0;
}

Source: community report, April 2026