/*
 * 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 opennlp.maxent;

import opennlp.model.MaxentModel;

/**
 * A object which can be used to ensure that a Maxent application can swap the
 * model currently in use with a new one in a thread-safe manner without
 * stopping the servicing of requests. Use this if your maxent application is
 * a heavy-weight one or you have only one particular MaxentModel to use with
 * your application. If your application class is lightweight and you will be
 * creating multiple instances of it with different underlying models, consider
 * using a DomainToModelMap object to ensure thread-safe model swapping.
 *
 * <p>For example, in your application, create a ModelReplacementManager as
 * follows:
 *
 *     <pre>
 *     private final ModelReplacementManager replacementManager =
 *	  new ModelReplacementManager(
 *	      new ModelSetter() {
 *		  public void setModel(MaxentModel m) {
 *		      model = m;
 *		  }
 *	      }
 *	  );
 *     </pre>
 *
 * where "model" would be the actual variable name of the model used by your
 * application which you wish to be able to swap (you might have other models
 * which need their own ModelReplacementManager).
 *
 * <p>You'll also need a method to swap the model which calls the manager's
 * replaceModel(MaxentModel m) method, e.g.,
 *
 *     <pre>
 *     public void replaceModel (MaxentModel newmod) {
 *	  replacementManager.replaceModel(newmod);
 *    }
 *     </pre>
 * 
 * Then, in the code that uses the model, you need to inform the
 * ModelReplacementManager when a thread is beginning to use the model and when
 * it no longer needs to be sure that the same model is being used.  For
 * example, it is quite common to evaluate a particular context, get back a
 * double[] which has the normalized probabilities of each of the outcomes given
 * that context, and then request the name of a particular outcome.  The model
 * cannot be swapped during that time since the mapping from outcome labels to
 * unique will (probably) be different between the different models.  So, do as
 * follows:
 *
 *     <pre>
 *	  replacementManager.startUsingModel();
 *	    // some code which evaluates the context, e.g.,
 *	    double[] probs = model.eval(someContext);
 *	    // some code which returns a particular outcome
 *	    if (model.getBestOutcome(probs).equals("T") ...
 *	  replacementManager.finishUsingModel();
 *     </pre>
 *
 * The manager will then make sure that all requests which are currently being
 * serviced are completed before the new model is swapped in.  New requests
 * which are made while the models are being swapped are forced to wait for the
 * swap to finish.  These requests will then be serviced by the new model.
 * 
 *
 * @author      Jason Baldridge
 * @version     $Revision: 1.2 $, $Date: 2010/09/06 08:02:18 $
 */
public class ModelReplacementManager {
    private ModelSetter setter;
    
    private int users = 0;
    private boolean replacementCanProceed = true;
    private Thread replacementThread = null;

    public ModelReplacementManager (ModelSetter ms) {
	setter = ms;
    }

    /**
     * Inform the manager that a thread is using the model.  If a replacement
     * is underway, the thread is forced to join the replacement thread and thus
     * wait until it is finished to begin using the model.
     */
    public void startUsingModel () {
	if (replacementThread != null) {
	    try { replacementThread.join(); }
	    catch (InterruptedException e) {}
	}
	replacementCanProceed = false;
	users++;
    }

    /**
     * Inform the manager that a thread is done using the model, and thus is
     * not dependending on it being unchanged.
     */
    public void finishUsingModel () {
	users--;
	if (users<=0) replacementCanProceed = true;
    }

    /**
     * Replace the old model with a new one, forcing the replacement to wait
     * until all threads using the old model have finished using it.
     *
     * @param model The new model which is being swapped in.
     */
    public synchronized void replaceModel (MaxentModel model) {
	replacementThread = Thread.currentThread();
	while (!replacementCanProceed) Thread.yield();
	setter.setModel(model);
	replacementThread = null;
    }
    
}
