Understanding the "cost" of cflock, part 1
And I wanted to offer some additional thoughts--first planning to offer them as a comment--because there's a lot behind the question and his observations. But as it got longer, I realized it was too long for a comment. Also, I didn't want people to think (in reading a comment on Ben's blog) that I was challenging Ben or questioning his understanding of the matter! Not at all. :-) Instead, I was just wanting to add more context, to help other readers, and based on my years of observing the community.
What I offer here is pretty much exactly what I wrote, but I have added headings, to help readers here:
Interesting thought experiment, Ben. If you'd been taking bets, I'd have put down good money that there'd be virtually NO impact in this scenario. The reason I say that may add some value to this post, whether one is using Lucee or ACF.I find that many people do think (or fear) that cf locks do things which "may be expensive"--or conversely they give no attention and use them only because they vaguely recall they're "supposed to" in some situations.
What locks DO do
To clarify for some readers, a cflock does not "lock code" nor does it "lock scopes". Like you said, it can "essentially single-threads access to a given block of code". Nicely worded. :-)A cf lock is a "gentlemen's agreement" (pardon my boomerism): when the cflock is hit, it causes cf to simply check, "is anyone else holding a *contending* lock of this NAME or Scope, for this TYPE?" That's really it.
If my TYPE is "read", and no one else holds an "exclusive" lock of the same name or scope, I can proceed, otherwise I wait. If my type is "exclusive" and no one else holds a "read" OR "exclusive", I can proceed, otherwise I wait.
How lock timeouts get involved
And the "wait" is controlled by the TIMEOUT atrribute (in the tag) or argument (in script). That's why one is required.Many misconstrue the TIMEOUT as having impact on "how long the thing in the lock can take", but it's not that at all. It's merely how long do I wait for another request (or thread), which is holding the same/contending lock, to let it go.
And if that timeout is exceeded, then I'll throw an error... unless the optional THROWONTIMEOUT is set to "no" or false, etc. Please BEWARE using that. Many do it without any idea of the implications: if you ignore the timeout failure, then the code in your lock will simply NOT BE RUN. Many folks find mysteriously that some variable is not set or some "code has not run", and this is easily a reason why.
We have so little insight into these goings-on in a CF request/s
Sadly, cf gives us zero insight into any of this. I've long lamented that we should have at LEAST optional logging of when a lock timeout occurred, or was ignored this way, or simply how long we have WAITED for or HELD a lock.To me, misuse/misunderstanding of locks is a cancer in much cf code, causing needless holdups or unexpected failures to run code. That's why I felt I should add this.
Some natural next questions
Of course, the next question some would ask would be "well, when should I or should I NOT use locks". And when should I use read or exclusive types? And others may ask, "I thought the SCOPE lock locked access to the given Scope. You're saying it doesn't?" It does not. Finally, when might you use a NAME lock instead?[Those are all fodder for a part 2 post. :-) And in the interest of time, I do want to get this out, as-is, as a part 1, thus this post. Ibhope this part 1 helps some, while news of a part 2 may whet the whistle of others.]
Bottom line
But bottom line to Ben's original post: nope, there's no reason a lock ITSELF should "take time". It's just does a check to say "does anyone hold a contending lock of the kind I want"? That alone is a trivial matter. Any delay due to the lock will be only if there IS contention FOR that lock.
I welcome comments, whether here or on Ben's post--and if there, preferably only if such a comment there would be written without presuming people HAVE read this post.
For more content like this from Charlie Arehart:Need more help with problems?
- Signup to get his blog posts by email:
- Follow his blog RSS feed
- View the rest of his blog posts
- View his blog posts on the Adobe CF portal
- If you may prefer direct help, rather than digging around here/elsewhere or via comments, he can help via his online consulting services
- See that page for more on how he can help a) over the web, safely and securely, b) usually very quickly, c) teaching you along the way, and d) with satisfaction guaranteed
It would also be great if the ColdFusion documentation could add more detail to what actually is supported in a "synchronized" Struct or Array. For example, the docs state that Structs and Arrays are always synchronized by default. But, I can recreate "concurrent modification exceptions" and "undefined values" by trying to mutate a struct/array that is currently being iterated-over by another thread. To be clear, I think those errors are _valid_ - but, without documentation that is more specific about which actions are / are not "thread safe", it leaves it as an exercise for the developer to run tests like this.
So about the matter of synchronized arrays, it's worth pointing out first that this IS something new (whether arrays may NOT be sync'ed), since CF2016. And it's indeed documented in at least the CFML Reference for arraynew (more below). Before I share it, note that there's no equivalent indication of any such option to control whether structs are implicitly sync'ed or not.
Back to arrays, of course many will not bother with an arraynew, when they can just use the [] nomenclature to create them implicitly. But what that does in creating an array is at least the default behavior of arraynew, so the below is worth noting. :-)
And to save folks going to dig through the docs page there (or for those who may not bother), I'll quote the main points below.
Note that the doc page clarifies how a new OPTIONAL argument was added, that allowed one to make such an array NOT synchronized/NOT thread safe--if they knew what they were doing. We can liken it to how folks using SQL may, for instance, use NOLOCK directives (but let's not go any further here on that!)
Here's the link to the arraynew docs, and the quote of that discussion there about this "isSynchronized" argument:
https://helpx.adobe....
--
"When false, creates an unsynchronized array. This parameter is new in the 2016 release of ColdFusion. When this parameter is true or no parameter is specified, the function returns a synchronized array.
The default value is true.
In ColdFusion 11, and previous versions, arrays were always thread-safe.
But thread safety comes at a cost of synchronizing the array object. In a synchronized array, two or more threads cannot access the array at the same time. The second thread has to wait until the first thread completes its job, resulting in significant performance deterioration.
In ColdFusion (2016 release), you can use an unsynchronized array and let multiple threads access the same array object simultaneously.
You can use unsynchronized arrays in situations where you need not worry about multiple threads accessing the same array. In other words, if you are defining an unsynchronized array in a thread safe object, like a UDF or a CF-Closure, you can use an unsynchronized array.
While a normal array is slower but thread safe, an unsynchronized array is faster by more than 90%."
--
Before leaving this comment, and for any who have read this far, note again that in my post I acknowledge that some will ask still other questions about when/why/when not to bother with locking. I'll address that more in part 2.
> Note, the type "synchronized" is no longer supported and will be ignored; all struct/scopes are "thread safe" since version 4.1.
I don't know if that was a brief, Lucee-specific feature that they rolled-back; or, if that was something they had for some ACF compatibility. But, in either case, Structs are always synced at this point.
But, even so, if you throw enough volume at a Struct, attempting to add/remove keys while also iterating over it, eventually you get a concurrent modification errors (at least in Lucee).
Looking forward to part 2 :D
I have LOTS I could share, and time will tell what I may. I could certainly modify what I'd say by pointing to something you may write, if it may allow me to then focus on still other facets. :-)