Modified: river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java?rev=1879521&r1=1879520&r2=1879521&view=diff ============================================================================== --- river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java (original) +++ river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/AvailabilityRegistrationWatcher.java Sun Jul 5 11:41:39 2020 @@ -1,382 +1,385 @@ -/* - * 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.river.outrigger; - -import java.io.IOException; -import java.rmi.RemoteException; -import java.rmi.MarshalledObject; -import java.util.Set; -import java.util.Iterator; -import net.jini.core.event.RemoteEventListener; -import net.jini.core.event.RemoteEvent; -import net.jini.core.event.UnknownEventException; -import net.jini.id.Uuid; -import net.jini.security.ProxyPreparer; -import net.jini.space.JavaSpace; -import org.apache.river.landlord.LeasedResource; - -/** - * Subclass of <code>TransitionWatcher</code> for availability event - * registrations. Also represents the event registration itself. - */ -abstract class AvailabilityRegistrationWatcher extends TransitionWatcher - implements EventRegistrationRecord -{ - /** - * The current expiration time of the registration - * Protected, but only for use by subclasses. - */ - long expiration; - - /** - * The UUID that identifies this registration - * Only for use by subclasses. - * Should not be changed. - */ - volatile Uuid cookie; - - /** - * The handback associated with this registration. - * Only for use by subclasses. - * Should not be changed. - */ - MarshalledObject handback; - - /** - * <code>true</code> if client is interested - * in only visibility events, <code>false</code> - * otherwise. - * Only for use by subclasses. - * Should not be changed. - */ - volatile boolean visibilityOnly; - - /** - * The event ID associated with this registration - * Protected, but only for use by subclasses. - * Should not be changed. - */ - long eventID; - - /** - * The current sequence number. - */ - private long currentSeqNum = 0; - - /** - * The <code>TemplateHandle</code>s associated with this - * watcher. - */ - private final Set<TemplateHandle> owners = new java.util.HashSet<TemplateHandle>(); - private boolean removed = false; - - /** - * The OutriggerServerImpl we are part of. - */ - private OutriggerServerImpl server; - - /** - * Used during log recovery to create a mostly empty - * <code>AvailabilityRegistrationWatcher</code>. - * <p> - * Note, we set the time stamp and tie-breaker here instead of - * getting them from the log. This means they will be inconstant - * with their value from the last VM we were in, but since they - * never leak out and events are read-only anyway this should not - * be a problem (this also allows us to keep the tie-breaker and - * time stamp in final fields). - * - * @param timestamp the value that is used - * to sort <code>TransitionWatcher</code>s. - * @param startOrdinal the highest ordinal associated - * with operations that are considered to have occurred - * before the operation associated with this watcher. - * @param currentSeqNum Sequence number to start with. - */ - AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, - long currentSeqNum) - { - super(timestamp, startOrdinal); - this.currentSeqNum = currentSeqNum; - } - - /** - * Create a new <code>AvailabilityRegistrationWatcher</code>. - * @param timestamp the value that is used - * to sort <code>TransitionWatcher</code>s. - * @param startOrdinal the highest ordinal associated - * with operations that are considered to have occurred - * before the operation associated with this watcher. - * @param cookie The unique identifier associated - * with this watcher. Must not be <code>null</code>. - * @param visibilityOnly pass <code>true</code> if client - * only wants visibility events - * @param handback The handback object that - * should be sent along with event - * notifications to the the listener. - * @param eventID The event ID for event type - * represented by this object. - * @throws NullPointerException if the <code>cookie</code> - * argument is <code>null</code>. - */ - AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, - Uuid cookie, boolean visibilityOnly, MarshalledObject handback, - long eventID) - { - super(timestamp, startOrdinal); - - if (cookie == null) - throw new NullPointerException("cookie must be non-null"); - - this.cookie = cookie; - this.handback = handback; - this.eventID = eventID; - this.visibilityOnly = visibilityOnly; - } - - /** - * Process the given transition by queuing up a task with the - * notifier for event delivery. Assumes the passed entry matches - * the template in the <code>TemplateHandle</code> associated with - * this watcher and that <code>isInterested</code> returned - * <code>true</code>. If <code>remove</code> has been called or - * the expiration time of this watcher has passed, this call - * should have no effect. This call may cause the watcher to be - * removed. - * @param transition A <code>EntryTransition</code> that - * describes the transition and what - * entry is transitioning. This method - * will assume that <code>transition.getHandle</code> - * returns a non-null value. - * @param now An estimate of the current time (not the time - * when the event occurred). - * @throws NullPointerException if <code>transition</code> is - * <code>null</code>. - */ - void process(EntryTransition transition, long now) { - boolean doneFor = false; - - // lock before checking the time and so we can update currentSeqNum - synchronized (this) { - if (removed) return; // Must have been removed - - if (now > expiration) { - doneFor = true; - } else { - server.enqueueDelivery( - new VisibilityEventSender( - transition.getHandle().rep(), - transition.isVisible(), - currentSeqNum++)); - } - } - - if (doneFor) cancel(); - } - - /** - * Return the remote listener associated with this - * <code>EventRegistrationWatcher</code>. Optionally - * prepare the listener if it has been recovered from - * the store and not yet re-prepared. - * @return the remote listener associated with this - * <code>EventRegistrationWatcher</code> - * @throws IOException if the listener can not - * be unmarshalled. May throw {@link RemoteException} - * if the call to the preparer does - * @throws ClassNotFoundException if the listener - * needs to be unmarshalled and a necessary - * class can not be found - * @throws SecurityException if the <code>prepareProxy</code> - * call does. - */ - abstract RemoteEventListener getListener(ProxyPreparer preparer) - throws ClassNotFoundException, IOException; - - /** - * Associate a <code>TemplateHandle</code> with this object. May - * call more than once. - * - * @param h The <code>TemplateHandle</code> associate - * with this watcher - * @return <code>true</code> if the handle was successfully added, - * and <code>false</code> if the watcher has already - * been removed - * @throws NullPointerException if <code>h</code> is - * <code>null</code> - */ - synchronized boolean addTemplateHandle(TemplateHandle h) { - if (removed) - return false; // Already removed! - - owners.add(h); - - if (server == null) - server = h.getServer(); - - return true; - } - - /** - * Set the expiration time of this object. This method may be - * called before <code>setTemplateHandle</code> is called. - * @param newExpiration The expiration time. - */ - public final synchronized void setExpiration(long newExpiration) { - expiration = newExpiration; - } - - public final synchronized long getExpiration() { - return expiration; - } - - /** - * Get the unique identifier associated with this object. This - * method may be called before <code>setTemplateHandle</code> is - * called. - * @return The unique identifier associated with this - * watcher. - */ - public Uuid getCookie() { - return cookie; - } - - /** - * Overridden by subclasses if there is any cleanup work they need - * to do as part of <code>cancel</code> or - * <code>removeIfExpired</code>. Called after releasing the lock - * on <code>this</code>. Will be called at most once. - * @param server A reference to the top level server object. - * @param expired <code>true</code> if being called from - * <code>removeIfExpired</code> and false otherwise. - */ - void cleanup(OutriggerServerImpl server, boolean expired) - {} - - /** - * The heavy lifting of removing ourselves. - * @param now The current time (or a bit earlier). - * @param doIt If <code>true</code> ignore - * <code>now</code> and just remove <code>this</code> - * object. - * @return <code>true</code> if this call removed - * <code>this</code> object, <code>false</code> if - * it had already been done. Should be ignored if - * <code>doIt</code> is <code>false</code>. - */ - private boolean doRemove(long now, boolean doIt) { - final Set<TemplateHandle> owners; - OutriggerServerImpl serv; - synchronized (this) { - if (removed) return false; // already removed - - // Is this a force, or past our expiration? - if (!doIt && (now < expiration)) return false; // don't remove, not our time - - owners = new java.util.HashSet<TemplateHandle>(this.owners); // Don't need to clone - this.owners.clear(); - removed = true; - expiration = Long.MIN_VALUE; //Make sure no one tries to renew us - serv = server; - } - - cleanup(serv, !doIt); - for (Iterator<TemplateHandle> i=owners.iterator(); i.hasNext(); ) { - final TemplateHandle h = i.next(); - h.removeTransitionWatcher(this); - } - - serv.removeEventRegistration(this); - return true; // we did the deed - } - - void removeIfExpired(long now) { - doRemove(now, false); - } - - public boolean cancel() { - return doRemove(0, true); - } - - /** - * Common implementation of <code>EventSender</code> for visibility events - */ - private class VisibilityEventSender implements EventSender { - /** the <code>EntryRep</code> for the entry that became visible */ - final private EntryRep rep; - /** the sequence number this event should have */ - final private long ourSeqNumber; - /** <code>true</code> if this is a visibility event */ - final private boolean isVisible; - - /** - * Create a new <code>VisibilityEventSender</code> that will send - * a new <code>OutriggerAvailabilityEvent</code>. - * @param rep the <code>EntryRep</code> for the entry - * that became visible/available - * @param isVisible <code>true</code> if this is a visibility event - * @param ourSeqNumber the sequence number this event should have - */ - private VisibilityEventSender(EntryRep rep, boolean isVisible, - long ourSeqNumber) - { - this.rep = rep; - this.ourSeqNumber = ourSeqNumber; - this.isVisible = isVisible; - } - - public void sendEvent(JavaSpace source, long now, ProxyPreparer preparer) - throws UnknownEventException, IOException, ClassNotFoundException - { - boolean doneFor = false; - RemoteEvent event; - synchronized (AvailabilityRegistrationWatcher.this) { - if (removed) - return; // Our registration must been - // canceled/expired, don't send event - - if (getExpiration() < now) { - doneFor = true; // Our registration has expired, remove it - } - - - // Now that we are outside the lock kill our watcher if doneFor. - if (doneFor) { - cancel(); - return; - } - event = new OutriggerAvailabilityEvent(source, eventID, ourSeqNumber, - handback, isVisible, rep); - }// end sync(AvailabilityRegistrationWatcher.this) - // Overridden keep out of sync block. - getListener(preparer).notify( event ); - } - - public void cancelRegistration() { - cancel(); - } - -// /** -// * Since we try to send every event that occurs, don't -// * care which order they run. -// */ -// public boolean runAfter(EventSender other) { -// return false; -// } - } -} +/* + * 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.river.outrigger; + +import java.io.IOException; +import java.rmi.RemoteException; +import java.rmi.MarshalledObject; +import java.util.Set; +import java.util.Iterator; +import net.jini.core.event.RemoteEventListener; +import net.jini.core.event.RemoteEvent; +import net.jini.core.event.UnknownEventException; +import net.jini.id.Uuid; +import net.jini.security.ProxyPreparer; +import net.jini.space.JavaSpace; +import org.apache.river.outrigger.proxy.EntryRep; +import org.apache.river.landlord.LeasedResource; +import org.apache.river.outrigger.proxy.OutriggerAvailabilityEvent; + + +/** + * Subclass of <code>TransitionWatcher</code> for availability event + * registrations. Also represents the event registration itself. + */ +abstract class AvailabilityRegistrationWatcher extends TransitionWatcher + implements EventRegistrationRecord +{ + /** + * The current expiration time of the registration + * Protected, but only for use by subclasses. + */ + long expiration; + + /** + * The UUID that identifies this registration + * Only for use by subclasses. + * Should not be changed. + */ + volatile Uuid cookie; + + /** + * The handback associated with this registration. + * Only for use by subclasses. + * Should not be changed. + */ + MarshalledObject handback; + + /** + * <code>true</code> if client is interested + * in only visibility events, <code>false</code> + * otherwise. + * Only for use by subclasses. + * Should not be changed. + */ + volatile boolean visibilityOnly; + + /** + * The event ID associated with this registration + * Protected, but only for use by subclasses. + * Should not be changed. + */ + long eventID; + + /** + * The current sequence number. + */ + private long currentSeqNum = 0; + + /** + * The <code>TemplateHandle</code>s associated with this + * watcher. + */ + private final Set<TemplateHandle> owners = new java.util.HashSet<TemplateHandle>(); + private boolean removed = false; + + /** + * The OutriggerServerImpl we are part of. + */ + private OutriggerServerImpl server; + + /** + * Used during log recovery to create a mostly empty + * <code>AvailabilityRegistrationWatcher</code>. + * <p> + * Note, we set the time stamp and tie-breaker here instead of + * getting them from the log. This means they will be inconstant + * with their value from the last VM we were in, but since they + * never leak out and events are read-only anyway this should not + * be a problem (this also allows us to keep the tie-breaker and + * time stamp in final fields). + * + * @param timestamp the value that is used + * to sort <code>TransitionWatcher</code>s. + * @param startOrdinal the highest ordinal associated + * with operations that are considered to have occurred + * before the operation associated with this watcher. + * @param currentSeqNum Sequence number to start with. + */ + AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, + long currentSeqNum) + { + super(timestamp, startOrdinal); + this.currentSeqNum = currentSeqNum; + } + + /** + * Create a new <code>AvailabilityRegistrationWatcher</code>. + * @param timestamp the value that is used + * to sort <code>TransitionWatcher</code>s. + * @param startOrdinal the highest ordinal associated + * with operations that are considered to have occurred + * before the operation associated with this watcher. + * @param cookie The unique identifier associated + * with this watcher. Must not be <code>null</code>. + * @param visibilityOnly pass <code>true</code> if client + * only wants visibility events + * @param handback The handback object that + * should be sent along with event + * notifications to the the listener. + * @param eventID The event ID for event type + * represented by this object. + * @throws NullPointerException if the <code>cookie</code> + * argument is <code>null</code>. + */ + AvailabilityRegistrationWatcher(long timestamp, long startOrdinal, + Uuid cookie, boolean visibilityOnly, MarshalledObject handback, + long eventID) + { + super(timestamp, startOrdinal); + + if (cookie == null) + throw new NullPointerException("cookie must be non-null"); + + this.cookie = cookie; + this.handback = handback; + this.eventID = eventID; + this.visibilityOnly = visibilityOnly; + } + + /** + * Process the given transition by queuing up a task with the + * notifier for event delivery. Assumes the passed entry matches + * the template in the <code>TemplateHandle</code> associated with + * this watcher and that <code>isInterested</code> returned + * <code>true</code>. If <code>remove</code> has been called or + * the expiration time of this watcher has passed, this call + * should have no effect. This call may cause the watcher to be + * removed. + * @param transition A <code>EntryTransition</code> that + * describes the transition and what + * entry is transitioning. This method + * will assume that <code>transition.getHandle</code> + * returns a non-null value. + * @param now An estimate of the current time (not the time + * when the event occurred). + * @throws NullPointerException if <code>transition</code> is + * <code>null</code>. + */ + void process(EntryTransition transition, long now) { + boolean doneFor = false; + + // lock before checking the time and so we can update currentSeqNum + synchronized (this) { + if (removed) return; // Must have been removed + + if (now > expiration) { + doneFor = true; + } else { + server.enqueueDelivery( + new VisibilityEventSender( + transition.getHandle().rep(), + transition.isVisible(), + currentSeqNum++)); + } + } + + if (doneFor) cancel(); + } + + /** + * Return the remote listener associated with this + * <code>EventRegistrationWatcher</code>. Optionally + * prepare the listener if it has been recovered from + * the store and not yet re-prepared. + * @return the remote listener associated with this + * <code>EventRegistrationWatcher</code> + * @throws IOException if the listener can not + * be unmarshalled. May throw {@link RemoteException} + * if the call to the preparer does + * @throws ClassNotFoundException if the listener + * needs to be unmarshalled and a necessary + * class can not be found + * @throws SecurityException if the <code>prepareProxy</code> + * call does. + */ + abstract RemoteEventListener getListener(ProxyPreparer preparer) + throws ClassNotFoundException, IOException; + + /** + * Associate a <code>TemplateHandle</code> with this object. May + * call more than once. + * + * @param h The <code>TemplateHandle</code> associate + * with this watcher + * @return <code>true</code> if the handle was successfully added, + * and <code>false</code> if the watcher has already + * been removed + * @throws NullPointerException if <code>h</code> is + * <code>null</code> + */ + synchronized boolean addTemplateHandle(TemplateHandle h) { + if (removed) + return false; // Already removed! + + owners.add(h); + + if (server == null) + server = h.getServer(); + + return true; + } + + /** + * Set the expiration time of this object. This method may be + * called before <code>setTemplateHandle</code> is called. + * @param newExpiration The expiration time. + */ + public final synchronized void setExpiration(long newExpiration) { + expiration = newExpiration; + } + + public final synchronized long getExpiration() { + return expiration; + } + + /** + * Get the unique identifier associated with this object. This + * method may be called before <code>setTemplateHandle</code> is + * called. + * @return The unique identifier associated with this + * watcher. + */ + public Uuid getCookie() { + return cookie; + } + + /** + * Overridden by subclasses if there is any cleanup work they need + * to do as part of <code>cancel</code> or + * <code>removeIfExpired</code>. Called after releasing the lock + * on <code>this</code>. Will be called at most once. + * @param server A reference to the top level server object. + * @param expired <code>true</code> if being called from + * <code>removeIfExpired</code> and false otherwise. + */ + void cleanup(OutriggerServerImpl server, boolean expired) + {} + + /** + * The heavy lifting of removing ourselves. + * @param now The current time (or a bit earlier). + * @param doIt If <code>true</code> ignore + * <code>now</code> and just remove <code>this</code> + * object. + * @return <code>true</code> if this call removed + * <code>this</code> object, <code>false</code> if + * it had already been done. Should be ignored if + * <code>doIt</code> is <code>false</code>. + */ + private boolean doRemove(long now, boolean doIt) { + final Set<TemplateHandle> owners; + OutriggerServerImpl serv; + synchronized (this) { + if (removed) return false; // already removed + + // Is this a force, or past our expiration? + if (!doIt && (now < expiration)) return false; // don't remove, not our time + + owners = new java.util.HashSet<TemplateHandle>(this.owners); // Don't need to clone + this.owners.clear(); + removed = true; + expiration = Long.MIN_VALUE; //Make sure no one tries to renew us + serv = server; + } + + cleanup(serv, !doIt); + for (Iterator<TemplateHandle> i=owners.iterator(); i.hasNext(); ) { + final TemplateHandle h = i.next(); + h.removeTransitionWatcher(this); + } + + serv.removeEventRegistration(this); + return true; // we did the deed + } + + void removeIfExpired(long now) { + doRemove(now, false); + } + + public boolean cancel() { + return doRemove(0, true); + } + + /** + * Common implementation of <code>EventSender</code> for visibility events + */ + private class VisibilityEventSender implements EventSender { + /** the <code>EntryRep</code> for the entry that became visible */ + final private EntryRep rep; + /** the sequence number this event should have */ + final private long ourSeqNumber; + /** <code>true</code> if this is a visibility event */ + final private boolean isVisible; + + /** + * Create a new <code>VisibilityEventSender</code> that will send + * a new <code>OutriggerAvailabilityEvent</code>. + * @param rep the <code>EntryRep</code> for the entry + * that became visible/available + * @param isVisible <code>true</code> if this is a visibility event + * @param ourSeqNumber the sequence number this event should have + */ + private VisibilityEventSender(EntryRep rep, boolean isVisible, + long ourSeqNumber) + { + this.rep = rep; + this.ourSeqNumber = ourSeqNumber; + this.isVisible = isVisible; + } + + public void sendEvent(JavaSpace source, long now, ProxyPreparer preparer) + throws UnknownEventException, IOException, ClassNotFoundException + { + boolean doneFor = false; + RemoteEvent event; + synchronized (AvailabilityRegistrationWatcher.this) { + if (removed) + return; // Our registration must been + // canceled/expired, don't send event + + if (getExpiration() < now) { + doneFor = true; // Our registration has expired, remove it + } + + + // Now that we are outside the lock kill our watcher if doneFor. + if (doneFor) { + cancel(); + return; + } + event = new OutriggerAvailabilityEvent(source, eventID, ourSeqNumber, + handback, isVisible, rep); + }// end sync(AvailabilityRegistrationWatcher.this) + // Overridden keep out of sync block. + getListener(preparer).notify( event ); + } + + public void cancelRegistration() { + cancel(); + } + +// /** +// * Since we try to send every event that occurs, don't +// * care which order they run. +// */ +// public boolean runAfter(EventSender other) { +// return false; +// } + } +}
Modified: river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java?rev=1879521&r1=1879520&r2=1879521&view=diff ============================================================================== --- river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java (original) +++ river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/BaseHandle.java Sun Jul 5 11:41:39 2020 @@ -1,70 +1,71 @@ -/* - * 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.river.outrigger; - -import java.util.Queue; - -/** - * Base class for handles to Entries and templates. - * - * @author Sun Microsystems, Inc. - * - */ -abstract class BaseHandle { - private final EntryRep rep; // the rep this handle manages - private final Queue<? extends BaseHandle> content; - - /** - * Create a new handle - * - * @param content thread safe Queue from which this BaseHandle will be removed - * atomically, BaseHandle is not added to content during construction, - * as it would allow this to escape. - * @param rep EntryRep managed by this BaseHandle. - */ - protected BaseHandle(EntryRep rep, Queue<? extends BaseHandle> content) { - this.rep = rep; - this.content = content; - } - - /** - * Return the handle's <code>EntryRep</code> object. - */ - EntryRep rep() { - return rep; - } - - // inherit doc comment - public String classFor() { - return rep.classFor(); - } - - public abstract boolean removed(); - - /** - * Overridden and called from subclass. - * - * @return true if removed. - */ - public boolean remove(){ - return content.remove(this); - } -} - - - +/* + * 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.river.outrigger; + +import java.util.Queue; +import org.apache.river.outrigger.proxy.EntryRep; + +/** + * Base class for handles to Entries and templates. + * + * @author Sun Microsystems, Inc. + * + */ +abstract class BaseHandle { + private final EntryRep rep; // the rep this handle manages + private final Queue<? extends BaseHandle> content; + + /** + * Create a new handle + * + * @param content thread safe Queue from which this BaseHandle will be removed + * atomically, BaseHandle is not added to content during construction, + * as it would allow this to escape. + * @param rep EntryRep managed by this BaseHandle. + */ + protected BaseHandle(EntryRep rep, Queue<? extends BaseHandle> content) { + this.rep = rep; + this.content = content; + } + + /** + * Return the handle's <code>EntryRep</code> object. + */ + EntryRep rep() { + return rep; + } + + // inherit doc comment + public String classFor() { + return rep.classFor(); + } + + public abstract boolean removed(); + + /** + * Overridden and called from subclass. + * + * @return true if removed. + */ + public boolean remove(){ + return content.remove(this); + } +} + + + Modified: river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java URL: http://svn.apache.org/viewvc/river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java?rev=1879521&r1=1879520&r2=1879521&view=diff ============================================================================== --- river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java (original) +++ river/jtsk/modules/modularize/apache-river/river-services/outrigger/outrigger-service/src/main/java/org/apache/river/outrigger/EntryHandle.java Sun Jul 5 11:41:39 2020 @@ -1,456 +1,458 @@ -/* - * 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.river.outrigger; - -import net.jini.io.MarshalledInstance; -import org.apache.river.landlord.LeasedResource; -import java.util.Queue; - -/** - * This object holds an annotated reference to an - * <code>EntryRep</code> object. Currently there is one annotation, - * which is a hash code for the object that can be used as a - * quick-reject comparison when scanning through the list. The handle - * holds a hash code that is based on the bytes that encode the first - * <i>N</i> fields, where <i>N</i> is the number of fields in the - * entry up to a maximum (currently this maximum is 16 (64 bits - * divided by 4 bits/field), so that 4 is the minimum number of bits - * per field in the hash). - * <p> - * When comparing, the template's own hash is calculated, and also a - * mask that masks out the hash codes of wildcard fields. A template - * will match an entry only if the entry's EntryHandle hash masked with - * the template's wildcard mask is the same as the template's hash. - * <p> - * Care must be taken since the template may be a supertype of the type - * being searched. This is why the number of fields in the static - * methods is passed as an argument, not simply taken from the entry in - * question. When a template's hash is being created, its hash value - * is calculated as if it were of the class being searched, with the - * subclass's field count. Any extra fields are assumed to be - * wildcards. This means that the template's hash must be recalculated - * for each subclass it is compared against, but this only happens once - * per known subclass, and so is probably not onerous. <p> - * - * There is a particular risk with the removal of entries outside of - * transactions. Ideally marking an entry as removed and making the - * removal durable would be atomic with respect to other - * operations. But this would require holding a lock across disk I/O - * which we try to avoid. In particular it would hold up the progress - * of searches that match the entry in question, even though the very - * next entry might be a suitable match. One alternative would be to - * make the removal durable, then while holding the entry's lock mark - * the entry as removed, but this would allow competing takes to both - * get the same entry (this could be corrected by making the 2nd take - * lose when it goes back to try and complete the removal, and then - * continues its query, but since logging happens up in - * OutriggerServerImpl restarting the query would be inconvenient, it - * would probably also result in a number of unnecessary log - * records). We could mark the entry as removed, release the entry's - * lock, and then make the removal durable. However, this allows for - * the possibility of a 2nd query that matches the entry coming in - * after the entry has been removed, but before the removal has been - * made durable, finding no matches and returning null, and then the - * server crashing before the removal is made durable. When the server - * came back up the entry would be available again, and if the 2nd - * query was repeated it could then return the entry that had been - * marked as removed. Effectively an entry would have disappeared and - * then reappeared. <p> - * - * Our solution is to introduce the <i>removePending</i> flag. When an - * entry is to be removed outside of a transaction the removePending - * flag is set by calling <code>provisionallyRemove</code>, the - * removal is made durable, the entry is removed internally and the - * removePending flag cleared (generally by calling - * <code>remove</code> on the appropriate <code>EntryHolder</code> or - * on the <code>EntryHolderSet</code> - either will remove the entry - * from all the internal tables and clear the removePending flag). <p> - * - * Any operation that will definitively indicate that a given entry - * has been removed must not only check to see if the entry has been - * removed but also that removePending is not set (the - * <code>isProvisionallyRemoved</code> method returns the state of the - * removePending flag). If removePending is set the operation must - * either block until removePending is cleared (this can be - * accomplished using the <code>waitOnCompleteRemoval</code> method), - * indicating that the removal has been made durable, or return in - * such a way that the entry's state is left ambiguous. Note, because - * any I/O failure while logging will result in the space crashing a - * set removePending flag will only transition to cleared after a - * removal has been made durable, thus an operation blocked on the - * removePending flag should never need to go back and see if the - * entry has become available. <p> - * - * Note some of the method of this class are synchronized internally, - * while other are synchronized externally. Methods which need to be - * synchronized externally are called out in their comments. - * - * @author Sun Microsystems, Inc. - */ -// We do not store this data on the EntryRep object itself because it -// is not really part of the client<->JavaSpaces service protocol -- -// some implementations of EntryHolder may not choose to use this -// mechanism. It does add an extra object per EntryRep object in -// those that *do* use it, and so we may want to re-examine this in the -// future. - -class EntryHandle extends BaseHandle implements LeaseDesc, Transactable { - /** the content hash for the rep */ - private final long hash; // Made final for toString() and hash(). - - /** - * If this entry is locked by one or more transaction the info - * on those transactions, otherwise <code>null</code>. - */ - private TxnState txnState; - - /** - * <code>true</code> if this entry has to been seen as removed, - * but the removal has not yet been committed to disk - */ - private boolean removePending = false; - private boolean removed = false; - - /** - * Create a new handle, calculating the hash for the object. - * If <code>mgr</code> is non-<code>null</code> start the entry - * as write locked under the given transaction. - * @param rep The rep of the entry this is a handle for - * @param mgr If this entry is being written under a transaction the - * manager for that transaction, otherwise <code>null</code> - * @param holder If mgr is non-<code>null</code> this must be - * the holder holding this handle. Otherwise it may be - * <code>null</code> - * @param content Queue this EntryHandle will be removed from. - */ - EntryHandle(EntryRep rep, TransactableMgr mgr, EntryHolder holder, Queue<EntryHandle> content) { - super(rep, content); - hash = (rep != null ? hashFor(rep, rep.numFields())[0] : -1); - if (mgr == null) { - txnState = null; - } else { - if (holder == null) - throw new NullPointerException("EntryHandle:If mgr is " + - "non-null holder must be non-null"); - txnState = new TxnState(mgr, TransactableMgr.WRITE, holder); - } - } - - // inherit doc comment - public LeasedResource getLeasedResource() { - return rep(); - } - - /** - * Return this handle's content hash. - */ - long hash() { - return hash; - } - - /** - * Calculate the hash for a particular entry, assuming the given - * number of fields, filling in the fields of <code>desc</code> - * with the relevant values. <code>desc</code> may be - * <code>null</code>. <code>numFields</code> must be >= the number - * of fields in the <code>rep</code> object (this is not - * checked). - * - * @see #hashFor(EntryRep,int) - * @see #descFor(EntryRep,int) - * @return long[4] containing the hash, bitsPerField, fieldsInHash and mask - * in that order. - */ - private static long [] - hashFor(EntryRep rep, int numFields) - { - long [] result = new long [4]; - if (rep == null || numFields == 0) return result; - - /** Number of bits allocated in the hash for each field */ - int bitsPerField = Math.max(64 / numFields, 4); // at least 4 bits - /** How many fields are used in the hash? */ - int fieldsInHash = 64 / bitsPerField; // max fields used - /** A mask with the lower <code>bitsPerField</code> bits set */ - long mask = // per-field bit mask - 0xffffffffffffffffL >>> (64 - bitsPerField); - long hash = 0; // current hash value - - // field counts will be different if rep is a template of a superclass - long endField = Math.min(fieldsInHash, rep.numFields()); - - // set the appropriate rep of the overall hash for the field's hash - for (int i = 0; i < endField; i++) - hash |= (hashForField(rep, i) & mask) << (i * bitsPerField); - - result [0] = hash; - result [1] = bitsPerField; - result [2] = fieldsInHash; - result [3] = mask; - return result; - } - - /** - * Return the template description -- mask and hash. - * - * @see EntryHandleTmplDesc - */ - static EntryHandleTmplDesc descFor(EntryRep tmpl, int numFields) { - EntryHandleTmplDesc tmplDesc; - - // Get the hash and the related useful information - long [] hashDesc = hashFor(tmpl, numFields); - long hash = hashDesc[0]; - long bitsPerField = hashDesc [1]; - long fieldsInHash = hashDesc [2]; - long mask = hashDesc [3]; - - long tmplMask = 0; - - // Create the mask to mask away wildcard fields - for (int i = 0; i < fieldsInHash; i++) { - // If this field is one we have a value for, set bits in the mask - if (i < tmpl.numFields() && tmpl.value(i) != null) - tmplMask |= (mask << (i * bitsPerField)); - } - - // Ensure that the non-value fields are masked out - hash &= tmplMask; - - tmplDesc = new EntryHandleTmplDesc(hash, tmplMask); - - return tmplDesc; - } - - /** - * Return the hash value for a given field, which is then merged in - * as part of the overall hash for the entry. The last 32 bytes of - * the field value are used (or fewer if there are fewer). - * - * @see #hashFor(EntryRep,int) - */ - static long hashForField(EntryRep rep, int field) { - MarshalledInstance v = rep.value(field); - if (v == null) // for templates, it's just zero - return 0; - else - return v.hashCode(); - } - - public String toString() { - return "0x" + Long.toHexString(hash) + " [" + rep() + "]"; - } - - /** - * Return <code>true</code> if the operation <code>op</code> under - * the given transaction (represented by the transaction's manager) - * can be performed on the object represented by this handle. The - * thread calling this method should own this object's lock. - */ - // $$$ Calling this method when we don't own the lock on this - // object seems a bit dicey, but that is exactly what we do in - // EntryHolder.SimpleRepEnum.nextRep(). Working it through - // it seems to work in that particular case, but it seems fragile. - boolean canPerform(TransactableMgr mgr, int op) { - synchronized (this){ // Audit revealed calling thread didn't always own lock see comment above. - if (txnState == null) - return true; // all operations are legal on a non-transacted entry - - return txnState.canPerform(mgr, op); - } - } - - /** - * Return <code>true</code> if the given transaction is already - * known to the entry this handle represents. The - * thread calling this method should own this object's lock. - */ - boolean knownMgr(TransactableMgr mgr) { - synchronized (this){ // Audit revealed caller methods didn't always hold lock. - if (txnState == null) - return (mgr == null); // The only mgr we know about is the null mgr - - return txnState.knownMgr(mgr); - } - } - - /** - * Return <code>true</code> if we are being managed the given - * manager is the only one we know about. The thread calling this - * method should own this object's lock. - */ - boolean onlyMgr(TransactableMgr mgr) { - if (txnState == null) // Audit revealed caller always held lock. - return false; - - return txnState.onlyMgr(mgr); - } - - /** - * Return <code>true</code> if the entry this handle represents is - * being managed within any transaction. The thread calling this - * method should own this object's lock. - */ - boolean managed() { - synchronized (this){ // Audit revealed caller didn't always have lock. - return txnState != null; - } - } - - /** - * Add into the collection any transactions that are known to this - * handle. The thread calling this method should own this object's - * lock. - */ - void addTxns(java.util.Collection collection) { - if (txnState == null) // Confirmed that calling method owns lock. - return; // nothing to add - - txnState.addTxns(collection); - } - - /** - * Add <code>mgr</code> to the list of known managers, setting the - * the type of lock on this entry to <code>op</code>. The thread - * calling this method should own this object's lock. Assumes - * that <code>op</code> is compatible with any lock currently - * associated with this entry. <code>holder</code> is the the - * <code>EntryHolder</code> holding this handle. - */ - void add(TransactableMgr mgr, int op, EntryHolder holder) { - synchronized (this){ - if (txnState == null) { - txnState = new TxnState(mgr, op, holder); - } else { - txnState.add(mgr, op); - } - } - } - - /** - * It this entry is read locked promote to take locked and return - * true, otherwise return false. Assumes that the object is - * locked and the take is being performed under the one - * transaction that owns a lock on the entry. - */ - boolean promoteToTakeIfNeeded() { - synchronized (this){ - return txnState.promoteToTakeIfNeeded(); - } - } - - /** - * Returns <code>true</code> it this entry has been removed - * outside of a transaction, but that removal has not yet been - * committed to disk.The thread calling this method should own this - * object's lock. - */ - boolean isProvisionallyRemoved() { - assert Thread.holdsLock(this); - return removePending; - } - - /** - * Marks this entry as being removed outside of a transaction but - * not yet committed to disk. The thread calling this method should - * own this object's lock. - */ - void provisionallyRemove() { - assert Thread.holdsLock(this); - assert !removePending; - removePending = true; - } - - /** - * Called after the removal of a provisionally removed entry has - * been committed to disk and the handle has been removed from its - * holder. The thread calling this method should own this object's - * lock. - */ - void removalComplete() { - assert Thread.holdsLock(this); - - if (removePending) { - removePending = false; - notifyAll(); - } - } - - /** - * If this entry has been marked for removal by a - * non-transactional operation, but that operation has not be - * yet been committed to disk, block until the operation has been - * committed to disk, otherwise return immediately. The - * thread calling this method should own this object's lock. - */ - void waitOnCompleteRemoval() throws InterruptedException { - assert Thread.holdsLock(this); - while (removePending) { - wait(); - } - } - - /************************************************** - * Methods required by the Transactable interface - **************************************************/ - public synchronized int prepare(TransactableMgr mgr, - OutriggerServerImpl space) - { - if (txnState == null) - throw new IllegalStateException("Can't prepare an entry not " + - "involved in a transaction"); - final int rslt = txnState.prepare(mgr, space, this); - if (txnState.empty()) - txnState = null; - - return rslt; - } - - public synchronized void abort(TransactableMgr mgr, - OutriggerServerImpl space) - { - if (txnState == null) - throw new IllegalStateException("Can't abort an entry not " + - "involved in a transaction"); - final boolean last = txnState.abort(mgr, space, this); - if (last) - txnState = null; - } - - public synchronized void commit(TransactableMgr mgr, - OutriggerServerImpl space) - { - if (txnState == null) - throw new IllegalStateException("Can't commit an entry not " + - "involved in a transaction"); - - final boolean last = txnState.commit(mgr, space, this); - if (last) - txnState = null; - } - - @Override - public synchronized boolean removed() { - return removed; - } - - public synchronized boolean remove() { - if (!removed) removed = super.remove(); - return removed; - } -} +/* + * 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.river.outrigger; + +import net.jini.io.MarshalledInstance; +import org.apache.river.landlord.LeasedResource; +import java.util.Queue; +import org.apache.river.outrigger.proxy.EntryRep; + + +/** + * This object holds an annotated reference to an + * <code>EntryRep</code> object. Currently there is one annotation, + * which is a hash code for the object that can be used as a + * quick-reject comparison when scanning through the list. The handle + * holds a hash code that is based on the bytes that encode the first + * <i>N</i> fields, where <i>N</i> is the number of fields in the + * entry up to a maximum (currently this maximum is 16 (64 bits + * divided by 4 bits/field), so that 4 is the minimum number of bits + * per field in the hash). + * <p> + * When comparing, the template's own hash is calculated, and also a + * mask that masks out the hash codes of wildcard fields. A template + * will match an entry only if the entry's EntryHandle hash masked with + * the template's wildcard mask is the same as the template's hash. + * <p> + * Care must be taken since the template may be a supertype of the type + * being searched. This is why the number of fields in the static + * methods is passed as an argument, not simply taken from the entry in + * question. When a template's hash is being created, its hash value + * is calculated as if it were of the class being searched, with the + * subclass's field count. Any extra fields are assumed to be + * wildcards. This means that the template's hash must be recalculated + * for each subclass it is compared against, but this only happens once + * per known subclass, and so is probably not onerous. <p> + * + * There is a particular risk with the removal of entries outside of + * transactions. Ideally marking an entry as removed and making the + * removal durable would be atomic with respect to other + * operations. But this would require holding a lock across disk I/O + * which we try to avoid. In particular it would hold up the progress + * of searches that match the entry in question, even though the very + * next entry might be a suitable match. One alternative would be to + * make the removal durable, then while holding the entry's lock mark + * the entry as removed, but this would allow competing takes to both + * get the same entry (this could be corrected by making the 2nd take + * lose when it goes back to try and complete the removal, and then + * continues its query, but since logging happens up in + * OutriggerServerImpl restarting the query would be inconvenient, it + * would probably also result in a number of unnecessary log + * records). We could mark the entry as removed, release the entry's + * lock, and then make the removal durable. However, this allows for + * the possibility of a 2nd query that matches the entry coming in + * after the entry has been removed, but before the removal has been + * made durable, finding no matches and returning null, and then the + * server crashing before the removal is made durable. When the server + * came back up the entry would be available again, and if the 2nd + * query was repeated it could then return the entry that had been + * marked as removed. Effectively an entry would have disappeared and + * then reappeared. <p> + * + * Our solution is to introduce the <i>removePending</i> flag. When an + * entry is to be removed outside of a transaction the removePending + * flag is set by calling <code>provisionallyRemove</code>, the + * removal is made durable, the entry is removed internally and the + * removePending flag cleared (generally by calling + * <code>remove</code> on the appropriate <code>EntryHolder</code> or + * on the <code>EntryHolderSet</code> - either will remove the entry + * from all the internal tables and clear the removePending flag). <p> + * + * Any operation that will definitively indicate that a given entry + * has been removed must not only check to see if the entry has been + * removed but also that removePending is not set (the + * <code>isProvisionallyRemoved</code> method returns the state of the + * removePending flag). If removePending is set the operation must + * either block until removePending is cleared (this can be + * accomplished using the <code>waitOnCompleteRemoval</code> method), + * indicating that the removal has been made durable, or return in + * such a way that the entry's state is left ambiguous. Note, because + * any I/O failure while logging will result in the space crashing a + * set removePending flag will only transition to cleared after a + * removal has been made durable, thus an operation blocked on the + * removePending flag should never need to go back and see if the + * entry has become available. <p> + * + * Note some of the method of this class are synchronized internally, + * while other are synchronized externally. Methods which need to be + * synchronized externally are called out in their comments. + * + * @author Sun Microsystems, Inc. + */ +// We do not store this data on the EntryRep object itself because it +// is not really part of the client<->JavaSpaces service protocol -- +// some implementations of EntryHolder may not choose to use this +// mechanism. It does add an extra object per EntryRep object in +// those that *do* use it, and so we may want to re-examine this in the +// future. + +class EntryHandle extends BaseHandle implements LeaseDesc, Transactable { + /** the content hash for the rep */ + private final long hash; // Made final for toString() and hash(). + + /** + * If this entry is locked by one or more transaction the info + * on those transactions, otherwise <code>null</code>. + */ + private TxnState txnState; + + /** + * <code>true</code> if this entry has to been seen as removed, + * but the removal has not yet been committed to disk + */ + private boolean removePending = false; + private boolean removed = false; + + /** + * Create a new handle, calculating the hash for the object. + * If <code>mgr</code> is non-<code>null</code> start the entry + * as write locked under the given transaction. + * @param rep The rep of the entry this is a handle for + * @param mgr If this entry is being written under a transaction the + * manager for that transaction, otherwise <code>null</code> + * @param holder If mgr is non-<code>null</code> this must be + * the holder holding this handle. Otherwise it may be + * <code>null</code> + * @param content Queue this EntryHandle will be removed from. + */ + EntryHandle(EntryRep rep, TransactableMgr mgr, EntryHolder holder, Queue<EntryHandle> content) { + super(rep, content); + hash = (rep != null ? hashFor(rep, rep.numFields())[0] : -1); + if (mgr == null) { + txnState = null; + } else { + if (holder == null) + throw new NullPointerException("EntryHandle:If mgr is " + + "non-null holder must be non-null"); + txnState = new TxnState(mgr, TransactableMgr.WRITE, holder); + } + } + + // inherit doc comment + public LeasedResource getLeasedResource() { + return rep(); + } + + /** + * Return this handle's content hash. + */ + long hash() { + return hash; + } + + /** + * Calculate the hash for a particular entry, assuming the given + * number of fields, filling in the fields of <code>desc</code> + * with the relevant values. <code>desc</code> may be + * <code>null</code>. <code>numFields</code> must be >= the number + * of fields in the <code>rep</code> object (this is not + * checked). + * + * @see #hashFor(EntryRep,int) + * @see #descFor(EntryRep,int) + * @return long[4] containing the hash, bitsPerField, fieldsInHash and mask + * in that order. + */ + private static long [] + hashFor(EntryRep rep, int numFields) + { + long [] result = new long [4]; + if (rep == null || numFields == 0) return result; + + /** Number of bits allocated in the hash for each field */ + int bitsPerField = Math.max(64 / numFields, 4); // at least 4 bits + /** How many fields are used in the hash? */ + int fieldsInHash = 64 / bitsPerField; // max fields used + /** A mask with the lower <code>bitsPerField</code> bits set */ + long mask = // per-field bit mask + 0xffffffffffffffffL >>> (64 - bitsPerField); + long hash = 0; // current hash value + + // field counts will be different if rep is a template of a superclass + long endField = Math.min(fieldsInHash, rep.numFields()); + + // set the appropriate rep of the overall hash for the field's hash + for (int i = 0; i < endField; i++) + hash |= (hashForField(rep, i) & mask) << (i * bitsPerField); + + result [0] = hash; + result [1] = bitsPerField; + result [2] = fieldsInHash; + result [3] = mask; + return result; + } + + /** + * Return the template description -- mask and hash. + * + * @see EntryHandleTmplDesc + */ + static EntryHandleTmplDesc descFor(EntryRep tmpl, int numFields) { + EntryHandleTmplDesc tmplDesc; + + // Get the hash and the related useful information + long [] hashDesc = hashFor(tmpl, numFields); + long hash = hashDesc[0]; + long bitsPerField = hashDesc [1]; + long fieldsInHash = hashDesc [2]; + long mask = hashDesc [3]; + + long tmplMask = 0; + + // Create the mask to mask away wildcard fields + for (int i = 0; i < fieldsInHash; i++) { + // If this field is one we have a value for, set bits in the mask + if (i < tmpl.numFields() && tmpl.value(i) != null) + tmplMask |= (mask << (i * bitsPerField)); + } + + // Ensure that the non-value fields are masked out + hash &= tmplMask; + + tmplDesc = new EntryHandleTmplDesc(hash, tmplMask); + + return tmplDesc; + } + + /** + * Return the hash value for a given field, which is then merged in + * as part of the overall hash for the entry. The last 32 bytes of + * the field value are used (or fewer if there are fewer). + * + * @see #hashFor(EntryRep,int) + */ + static long hashForField(EntryRep rep, int field) { + MarshalledInstance v = rep.value(field); + if (v == null) // for templates, it's just zero + return 0; + else + return v.hashCode(); + } + + public String toString() { + return "0x" + Long.toHexString(hash) + " [" + rep() + "]"; + } + + /** + * Return <code>true</code> if the operation <code>op</code> under + * the given transaction (represented by the transaction's manager) + * can be performed on the object represented by this handle. The + * thread calling this method should own this object's lock. + */ + // $$$ Calling this method when we don't own the lock on this + // object seems a bit dicey, but that is exactly what we do in + // EntryHolder.SimpleRepEnum.nextRep(). Working it through + // it seems to work in that particular case, but it seems fragile. + boolean canPerform(TransactableMgr mgr, int op) { + synchronized (this){ // Audit revealed calling thread didn't always own lock see comment above. + if (txnState == null) + return true; // all operations are legal on a non-transacted entry + + return txnState.canPerform(mgr, op); + } + } + + /** + * Return <code>true</code> if the given transaction is already + * known to the entry this handle represents. The + * thread calling this method should own this object's lock. + */ + boolean knownMgr(TransactableMgr mgr) { + synchronized (this){ // Audit revealed caller methods didn't always hold lock. + if (txnState == null) + return (mgr == null); // The only mgr we know about is the null mgr + + return txnState.knownMgr(mgr); + } + } + + /** + * Return <code>true</code> if we are being managed the given + * manager is the only one we know about. The thread calling this + * method should own this object's lock. + */ + boolean onlyMgr(TransactableMgr mgr) { + if (txnState == null) // Audit revealed caller always held lock. + return false; + + return txnState.onlyMgr(mgr); + } + + /** + * Return <code>true</code> if the entry this handle represents is + * being managed within any transaction. The thread calling this + * method should own this object's lock. + */ + boolean managed() { + synchronized (this){ // Audit revealed caller didn't always have lock. + return txnState != null; + } + } + + /** + * Add into the collection any transactions that are known to this + * handle. The thread calling this method should own this object's + * lock. + */ + void addTxns(java.util.Collection collection) { + if (txnState == null) // Confirmed that calling method owns lock. + return; // nothing to add + + txnState.addTxns(collection); + } + + /** + * Add <code>mgr</code> to the list of known managers, setting the + * the type of lock on this entry to <code>op</code>. The thread + * calling this method should own this object's lock. Assumes + * that <code>op</code> is compatible with any lock currently + * associated with this entry. <code>holder</code> is the the + * <code>EntryHolder</code> holding this handle. + */ + void add(TransactableMgr mgr, int op, EntryHolder holder) { + synchronized (this){ + if (txnState == null) { + txnState = new TxnState(mgr, op, holder); + } else { + txnState.add(mgr, op); + } + } + } + + /** + * It this entry is read locked promote to take locked and return + * true, otherwise return false. Assumes that the object is + * locked and the take is being performed under the one + * transaction that owns a lock on the entry. + */ + boolean promoteToTakeIfNeeded() { + synchronized (this){ + return txnState.promoteToTakeIfNeeded(); + } + } + + /** + * Returns <code>true</code> it this entry has been removed + * outside of a transaction, but that removal has not yet been + * committed to disk.The thread calling this method should own this + * object's lock. + */ + boolean isProvisionallyRemoved() { + assert Thread.holdsLock(this); + return removePending; + } + + /** + * Marks this entry as being removed outside of a transaction but + * not yet committed to disk. The thread calling this method should + * own this object's lock. + */ + void provisionallyRemove() { + assert Thread.holdsLock(this); + assert !removePending; + removePending = true; + } + + /** + * Called after the removal of a provisionally removed entry has + * been committed to disk and the handle has been removed from its + * holder. The thread calling this method should own this object's + * lock. + */ + void removalComplete() { + assert Thread.holdsLock(this); + + if (removePending) { + removePending = false; + notifyAll(); + } + } + + /** + * If this entry has been marked for removal by a + * non-transactional operation, but that operation has not be + * yet been committed to disk, block until the operation has been + * committed to disk, otherwise return immediately. The + * thread calling this method should own this object's lock. + */ + void waitOnCompleteRemoval() throws InterruptedException { + assert Thread.holdsLock(this); + while (removePending) { + wait(); + } + } + + /************************************************** + * Methods required by the Transactable interface + **************************************************/ + public synchronized int prepare(TransactableMgr mgr, + OutriggerServerImpl space) + { + if (txnState == null) + throw new IllegalStateException("Can't prepare an entry not " + + "involved in a transaction"); + final int rslt = txnState.prepare(mgr, space, this); + if (txnState.empty()) + txnState = null; + + return rslt; + } + + public synchronized void abort(TransactableMgr mgr, + OutriggerServerImpl space) + { + if (txnState == null) + throw new IllegalStateException("Can't abort an entry not " + + "involved in a transaction"); + final boolean last = txnState.abort(mgr, space, this); + if (last) + txnState = null; + } + + public synchronized void commit(TransactableMgr mgr, + OutriggerServerImpl space) + { + if (txnState == null) + throw new IllegalStateException("Can't commit an entry not " + + "involved in a transaction"); + + final boolean last = txnState.commit(mgr, space, this); + if (last) + txnState = null; + } + + @Override + public synchronized boolean removed() { + return removed; + } + + public synchronized boolean remove() { + if (!removed) removed = super.remove(); + return removed; + } +}
