Reported September 2026
OpenAIsimulation

Modal Lock and Fair Modal Lock

Reported by candidates from OpenAI's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.

Get StealthCoderRuns invisibly during the live OpenAI OA. Under 2s to a working solution.
Founder's read

The OpenAI OA reported in September 2026 is a pure simulation problem, and the mistake that sinks a first attempt is treating the two locks as one set of rules. You get a compatibility matrix, two independent locks, and a stream of acquire and release operations. The only difference between the locks is whether a new request can barge past the queue. It reads like a systems question but it's bookkeeping. If you blank on the release logic, StealthCoder is the safety net running invisibly during the live OA. Still, the model is small enough to hold in your head tonight.

The problem

Simulate two independent modal locks. Lock type 0 is a non-fair modal lock, and lock type 1 is a fair modal lock. A symmetric matrix compatibility describes which requested modes may be held together: mode a is compatible with held mode b when compatibility[a][b] == 1.
Each operation is [lockType, action, threadId, mode], where action == 0 requests acquisition and action == 1 releases a held mode. The two locks have separate holders and FIFO wait queues.
An acquisition is compatible only when its requested mode is compatible with every currently held mode.
For the non-fair lock, a compatible new request is granted immediately even when older requests are queued. Otherwise it joins the queue.
For the fair lock, a new request is granted immediately only when it is compatible and the queue is empty. Otherwise it joins the queue.
After a release, inspect the queue from the front. Grant and remove compatible requests in FIFO order, updating the holder set after each grant. Stop at the first request that is not compatible.
Return one row per operation. The row contains, in grant order, every thread whose acquisition became granted because of that operation. A queued acquisition or a release that grants nobody contributes an empty row.

Function
simulateModalLocks(compatibility: int[][], operations: int[][]) → int[][]

Examples
Example 1
compatibility = [[1,1],[1,0]]
operations = [[0,0,1,1],[0,0,2,0],[0,0,3,1],[0,1,1,1]]
return = [[1],[2],[],[3]]
Threads 1 and 2 hold compatible modes. Thread 3 waits because mode 1 conflicts with the same held mode, then is granted when thread 1 releases.
Example 2
compatibility = [[1,1],[1,0]]
operations = [[1,0,1,1],[1,0,2,1],[1,0,3,0],[1,1,1,1]]
return = [[1],[],[],[2,3]]
On the fair lock, thread 3 queues behind thread 2 even though its mode is compatible with the current holder. The release grants the compatible FIFO prefix 2, 3.
Example 3
compatibility = [[1,1],[1,0]]
operations = [[0,0,1,1],[0,0,2,1],[0,0,3,0],[0,1,1,1]]
return = [[1],[],[3],[2]]
The non-fair lock lets compatible thread 3 barge ahead of queued thread 2. After thread 1 releases, thread 2 becomes compatible with the remaining holder and is granted.

Constraints
1 <= compatibility.length <= 20.
compatibility is square, symmetric, and contains only 0 or 1.
1 <= operations.length <= 2000.
Every operation has four integers and uses a valid lock type, action, and mode.
Within each lock, a thread has at most one held or pending request at a time.
Every release names a thread that currently holds the stated mode on that lock.

Reported by candidates. Source: FastPrep

Pattern and pitfall

Keep a holder collection and a FIFO queue per lock. Index them by lockType so the code path is shared. Acquire: check the requested mode against every held mode using the matrix. Non-fair grants if compatible, period. Fair grants only if compatible and the queue is empty. Otherwise enqueue. Release: remove the thread's held mode, then scan the queue from the front. Grant each compatible request, add it to holders immediately, and stop at the first incompatible one. Don't skip past it, that's the classic pitfall, and it breaks the fair semantics. Also update holders after each grant, since the next check depends on it. Example 3 shows barging, where thread 3 jumps ahead of queued thread 2. With 20 modes and 2000 operations, brute force is fine. If you freeze on the release loop, StealthCoder can hand you the structure live, but the logic is just careful simulation.

The honest play: practice the pattern, and have StealthCoder ready for the one you didn't see coming.

If this hits your live OA

You can drill Modal Lock and Fair Modal Lock cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play.

Get StealthCoder

Related leaked OAs

⏵ The honest play

You've seen the question. Make sure you actually pass OpenAI's OA.

OpenAI reuses patterns across OAs. Built for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play. Works on HackerRank, CodeSignal, CoderPad, and Karat.

Modal Lock and Fair Modal Lock FAQ

How hard is the Modal Lock problem really?+

Algorithmically it's easy. There's no clever data structure, just two queues and a holder list. The difficulty is reading the rules precisely and not mixing fair and non-fair behavior. Most failures come from the release scan, not the acquire step.

What's the trick to the release step?+

After removing the released holder, walk the queue from the front. Grant each request that's compatible with the current holders, adding it to holders right away. Stop at the first request that isn't compatible. Never skip it to find a later compatible one, even on the non-fair lock.

What's the difference between fair and non-fair here?+

Only the acquire path differs. Non-fair grants any compatible new request immediately, even with older ones queued. Fair grants only when compatible and the queue is empty. Release behavior is identical for both, using the FIFO prefix rule.

How do I structure the output rows?+

Return one row per operation, in input order. An acquire that's granted immediately gives a row with that thread. A queued acquire gives an empty row. A release gives the threads it granted, in grant order, or an empty row if nobody was granted.

How do I prepare for this in 48 hours?+

Hand-trace the three examples until you can predict each row. Then write the simulation with locks stored in an array of two objects. Test edge cases: empty queue release, a blocked head with compatible threads behind it, and a release that grants several threads.

Problem reported by candidates from a real Online Assessment. Sourced from a publicly-available candidate-aggregated repository. Not affiliated with OpenAI.

OA at OpenAI?
Invisible during screen share
Get it