SECRET OF CSS

How to Prevent Code Execution Till All Triggers Are Down (or Timer Is Expired) | by Vishal Ratna | Jun, 2022


Plant a MultiTriggerBomb

0*Z8O 6aXKT
Photo by Jason Mitrione on Unsplash

The last time you were in a situation where you needed to prevent (not wait for) the execution of a code until n conditions were true, or see what happened if the n conditions did not become true and execute the code anyway after a deadline is expired — how did you solve the problem?

The first solution that comes to mind when you look at this problem is Java’s CountDownLatch. Countdown latch has an await() call that makes the calling thread wait until n number of countDowns() are called. Here, we do not want the thread to wait. It is more like we are using the application and when ’n’ events happen, we fire a piece of code, or if a deadline is overridden, we execute the code anyways.

The requirement is that the conditions can become true on multiple threads, but we have to ensure that the code should not execute more than once. We have to handle the case when timer expiration and condition matching happens at the same time and make sure it doesn’t fire the code multiple times or get into an unstable state.

We can build a solution by using if-else checks and booleans to maintain the states of the conditions. But, we encounter these kinds of situations multiple times while working at Microsoft Teams. So, that is not scalable, and it is prone to errors.

One example could be that we want to prevent some non-critical piece of code (such as prefetch images, refreshing auth tokens, download user avatars, etc.) until some critical code paths are completed(some critical paths are, 1. Screen gets rendered, 2. The message count has updated, and 3. Current screen has finished syncing) that is tracked as critical OKRs of the app and impact overall app performance. So basically we hold on other pieces of work till 1,2 and, 3 are done. And what if some errors surface out and one or all of the steps fail to happen, then the timeout helps us executing the code.

If your calls are tied to lifecycle callbacks of android, we have build a construct called LazyLifecycleCallbacks, that you can check here, that has onLazyCreate(), onLazyStart(), and onLazyResume().We have built this construct using a concept called Barrier, but soon realised that barrier has its limitations so we re-implemented it using MultiTriggerBomb.

Coming back to the topic, we needed something generic that will work on multiple conditions, is thread safe, and can be onboarded by developers easily. Moreover, is properly tested so that we can trust the functionality.

This use case looked like a multi-trigger bomb. A bomb has multiple triggers to be pressed before the bomb is exploded and has a timer attached to it. It looks something like this:

* |-----------------------------------------------|
* | |
* | [s1] --- [s2] --- [s3] ---[s4] --- [s5] --- | ----> [CHARGE]
* | | ^
* |------------|----------------------------------| |
* | |
* |________________t seconds__________________|

I felt it would be great if I can make an API surface that would look similar to a real bomb’s way of interaction. Developers will be able to easily relate to it and a new jargon will be introduced to solve this kind of problem. So the exposed APIs were the following:

var hasExploded = false
private set
var isPlanted = false
private set
private val charge: () -> Unitfun plant()
fun down(cause: String = "")
fun tryDiffuse()
private fun explode()

Just like a real one, once a bomb is created it has a charge planted in it, you can plant() it, at the same time the timer also kicks off. To press the trigger, you need a down() button. And once all the triggers are down or the timer is expired, the bomb is exploded. The bomb can also be diffused() before the explosion takes place.

Now I knew all the contracts that the bomb needs to adhere and we all can relate to it. Isn’t it? Then I wrote the actual implementation.

MultiTriggerBomb.kt

To use this, we need to write a simple code:

// Create a bomb.
multiTriggerBomb = MultiTriggerBomb(5, timerDuration) {
// Code that needs to get fired on explosion
}
// plant it!
multiTriggerBomb?.plant()

Whenever one of the conditions becomes true, call down() with the optional reason param.

multiTriggerBomb?.down("some condition satisfied")

When all the conditions become true or the timer expires, our bomb will explode and execute the code that is planted inside it.

That is how we planted a bomb in our code base!

Pro Tip — You can use bomb to connect multiple callbacks/events/checks in the codebase and fire a piece of code and get the state management and thread-safety out of the box.



News Credit

%d bloggers like this: