Constructing thread-safe counters in ColdFusion is one thing that I’ve checked out a number of occasions earlier than. First, as a part of my CUID implementation, I checked out utilizing an AtomicInteger
with looping logic; then, at utilizing an AtomicLong
with a modulo operator. Within the feedback to my first submit, BR talked about that he makes use of AtomicInteger
‘s getAndUpdate()
technique to perform the identical factor. The getAndUpdate()
technique accepts a java.util.Perform
interface. I needed to see if I may get his method working utilizing the createDynamicProxy()
operate in ColdFusion.
CAUTION: Whereas I like to leverage the underlying Java API in a ColdFusion software, my understanding of Java is definitely fairly poor. As such, I will not go into a lot element right here. Think about this extra of a code kata than a severe exploration.
The purpose right here is to create a thread-safe ColdFusion element that may iterate up over a spread of values. And, as soon as the element hits the upper-bound on stated vary, it is going to loop again to the beginning of the vary and start counting up as soon as once more.
The ColdFusion element that I created acts as each the general public API within the ColdFusion context and because the implementation for the createDynamicProxy()
goal. This fashion, I haven’t got to create a separate ColdFusion element only for the getAndUpdate()
Perform. The draw back to this method is that I’ve to outline one of many strategies as public
which isn’t meant to be consumed externally.
Here is my IncrementingAtomicRange.cfc
element. The general public technique subsequent()
will get the subsequent accessible vary worth. And, the “non-public” (really public) technique applyAsInt()
is the concrete implementation of the IntUnaryOperator
Java interface:
element
output = false
trace = "I present a thread-safe counter that increments over the given repeating vary."
{
/**
* I initialize the atomic vary over the given values, inclusively.
*/
public void operate init(
required numeric rangeStart,
required numeric rangeEnd,
numeric initialValue = arguments.rangeStart
) {
// Validate vary.
if ( rangeStart > rangeEnd ) {
throw(
sort = "InvalidArgument",
message = "Vary begin have to be lower than or equal to vary finish."
);
}
// Validate preliminary worth.
if (
( initialValue < rangeStart ) ||
( initialValue > rangeEnd )
) {
throw(
sort = "InvalidArgument",
message = "Preliminary worth have to be between vary begin and finish inclusive."
);
}
variables.rangeStart = arguments.rangeStart;
variables.ranedEnd = arguments.rangeEnd;
// Internally, our vary goes to be carried out utilizing the AtomicIngeger class.
// This enables us to create a thread-safe integer with out having to use locking
// across the worth entry and mutation.
variables.counter = createObject( "java", "java.util.concurrent.atomic.AtomicInteger" )
.init( javaCast( "int", initialValue ) )
;
// CAUTION: This ColdFusion element acts as BOTH the general public API for ColdFusion
// and because the dynamic proxy goal for Java. The strategy, applyAsInt(), is being
// used to implement the given Java interface (IntUnaryOperator), which in flip is
// being consumed by the AtomicInteger class. This fashion, we do not have to create a
// separate ColdFusion element only for the increment operation.
variables.operator = createDynamicProxy(
this,
[ "java.util.function.IntUnaryOperator" ]
);
}
// ---
// PUBLIC MEHTODS.
// ---
/**
* I get the subsequent worth within the vary.
*/
public numeric operate subsequent() {
return( counter.getAndUpdate( operator ) );
}
// ---
// PRIVATE MEHTODS.
// ---
/**
* INTERNAL USE ONLY: IMPLEMENTATION OF IntUnaryOperator INTERFACE for AtomicInteger.
* That is the operate that will likely be consumed within the Dynamic Proxy that powers the
* ".getAndUpdate()" technique name on the AtomicInteger counter.
*
* Notice that whereas this technique is marked as PUBLIC, I am preserving it within the PRIVATE
* strategies part of the element as a sign that it shouldn't be consumed
* externally. Notice that in Lucee CFML, I may mark this technique as PRIVATE and the
* dynamic proxy technology would nonetheless work. Adobe ColdFusion complains if the tactic
* is marked non-public.
*/
public numeric operate applyAsInt( required numeric enter ) {
if ( enter == ranedEnd ) {
return( rangeStart );
}
return( enter + 1 );
}
}
As you possibly can see, the applyAsInt()
“non-public” (public) technique handles the logic for incrementing the vary worth after which looping again to the beginning of the vary. Solely, I haven’t got to synchronize entry to this technique as a result of the AtomicInteger
class is already doing that for me. I am merely passing this technique – by the use of the createDynamicProxy()
end result – into the AtomicInteger
because the operator of the .getAndUpdate()
technique.
We are able to now devour this ColdFusion element to get a thread-safe vary counter:
<cfscript>
counter = new IncrementingAtomicRange( 1, 5 );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
writeDump( counter.subsequent() );
</cfscript>
I am not bothering to invoke this throughout a number of, parallel threads as a result of earlier posts have already confirmed that the Atomic lessons in Java are thread-safe (as marketed). That stated, once we run this in Lucee CFML 5.3 and Adobe ColdFusion 2021 we get the next output:

As you possibly can see, the vary index loops again to the lower-bound after it hits the upper-bound.
That is fairly neat. I’ve solely tried the createDynamicProxy()
operate a handful of occasions over time. One factor that is particularly attention-grabbing right here is that I suppose I am utilizing a “class” because the implementation for a “operate reference”. From the Java docs:
It is a useful interface and may subsequently be used because the project goal for a lambda expression or technique reference.
If I am understanding this appropriately, it seems like I can use this sort of approach in any state of affairs through which a Java technique takes a Perform/Lambda expression as its argument. That stated, I do know little or no about Java; so, it is also possible that I am not totally understanding the ramifications right here.
Anyway, only a enjoyable code kata to have in my again pocket now.
Wish to use code from this submit?
Try the license.