Package hudson.slaves

Source Code of hudson.slaves.WorkspaceList$Entry

/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.slaves;

import hudson.FilePath;
import hudson.Functions;
import hudson.model.Computer;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Used by {@link Computer} to keep track of workspaces that are actively in use.
*
* @author Kohsuke Kawaguchi
* @since 1.319
* @see Computer#getWorkspaceList()
*/
public final class WorkspaceList {
    /**
     * Book keeping for workspace allocation.
     */
    public static final class Entry {
        /**
         * Who acquired this workspace?
         */
        public final Thread holder = Thread.currentThread();

        /**
         * When?
         */
        public final long time = System.currentTimeMillis();

        /**
         * From where?
         */
        public final Exception source = new Exception();

        /**
         * True makes the caller of {@link WorkspaceList#allocate(FilePath)} wait
         * for this workspace.
         */
        public final boolean quick;

        public final FilePath path;

        /**
         * Multiple threads can acquire the same lock if they share the same context object.
         */
        public final Object context;
       
        public int lockCount=1;

        private Entry(FilePath path, boolean quick) {
            this(path,quick,new Object()); // unique context
        }
       
        private Entry(FilePath path, boolean quick, Object context) {
            this.path = path;
            this.quick = quick;
            this.context = context;
        }

        @Override
        public String toString() {
            String s = path+" owned by "+holder.getName()+" from "+new Date(time);
            if(quick) s+=" (quick)";
            s+="\n"+Functions.printThrowable(source);
            return s;
        }
    }

    /**
     * Represents a leased workspace that needs to be returned later.
     */
    public static abstract class Lease {
        public final FilePath path;

        protected Lease(FilePath path) {
            this.path = path;
        }

        /**
         * Releases this lease.
         */
        public abstract void release();

        /**
         * Creates a dummy {@link Lease} object that does no-op in the release.
         */
        public static Lease createDummyLease(FilePath p) {
            return new Lease(p) {
                public void release() {
                    // noop
                }
            };
        }

        /**
         * Creates a {@link Lease} object that points  to the specified path, but the lock
         * is controlled by the given parent lease object.
         */
        public static Lease createLinkedDummyLease(FilePath p, final Lease parent) {
            return new Lease(p) {
                public void release() {
                    parent.release();
                }
            };
        }
    }

    private final Map<FilePath,Entry> inUse = new HashMap<FilePath,Entry>();

    public WorkspaceList() {
    }

    /**
     * Allocates a workspace by adding some variation to the given base to make it unique.
     *
     * <p>
     * This method doesn't block prolonged amount of time. Whenever a desired workspace
     * is in use, the unique variation is added.
     */
    public synchronized Lease allocate(FilePath base) throws InterruptedException {
        return allocate(base,new Object());
    }

    /**
     * See {@link #allocate(FilePath)}
     *
     * @param context
     *      Threads that share the same context can re-acquire the same lock (which will just increment the lock count.)
     *      This allows related executors to share the same workspace.
     */
    public synchronized Lease allocate(FilePath base, Object context) throws InterruptedException {
        for (int i=1; ; i++) {
            FilePath candidate = i==1 ? base : base.withSuffix(COMBINATOR+i);
            Entry e = inUse.get(candidate);
            if(e!=null && !e.quick && e.context!=context)
                continue;
            return acquire(candidate,false,context);
        }
    }

    /**
     * Just record that this workspace is being used, without paying any attention to the synchronization support.
     */
    public synchronized Lease record(FilePath p) {
        log("recorded  "+p);
        Entry old = inUse.put(p, new Entry(p, false));
        if (old!=null)
            throw new AssertionError("Tried to record a workspace already owned: "+old);
        return lease(p);
    }

    /**
     * Releases an allocated or acquired workspace.
     */
    private synchronized void _release(FilePath p) {
        Entry old = inUse.get(p);
        if (old==null)
            throw new AssertionError("Releasing unallocated workspace "+p);
        old.lockCount--;
        if (old.lockCount==0)
            inUse.remove(p);
        notifyAll();
    }

    /**
     * Acquires the given workspace. If necessary, this method blocks until it's made available.
     *
     * @return
     *      The same {@link FilePath} as given to this method.
     */
    public synchronized Lease acquire(FilePath p) throws InterruptedException {
        return acquire(p,false);
    }

    /**
     * See {@link #acquire(FilePath)}
     *
     * @param quick
     *      If true, indicates that the acquired workspace will be returned quickly.
     *      This makes other calls to {@link #allocate(FilePath)} to wait for the release of this workspace.
     */
    public synchronized Lease acquire(FilePath p, boolean quick) throws InterruptedException {
        return acquire(p,quick,new Object());
    }
   
    /**
     * See {@link #acquire(FilePath,boolean)}
     *
     * @param context
     *      Threads that share the same context can re-acquire the same lock (which will just increment the lock count.)
     *      This allows related executors to share the same workspace.
     */
    public synchronized Lease acquire(FilePath p, boolean quick, Object context) throws InterruptedException {
        Entry e;

        Thread t = Thread.currentThread();
        String oldName = t.getName();
        t.setName("Waiting to acquire "+p+" : "+t.getName());
        try {
            while (true) {
                e = inUse.get(p);
                if (e==null || e.context==context)
                    break;
                wait();
            }
        } finally {
            t.setName(oldName);
        }
        log("acquired "+p);
       
        if (e!=null)    e.lockCount++;
        else            inUse.put(p,new Entry(p,quick,context));
        return lease(p);
    }

    /**
     * Wraps a path into a valid lease.
     */
    private Lease lease(FilePath p) {
        return new Lease(p) {
            public void release() {
                _release(path);
            }
        };
    }

    private void log(String msg) {
        if (LOGGER.isLoggable(Level.FINE))
            LOGGER.fine(Thread.currentThread().getName() + " " + msg);
    }

    private static final Logger LOGGER = Logger.getLogger(WorkspaceList.class.getName());

    /**
     * The token that combines the project name and unique number to create unique workspace directory.
     */
    private static final String COMBINATOR = System.getProperty(WorkspaceList.class.getName(),"@");
}
TOP

Related Classes of hudson.slaves.WorkspaceList$Entry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.