protected internal virtual bool ProcessNextBatch(int batchCount, bool exitIfNoJobsAvailable)
        {
            try
            {
                bool waitForMessage = false;
                bool loop           = false;
                do
                {
                    // exit loop if we'll close the connection
                    if (WillClose())
                    {
                        break;
                    }

                    // do we have pending jobs or shall we wait for new jobs to
                    // arrive, which is required only for releasing stickiness
                    // condition to this thread
                    if (waitForMessage || !_queue.Empty)
                    {
                        _queue.drainTo(_batch, batchCount);
                        // if we expect one message but did not get any (because it was already
                        // processed), silently exit
                        if (_batch.Count == 0 && !exitIfNoJobsAvailable)
                        {
                            // loop until we get a new job, if we cannot then validate
                            // transaction to check for termination condition. We'll
                            // break loop if we'll close the connection
                            while (!WillClose())
                            {
                                Job nextJob = _queue.poll(10, SECONDS);
                                if (nextJob != null)
                                {
                                    _batch.Add(nextJob);

                                    break;
                                }
                                else
                                {
                                    _machine.validateTransaction();
                                }
                            }
                        }
                        NotifyDrained(_batch);

                        // execute each job that's in the batch
                        while (_batch.Count > 0)
                        {
                            Job current = _batch.RemoveAt(0);

                            current.Perform(_machine);
                        }

                        // do we have any condition that require this connection to
                        // stick to the current thread (i.e. is there an open statement
                        // or an open transaction)?
                        loop           = _machine.shouldStickOnThread();
                        waitForMessage = loop;
                    }

                    // we processed all pending messages, let's flush underlying channel
                    if (_queue.size() == 0)
                    {
                        _output.flush();
                    }
                } while (loop);

                // assert only if we'll stay alive
                if (!WillClose())
                {
                    Debug.Assert(!_machine.hasOpenStatement());
                }
            }
            catch (BoltConnectionAuthFatality ex)
            {
                _shouldClose.set(true);
                if (ex.Loggable)
                {
                    _userLog.warn(ex.Message);
                }
            }
            catch (BoltProtocolBreachFatality ex)
            {
                _shouldClose.set(true);
                _log.error(string.Format("Protocol breach detected in bolt session '{0}'.", Id()), ex);
            }
            catch (InterruptedException)
            {
                _shouldClose.set(true);
                _log.info("Bolt session '%s' is interrupted probably due to server shutdown.", Id());
            }
            catch (Exception t)
            {
                _shouldClose.set(true);
                _userLog.error(string.Format("Unexpected error detected in bolt session '{0}'.", Id()), t);
            }
            finally
            {
                if (WillClose())
                {
                    Close();
                }
            }

            return(!_closed.get());
        }