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 wrapper()
or 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.
If needed, malloc()
, free()
and NULL
(==(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.
The handler()
calls don't need to happen in the same order as their corresponding wrapper()
was triggered.
The solution is an implementation of wrapper()
.
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 a
and 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 wrapper()
or 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.