Repository navigation
Make a caller wait for a free pid when all of them are taken - #194
Conversation
With every pid in use, ios_nextAvailablePid looped over the table while holding pid_mtx, and the ending commands needed pid_mtx to free their pid, so none was ever freed and ios_waitpid never returned. The caller now gives pid_mtx back and waits on the thread completion condition until a pid is free. tests/pid-admission.m runs 200 callers over 127 pids. It measures the CPU the waiting callers use while the table is full, and a watchdog turns a deadlock into a failure with the stacks of all threads. Cancellation is disabled while the caller waits, so a thread cancelled there does not keep thread_completion_mutex.
|
That fix makes sense, but it got me wondering: is that an issue that you actually met (and in that case, would it make sense to increase the limit to a higher value? and if so which one?) |
|
Yes, in a stress test, not in normal use. I ran 160 commands at once from separate threads on the Simulator, and all 12 configurations I tried without this change froze: the stacks showed the caller spinning in I'd keep the limit at 128. With this change a caller past the limit waits for a free pid instead of hanging, so the limit only caps how many commands run at the same time. Raising it is cheap: a slot costs about 1 KB of static memory, almost all of it |
|
Thanks for the explanation. |
Depends on #192; until it lands this diff also shows its commits, and the new test needs it to finish.
ios_nextAvailablePidhands out one of 127 pids. With all of them taken, the caller looped over the pid table while holdingpid_mtx, which ending commands need to free their slot (cleanup_functionspins onpthread_mutex_trylock(&pid_mtx)). No slot was ever freed: the other callers blocked inpthread_mutex_lock, the ending commands spun, andios_waitpidnever returned.A caller that finds no free slot now releases
pid_mtxand sleeps on the condition variableios_waitpidalready uses, and the broadcast an ending command already sends wakes it. Cancellation is disabled around that wait, since a thread cancelled inpthread_cond_waitwould keep the mutex every ending command takes. Once awake, the caller retakespid_mtx, rereads the parent command's environment (ios_setenvmay have reallocated it meanwhile) and looks for a free slot again.Test
tests/pid-admission.mstarts 200 threads that each runios_fork,ios_system("hold")andios_waitpid.holdblocks until the test releases it, so after 127 commands start, the other 73 callers wait for a pid and nothing ends. The test fails if the process uses more than 0.5 s of CPU over the next second: sleeping callers use none, while a caller that loops on the pid table uses a whole core. A watchdog prints the stacks of all threads (tests/hang-dump.h) and exits 124 after 10 seconds without progress.On the iOS 27 Simulator:
FAIL, 0 of 200 commands finished, no progress for 10 s, using 0.96 to 1.00 s of CPU in the 1 s window, with 72 callers inpthread_mutex_lock.200 commands over 127 pids, wall=2.8s PASStowall=3.1s, with 0.00 s of CPU in the window.dash in a-Shell never holds 127 pids at once: a pipeline takes one pid, and a
&job runs to completion before the next one starts (evalsubshellin dash_iOSsrc/eval.c). So the pid limit is covered by this Simulator test only.