
org.apache.activemq.apollo.broker.QueueEntry.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apollo-broker Show documentation
Show all versions of apollo-broker Show documentation
A reliable messaging server.
The newest version!
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.apollo.broker
import org.fusesource.hawtdispatch._
import org.apache.activemq.apollo.broker.protocol.{MessageCodecFactory, ProtocolFactory, Protocol}
import org.apache.activemq.apollo.broker.store._
import org.apache.activemq.apollo.util._
import org.apache.activemq.apollo.util.list._
import java.util.concurrent.atomic.AtomicReference
import java.lang.UnsupportedOperationException
import org.fusesource.hawtbuf._
import collection.mutable.ListBuffer
import Queue._
/**
* @author Hiram Chirino
*/
object QueueEntry extends Sizer[QueueEntry] with Log {
def size(value: QueueEntry): Int = value.size
}
class QueueEntry(val queue:Queue, val seq:Long) extends LinkedNode[QueueEntry] with Comparable[QueueEntry] {
import QueueEntry._
// Subscriptions waiting to dispatch this entry.
var parked:List[Subscription] = Nil
// subscriptions will set this to true if they are interested
// in the entry.
var prefetched = false
// The current state of the entry: Head | Tail | Loaded | Swapped | SwappedRange
var state:EntryState = new Tail
def <(value:QueueEntry) = this.seq < value.seq
def <=(value:QueueEntry) = this.seq <= value.seq
def head():QueueEntry = {
state = new Head
this
}
def tail():QueueEntry = {
state = new Tail
this
}
def init(delivery:Delivery):QueueEntry = {
if( delivery.message == null ) {
// This must be a swapped out message which has been previously persisted in
// another queue. We need to enqueue it to this queue..
queue.swap_out_size_counter += delivery.size
queue.swap_out_item_counter += 1
state = new Swapped(delivery.storeKey, delivery.storeLocator, delivery.size, delivery.expiration, 0, null, delivery.sender)
} else {
queue.producer_swapped_in += delivery
state = new Loaded(delivery, false, queue.producer_swapped_in)
}
this
}
def init(qer:QueueEntryRecord):QueueEntry = {
val sender = qer.sender.map(x=> SimpleAddress(x.utf8().toString))
state = new Swapped(qer.message_key, qer.message_locator, qer.size, qer.expiration, qer.redeliveries, null, sender)
this
}
def init(range:QueueEntryRange):QueueEntry = {
state = new SwappedRange(range.last_entry_seq, range.count, range.size, range.expiration)
this
}
def hasSubs = !parked.isEmpty
/**
* Dispatches this entry to the consumers and continues dispatching subsequent
* entries as long as the dispatch results in advancing in their dispatch position.
*/
final val task = new Task {
def run() {
queue.assert_executing
var cur = QueueEntry.this;
while( cur!=null && cur.isLinked ) {
val next = cur.getNext
cur = if( cur.dispatch ) {
next
} else {
null
}
}
}
}
def ::=(sub:Subscription) = {
parked = parked ::: sub :: Nil
}
def :::=(l:List[Subscription]) = {
parked = parked ::: l
}
def -=(s:Subscription) = {
parked = parked.filterNot(_ == s)
}
def nextOrTail():QueueEntry = {
var entry = getNext
if (entry == null) {
entry = queue.tail_entry
}
entry
}
def compareTo(o: QueueEntry) = {
(seq - o.seq).toInt
}
def toQueueEntryRecord = {
val qer = new QueueEntryRecord
qer.queue_key = queue.store_id
qer.entry_seq = seq
qer.message_key = state.message_key
qer.message_locator = state.message_locator
qer.message_locator = state.message_locator
qer.size = state.size
qer.expiration = expiration
qer.sender = state.sender.map(x=> new UTF8Buffer(x.toString))
qer
}
override def toString = {
"{seq: "+seq+", prefetched: "+prefetched+", value: "+state+", subscriptions: "+parked+"}"
}
/////////////////////////////////////////////////////
//
// State delegates..
//
/////////////////////////////////////////////////////
// What state is it in?
def as_head = state.as_head
def as_tail = state.as_tail
def as_swapped = state.as_swapped
def as_swapped_range = state.as_swapped_range
def as_loaded = state.as_loaded
def label = state.label
def is_tail = as_tail!=null
def is_head = as_head!=null
def is_loaded = as_loaded!=null
def is_swapped = as_swapped!=null
def is_swapped_range = as_swapped_range!=null
def is_swapped_or_swapped_range = is_swapped || is_swapped_range
def is_loading = state match {
case state:SwappedRange => state.loading
case state:Swapped => state.loading
case _ => false
}
// These should not change the current state.
def count = state.count
def size = state.size
def expiration = state.expiration
def redelivery_count = state.redelivery_count
def redelivered = state.redelivered
def messageKey = state.message_key
def is_swapped_or_swapping_out = state.is_swapped_or_swapping_out
def is_acquired = state.is_acquired
def acquiring_subscription = state.acquiring_subscription
def dispatch() = state.dispatch
def memory_space = state.memory_space
var expiring = false
// These methods may cause a change in the current state.
def swap(asap:Boolean) = state.swap_out(asap)
def load(space:MemorySpace) = state.swap_in(space)
def remove = state.remove
var queued = true
def dequeue(uow: StoreUOW) = {
if ( queued ) {
if (messageKey != -1) {
val actual_uow = queue.create_uow(uow)
actual_uow.dequeue(toQueueEntryRecord)
actual_uow.release
}
queue.dequeue_item_counter += 1
queue.dequeue_size_counter += size
queue.dequeue_ts = queue.now
queued = false
}
}
def swapped_range = state.swap_range
def can_combine_with_prev = {
var prev = getPrevious
if ( prev == null ) {
false
} else {
val prev_range = prev.as_swapped_range
if ( prev_range == null ) {
false
} else {
(!prev_range.loading) &&
(!is_loading) &&
( (is_swapped && !is_acquired) || is_swapped_range ) &&
(prev.count + count < queue.tune_swap_range_size)
}
}
}
trait EntryState {
final def entry:QueueEntry = QueueEntry.this
def as_tail:Tail = null
def as_loaded:Loaded = null
def as_swapped:Swapped = null
def as_swapped_range:SwappedRange = null
def as_head:Head = null
/**
* Gets the size of this entry in bytes. The head and tail entries always return 0.
*/
def size = 0
/**
* When the entry expires or 0 if it does not expire.
*/
def expiration = 0L
/**
* When the entry expires or 0 if it does not expire.
*/
def redelivery_count:Short = throw new UnsupportedOperationException()
/**
* Called to increment the redelivery counter
*/
def redelivered:Unit = {}
/**
* Gets number of messages that this entry represents
*/
def count = 0
/**
* Retuns a string label used to describe this state.
*/
def label:String
/**
* Gets the message key for the entry.
* @returns -1 if it is not known.
*/
def message_key = -1L
def message_locator: AtomicReference[Object] = null
def sender = List[DestinationAddress]()
/**
* Attempts to dispatch the current entry to the subscriptions position at the entry.
* @return true if at least one subscription advanced to the next entry as a result of dispatching.
*/
def dispatch() = false
/**
* Is the entry acquired by a subscription.
*/
def is_acquired = acquiring_subscription!=null
def acquiring_subscription:Acquirer = null
/**
* @returns true if the entry is either swapped or swapping.
*/
def is_swapped_or_swapping_out = false
/**
* Triggers the entry to get swapped in if it's not already swapped in.
*/
def swap_in(space:MemorySpace) = {}
def memory_space:MemorySpace = null
/**
* Triggers the entry to get swapped out if it's not already swapped.
*/
def swap_out(asap:Boolean) = {}
def swap_range:Unit = throw new AssertionError("should only be called on swapped entries");
/**
* Removes the entry from the queue's linked list of entries. This gets called
* as a result of an acquired ack.
*/
def remove:Unit = {
// advance subscriptions that were on this entry..
advance(parked)
parked = Nil
// take the entry of the entries list..
unlink
//TODO: perhaps refill subscriptions.
}
/**
* Advances the specified subscriptions to the next entry in
* the linked list
*/
def advance(advancing: Seq[Subscription]): Unit = {
val nextPos = nextOrTail
nextPos :::= advancing.toList
advancing.foreach(_.advance(nextPos))
queue.trigger_swap
}
}
/**
* Used for the head entry. This is the starting point for all new subscriptions.
*/
class Head extends EntryState {
def label = "head"
override def toString = "head"
override def as_head = this
/**
* New subs get parked here at the Head. There is nothing to actually dispatch
* in this entry.. just advance the parked subs onto the next entry.
*/
override def dispatch() = {
if( parked != Nil ) {
advance(parked)
parked = Nil
true
} else {
false
}
}
override def remove = throw new AssertionError("Head entry cannot be removed")
override def swap_in(space:MemorySpace) = throw new AssertionError("Head entry cannot be loaded")
override def swap_out(asap:Boolean) = throw new AssertionError("Head entry cannot be swapped")
}
/**
* This state is used on the last entry of the queue. It still has not been initialized
* with a message, but it may be holding subscriptions. This state transitions to Loaded
* once a message is received.
*/
class Tail extends EntryState {
def label = "tail"
override def toString = "tail"
override def as_tail:Tail = this
override def remove = throw new AssertionError("Tail entry cannot be removed")
override def swap_in(space:MemorySpace) = throw new AssertionError("Tail entry cannot be loaded")
override def swap_out(asap:Boolean) = throw new AssertionError("Tail entry cannot be swapped")
}
/**
* The entry is in this state while a message is loaded in memory. A message must be in this state
* before it can be dispatched to a subscription.
*/
class Loaded(val delivery: Delivery, var enqueue_stored:Boolean, var space:MemorySpace) extends EntryState {
assert( delivery!=null, "delivery cannot be null")
var acquirer:Acquirer = _
override def acquiring_subscription = acquirer
override def memory_space = space
var swapping_out = false
var storing_enqueue = false
queue.loaded_items += 1
queue.loaded_size += size
def label = {
var rc = "loaded"
if( is_acquired ) {
rc += "|acquired"
}
if( swapping_out ) {
rc += "|swapping out"
}
rc
}
override def toString = { "loaded:{ enqueue_stored: "+enqueue_stored+", swapping_out: "+swapping_out+", acquired: "+acquirer+", size:"+size+"}" }
override def count = 1
override def size = delivery.size
override def expiration = delivery.expiration
override def message_key = delivery.storeKey
override def message_locator = delivery.storeLocator
override def redelivery_count = delivery.redeliveries
override def sender = delivery.sender
override def redelivered = delivery.redeliveries = ((delivery.redeliveries+1).min(Short.MaxValue)).toShort
var remove_pending = false
override def is_swapped_or_swapping_out = {
swapping_out
}
override def as_loaded = this
def store_enqueue(uow:StoreUOW) = {
assert(queue.service_state.is_starting_or_started)
if(!enqueue_stored && !storing_enqueue) {
storing_enqueue = true
assert( uow!=null )
uow.enqueue(toQueueEntryRecord)
queue.swapping_out_size+=size
uow.on_flush { canceled =>
queue.defer {
this.swapped_out(!canceled)
queue.swapping_out_size-=size
if( queue.swapping_out_size==0 ) {
queue.on_queue_flushed
}
}
}
}
}
override def swap_out(asap:Boolean) = {
if( queue.tune_swap && !swapping_out ) {
if( enqueue_stored ) {
switch_to_swapped
} else {
swapping_out=true
val uow = queue.create_uow
// Are we swapping out a non-persistent message?
val flush = if( delivery.storeKey == -1 ) {
delivery.storeLocator = new AtomicReference[Object]()
delivery.storeKey = uow.store(delivery.createMessageRecord )
false
} else {
true
}
store_enqueue(uow)
if( asap ) {
if ( flush ) {
queue.virtual_host.store.flush_message(delivery.storeKey) {}
} else {
uow.complete_asap
}
}
uow.release
}
}
}
var on_swap_out = List[()=>Unit]()
def fire_swap_out_watchers = if(!on_swap_out.isEmpty) {
val t = on_swap_out
on_swap_out = Nil
for ( task <- t ) {
task()
}
}
def swapped_out(not_canceled:Boolean) = {
assert( state == this )
assert( delivery.storeKey != -1 )
storing_enqueue = false
if( not_canceled ) {
enqueue_stored = true
} else {
delivery.storeKey = -1
}
if( swapping_out ) {
swapping_out = false
if( not_canceled ) {
queue.swap_out_size_counter += size
queue.swap_out_item_counter += 1
switch_to_swapped
}
fire_swap_out_watchers
}
if( remove_pending ) {
remove_pending = false
delivery.message.release
space -= delivery
super.remove
}
}
def switch_to_swapped = {
space -= delivery
state = new Swapped(delivery.storeKey, delivery.storeLocator, size, expiration, redelivery_count, acquirer, sender)
if( remove_pending ) {
remove_pending = false
state.remove
} else {
if( can_combine_with_prev ) {
getPrevious.as_swapped_range.combineNext
}
queue.loaded_items -= 1
queue.loaded_size -= size
}
}
override def swap_in(space:MemorySpace) = {
if(space ne this.space) {
this.space -= delivery
this.space = space
this.space += delivery
}
swapping_out = false
fire_swap_out_watchers
}
override def remove = {
if( !remove_pending ) {
queue.loaded_items -= 1
queue.loaded_size -= size
}
if( storing_enqueue | remove_pending ) {
remove_pending = true
} else {
delivery.message.release
space -= delivery
super.remove
}
}
override def dispatch():Boolean = {
queue.assert_executing
if( !is_acquired && expiration != 0 && expiration <= queue.now ) {
val uow = queue.create_uow
entry.dequeue(uow)
queue.expired(uow, entry) {
if( isLinked ) {
remove
}
}
uow.release
return true
}
// Nothing to dispatch if we don't have subs..
if( parked.isEmpty ) {
return false
}
var heldBack = ListBuffer[Subscription]()
var advancing = ListBuffer[Subscription]()
// avoid doing the copy if its' not needed.
var _browser_copy:Delivery = null
def browser_copy = {
if( _browser_copy==null ) {
_browser_copy = delivery.copy
// TODO: perhaps only avoid adding the address in the durable sub case..
if( _browser_copy.sender == Nil ) {
_browser_copy.sender ::= queue.address
}
}
_browser_copy
}
var acquiringSub: Subscription = null
// Find the the first exclusive target of the message
var exclusive_target = queue.exclusive_subscriptions.find( _.matches(delivery) )
// Should we looks for the message group bucket?
if ( exclusive_target.isEmpty && delivery.message.message_group != null ) {
var iterator = queue.message_group_buckets.iterator(delivery.message.message_group)
while (exclusive_target==None && iterator.hasNext) {
val bucket = iterator.next();
if( bucket.sub.matches(delivery) ) {
exclusive_target = Some(bucket.sub)
}
}
}
parked.foreach{ sub=>
if( sub.browser ) {
if (!sub.matches(delivery)) {
// advance: not interested.
advancing += sub
} else {
if (sub.offer(browser_copy)) {
// advance: accepted...
advancing += sub
} else {
// hold back: flow controlled
heldBack += sub
}
}
} else {
if( is_acquired ) {
// advance: another sub already acquired this entry..
advancing += sub
} else {
// Is the current sub not the exclusive target?
if( (exclusive_target.isDefined && (exclusive_target.get != sub))
|| !sub.matches(delivery)
|| (exclusive_target.isEmpty && delivery.message.message_group!=null) ) {
// advance: not interested.
advancing += sub
} else {
// Is the sub flow controlled?
if( sub.full ) {
// hold back: flow controlled
heldBack += sub
} else {
// advance: accepted...
if( queue.tune_round_robin ) {
acquiringSub = sub
} else {
advancing += sub
}
acquirer = sub
val acquiredQueueEntry = sub.acquire(entry)
val acquiredDelivery = delivery.copy
if( acquiredDelivery.sender == Nil) {
acquiredDelivery.sender ::= queue.address
}
acquiredDelivery.ack = (consumed, uow)=> {
if( uow!=null ) {
uow.retain
}
queue.process_ack(acquiredQueueEntry, consumed, uow)
}
val accepted = sub.offer(acquiredDelivery)
assert(accepted, "sub should have accepted, it had reported not full earlier.")
}
}
}
}
}
// The acquiring sub is added last to the list so that
// the other competing subs get first dibs at the next entry.
if( acquiringSub != null ) {
advancing += acquiringSub
}
if ( advancing.isEmpty ) {
return false
} else {
// The held back subs stay on this entry..
parked = heldBack.toList
// the advancing subs move on to the next entry...
advance(advancing)
// We can drop after dispatch in some cases.
if( queue.is_topic_queue && parked.isEmpty && getPrevious.is_head ) {
dequeue(null)
remove
}
queue.trigger_swap
return true
}
}
}
/**
* Loaded entries are moved into the Swapped state reduce memory usage. Once a Loaded
* entry is persisted, it can move into this state. This state only holds onto the
* the massage key so that it can reload the message from the store quickly when needed.
*/
class Swapped(override val message_key:Long, override val message_locator:AtomicReference[Object], override val size:Int, override val expiration:Long, var _redeliveries:Short, var acquirer:Acquirer, override val sender:List[DestinationAddress]) extends EntryState {
assert( message_key!= -1 )
queue.individual_swapped_items += 1
var space:MemorySpace = _
override def redelivery_count = _redeliveries
override def redelivered = _redeliveries = ((_redeliveries+1).min(Short.MaxValue)).toShort
override def acquiring_subscription = acquirer
override def count = 1
override def as_swapped = this
override def is_swapped_or_swapping_out = true
override def memory_space = space
def label = {
var rc = "swapped"
if( is_acquired ) {
rc += "|acquired"
}
if( space!=null ) {
rc += "|swapping in"
}
rc
}
override def toString = { "swapped:{ swapping_in: "+space+", acquired:"+acquirer+", size:"+size+"}" }
def loading = this.space!=null
override def swap_in(mem_space:MemorySpace) = {
if( this.space==null ) {
// trace("Start entry load of message seq: %s", seq)
// start swapping in...
space = mem_space
queue.swapping_in_size += size
queue.virtual_host.store.load_message(message_key, message_locator) { delivery =>
// pass off to a source so it can aggregate multiple
// loads to reduce cross thread synchronization
if( delivery.isDefined ) {
queue.defer {
swapped_in(delivery.get)
if( entry.hasSubs ) {
entry.task.run
}
}
} else {
warn("Queue '%s' detected store dropped message at seq: %d", queue.id, seq)
// Looks like someone else removed the message from the store.. lets just
// tombstone this entry now.
queue.dispatch_queue {
remove
}
}
}
}
}
def to_delivery = {
val delivery = new Delivery()
delivery.seq = seq
delivery.size = size
delivery.persistent = true
delivery.expiration = expiration
delivery.storeKey = message_key
delivery.storeLocator = message_locator
delivery.redeliveries = redelivery_count
delivery.sender = sender
delivery
}
var swapped_in_watchers = List[()=>Unit]()
def fire_swapped_in_watchers = {
val watchers = swapped_in_watchers
swapped_in_watchers = Nil
watchers.foreach(_())
}
def swapped_in(messageRecord:MessageRecord) = {
if( space!=null ) {
// debug("Loaded message seq: ", seq )
queue.swapping_in_size -= size
val delivery = to_delivery
delivery.message = MessageCodecFactory(messageRecord.codec.toString).get.decode(messageRecord)
space += delivery
queue.swap_in_size_counter += size
queue.swap_in_item_counter += 1
queue.individual_swapped_items -= 1
state = new Loaded(delivery, true, space)
space = null
}
fire_swapped_in_watchers
}
override def remove = {
if( space!=null ) {
space = null
queue.swapping_in_size -= size
}
queue.individual_swapped_items -= 1
super.remove
}
override def swap_range = {
// You can't swap range an acquired entry.
assert(!is_acquired)
if( space!=null ) {
space = null
queue.swapping_in_size -= size
}
queue.individual_swapped_items -= 1
state = new SwappedRange(seq, 1, size, expiration)
}
override def dispatch():Boolean = {
queue.assert_executing
if( !is_acquired && expiration != 0 && expiration <= queue.now ) {
val uow = queue.create_uow
entry.dequeue(uow)
queue.expired(uow, entry) {
if( isLinked ) {
remove
}
}
uow.release
return true
}
// Nothing to dispatch if we don't have subs..
if( parked.isEmpty ) {
return false
}
var heldBack = ListBuffer[Subscription]()
var advancing = ListBuffer[Subscription]()
parked.foreach{ sub=>
if( sub.browser ) {
heldBack += sub
} else {
if( is_acquired ) {
// advance: another sub already acquired this entry.. we don't need to load.. yay!
advancing += sub
} else {
heldBack += sub
}
}
}
if ( advancing.isEmpty ) {
if (space==null && !parked.isEmpty) {
// If we are not swapping in try to get a sub to prefetch us.
parked.foreach(_.refill_prefetch)
}
return false
} else {
// The held back subs stay on this entry..
parked = heldBack.toList
if (space==null && !parked.isEmpty) {
// If we are not swapping in try to get a sub to prefetch us.
parked.foreach(_.refill_prefetch)
}
// the advancing subs move on to the next entry...
advance(advancing)
return true
}
}
}
/**
* A SwappedRange state is assigned entry is used to represent a rage of swapped entries.
*
* Even entries that are Swapped can us a significant amount of memory if the queue is holding
* thousands of them. Multiple entries in the swapped state can be combined into a single entry in
* the SwappedRange state thereby conserving even more memory. A SwappedRange entry only tracks
* the first, and last sequnce ids of the range. When the entry needs to be loaded from the range
* it replaces the swapped range entry with all the swapped entries by querying the store of all the
* message keys for the entries in the range.
*/
class SwappedRange(
/** the last seq id in the range */
var last:Long,
/** the number of items in the range */
var _count:Int,
/** size in bytes of the range */
var _size:Int,
var _expiration:Long) extends EntryState {
override def count = _count
override def size = _size
override def expiration = _expiration
var loading = false
override def as_swapped_range = this
override def is_swapped_or_swapping_out = true
def label = {
var rc = "swapped_range"
if( loading ) {
rc = "swapped_range|swapping in"
}
rc
}
override def toString = { "swapped_range:{ swapping_in: "+loading+", count: "+count+", size: "+size+"}" }
var swapped_in_watchers = List[()=>Unit]()
def fire_swapped_in_watchers = {
val watchers = swapped_in_watchers
swapped_in_watchers = Nil
watchers.foreach(_())
}
override def swap_in(space:MemorySpace):Unit = {
if( !loading ) {
loading = true
def complete_load(attempt_counter:Int, records:Seq[QueueEntryRecord]):Unit = {
assert(isLinked)
var item_count=0
var size_count=0
val tmpList = new LinkedNodeList[QueueEntry]()
records.foreach { record =>
val entry = new QueueEntry(queue, record.entry_seq).init(record)
tmpList.addLast(entry)
item_count += 1
size_count += record.size
}
// we may need to adjust the enqueue count if entries
// were dropped at the store level
var item_delta = (count - item_count)
val size_delta: Int = size - size_count
if ( item_delta!=0 || size_delta!=0 ) {
if ( attempt_counter < 10) {
warn("Retrying "+attempt_counter+" load do to Queue '%s' detected store change in range [%d:%d]. %d message(s) and %d bytes", queue.id, seq, last, item_delta, size_delta)
attempt_load(attempt_counter+1)
return
} else {
warn("Queue '%s' detected store change in range [%d:%d]. %d message(s) and %d bytes", queue.id, seq, last, item_delta, size_delta)
queue.enqueue_item_counter += item_delta
queue.enqueue_size_counter += size_delta
}
} else if( attempt_counter > 1 ) {
warn("Recoved!!!! @ "+attempt_counter)
}
loading = false
linkAfter(tmpList)
val next = getNext
// move the subs to the first entry that we just loaded.
parked.foreach(_.advance(next))
next :::= parked
queue.trigger_swap
unlink
fire_swapped_in_watchers
}
def attempt_load(attempt_counter:Int):Unit = {
queue.virtual_host.store.list_queue_entries(queue.store_id, seq, last) { records =>
queue.dispatch_queue {
complete_load(attempt_counter, records)
}
}
}
attempt_load(1)
}
}
/**
* Combines this queue entry with the next queue entry.
*/
def combineNext():Unit = {
val value = getNext
assert(value!=null)
assert(value.is_swapped || value.is_swapped_range)
assert(!value.is_acquired)
assert(!value.is_loading)
if( value.is_swapped ) {
assert(last < value.seq )
last = value.seq
_count += 1
} else if( value.is_swapped_range ) {
assert(last < value.seq )
last = value.as_swapped_range.last
_count += value.as_swapped_range.count
}
if(_expiration == 0){
_expiration = value.expiration
} else {
if( value.expiration != 0 ) {
_expiration = value.expiration.min(_expiration)
}
}
_size += value.size
value.remove
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy