The question uses C to formulate the problem, but solutions in any language can be accepted. At the end of the question you can find a pythonic version.
As part of a bigger software, we have a C function:
void wrapper(void* p);
Which is a wrapper function around the C function:
void handler(void* p);
wrapper() is triggered by an external effect (hardware interrupt, unix signal, it doesn't matter), which also provides a
p as its argument. The triggering can happen after the execution of any CPU instruction, even if
handler() is being executed. The triggering happens similarly to the interrupt requests in a cpu, or to the signal handlers in unix: after
wrapper() returns, the execution continues from exactly the same CPU state as the triggering found it.
We don't know what does
handler(), but we know that it doesn't interact with the triggering mechanism.
As a help, we have
void swap(void **a, void **b);
which swaps the contents of two
void* pointers in a single cpu instruction.
The solution of the problem is a
wrapper() function, which "serializes"
handler(): it makes sure, that
handler() never is called in multiple times: every new
handler() will be called only after the previous returned.
Trigger can't be lost: for every
wrapper(), exactly one
handler() must be called. It shouldn't be called from the same
wrapper() which was actually triggered, but
p can't be lost.
Similar solutions in practical situations often block the triggering for a short period, here it is impossible.
==(void*)0) can be used, but out of it, no library calls. These functions are considered reentrant (if triggering happens in them, they will work correctly). Especially any direct or indirect usage of any multithreading isn't allowed.
There is no speed criteria for
wrapper(), but it must be ready in a finite time. We know, that the triggering won't happen so fast to cause some hardware/stack/etc. overflow.
wrapper() must handle any deep of reentrancy.
Any language can be used for the task (incl. asm or interpreted languages), but no language construction or api calls which would make the task essentially easier, as in C.
handler() calls don't need to happen in the same order as their corresponding
wrapper() was triggered.
The solution is an implementation of
Analogous problem in Python:
Look for the signal handlers module in python (here). This what this problem is about. Essentially, we have a
def wrapper(p): ..the solution...
What is a wrapper around
def handler(p): ...we don't know what is here...
The execution of
wrapper is triggered by an external event. It means, that it can happen any time, even while we are in
wrapper or in
handler. If the triggering event happens,
wrapper will be called. After it returns, the normal program execution will continue. The task is to implement
wrapper in a such way, that
handler won't run multiple times in the same moment. I.e. the next
handler will be called only after the previous returned, even if the triggering happens while the
wrapper(s) of previous trigger(s) run.
No python API call or external module can be used, with the single exeception of a
def swap(a, b):
...which exchanges the values of
b in a single instruction (equivalent of a
tmp = a; a = b; b = tmp, but it does in a single moment, without a triggering would be happened).
Bonus Python problem: the internal Python functions which handles the lists, dicts, etc, they all aren't reentrant. This means, if you call
mylist.append(), it is possible that the trigger will happen during the execution of the
append() method. If another
handler() try to do anything with a such list, your program will segfault and solution won't be accepted. The case is the same for every higher-level language. In short: if you use _any_ higher level language construct, i.e. list/dict/object/etc, you need to make sure that it won't be used by any other
wrapper which is triggered meanwhile.
The objective win criterion:
Afaik, this is a hard problem. If anybody can find a solution, is already a big success (to me was it around a week to find one), and on my opinion, there won't be a big difference between the different results. But it is required here to have an "objective winning" criterion, and so, lets say "the shortest solution in bytes" is the winner.