I was recently tech editing a future article for CFDJ when I came across the following set of code (which has been turned into pseudo-code for brevity's sake):
if not defined("server.foo")
lock:exclusive
create server.foo
end the lock
end if
The idea was - if server.foo doesn't exist, create it, but wrap the creation inside an exclusive lock. However, this code has one problem. Can you see it? Imagine two threads, A and B, come into this page at the same time. They both hit the if not defined line at the same time. It's true for both, so both threads go to the next line. Thread B is ahead, so it gets the lock. It then creates the object and unlocks the code. Thread A then enters the lock as well and ALSO creates the object.
So what happaned? At the time when the threads checked for the object existence, it did not exist, but before the second thread could create it, another thread was ahead of it and made the object. So how would you change it?
if not defined("server.foo")
lock:exclusive
if still not defined,
create server.foo
end the lock
end if
Don't forget that most of the time, users define persistant variables that act as constants, i.e., application.dsn = "foo". In these cases, under MX only, locking is not needed at all.
Archived Comments
What are you using to test your concurrency solutions?
Ray, I think you''ll find that the act of testing for existence is a READ and strictly speaking needs to be locked also :)
I disagree. I think it could be locked, but I don''t think it needs to be locked, and I don''t think it would help. Taking the example of threads A and B again, wouldn''t both be able to get past the read lock since the ob wouldn''t exist? The only reason I can see for locking here would be for CF servers that are not MX.
I blogged something similar in January: http://www.corfield.org/blo... and credited Marcello Frutig for the insight.
isnt it true that named locks and scope locks are actually the same thing?
what i mean is, if everyone used this:
cflock name="Session_Scope_Lock"
instead of this:
cflock scope="Session"
the behavior of the locking wouldnt be any different, right?
A named lock applies across the whole server, a scoped lock applies only to that scope. So it would be the same as locking on server scope. If you lock on session scope, you would only block threads in your session (so multiple users, with different sessions, do not block each other).
I run into this all the time with my Application variables.
This is what I do:
lock:exclusive
cfparam server.foo to ""
duplicate server.foo to request.serv.foo
end the lock
if request.server.foo is ""
lock: exclusive
update the value of server.foo
end the lock
end if
I never even use unlocked scoped variables in cfif statments. I duplicate everything to request scopes and work that way.
cfset Request.Ses = Duplicate(Session)
cfset Request.App = Duplicate(Application)
That''s at the bottom on my Application.cfm (with the appropriate locks, of course.