With the dev version of CB 3.5.2 one big concurrency issues is fixed already, but i was still getting exceptions every once in a while. While these errors are very rare, it was really hard to find the cause, but i think i found it now:
the problem is in Injector.getInstance (line 207)… it checks whether a mapping is already defined for particular instanciation path and if not creates it, but i was getting different errors and i think they all have the same cause
- Invalid Construction Type
- Empty instanciation path when executing getComponentMetaData
- missing constructor arguments when using the constructor injection method
so, sometimes one thread is working with a broken mapping object, where different attributes where missing/not set. Every mapping is created through registerNewInstance in the Injector, which is locked, what generally should prevent these errors. As this code in registerNewInstance can only executed by one thread per mapping/instanciation path, the code in getInstance is not locked and it is testing whether one mapping already exists:
if( NOT instance.binder.mappingExists(arguments.name) ) -> Inector.cfc line 229
but this is not enough, if the thread that executes the locked part in registerNewInstance creates, it also registers this empy mapping in the binder. All properties like type, path are set AFTERWARDS. So if another threads checks if the instance exists right at the time between creating the mapping and setting the properties, it will work on a broken mapping and cause different exceptions like mentioned above.
To fix this i added another flag to the mapping, which i called mappingComplete, what is initally set to false. then i added the following code to injector before line 229:
if(instance.binder.mappingExists(arguments.name)) {
mapping = instance.binder.getMapping( arguments.name );
mappingComplete = mapping.isMappingComplete();
}
and changed line 229 to:
if( NOT instance.binder.mappingExists(arguments.name) OR NOT mappingComplete )
and at last i set mappingComplete to true in Injector.registerNewInstance right before the mapping is returned. This makes sure, that every request will run into the lock of registerNewInstance until the first thread has completely setup the mapping.
i am currently running the test again to provoke the exceptions, but everything is fine until now.
Any feedback on this is appreciated.
best regards