Katkat

Thread-Safe Stock Inventory: Buy and Sell Without Overselling

OptiverSoftware Engineer · Technical Screen

Last updated

This question evaluates understanding of concurrent programming, synchronization primitives, atomicity, and correctness of shared mutable state by requiring implementation of a thread-safe inventory that prevents overselling and preserves updates.

Thread-Safe Stock Inventory: Buy and Sell Without Overselling

You are implementing the inventory-management core of a small brokerage service. The service tracks the number of shares of a single stock that are currently available, and it exposes two operations that will be called concurrently from many threads.

Implement a class StockInventory with the following interface:

  • StockInventory(initial) — construct the inventory with initial shares available.
  • buy(quantity) — acquire quantity additional shares and add them to the available inventory. A buy always succeeds. Returns the number of shares available immediately after this purchase is applied.
  • sell(quantity) — attempt to sell quantity shares out of the available inventory. The sale is all-or-nothing: if at least quantity shares are available at the moment the sale is applied, deduct exactly quantity shares and return true; otherwise leave the inventory unchanged and return false. Partial fills are not allowed.

Correctness requirements

Your implementation must remain correct when an arbitrary number of threads call buy and sell simultaneously on the same StockInventory object:

  1. No overselling. The available share count must never become negative at any point in time. Two concurrent sell calls must never both succeed if the inventory can only cover one of them.
  2. No lost updates. Every buy and every successful sell must be reflected exactly once in the inventory. After all threads finish, the final count must equal initial + (sum of all bought quantities) - (sum of all successfully sold quantities).
  3. Atomic check-then-act. The availability check inside sell and the corresponding deduction must happen atomically with respect to all other buy and sell calls — no other operation may interleave between the check and the update.

You may use the standard synchronization primitives provided by your language's library (mutexes/locks, atomic operations, condition variables, etc.). Keep the critical sections as small as correctness allows.

Example

Single-threaded illustration of the required semantics:

inv = StockInventory(100)
inv.sell(30) -> true (70 shares remain)
inv.sell(80) -> false (only 70 available; inventory unchanged at 70)
inv.buy(50) -> 120 (70 + 50)
inv.sell(120) -> true (0 shares remain)
inv.sell(1) -> false (inventory unchanged at 0)

Under concurrency, any interleaving of the calls is acceptable as long as every individual operation is atomic, the no-overselling invariant holds, and the final count is consistent with the set of operations that succeeded.

Constraints

  • 0 <= initial <= 10^9
  • 1 <= quantity <= 10^9 for every buy and sell call
  • Up to 10^5 total operations, issued from up to 64 concurrent threads
  • Each operation should complete in amortized $O(1)$ time outside of contention for the synchronization primitive