<commit_before>/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., <NAME>, <NAME>, Red Hat, Inc., <NAME>, <NAME>
 * 
 * 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.model;

import hudson.Util;
import hudson.model.Queue.*;
import hudson.FilePath;
import hudson.util.TimeUnit2;
import hudson.util.InterceptingProxy;
import hudson.security.ACL;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.export.Exported;
import org.acegisecurity.context.SecurityContextHolder;

import javax.servlet.ServletException;
import java.io.IOException;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.lang.reflect.Method;


/**
 * Thread that executes builds.
 *
 * @author <NAME>
 */
@ExportedBean
public class Executor extends Thread implements ModelObject {
    protected final Computer owner;
    private final Queue queue;

    private long startTime;
    /**
     * Used to track when a job was last executed.
     */
    private long finishTime;

    /**
     * Executor number that identifies it among other executors for the same {@link Computer}.
     */
    private int number;
    /**
     * {@link Queue.Executable} being executed right now, or null if the executor is idle.
     */
    private volatile Queue.Executable executable;

    private Throwable causeOfDeath;

    public Executor(Computer owner, int n) {
        super("Executor #"+n+" for "+owner.getDisplayName());
        this.owner = owner;
        this.queue = Hudson.getInstance().getQueue();
        this.number = n;
    }

    @Override
    public void run() {
        // run as the system user. see ACL.SYSTEM for more discussion about why this is somewhat broken
        SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);

        try {
            finishTime = System.currentTimeMillis();
            while(shouldRun()) {
                executable = null;

                synchronized(owner) {
                    if(owner.getNumExecutors()<owner.getExecutors().size()) {
                        // we've got too many executors.
                        owner.removeExecutor(this);
                        return;
                    }
                }

                // clear the interrupt flag as a precaution.
                // sometime an interrupt aborts a build but without clearing the flag.
                // see issue #1583
                Thread.interrupted();

                Queue.Item queueItem;
                Queue.Task task;
                try {
                    synchronized (queue) {
                        queueItem = grabJob();
                        task = queueItem.task;
                        startTime = System.currentTimeMillis();
                        executable = task.createExecutable();
                    }
                } catch (IOException e) {
                    LOGGER.log(Level.SEVERE, "Executor throw an exception unexpectedly", e);
                    continue;
                } catch (InterruptedException e) {
                    continue;
                }

                Throwable problems = null;
                final String threadName = getName();
                try {
                    owner.taskAccepted(this, task);

                    if (executable instanceof Actionable) {
                        for (Action action: queueItem.getActions()) {
                            ((Actionable) executable).addAction(action);
                        }
                    }
                    setName(threadName+" : executing "+executable.toString());
                    queue.execute(executable, task);
                } catch (Throwable e) {
                    // for some reason the executor died. this is really
                    // a bug in the code, but we don't want the executor to die,
                    // so just leave some info and go on to build other things
                    LOGGER.log(Level.SEVERE, "Executor throw an exception unexpectedly", e);
                    problems = e;
                } finally {
                    setName(threadName);
                    finishTime = System.currentTimeMillis();
                    if (problems == null) {
                        queueItem.future.set(executable);
                        owner.taskCompleted(this, task, finishTime - startTime);
                    } else {
                        queueItem.future.set(problems);
                        owner.taskCompletedWithProblems(this, task, finishTime - startTime, problems);
                    }
                }
            }
        } catch(RuntimeException e) {
            causeOfDeath = e;
            throw e;
        } catch (Error e) {
            causeOfDeath = e;
            throw e;
        }
    }

    /**
     * Returns true if we should keep going.
     */
    protected boolean shouldRun() {
        return Hudson.getInstance() != null && !Hudson.getInstance().isTerminating();
    }

    protected Queue.Item grabJob() throws InterruptedException {
        return queue.pop();
    }

    /**
     * Returns the current {@link Queue.Task} this executor is running.
     *
     * @return
     *      null if the executor is idle.
     */
    @Exported
    public Queue.Executable getCurrentExecutable() {
        return executable;
    }

    /**
     * If {@linkplain #getCurrentExecutable() current executable} is {@link AbstractBuild},
     * return the workspace that this executor is using, or null if the build hasn't gotten
     * to that point yet.
     */
    public FilePath getCurrentWorkspace() {
        Executable e = executable;
        if(e==null) return null;
        if (e instanceof AbstractBuild) {
            AbstractBuild ab = (AbstractBuild) e;
            return ab.getWorkspace();
        }
        return null;
    }

    /**
     * Same as {@link #getName()}.
     */
    public String getDisplayName() {
        return "Executor #"+getNumber();
    }

    /**
     * Gets the executor number that uniquely identifies it among
     * other {@link Executor}s for the same computer.
     *
     * @return
     *      a sequential number starting from 0.
     */
    @Exported
    public int getNumber() {
        return number;
    }

    /**
     * Returns true if this {@link Executor} is ready for action.
     */
    @Exported
    public boolean isIdle() {
        return executable==null;
    }

    /**
     * The opposite of {@link #isIdle()} &mdash; the executor is doing some work.
     */
    public boolean isBusy() {
        return executable!=null;
    }

    /**
     * If this thread dies unexpectedly, obtain the cause of the failure.
     *
     * @return null if the death is expected death or the thread is {@link #isAlive() still alive}.
     * @since 1.142
     */
    public Throwable getCauseOfDeath() {
        return causeOfDeath;
    }

    /**
     * Returns the progress of the current build in the number between 0-100.
     *
     * @return -1
     *      if it's impossible to estimate the progress.
     */
    @Exported
    public int getProgress() {
        Queue.Executable e = executable;
        if(e==null)     return -1;
        long d = e.getParent().getEstimatedDuration();
        if(d<0)         return -1;

        int num = (int)(getElapsedTime()*100/d);
        if(num>=100)    num=99;
        return num;
    }

    /**
     * Returns true if the current build is likely stuck.
     *
     * <p>
     * This is a heuristics based approach, but if the build is suspiciously taking for a long time,
     * this method returns true.
     */
    @Exported
    public boolean isLikelyStuck() {
        Queue.Executable e = executable;
        if(e==null)     return false;

        long elapsed = getElapsedTime();
        long d = e.getParent().getEstimatedDuration();
        if(d>=0) {
            // if it's taking 10 times longer than ETA, consider it stuck
            return d*10 < elapsed;
        } else {
            // if no ETA is available, a build taking longer than a day is considered stuck
            return TimeUnit2.MILLISECONDS.toHours(elapsed)>24;
        }
    }

    public long getElapsedTime() {
        return System.currentTimeMillis() - startTime;
    }

    /**
     * Gets the string that says how long since this build has started.
     *
     * @return
     *      string like "3 minutes" "1 day" etc.
     */
    public String getTimestampString() {
        return Util.getPastTimeString(getElapsedTime());
    }

    /**
     * Computes a human-readable text that shows the expected remaining time
     * until the build completes.
     */
    public String getEstimatedRemainingTime() {
        Queue.Executable e = executable;
        if(e==null)     return Messages.Executor_NotAvailable();

        long d = e.getParent().getEstimatedDuration();
        if(d<0)         return Messages.Executor_NotAvailable();

        long eta = d-getElapsedTime();
        if(eta<=0)      return Messages.Executor_NotAvailable();

        return Util.getTimeSpanString(eta);
    }

    /**
     * The same as {@link #getEstimatedRemainingTime()} but return
     * it as a number of milli-seconds.
     */
    public long getEstimatedRemainingTimeMillis() {
        Queue.Executable e = executable;
        if(e==null)     return -1;

        long d = e.getParent().getEstimatedDuration();
        if(d<0)         return -1;

        long eta = d-getElapsedTime();
        if(eta<=0)      return -1;

        return eta;
    }

    /**
     * Stops the current build.
     */
    public void doStop( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
        Queue.Executable e = executable;
        if(e!=null) {
            e.getParent().checkAbortPermission();
            interrupt();
        }
        rsp.forwardToPreviousPage(req);
    }

    /**
     * Checks if the current user has a permission to stop this build.
     */
    public boolean hasStopPermission() {
        Queue.Executable e = executable;
        return e!=null && e.getParent().hasAbortPermission();
    }

    public Computer getOwner() {
        return owner;
    }

    /**
     * Returns when this executor started or should start being idle.
     */
    public long getIdleStartMilliseconds() {
        if (isIdle())
            return Math.max(finishTime, owner.getConnectTime());
        else {
            return Math.max(startTime + Math.max(0, executable.getParent().getEstimatedDuration()),
                    System.currentTimeMillis() + 15000);
        }
    }

    /**
     * Exposes the executor to the remote API.
     */
    public Api getApi() {
        return new Api(this);
    }

    /**
     * Creates a proxy object that executes the callee in the context that impersonates
     * this executor. Useful to export an object to a remote channel. 
     */
    public <T> T newImpersonatingProxy(Class<T> type, T core) {
        return new InterceptingProxy() {
            protected Object call(Object o, Method m, Object[] args) throws Throwable {
                final Executor old = IMPERSONATION.get();
                IMPERSONATION.set(Executor.this);
                try {
                    return m.invoke(o,args);
                } finally {
                    IMPERSONATION.set(old);
                }
            }
        }.wrap(type,core);
    }

    /**
     * Returns the executor of the current thread or null if current thread is not an executor.
     */
    public static Executor currentExecutor() {
        Thread t = Thread.currentThread();
        if (t instanceof Executor) return (Executor) t;
        return IMPERSONATION.get();
    }

    /**
     * Mechanism to allow threads (in particular the channel request handling threads) to
     * run on behalf of {@link Executor}.
     */
    private static final ThreadLocal<Executor> IMPERSONATION = new ThreadLocal<Executor>();

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

}
<commit_msg>better documenting my intention in rev.32528.<commit_after>/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., <NAME>, <NAME>, Red Hat, Inc., <NAME>, <NAME>
 * 
 * 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.model;

import hudson.Util;
import hudson.model.Queue.*;
import hudson.FilePath;
import hudson.util.TimeUnit2;
import hudson.util.InterceptingProxy;
import hudson.security.ACL;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.export.Exported;
import org.acegisecurity.context.SecurityContextHolder;

import javax.servlet.ServletException;
import java.io.IOException;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.lang.reflect.Method;


/**
 * Thread that executes builds.
 *
 * @author <NAME>
 */
@ExportedBean
public class Executor extends Thread implements ModelObject {
    protected final Computer owner;
    private final Queue queue;

    private long startTime;
    /**
     * Used to track when a job was last executed.
     */
    private long finishTime;

    /**
     * Executor number that identifies it among other executors for the same {@link Computer}.
     */
    private int number;
    /**
     * {@link Queue.Executable} being executed right now, or null if the executor is idle.
     */
    private volatile Queue.Executable executable;

    private Throwable causeOfDeath;

    public Executor(Computer owner, int n) {
        super("Executor #"+n+" for "+owner.getDisplayName());
        this.owner = owner;
        this.queue = Hudson.getInstance().getQueue();
        this.number = n;
    }

    @Override
    public void run() {
        // run as the system user. see ACL.SYSTEM for more discussion about why this is somewhat broken
        SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);

        try {
            finishTime = System.currentTimeMillis();
            while(shouldRun()) {
                executable = null;

                synchronized(owner) {
                    if(owner.getNumExecutors()<owner.getExecutors().size()) {
                        // we've got too many executors.
                        owner.removeExecutor(this);
                        return;
                    }
                }

                // clear the interrupt flag as a precaution.
                // sometime an interrupt aborts a build but without clearing the flag.
                // see issue #1583
                Thread.interrupted();

                Queue.Item queueItem;
                Queue.Task task;
                try {
                    synchronized (queue) {// perform this state change as an atomic operation wrt other queue operations
                        queueItem = grabJob();
                        task = queueItem.task;
                        startTime = System.currentTimeMillis();
                        executable = task.createExecutable();
                    }
                } catch (IOException e) {
                    LOGGER.log(Level.SEVERE, "Executor throw an exception unexpectedly", e);
                    continue;
                } catch (InterruptedException e) {
                    continue;
                }

                Throwable problems = null;
                final String threadName = getName();
                try {
                    owner.taskAccepted(this, task);

                    if (executable instanceof Actionable) {
                        for (Action action: queueItem.getActions()) {
                            ((Actionable) executable).addAction(action);
                        }
                    }
                    setName(threadName+" : executing "+executable.toString());
                    queue.execute(executable, task);
                } catch (Throwable e) {
                    // for some reason the executor died. this is really
                    // a bug in the code, but we don't want the executor to die,
                    // so just leave some info and go on to build other things
                    LOGGER.log(Level.SEVERE, "Executor throw an exception unexpectedly", e);
                    problems = e;
                } finally {
                    setName(threadName);
                    finishTime = System.currentTimeMillis();
                    if (problems == null) {
                        queueItem.future.set(executable);
                        owner.taskCompleted(this, task, finishTime - startTime);
                    } else {
                        queueItem.future.set(problems);
                        owner.taskCompletedWithProblems(this, task, finishTime - startTime, problems);
                    }
                }
            }
        } catch(RuntimeException e) {
            causeOfDeath = e;
            throw e;
        } catch (Error e) {
            causeOfDeath = e;
            throw e;
        }
    }

    /**
     * Returns true if we should keep going.
     */
    protected boolean shouldRun() {
        return Hudson.getInstance() != null && !Hudson.getInstance().isTerminating();
    }

    protected Queue.Item grabJob() throws InterruptedException {
        return queue.pop();
    }

    /**
     * Returns the current {@link Queue.Task} this executor is running.
     *
     * @return
     *      null if the executor is idle.
     */
    @Exported
    public Queue.Executable getCurrentExecutable() {
        return executable;
    }

    /**
     * If {@linkplain #getCurrentExecutable() current executable} is {@link AbstractBuild},
     * return the workspace that this executor is using, or null if the build hasn't gotten
     * to that point yet.
     */
    public FilePath getCurrentWorkspace() {
        Executable e = executable;
        if(e==null) return null;
        if (e instanceof AbstractBuild) {
            AbstractBuild ab = (AbstractBuild) e;
            return ab.getWorkspace();
        }
        return null;
    }

    /**
     * Same as {@link #getName()}.
     */
    public String getDisplayName() {
        return "Executor #"+getNumber();
    }

    /**
     * Gets the executor number that uniquely identifies it among
     * other {@link Executor}s for the same computer.
     *
     * @return
     *      a sequential number starting from 0.
     */
    @Exported
    public int getNumber() {
        return number;
    }

    /**
     * Returns true if this {@link Executor} is ready for action.
     */
    @Exported
    public boolean isIdle() {
        return executable==null;
    }

    /**
     * The opposite of {@link #isIdle()} &mdash; the executor is doing some work.
     */
    public boolean isBusy() {
        return executable!=null;
    }

    /**
     * If this thread dies unexpectedly, obtain the cause of the failure.
     *
     * @return null if the death is expected death or the thread is {@link #isAlive() still alive}.
     * @since 1.142
     */
    public Throwable getCauseOfDeath() {
        return causeOfDeath;
    }

    /**
     * Returns the progress of the current build in the number between 0-100.
     *
     * @return -1
     *      if it's impossible to estimate the progress.
     */
    @Exported
    public int getProgress() {
        Queue.Executable e = executable;
        if(e==null)     return -1;
        long d = e.getParent().getEstimatedDuration();
        if(d<0)         return -1;

        int num = (int)(getElapsedTime()*100/d);
        if(num>=100)    num=99;
        return num;
    }

    /**
     * Returns true if the current build is likely stuck.
     *
     * <p>
     * This is a heuristics based approach, but if the build is suspiciously taking for a long time,
     * this method returns true.
     */
    @Exported
    public boolean isLikelyStuck() {
        Queue.Executable e = executable;
        if(e==null)     return false;

        long elapsed = getElapsedTime();
        long d = e.getParent().getEstimatedDuration();
        if(d>=0) {
            // if it's taking 10 times longer than ETA, consider it stuck
            return d*10 < elapsed;
        } else {
            // if no ETA is available, a build taking longer than a day is considered stuck
            return TimeUnit2.MILLISECONDS.toHours(elapsed)>24;
        }
    }

    public long getElapsedTime() {
        return System.currentTimeMillis() - startTime;
    }

    /**
     * Gets the string that says how long since this build has started.
     *
     * @return
     *      string like "3 minutes" "1 day" etc.
     */
    public String getTimestampString() {
        return Util.getPastTimeString(getElapsedTime());
    }

    /**
     * Computes a human-readable text that shows the expected remaining time
     * until the build completes.
     */
    public String getEstimatedRemainingTime() {
        Queue.Executable e = executable;
        if(e==null)     return Messages.Executor_NotAvailable();

        long d = e.getParent().getEstimatedDuration();
        if(d<0)         return Messages.Executor_NotAvailable();

        long eta = d-getElapsedTime();
        if(eta<=0)      return Messages.Executor_NotAvailable();

        return Util.getTimeSpanString(eta);
    }

    /**
     * The same as {@link #getEstimatedRemainingTime()} but return
     * it as a number of milli-seconds.
     */
    public long getEstimatedRemainingTimeMillis() {
        Queue.Executable e = executable;
        if(e==null)     return -1;

        long d = e.getParent().getEstimatedDuration();
        if(d<0)         return -1;

        long eta = d-getElapsedTime();
        if(eta<=0)      return -1;

        return eta;
    }

    /**
     * Stops the current build.
     */
    public void doStop( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
        Queue.Executable e = executable;
        if(e!=null) {
            e.getParent().checkAbortPermission();
            interrupt();
        }
        rsp.forwardToPreviousPage(req);
    }

    /**
     * Checks if the current user has a permission to stop this build.
     */
    public boolean hasStopPermission() {
        Queue.Executable e = executable;
        return e!=null && e.getParent().hasAbortPermission();
    }

    public Computer getOwner() {
        return owner;
    }

    /**
     * Returns when this executor started or should start being idle.
     */
    public long getIdleStartMilliseconds() {
        if (isIdle())
            return Math.max(finishTime, owner.getConnectTime());
        else {
            return Math.max(startTime + Math.max(0, executable.getParent().getEstimatedDuration()),
                    System.currentTimeMillis() + 15000);
        }
    }

    /**
     * Exposes the executor to the remote API.
     */
    public Api getApi() {
        return new Api(this);
    }

    /**
     * Creates a proxy object that executes the callee in the context that impersonates
     * this executor. Useful to export an object to a remote channel. 
     */
    public <T> T newImpersonatingProxy(Class<T> type, T core) {
        return new InterceptingProxy() {
            protected Object call(Object o, Method m, Object[] args) throws Throwable {
                final Executor old = IMPERSONATION.get();
                IMPERSONATION.set(Executor.this);
                try {
                    return m.invoke(o,args);
                } finally {
                    IMPERSONATION.set(old);
                }
            }
        }.wrap(type,core);
    }

    /**
     * Returns the executor of the current thread or null if current thread is not an executor.
     */
    public static Executor currentExecutor() {
        Thread t = Thread.currentThread();
        if (t instanceof Executor) return (Executor) t;
        return IMPERSONATION.get();
    }

    /**
     * Mechanism to allow threads (in particular the channel request handling threads) to
     * run on behalf of {@link Executor}.
     */
    private static final ThreadLocal<Executor> IMPERSONATION = new ThreadLocal<Executor>();

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

}

{'fraction_non_alphanumeric': 0.09097801364670205, 'fraction_numerical': 0.004510993176648976, 'mean_word_length': 1.9092412880458756, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2549478', 'n_tokens_mistral': 7128, 'n_tokens_neox': 6697, 'n_words': 2257}