Skip to main content
Ben Nadel at Ellen's Stardust Diner (New York City) with: Colin Silverberg and Carol Loffelmann and Daniel Silverberg
Ben Nadel at Ellen's Stardust Diner (New York City) with: Colin Silverberg Carol Loffelmann Daniel Silverberg

Understanding ReadOnly And Exclusive Named Locks In Lucee CFML 5.3.5.92

By
Published in Comments (2)

The other weekend, as I was reading through the Learn Modern ColdFusion <CFML> in 100 Minutes book by Ortus Solutions, something about the way in which they described the CFLock tag really clicked for me. Historically, my understanding of the different types of locking has been fairly poor. As such, I tend to just lean on exclusive name locks all the time. But, those Ortus chaps really broke down my mental barrier; and I think I get it! So, I wanted to see if I could create a ColdFusion locking demo that would help me convince myself that I finally understand the interplay between ReadOnly and Exclusive named locks in Lucee CFML 5.3.5.92.

When you lock (often called "synchronize") access to shared resources in ColdFusion, you have the option to tell the CFML runtime that the lock is either Exclusive or ReadOnly. ReadOnly locks, as the name implies, are for code that reads but does not modify the shared resource. Exclusive locks, on the other hand, are for when you need to modify the shared resource.

On their own, each type of lock makes abstract sense. But, I've always been a bit fuzzy on what actually happens when the two types of locks start to compete for a shared resource. Here are some facts:

  • Two ReadOnly locks can access the same code at the same time.

  • A ReadOnly lock and an Exclusive lock cannot access the same code at the same time.

  • If a ReadOnly lock is active, an Exclusive lock will block and wait for the ReadOnly lock to be released before the Exclusive assumes exclusive control over the code.

  • If an Exclusive lock is active, a ReadOnly lock will block and wait for the Exclusive lock to be released before the ReadOnly assumes read-only control over the code.

CAUTION: Just because two ReadOnly locks can access the same shared resource at the same time, it doesn't necessarily mean that the given shared resource is inherently "thread safe". For example, having two threads trying to iterate over a shared Array can lead to deadlocks. As such, you still have to exercise proper thread hygiene even when you have synchronization semantics in place.

To see these ColdFusion CFLock rules play out in action, I've created two CFML pages: one that reads a counter; and, one that increments a counter. The one that reads the counter uses a ReadOnly lock; and, the one that increments the counter uses an Exclusive lock.

Both of these CFML pages can run at two different speeds: Fast and Slow. The Slow speed sleeps for 3-seconds before refreshing the page and trying to reacquire the lock.

Here's the ReadOnly CFML page:

<cfscript>

	// Set defaults for URL parameters.
	param name = "url.speed" type = "string" default = "fast";

	// Let's flush some content before we try to enter the lock so that the browser can
	// reset the page content (helps demonstrate when the lock is blocking).
	echo( "<p> Read using: #url.speed# </p>" );
	flush interval = 1;

	lock
		name = "CounterLock"
		type = "readonly"
		timeout = 60
		throwOnTimeout = true
		{

		// For slow speed access, sleep inside the LOCK for a few seconds.
		if ( url.speed == "slow" ) {

			sleep( 3 * 1000 );

		}

		// Echo state to browser.
		echo( application.counter );
		echo( " @ " );
		echo( now().timeFormat( "mm:ss.l" ) );

	}

</cfscript>
<script type="text/javascript">

	// Refresh the browser window (iframe).
	setTimeout(
		() => {

			window.location.reload();

		},
		100
	);

</script>

As you can see, this ColdFusion code is attempting to acquire ReadOnly access to a lock with name CounterLock. Then, within the lock, it reads the value of application.counter.

Now, on the "write" side, we have this ColdFusion page:

<cfscript>

	// Set defaults for URL parameters.
	param name = "url.speed" type = "string" default = "fast";	

	// Let's flush some content before we try to enter the lock so that the browser can
	// reset the page content (helps demonstrate when the lock is blocking).
	echo( "<p> Write using: #url.speed# </p>" );
	flush interval = 1;

	lock
		name = "CounterLock"
		type = "exclusive"
		timeout = 60
		throwOnTimeout = true
		{

		// For slow speed access, sleep inside the LOCK for a few seconds.
		if ( url.speed == "slow" ) {

			sleep( 3 * 1000 );

		}

		// INCREMENT and echo state to browser.
		echo( ++application.counter );
		echo( " @ " );
		echo( now().timeFormat( "mm:ss.l" ) );

	}

</cfscript>
<script type="text/javascript">

	// Refresh the browser window (iframe).
	setTimeout(
		() => {

			window.location.reload();

		},
		100
	);

</script>

This ColdFusion code is basically the same; only, it's trying to acquire an Exclusive lock named CounterLock, from within which it will pre-increment the application.counter value.

And now that we have two ColdFusion pages trying to compete for the same shared-access, let's pit them against each other. To do this, I've created a page that renders three iframe elements: 2 ReadOnly pages, and 1 Exclusive page. We can turn each of these pages on and off; and, adjust the processing speed:

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />

	<title>
		Exploring Read / Write Named Locks in Lucee CFML 5.3.5.92
	</title>

	<link rel="stylesheet" type="text/css" href="./demo.css" />
</head>
<body>

	<div class="panels">
		<div class="panel">
			<div class="actions">
				<a href="./read.cfm?speed=fast" target="read1">Read Fast</a>
				<a href="./read.cfm?speed=slow" target="read1">Slow</a>
				<a href="about:blank" target="read1">x</a>
			</div>
			<iframe name="read1" src="about:blank" class="iframe"></iframe>
		</div>
		<div class="panel">
			<div class="actions">
				<a href="./read.cfm?speed=fast" target="read2">Read Fast</a>
				<a href="./read.cfm?speed=slow" target="read2">Slow</a>
				<a href="about:blank" target="read2">x</a>
			</div>
			<iframe name="read2" src="about:blank" class="iframe"></iframe>
		</div>
		<div class="panel">
			<div class="actions">
				<a href="./write.cfm?speed=fast" target="write1">Write Fast</a>
				<a href="./write.cfm?speed=slow" target="write1">Slow</a>
				<a href="about:blank" target="write1">x</a>
			</div>
			<iframe name="write1" src="about:blank" class="iframe"></iframe>
		</div>
	</div>

</body>
</html>

Because locking is all about time-based contention, understanding how all of this plays-out will be easier if you watch the video. That said, I've tried to put together some animated GIFs to illustrate the interplay.

Two ReadOnly Locks: Fast + Slow

If we run the demo page with just the two ReadOnly locks, we can see that the "fast" one continues to execute quickly while the "slow" one sleeps within the lock:

A fast ReadOnly and slow ReadOnly lock competing for the same named lock in Lucee CFML.

As you can see, the fast ReadOnly lock doesn't care that another ReadOnly lock is hanging - it continues to access and re-access the named lock without any blocking.

One ReadOnly And One Exclusive Lock: Fast + Fast

If we run the demo page with one ReadOnly lock and one Exclusive lock, both running "fast", they will be competing for access; however, they will both be running fast enough such that the competition is mostly unnoticeable:

A fast ReadOnly and fast Exlcusive lock competing for the same named lock in Lucee CFML.

As you can see, both the ReadOnly lock and the Exclusive lock are refreshing at a healthy clip.

One ReadOnly And One Exclusive Lock: Slow Read + Fast Write

Now, let's take a look at what happens when the two named locks really do have to compete for access. In this demo, we're going to write fast but read slow:

A slow ReadOnly and fast Exlcusive lock competing for the same named lock in Lucee CFML.

This is where it starts to get interesting! As you can see, when we introduce a slow ReadOnly lock, it prevents the Exclusive lock from entering the shared code. The Exclusive lock has to block and wait for the ReadOnly lock to be released.

One ReadOnly And One Exclusive Lock: Fast Read + Slow Write

Still using a ReadOnly and an Exclusive lock again, this time we're going to reverse the speed: the read will be fast, but the write will be slow:

A fast ReadOnly and slow Exlcusive lock competing for the same named lock in Lucee CFML.

As you can see, when the slow Exclusive lock is introduced, the fast ReadOnly lock has to block and wait for the Exclusive lock to be released before the ReadOnly lock can acquire access rights.

Again, this is probably easier to follow in the video; but, hopefully these GIFs have helped to illustrate the interplay between ReadOnly and Exclusive locks. It seems silly to me that it took me so long to build a more concrete mental model for locking in ColdFusion; but, it's better late than never.

Want to use code from this post? Check out the license.

Reader Comments

447 Comments

This is a really useful exploration of a topic that I believe is badly understood.

I never found out after many years of research whether SESSION variables need to be locked, so I have always played safe and locked SESSION variables, because I once read an article about an edge case that can cause race conditions when trying to read/write SESSION variables from within different frames [frameset]. Now, I know very few people use framesets anymore, but, ironically, I am working on a legacy application, at the moment, that uses a frameset.

15,841 Comments

@Charles,

Locking is pretty confusing. Especially because, in ColdFusion / Lucee the Struct object is documented (somewhere) thread-safe / synchronized, but the Array is not.

StructNew Documentation says:

Note, the type "synchronized" is no longer supported and will be ignored; all struct/scopes are "thread safe" since version 4.1.

But, even with that, you still occasionally find a java.util.ConcurrentModificationException error being thrown somewhere. And, of course, just because a Struct is thread-safe, it doesn't mean that you won't run into race-conditions when read/writing to values stored in a scope -- it just means things won't "blow up" :D

So, all to say, I agree -- even understanding when to lock Session access is confusing. I am happy that, at the very least, I have a better sense now of how ReadOnly and Exclusive interact. This is probably something I should have learned years ago.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel