MIRTH-2565: Optimized Channel.dispatchRawMessage so that it doesn't need to instantiate and execute a Callable for every message. …
MIRTH-2565: Optimized Channel.dispatchRawMessage so that it doesn't need to instantiate and execute a Callable for every message.
MessageTask is gone; now the logic for creating the source RECEIVED message and processing/queuing is back in Channel. To handle synchronization, we switched the process lock from a wait/notify AtomicBoolean to a Semaphore. The semaphore is initialized with one permit and is fair, meaning that multiple threads trying to acquire a lock in Channel.dispatchRawMessage will be able to do so in order. In addition, a semaphore can be acquired/released on separate threads, whereas a ReentrantLock cannot be.
The channel executor is now gone. To handle cancellation/halting, we now also keep a set of threads on the channel itself. This set represents all threads that are currently processing through Channel.dispatchRawMessage (but doesn't include threads that are in the finishDispatch portion of the lifecycle, even though in most cases that will still be the same thread). Before the process lock is obtained, the thread is added to the set.
When the channel is halted, each thread currently in the set will be interrupted manually. This means that no changes need to be made to the process methods in Channel, DestinationConnector, etc.; the ThreadUtils.checkInterruptedStatus() will still work the same way. When the channel is stopped, a shuttingDown boolean is set to true, meaning that no new threads will be able to be added to the set (if any attempt to, a ChannelException will be thrown). The stop method also obtains and releases the process lock. This is done because even if there are no threads in the dispatchThreads set, there may still be threads processing through SourceConnector.finishDispatch. To prevent deadlocks, we need to obtain the lock to ensure that everything has completed.
Also, the DispatchResult is now instantiated in the finally block, to ensure that the correct value for lockAcquired is used. Because it's possible (though incorrect) for a receiver to call finishDispatch multiple times, we added a protected setLockAcquired method to DispatchResult. In finishDispatch when the lock gets released, the dispatch result is updated accordingly, to ensure that the semaphore's permits don't get incorrectly incremented.