Hone logo
Hone
Problems

Bounded Parallel Task Execution in Go

Bounded parallelism is a crucial technique for managing resource utilization and preventing runaway processes, especially when dealing with potentially unbounded input streams or tasks. This challenge asks you to implement a worker pool in Go that limits the number of concurrent tasks executing at any given time, ensuring controlled resource consumption and preventing system overload. This is useful in scenarios like processing large files, making numerous API calls, or handling a high volume of incoming requests.

Problem Description

You are tasked with creating a BoundedWorkerPool that manages a pool of worker goroutines. The pool should accept tasks (represented as functions that return an error) and execute them concurrently, but with a limit on the number of tasks running simultaneously. The pool should also gracefully handle errors returned by the tasks and provide a mechanism to shut down the pool when no more tasks are expected.

Key Requirements:

  • Bounded Concurrency: The pool must ensure that no more than maxWorkers tasks are running concurrently.
  • Task Queue: Tasks should be queued and processed in the order they are submitted.
  • Error Handling: Errors returned by the worker functions should be collected and returned to the caller.
  • Shutdown: The pool should have a Close() method that prevents further task submission and waits for all currently running tasks to complete.
  • Task Type: Tasks are functions of type func() error.

Expected Behavior:

  1. When a task is submitted, it should be added to the queue.
  2. If the number of currently running tasks is less than maxWorkers, a worker goroutine should be launched to execute the task.
  3. If the number of currently running tasks is equal to maxWorkers, the task should wait in the queue until a worker becomes available.
  4. The Close() method should block until all tasks in the queue have been processed and all worker goroutines have exited.
  5. Any error returned by a task should be collected and returned by the BoundedWorkerPool.

Edge Cases to Consider:

  • Submitting tasks after the pool has been closed.
  • Tasks that panic. (Handle panics gracefully, ideally by logging and continuing.)
  • maxWorkers being zero or negative (should be handled gracefully, possibly by returning an error during initialization).

Examples

Example 1:

Input: maxWorkers = 2, tasks = [func() error { time.Sleep(100 * time.Millisecond); return nil }, func() error { time.Sleep(200 * time.Millisecond); return nil }, func() error { time.Sleep(50 * time.Millisecond); return nil }]
Output: []error{}
Explanation: Three tasks are submitted. Two workers start immediately. The third task waits until one of the first two completes. All tasks finish successfully.

Example 2:

Input: maxWorkers = 1, tasks = [func() error { return errors.New("task failed") }, func() error { return nil }]
Output: [error{message: "task failed"}]
Explanation:  One worker processes the first task, which returns an error. The error is collected. The second task is processed and completes successfully, but the error from the first task is already returned.

Example 3: (Edge Case)

Input: maxWorkers = 0, tasks = [func() error { return nil }]
Output: error{message: "maxWorkers must be positive"}
Explanation:  The pool initialization fails because maxWorkers is zero.

Constraints

  • maxWorkers must be a positive integer.
  • Tasks are functions that return an error.
  • The pool should be reasonably efficient in terms of memory usage and goroutine management.
  • The Close() method should not return until all tasks are completed.
  • The pool should handle panics within tasks gracefully (e.g., log the panic and continue processing other tasks).

Notes

Consider using channels to manage the task queue and signal worker availability. Think about how to handle errors returned by the worker functions and ensure they are propagated to the caller. The sync.WaitGroup can be helpful for coordinating the shutdown of worker goroutines. Error handling within the worker goroutines is important to prevent a single failing task from crashing the entire pool.

Loading editor...
go