/* * 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 com.sun.jini.outrigger; import com.sun.jini.config.Config; import com.sun.jini.thread.TaskManager; import com.sun.jini.thread.RetryTask; import com.sun.jini.thread.WakeupManager; import net.jini.config.Configuration; import net.jini.config.ConfigurationException; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.logging.Level; import java.util.logging.Logger; /** * This class provides a driver for monitoring the state of transactions * that have blocked progress of other operations recently. It creates * tasks that monitor each transaction by intermittently querying the * transaction's state. If it finds that the transaction has aborted, * it makes sure that the local space aborts the transaction, too, so * that operations will cease to be blocked by the transaction. * * @author Sun Microsystems, Inc. * * @see TxnMonitorTask * @see OutriggerServerImpl#monitor */ class TxnMonitor implements Runnable { /** * Each <code>ToMonitor</code> object represents a need to monitor * the given transactions, possibly under a lease. * * @see #pending */ private static class ToMonitor { QueryWatcher query; // query governing interest in txns Collection txns; // the transactions to monitor ToMonitor(QueryWatcher query, Collection txns) { this.query = query; this.txns = txns; } } /** * This list is used to contain requests to monitor interfering * transactions. We use a list like this so that the * <code>getMatch</code> request that detected the conflict * doesn't have to wait for all the setup before returning -- it * just puts the data on this list and the <code>TxnMonitor</code> * pulls it off using its own thread. * * @see #ToMonitor * @see OutriggerServerImpl#getMatch */ private LinkedList pending = new LinkedList(); /** wakeup manager for <code>TxnMonitorTask</code>s */ private final WakeupManager wakeupMgr = new WakeupManager(new WakeupManager.ThreadDesc(null, true)); /** * The manager for <code>TxnMonitorTask</code> objects. */ private TaskManager taskManager; /** * The space we belong to. Needed for aborts. */ private OutriggerServerImpl space; /** * The thread running us. */ private Thread ourThread; /** Set when we are told to stop */ private boolean die = false; /** Logger for logging transaction related information */ private static final Logger logger = Logger.getLogger(OutriggerServerImpl.txnLoggerName); /** * Create a new TxnMonitor. */ TxnMonitor(OutriggerServerImpl space, Configuration config) throws ConfigurationException { if (space == null) throw new NullPointerException("space must be non-null"); this.space = space; taskManager = (TaskManager)Config.getNonNullEntry(config, OutriggerServerImpl.COMPONENT_NAME, "txnMonitorTaskManager", TaskManager.class, new TaskManager()); ourThread = new Thread(this, "TxnMonitor"); ourThread.setDaemon(true); ourThread.start(); } public void destroy() { taskManager.terminate(); wakeupMgr.stop(); synchronized (this) { die = true; notifyAll(); } try { ourThread.join(); } catch(InterruptedException ie) { // ignore } } /** * Return the space we're part of. */ OutriggerServerImpl space() { return space; } /** * Add a set of <code>transactions</code> to be monitored under the * given query. */ synchronized void add(QueryWatcher query, Collection transactions) { if (logger.isLoggable(Level.FINEST)) { final StringBuffer buf = new StringBuffer(); buf.append("Setting up monitor for "); buf.append(query); buf.append(" toMonitor:"); boolean notFirst = false; for (Iterator i=transactions.iterator(); i.hasNext();) { if (notFirst) { buf.append(","); notFirst = true; } buf.append(i.next()); } logger.log(Level.FINEST, buf.toString()); } pending.add(new ToMonitor(query, transactions)); notifyAll(); } /** * Add a set of <code>transactions</code> to be monitored under no * lease. */ void add(Collection transactions) { add(null, transactions); } /** * Take pending monitor requests off the queue, creating the * required <code>TxnMonitorTask</code> objects and scheduling them. */ public void run() { try { ToMonitor tm; for (;;) { synchronized (this) { // Sleep if nothing is pending. while (pending.isEmpty() && !die) wait(); if (die) return; tm = (ToMonitor)pending.removeFirst(); } logger.log(Level.FINER, "creating monitor tasks for {0}", tm.query); Iterator it = tm.txns.iterator(); while (it.hasNext()) { Txn txn = (Txn) it.next(); TxnMonitorTask task = taskFor(txn); task.add(tm.query); } } } catch (InterruptedException e) { return; } } /** * Return the monitor task for this transaction, creating it if * necessary. */ private TxnMonitorTask taskFor(Txn txn) { TxnMonitorTask task = txn.monitorTask(); if (task == null) { logger.log(Level.FINER, "creating TxnMonitorTask for {0}", txn); task = new TxnMonitorTask(txn, this, taskManager, wakeupMgr); txn.monitorTask(task); taskManager.add(task); // add it after we've set it in the txn } return task; } }