Image Transform Pipeline
Scenario. A media service receives batches of uploaded images and applies a chain of transforms (invert, brighten, flip...). It runs on a box with limited RAM. Build a concurrent pipeline that is fast, but also predictable: output order matches input order, one bad image doesn't sink the batch, and the total size of images being processed at once stays under a memory budget.
Model (package imaging)
Image(String name, int width, int height, int[] pixels)grayscale, values 0..255, row-major. Given.
sizeBytes() = width * height * 4.
Transformis a functional interfaceImage apply(Image in). Given.Transformshas three transforms for you to implement:invert()(255 - v),brighten(int delta)(clamp to
0..255), flipHorizontal() (mirror each row). Transforms must not mutate their input (return a new image).
Resultis given:success(Image)/failure(String name, String error).
Pipeline(int workers, long memoryBudgetBytes)
List<Result> process(List<Image> inputs, List<Transform> chain)
- Apply
chainin order to every image, usingworkersthreads. - Order:
results.get(i)corresponds toinputs.get(i), no matter which finished first. - Failure isolation: if any transform throws for an image, that image's result is
Result.failure(name, message) and every other image is unaffected.
- Memory budget: at no moment may the sum of
sizeBytes()of images *admitted but not yet finished* exceed
memoryBudgetBytes. An image larger than the whole budget is not processed: its result is a failure with an error containing "exceeds memory budget".
- FIFO admission: images are admitted strictly in input order. A small image must not jump ahead of a large one
that is waiting for memory. (This avoids starving big images.)
processreturns only when every image is done. Invalid constructor args (workers < 1, budget < 1) →
IllegalArgumentException. Shut threads down when finished (or reuse a pool and expose close(); tests call process once per instance).
Hints
- A fair
Semaphorewith one permit per byte (budget ≤Integer.MAX_VALUEis fine here), acquired by the
submitting thread in input order, gives you both the budget and FIFO admission.
- Release permits in a
finallyblock, or one failure leaks memory forever. - Collect
Future<Result>in input order andget()them in order.