Timer class implementation
I'm a bit hesitant to comment on anything related to concurrency, but I'll give it a try:


Why does Dispose wait for runTask to finish? To me, that's a rather surprising side-effect. I'd expect Dispose to stop the timer, and to abort the current invocation if possible, but not to block.
Letting a callback dispose the CompletionTimer that's running it sounds like a bad idea - it seems like an inverted ownership situation.
The deadlock seems fairly straightforward: RunInternal invokes callback asynchronously, so the rest of the code is registered as a continuation and will be run when callback finishes. However, callback disposes the CompletionTimer, which waits for runTask (RunInternal) to finish. The callback and the run task are now both waiting for each other to finish.
If you want the callback to be able to stop the timer, perhaps changing its return type to Task&lt;bool&gt; is a better idea: the timer can then terminate its loop after the callback returns false.
The requiresAtLeastOne construction looks a little confusing, with it being compared against a few 'magic values'. A requiredInvocationsLeft field that is decremented after each invocation seems a little easier to grasp - and would also allow you to configure the number of required invocations after a wake-up.
Perhaps SkipCurrentDelay is a more descriptive name than WakeUp. WakeUp could be interpreted as the timer being in sleep mode (as in, it's paused, it's not periodically invoking its callback).
You may want to document the intended behavior of your class. As my comments may show, other people likely have different assumptions about how a timer will behave.


Creating a separate StopAndWaitForCompletionAsync method is probably a better idea if you need to wait for the last invocation to finish.