Yes, check Process >> #terminate, there's special code for skipping a context if the suspended process was waiting for Semaphore.
The way I see it, the purpose of the caught variable in Semaphore is to not execute the ensure: if the process was suspended (and then terminated)*in* the ensure: method, before block ever executes and one starts waiting on the semaphore.
If one has made it to the wait call, caught is already true, and the ensure block would be executed on termination unwind..
That part is, as I said above, handled directly in Process >> #terminate (near the bottom)
AFAICT, both parts would be need to be reflected for Monitor >> #critical: to work correctly under termination.
At which point, it would seem to me a better idea (if possible) if the terminated context was culled into the ensure block, instead of muddying up Process >> #terminate further, one could then write a few ugly, but localized:
ensure: [:unwoundContext | (caught and: [unwoundContext isWaitContext notl]) ifTrue: [sem signal]]
instead of having to keep adding special case handling to Process >> #terminate.