I don’t remember all the course elements, but I do remember the final project. We had to create a program that employed mutual exclusion algorithms to protect a “critical area.” In other words, no matter how many copies of the program we were running, it had to ensure that only one could access that “critical area” at any one time. The others would have to wait their turn.
I didn’t realize it at the time, but this mutual exclusion is a critical part of computer science. Without proper mutual exclusion, services web developers rely on every day wouldn’t function as expected. Databases would lose data points and log files would overwrite each other. Logically, it makes perfect sense. If a process is working on something, it has to have some assurance that another process isn’t going to come in and change what it’s doing at the same time.
In the past couple of months, more than one project has come across my desk that had bugs as a result of improper mutual exclusion. All of them were HTML5 based systems using JavaScript. It seems web developers, no matter how experienced, are in danger of falling victim to this problem.
In current browser implementations, JavaScript is single threaded. A webpage cannot have multiple JavaScript processes running simultaneously. The engine is only ever performing one action at any given time. This is most likely the reason that web developers don’t think about mutual exclusion. No possibility exists of two different processes changing something they both need at the exact same time.
However, mutual exclusion does become important when creating asynchronous functions in JavaScript using callbacks. This is because JavaScript continues to execute any other function it is asked to while the callback either waits for its response or timeout/interval.
The best example I have of this is a custom slideshow we developed. Since it was running on very low-end hardware, we developed it entirely from scratch. That way we could ensure it was as lean as possible. The slideshow had two issues with mutual exclusion.
The first issue related to a countdown timer and transitions. When moving from one slide to the next, the first one faded out while the second one faded in. Users could control the slideshow with navigation buttons, but there was also a visible countdown timer that would transition slides after a set period of time.
The problem with the navigation buttons was that there was nothing stopping a user from clicking navigation buttons while the slides were fading in and out. Remember, as JavaScript is asynchronous, other functions can execute while timeouts are waiting to run. The result was a new timeout countdown timer starting each time the transition finished. Multiple timers would start running and conflict with each other, both visibly and functionally. This type of race condition is a textbook example of mutual exclusion.
The second problem was a bit different. The slideshow needed to automatically check the server for new slides. To create a clean look, we decided to check on transition. After a set number of slide transitions happened, the check for new slides would execute.
The problem again related to the navigation buttons. If a user clicked the navigation on the slide transition where a check was needed, but then clicked again before the AJAX call from the server was returned, two AJAX calls would happen. In the event of new slides, this would result in the slideshow restarting, then restarting a second later.
One relatively simple program exhibited two mutual exclusion issues. The first was a result of a timeout function and the second an AJAX function. These asynchronous functions are where mutual exclusion is most needed. These were relatively simple checks to make sure only one AJAX or transition call could happen at a time.
It makes perfect sense in hindsight. The problem is that a lot of web developers don’t think about it ahead of time. Sometimes it helps to get back to basics.