Skip to main content
summaryrefslogtreecommitdiffstats
blob: 24d0748641dcc5d2f6e31a74c57e5ead19adf852 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
 * Copyright (c) 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.emf.cdo.internal.efs;

import org.eclipse.emf.cdo.internal.efs.bundle.OM;

import org.eclipse.net4j.util.WrappedException;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;

import java.util.concurrent.Callable;

/**
 * This class provides a simulation of progress. This is useful for situations where computing the amount of work to do
 * in advance is too costly. The monitor will accept any number of calls to {@link #worked(int)}, and will scale the
 * actual reported work appropriately so that the progress never quite completes.
 */
public class InfiniteProgress extends ProgressMonitorWrapper
{
  private static final Object NO_RESULT = new Object();

  /*
   * Fields for progress monitoring algorithm. Initially, give progress for every 4 resources, double this value at
   * halfway point, then reset halfway point to be half of remaining work. (this gives an infinite series that converges
   * at total work after an infinite number of resources).
   */
  private int totalWork;

  private int currentIncrement = 4;

  private int halfWay;

  private int nextProgress = currentIncrement;

  private int worked = 0;

  protected InfiniteProgress(IProgressMonitor monitor)
  {
    super(monitor);
  }

  @Override
  public void beginTask(String name, int work)
  {
    super.beginTask(name, work);
    totalWork = work;
    halfWay = totalWork / 2;
  }

  @Override
  public void worked(int work)
  {
    if (--nextProgress <= 0)
    {
      // we have exhausted the current increment, so report progress
      super.worked(1);
      worked++;
      if (worked >= halfWay)
      {
        // we have passed the current halfway point, so double the
        // increment and reset the halfway point.
        currentIncrement *= 2;
        halfWay += (totalWork - halfWay) / 2;
      }
      // reset the progress counter to another full increment
      nextProgress = currentIncrement;
    }
  }

  @SuppressWarnings("unchecked")
  public static <T> T call(final String label, final Callable<T> callable)
  {
    final Object[] result = { NO_RESULT };
    final IProgressMonitor[] jobMonitor = { null };

    Job job = new Job(label)
    {
      @Override
      protected IStatus run(IProgressMonitor monitor)
      {
        jobMonitor[0] = new InfiniteProgress(monitor);
        jobMonitor[0].beginTask(label, 10);

        try
        {
          result[0] = callable.call();

          synchronized (result)
          {
            result.notifyAll();
          }

          return Status.OK_STATUS;
        }
        catch (Throwable t)
        {
          result[0] = t;
          if (jobMonitor[0].isCanceled())
          {
            return Status.OK_STATUS;
          }

          return new Status(IStatus.ERROR, OM.BUNDLE_ID, t.getLocalizedMessage(), t);
        }
        finally
        {
          monitor = jobMonitor[0];
          jobMonitor[0] = null;
          monitor.done();
        }
      }
    };

    job.schedule();

    while (result[0] == NO_RESULT)
    {
      synchronized (result)
      {
        if (jobMonitor[0] != null)
        {
          if (jobMonitor[0].isCanceled())
          {
            job.cancel();
            throw new OperationCanceledException();
          }

          jobMonitor[0].worked(1);
        }

        try
        {
          System.out.println("wait...");
          result.wait(100L);
        }
        catch (InterruptedException ex)
        {
          job.cancel();
          throw new OperationCanceledException();
        }
      }
    }

    if (result[0] instanceof Error)
    {
      throw (Error)result[0];
    }

    if (result[0] instanceof Exception)
    {
      throw WrappedException.wrap((Exception)result[0]);
    }

    return (T)result[0];
  }
}

Back to the top