Improve config system shutdown
On shutdown, the ModuleFactoryBundleTracker is invoked for every removed
bundle which executes the "blank" transaction which in turn calls
ConfigRegistryImpl#beginConfig. This method is synchronized and may block
if a config commit is in progress (which might be waiting on capabilities or
a ModuleFactory). The ModuleFactoryBundleTracker is invoked
synchronously so this can block karaf shut down. It can affect single feature
tests in particular which start and stop karaf quickly.
I'm not exactly sure what the blank tx is for - I think its purpose is to
indirectly cause the deactivated bundle's module(s) to be destroyed. On
shutdown, the ConfigManagerActivator stop method calls ConfigRegistryImpl#close
which destroys all the modules so we really don't need the blank tx to
run on every bundle on shutdown.
On shutdown, karaf first sends a STOPPING event for the system bundle
(id 0). So I added the ConfigManagerActivator as a bundle listener and when
the system bundle 0 transitions to STOPPING, it calls ConfigRegistryImpl#close.
I also added a closed flag in ConfigRegistryImpl which is checked by
beginConfig and commitConfig. In order to make this work right, I had to change
the synchronization in ConfigRegistryImpl, which really was over-synchronized on
"this". I removed synchronized from the beginConfig and close methods to
break the locking between the two and I made ConfigHolder and TransactionsHolder
classes thread-safe by using ConcurrentHashMap which allows close to access them
safely w/o synchronization. If the closed flag is set, beginConfig
returns a no-op ObjectName, otherwise it calls the synchronized beginConfigSafe
method. In commitConfig, if closed is set or the ON is the no-op ObjectName,
it returns an an empty CommitStatus.
I also changed from synchronizing "this" to using a Lock to take advantage
of the tryLock functionality. As an added measure of safety against
prolonged blocking, if beginConfig is called with the blank tx flag set,
it tries to acquire the lock for 5 sec. If it fails it returns the no-op
ObjectName.
After the modules are destroyed by ConfigRegistryImpl#close. the container
will proceed to stop the rest of the bundles and the ModuleFactoryBundleTracker
will initiate blank transactions but they'll be no-ops and won't block.
The ModuleFactoryBundleTracker was also changed to only initiate a blank
transaction for bundles that contain a ModuleFactory (via Boolean state
stored with the BundleTracker).
With these changes, karaf shuts down noticeably faster.
The BlankTransactionServiceTracker also initiates a blank transaction
when services are added and removed. Since these are synchronous calls
we really shouldn't block the OSGi container. So I added a
single-threaded executor to process the blank transactions for the
service changes. The blank transaction on bundle removed has to be done
synchronously as it may still need access to the BundleContext which
becomes invalid after the bundle removed event.
Change-Id: I5482caa4182dcc64df9b6bafefac9b8f2d505d3e
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>