Technique: Semaphores

For the past year(!) or so I’ve had a series of articles I meant to write about some intermediate programming concepts that can allow you to write code at a higher level. I regret that it’s taken so long to come around to them, but I hope they can be of some use. This series will cover semaphores, asynchronous tokens, and green threads, and I may extend it with some brief notes on assertions and iterators. Today I’d like to talk about semaphores.

Part The First, In Which There is Much Hemming and Hawing

First, allow me a word of philosophical introduction. None of these concepts is very complicated, and even if you’re unfamiliar with the terminology, you are likely to have programmed something similar to them yourself. However, ingesting them conceptually and being able to use them in an encapsulated way makes code you write more high-level, and thus more powerful and more succinct, and to me, more beautiful. For any given programming language, some concepts are encoded in the language itself, but all other concepts must be built on top of the language. For example, the idea of objects is built into ActionScript 3.0 with keywords like new and class, but a concept like a loading queue is built using the language. For every language there is a line where this distinction is drawn, making for interesting languages like JavaScript where you are free to simulate Object Oriented programming in your own way with your own syntax by using closures. However, there is no Turing complete language – which is to say, no language you’re likely to use on a computer – for which there is a line defining things you simply cannot build. And the point here is that the distinction between things the language provides and things you build yourself is only the syntax, and how it runs. Furthermore, perhaps I am biased to say so, but I believe that OOP gives us fairly good syntax with which to define our own ways of doing things. What I’m coming to here, is that you can make a language smarter – and make it look natural doing it – and in this I take great joy.

So we come to my series of topics for some intermediate computer science topics. My articles have this in common: They cover features that could have been in the language, but since they aren’t, we can create our own versions which are no less, or only slightly less, useable and readable. By encapsulating these concepts in objects rather than solving them differently in different places, we are freed from again thinking of that specific problem, and code which relies on the concept becomes beautifully concise.

If you are a student of design patterns, you may well read this introduction and think that I will be writing a series of articles on patterns. However, I am reluctant to call these topics patterns. Perhaps this is because their use was understood far before patterns, or because I learned of them far before patterns. Or perhaps it has something to do with the fact that you can build these structures once and use them, so that they are more like utilities. However, if you want to call them patterns, go ahead. I think patterns serve the same purpose. They are ways to organize ideas in a higher level than the syntax of the language itself; and indeed some languages have no need for some patterns because the pattern is behind that invisible line of “things you can do inside the language.”

So, You Mentioned Semaphores…

At any rate, today’s topic is the humble semaphore. Semaphore is one of those glorious Computer Science words that makes me feel good about having spent 4 years at a prestigious university. Actually there are two categories of such words: inscrutable names for simple things, and inscrutable names for quite difficult things. Semaphore is one of the former. (NP-Complete is one of the latter.)

A semaphore in CS-speak is just a lock. Let’s not forget that semaphores are also signals made with flags in two arms to transmit information at a distance. It’s quite probable that the original meaning inspired the name for a lock, so let’s use it as an analogy.

These objects are quite old and venerable, because once our hardware and software was slow and not quite so parallel as it is now, and we used locks as a solution. Let’s say you have an old 15MB hard drive in your computer. Process A wants to write some data to the disk. Process B also wants to write some data to the disk. With nobody watching, they both go ahead and start writing merrily away. If you’re lucky, everything goes fine. If you’re not, they both end up writing to the same spot on the disk and one, or both, gets hosed. So venerable old operating systems put in a gatekeeper, and that’s your semaphore. Instead of saying

disk.write();

You now check with the gatekeeper.

while (disk.isLocked())
{
	wait();
}
disk.lock();
disk.write();
disk.unlock();

Okay, so in our imaginations let’s picture a little man with bronzed skin and zinc on his nose, sitting on his tall chair on the disk, and he signals by crossing his arms with two flags: “Sorry buddy, disk is closed for business right now. Some other fella is using it.” You check up later and he traces out the path of a circle, one hemisphere with each arm, from top to bottom: “Coast is clear, go ahead!” I guess semaphore kinda works as an analogy. But it’s really just easier to call it a lock.

No matter what you call it, a semaphore has only one purpose: prevent concurrent access.

What Are They Good For?

Thankfully we don’t have to lock down the entire disk when we’re writing data nowadays, though the physical limitations of the device really do prevent us from writing two bits of data to two locations at the same time. Operating systems are smarter than this now and will do all the queueing, and indeed often journaling, of disk writes for you.

However, we run into plenty of other situations where we still lock things down unequivocally while in use. For instance, some tools will lock a database from being accessed while writing to it.

You might want to use a semaphore for…

  1. preventing actions which interfere with a process in motion
  2. objects that can’t be used by two simultaneous clients
  3. processes that take multiple steps over a period of time

And here’s the thing… they’re pretty useful in websites too. I usually want to prevent any kind of navigations when a transition is in progress, or when the next section is loading. You want to prevent input into a form while a form is submitting. Because web programming is so event-driven, asynchronous, and animation-heavy, it often makes sense to lock actions or objects for a short period of time.

Does It Really Need Its Own Class?

But wouldn’t a simple “locked” member variable do the trick, you might ask? Well, you can do it that way, but you can also add some useful features to the basic implementation of a semaphore and suddenly it becomes a lot more powerful. For instance, you could allow multiple reservations. You could track who has a reservation on the shared resource. You could broadcast an event when an object becomes available or unavailable. Here I will present two implementations of a semaphore. First, the abstract base class, so that we can use either implementation at our will.

Abstract Semaphore

package com.partlyhuman.semaphore
{
	import flash.events.EventDispatcher;

	[Event(type="com.partlyhuman.semaphore.SemaphoreEvent", name="available")]
	[Event(type="com.partlyhuman.semaphore.SemaphoreEvent", name="unavailable")]
	public class Semaphore extends EventDispatcher
	{
		protected var _description:String;
		public function get description():String {return _description;}

		protected var _owner:Object;
		public function get owner():Object {return _owner;}

		public function Semaphore(owner:Object = null, description:String = null)
		{
			_owner = owner;
			_description = description;
			initialize();
		}

		protected function initialize():void
		{
			throw new Error("Abstract Method");
		}

		public function get locks():int
		{
			throw new Error("Abstract Method");
			return 0;
		}

		public function get isLocked():Boolean
		{
			throw new Error("Abstract Method");
			return false;
		}

		override public function toString():String
		{
			return "[Semaphore " + description + "]";
		}
	}
}

And the semaphore events,

package com.partlyhuman.semaphore
{
	import flash.events.Event;

	public class SemaphoreEvent extends Event
	{
		public static const AVAILABLE:String = "semaphoreAvailable";
		public static const UNAVAILABLE:String = "semaphoreUnavailable";

		public function SemaphoreEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}

	}
}

A Basic Implementation

This implementation merely uses a counter. Straightforward, but once you start using semaphore objects to lock your navigation or your input fields, you will start to see how this simple object can untangle your state logic.

package com.partlyhuman.semaphore
{
	public class SimpleSemaphore extends Semaphore
	{
		protected var _locks:int = 0;

		public function SimpleSemaphore(owner:Object = null, description:String = null)
		{
			super(owner, description);
		}

		override protected function initialize():void
		{
			_locks = 0;
		}

		override public function get locks():int
		{
			return _locks;
		}

		override public function get isLocked():Boolean
		{
			return (_locks > 0);
		}

		public function lock():void
		{
			if (_locks++ == 0)
			{
				dispatchEvent(new SemaphoreEvent(SemaphoreEvent.UNAVAILABLE));
			}
		}

		public function unlock():void
		{
			_locks--;
			if (locks < 0)
			{
				throw new Error("Semaphore Asymmetrically Unlocked!");
				locks = 0;
			} else if (locks == 0) {
				dispatchEvent(new SemaphoreEvent(SemaphoreEvent.AVAILABLE));
			}
		}
	}
}

An Alternative Implementation

In this implementation, you get a receipt for your reservation. If you happen to need to hold on to a shared resource through a multi-step process that involves many objects, you might end up with a lot of dependencies. This implementation solves the problem by allowing you to pass around a lock object. Any code with a reference to the lock can release it, without requiring a dependency on the source of the lock itself.

package com.partlyhuman.semaphore
{
	public class Lock
	{
		protected var owner:TokenSemaphore;

		public function Lock(owner:TokenSemaphore)
		{
			this.owner = owner;
		}

		public function release():void
		{
			owner.releaseLock(this);
		}
	}
}
package com.partlyhuman.semaphore
{
	public class TokenSemaphore extends Semaphore
	{
		protected var tokenPool:Array;

		public function TokenSemaphore(owner:Object = null, description:String = null)
		{
			super(owner, description);
		}

		override protected function initialize():void
		{
			tokenPool = new Array();
		}

		override public function get locks():int
		{
			return tokenPool.length;
		}

		override public function get isLocked():Boolean
		{
			return (tokenPool.length > 0);
		}

		public function getLock():Lock
		{
			if (locks == 0)
			{
				dispatchEvent(new SemaphoreEvent(SemaphoreEvent.UNAVAILABLE));
			}

			var lock:Lock = new Lock(this);
			tokenPool.push(lock);
			return lock;
		}

		internal function releaseLock(lock:Lock):void
		{
			var index:int = tokenPool.indexOf(lock);
			if (index == -1)
			{
				throw new Error("TokenSemaphore: a lock was released that could not be found");
				return;
			}

			tokenPool.splice(index, 1);
			lock = null;

			if (!isLocked) dispatchEvent(new SemaphoreEvent(SemaphoreEvent.AVAILABLE));
		}

	}
}

Conclusion

Using semaphores in your code is a great way to avoid complicated state logic. You can move the logic of tracking usage to the semaphore object, and handle the results of locking or unlocking only when it has an effect. Though a simple concept, using it as a tool in your toolkit can make your code beautiful and concise. And you can design additional functionality into your own implementation. For example, I will soon explain a semaphore which binds to an asynchronous token... or you could write a semaphore which actually queued up requests.

Let me know what you think, and see you soon with the next installment!

This entry was posted in AS3, Programming, Tutorial. Bookmark the permalink.

15 Responses to Technique: Semaphores

  1. majman says:

    smaaaaaaaart!

  2. MorphX says:

    Really c00l :D

  3. Kyle Simpson says:

    As a fellow CS geek… I should also point out that the above is not really a *true* semaphore, except in the sense that the script interpreter is *not* truly parallel, and thus will not execute two parts of a single command at the same time, anyway, with or without a “higher” level locking system like the one you present.

    True semaphores were/are really used to prevent much more low level things, such as the “atomic” set of multiple machine instructions during a variable assignment, from being partially interrupted by a parallel set of instructions being executed on the same registers or memory space (such as a comparison on a shared variable from a different thread right in the middle of that variable being assigned by another thread). But, true semaphores are not something that more than a handful of low-level languages even fully implement, which means the ability to guarantee no race-conditions is difficult to achive.

    The above is very helpful, especially in languages like AS and JS, which can implement semi-parallelism, where timers and blocks of code can cause the interpreter to jump around, and even be recursive/re-entrant. But actually, the concept here is more like a Semi-semaphore. :)

    Still a valuable concept to get your head around. I used a similar concept in a flash project a few years back where I had an “Interval Manager” which managed all my setIntervals and managed which things were allowed to fire and which things were “locked” from firing while other things were running, etc.

  4. @Kyle, good point, why don’t we call it a meta-semaphore… a metaphor? Kidding.

    It’s a higher-level semaphore for a higher-level language. As you rightly say, just because the AVM doesn’t execute ActionScript code in parallel doesn’t mean the concept is any less useful. It is an asynchronous language, and the order of code execution can sometimes be unpredictable if not parallel, for example code triggered by mouse events.

    I have had a fun time experimenting with alternate ways of representing and dealing with asynchronous code in my applications, and this semaphore pattern works well together with my asynchronous token pattern as well. Keep an eye out for that article!

  5. F.Tamy says:

    Your idea is fine and cute, Roger Braunstein!
    Thanks,
    F.Tamy

  6. Mims Wright says:

    I may be wrong here but i think you might have a typo. In your basic implementation…

    public function lock():void {
    if (_locks++ == 0) {
    dispatchEvent(new SemaphoreEvent(SemaphoreEvent.UNAVAILABLE));
    }
    }

    Shouldn’t that be
    if (_locks++ >= 0) ?

    or if i’m wrong maybe it would be more clear to say
    _locks ++
    if (_locks == 1) {

  7. Hey Mims,

    The idea was to broadcast that the semaphore is becoming locked as it locks. This implementation allows you to lock it while it’s already locked, if you choose… but it’s not actually turning to the locked state unless it’s coming from 0 locks. That’s why i do _locks++ == 0. This is equivalent to doing

    if (_locks == 0) { ... }
    _locks++;
    

    In other words, if you had 0 locks to begin with and you’re locking it, tell everyone you’re locked, but also increment the lock count after you check.

  8. Mims Wright says:

    Roger,

    I finally got around to reading this. It looks pretty useful and my brain is already swimming with other, wackier implementations of Semaphore.

    I have some locks in KitchenSync already and i think this could beef it up. It would help the function sequences that already exist act even more like full-fledged command queues.

    Great stuff!

  9. Hed says:

    I’m Just returning to flash after some years,
    Can you do a blocking method like you’ve written:
    wait();
    and continuing to perform the sript after some event happens?

  10. @Hed,

    Nope, scripts are executed synchronously without blocking. There are some methods in the API that execute asynchronously, such as the timer or network access. But in general all code is executed every frame from start to finish.

  11. Kekoav says:

    I think it’s an interesting solution, but, coming from threaded languages, I want to do a blocking wait on a semaphore, without resorting to a spin lock(or busy wait). I don’t see how this is any different from using Events directly to notify and talk to each other.

    I like events, but having a true semaphore would be very helpful to sync up the few async calls without having to mess with a slew of events.

    Semaphores are much more useful than a single lock, it should be noted that the one use case focused on this article is really a Mutex, or Binary semaphore. Semaphores really get interesting when combined to solve interesting problems, e.g. reader/writers producer/consumer, etc.

    If I have a semaphore I want to wait() on it or it’s really just masquerading as a semaphore and is some other animal. I wish that AS3 could do it.

  12. Pingback: Synchronization Techniques for Flash & AS3: Part I – The Semaphore- Touch My Blog

  13. Wilson Silva says:

    Shouldn’t it be:

    while (disk.isLocked())
    {
    wait();
    }
    disk.unlock();
    disk.write();
    disk.lock();

  14. Hey Wilson, actually not quite! First, the while loop waits until the disk is unlocked for use… so as soon as you are done that loop it’s just been unlocked; no need to unlock it. Secondly, you want to lock the disk so nobody else can use it while you’re doing your stuff. When the lock is on this signals that someone is using the resource, and that nobody else should step on his/her feet. Put another way, it guarantees that one person has access to the resource at any time, not that nobody has access.

    So recap, the while loop either sees that no lock is present and skips ahead, or waits until there is no lock… then you lock the resource for your own use, do what you need, and unlock it so someone else can grab it.

    Also @Kekoav, please note that the infinite while loop is just pseudocode. Since Flash is naturally single-threaded, you should never actually do that or it will hang. But you and Kyle are absolutely right that these are concepts implemented at the user level, to get some of the benefits of the thing without the real thing itself. I don’t pretend that this user-level code is as dependable or useful as a lower-level or real threadable semaphore, but neither is it entirely useless!

  15. Hi, I would like to ask if it’s possible to include a example of usage. Let’s say for example that we have two timers storing in a shared arraycollection the return values of a remote call. Before storing new elements in the array, they must do some preprocessing in order to avoid duplicate items and update old ones. The processes of storing new elements should be synchronized. I can’t understand if I should set up event listeners or something like that and , in that case, is it possible that we fall into starvation?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>