Due to a bug in the MemoryAwareConcurrentReadMap implementation, a call to remove entries from the MetaClassRegistry can result in other entries becoming inaccessible.
This can cause very unpredictable behavior/failures when relying on dynamic modifications to a Class' ExpandoMetaClass. For example, this bug is responsible for the behavior described in GRAILS-3666.
The failure occurs when a hash table entry that is not at the head of its bucket is removed. The mechanism to recreate the bucket's linked list after removal does not dereference the SoftReference objects for entries following the removal. As a result, any entries in the list after the removed entry will no longer be accessible by their original key.
A simple patch for the problem has been attached. Note that the fix is not necessary for the sremove() method, as proper dereferencing is used there.
The failure case can be easily demonstrated with the following test code:
In the example above, the load factor has been increased to an impracticable level to ensure failure will occur. The assertion will still fail with the default load factor, but not as consistently.