Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 272811
Collapse All | Expand All

(-)src/org/eclipse/rwt/internal/lifecycle/UICallBackManager_Test.java (-38 / +81 lines)
Lines 11-16 Link Here
11
11
12
package org.eclipse.rwt.internal.lifecycle;
12
package org.eclipse.rwt.internal.lifecycle;
13
13
14
import java.util.ArrayList;
15
import java.util.List;
16
14
import junit.framework.TestCase;
17
import junit.framework.TestCase;
15
18
16
import org.eclipse.rwt.Fixture;
19
import org.eclipse.rwt.Fixture;
Lines 24-46 Link Here
24
27
25
28
26
public class UICallBackManager_Test extends TestCase {
29
public class UICallBackManager_Test extends TestCase {
27
  
30
28
  private static final int SLEEP_TIME = 200;
31
  private static final int SLEEP_TIME = 200;
29
  private static final int TIMER_EXEC_DELAY = 1000;
32
  private static final int TIMER_EXEC_DELAY = 1000;
30
  private static final String RUN_ASYNC_EXEC = "run async exec|";
33
  private static final String RUN_ASYNC_EXEC = "run async exec|";
31
  private static final String RUN_SYNC_EXEC = "run sync exec|";
34
  private static final String RUN_SYNC_EXEC = "run sync exec|";
32
  private static final String RUN_TIMER_EXEC = "timerExecCode|";
35
  private static final String RUN_TIMER_EXEC = "timerExecCode|";
33
  private static String log = "";
36
  private static String log = "";
34
  
37
35
  protected void setUp() throws Exception {
38
  protected void setUp() throws Exception {
36
    RWTFixture.setUp();
39
    RWTFixture.setUp();
37
    log = "";
40
    log = "";
38
  }
41
  }
39
  
42
40
  protected void tearDown() throws Exception {
43
  protected void tearDown() throws Exception {
41
    RWTFixture.tearDown();
44
    RWTFixture.tearDown();
42
  }
45
  }
43
  
46
44
  public void testWaitFor() throws InterruptedException {
47
  public void testWaitFor() throws InterruptedException {
45
    final Throwable[] uiCallBackServiceHandlerThrowable = { null };
48
    final Throwable[] uiCallBackServiceHandlerThrowable = { null };
46
    final ServiceContext context[] = { ContextProvider.getContext() };
49
    final ServiceContext context[] = { ContextProvider.getContext() };
Lines 48-54 Link Here
48
      public void run() {
51
      public void run() {
49
        ContextProvider.setContext( context[ 0 ] );
52
        ContextProvider.setContext( context[ 0 ] );
50
        Fixture.fakeResponseWriter();
53
        Fixture.fakeResponseWriter();
51
        UICallBackServiceHandler uiCallBackServiceHandler 
54
        UICallBackServiceHandler uiCallBackServiceHandler
52
          = new UICallBackServiceHandler();
55
          = new UICallBackServiceHandler();
53
        try {
56
        try {
54
          UICallBackManager.getInstance().setActive( true );
57
          UICallBackManager.getInstance().setActive( true );
Lines 65-78 Link Here
65
    }
68
    }
66
    assertNull( uiCallBackServiceHandlerThrowable[ 0 ] );
69
    assertNull( uiCallBackServiceHandlerThrowable[ 0 ] );
67
    assertTrue( UICallBackManager.getInstance().isCallBackRequestBlocked() );
70
    assertTrue( UICallBackManager.getInstance().isCallBackRequestBlocked() );
68
    
71
69
    UICallBackManager.getInstance().sendUICallBack();
72
    UICallBackManager.getInstance().sendUICallBack();
70
    Thread.sleep( SLEEP_TIME );
73
    Thread.sleep( SLEEP_TIME );
71
    assertFalse( UICallBackManager.getInstance().isCallBackRequestBlocked() );
74
    assertFalse( UICallBackManager.getInstance().isCallBackRequestBlocked() );
72
    Thread.sleep( SLEEP_TIME );
75
    Thread.sleep( SLEEP_TIME );
73
    assertFalse( thread.isAlive() );
76
    assertFalse( thread.isAlive() );
74
  }
77
  }
75
  
78
76
  public void testSyncRunnableWrapper() throws InterruptedException {
79
  public void testSyncRunnableWrapper() throws InterruptedException {
77
    final SyncRunnable[] syncRunnable = new SyncRunnable[ 1 ];
80
    final SyncRunnable[] syncRunnable = new SyncRunnable[ 1 ];
78
    Thread backgroundThread = new Thread( new Runnable() {
81
    Thread backgroundThread = new Thread( new Runnable() {
Lines 93-104 Link Here
93
    assertFalse( backgroundThread.isAlive() );
96
    assertFalse( backgroundThread.isAlive() );
94
    assertEquals( RUN_SYNC_EXEC, log );
97
    assertEquals( RUN_SYNC_EXEC, log );
95
  }
98
  }
96
  
99
97
  public void testAddAsync() throws InterruptedException {
100
  public void testAddAsync() throws InterruptedException {
98
    final Throwable[] uiCallBackServiceHandlerThrowable = { null };
101
    final Throwable[] uiCallBackServiceHandlerThrowable = { null };
99
    final ServiceContext context[] = { ContextProvider.getContext() };
102
    final ServiceContext context[] = { ContextProvider.getContext() };
100
    Display display = new Display();
103
    Display display = new Display();
101
    
104
102
    // test initial blocking of uiCallBack thread
105
    // test initial blocking of uiCallBack thread
103
    Thread uiCallBackThread
106
    Thread uiCallBackThread
104
      = simulateUiCallBackThread( uiCallBackServiceHandlerThrowable, context );
107
      = simulateUiCallBackThread( uiCallBackServiceHandlerThrowable, context );
Lines 107-131 Link Here
107
    }
110
    }
108
    assertNull( uiCallBackServiceHandlerThrowable[ 0 ] );
111
    assertNull( uiCallBackServiceHandlerThrowable[ 0 ] );
109
    assertTrue( UICallBackManager.getInstance().isCallBackRequestBlocked() );
112
    assertTrue( UICallBackManager.getInstance().isCallBackRequestBlocked() );
110
    
113
111
    // test unblocking in case of background addition of runnables
114
    // test unblocking in case of background addition of runnables
112
    simulateBackgroundAddition( context, display );
115
    simulateBackgroundAddition( context, display );
113
    assertFalse( UICallBackManager.getInstance().isCallBackRequestBlocked() );
116
    assertFalse( UICallBackManager.getInstance().isCallBackRequestBlocked() );
114
    assertFalse( uiCallBackThread.isAlive() );
117
    assertFalse( uiCallBackThread.isAlive() );
115
    assertEquals( "", log );
118
    assertEquals( "", log );
116
    
119
117
    // test runnables execution during lifecycle with interlocked additions
120
    // test runnables execution during lifecycle with interlocked additions
118
    fakeRequestParam( display );
121
    fakeRequestParam( display );
119
    simulateBackgroundAdditionDuringLifeCycle( context, display );
122
    simulateBackgroundAdditionDuringLifeCycle( context, display );
120
    RWTFixture.executeLifeCycleFromServerThread();
123
    RWTFixture.executeLifeCycleFromServerThread();
121
    assertFalse( UICallBackManager.getInstance().isCallBackRequestBlocked() );
124
    assertFalse( UICallBackManager.getInstance().isCallBackRequestBlocked() );
122
    assertEquals( RUN_ASYNC_EXEC + RUN_ASYNC_EXEC + RUN_ASYNC_EXEC, log );
125
    assertEquals( RUN_ASYNC_EXEC + RUN_ASYNC_EXEC + RUN_ASYNC_EXEC, log );
123
    
126
124
    // test runnables addition while no uiCallBack thread is not blocked
127
    // test runnables addition while no uiCallBack thread is not blocked
125
    UICallBackManager.getInstance().notifyUIThreadEnd();
128
    UICallBackManager.getInstance().notifyUIThreadEnd();
126
    log = "";
129
    log = "";
127
    simulateBackgroundAddition( context, display );
130
    simulateBackgroundAddition( context, display );
128
    uiCallBackThread 
131
    uiCallBackThread
129
      = simulateUiCallBackThread( uiCallBackServiceHandlerThrowable, context );
132
      = simulateUiCallBackThread( uiCallBackServiceHandlerThrowable, context );
130
    if( uiCallBackServiceHandlerThrowable[ 0 ] != null ) {
133
    if( uiCallBackServiceHandlerThrowable[ 0 ] != null ) {
131
      uiCallBackServiceHandlerThrowable[ 0 ].printStackTrace();
134
      uiCallBackServiceHandlerThrowable[ 0 ].printStackTrace();
Lines 136-148 Link Here
136
    // runnables available do not block
139
    // runnables available do not block
137
    assertFalse( uiCallBackThread.isAlive() );
140
    assertFalse( uiCallBackThread.isAlive() );
138
    UICallBackManager.getInstance().notifyUIThreadStart();
141
    UICallBackManager.getInstance().notifyUIThreadStart();
139
    
142
140
    // test blocking of incomming uiCallBack thread while UI thread is running
143
    // test blocking of incomming uiCallBack thread while UI thread is running
141
    fakeRequestParam( display );
144
    fakeRequestParam( display );
142
    simulateUICallBackThreadLockDuringLifeCycle( 
145
    simulateUICallBackThreadLockDuringLifeCycle(
143
      context, 
146
      context,
144
      uiCallBackServiceHandlerThrowable );
147
      uiCallBackServiceHandlerThrowable );
145
    RWTFixture.executeLifeCycleFromServerThread();    
148
    RWTFixture.executeLifeCycleFromServerThread();
146
    if( uiCallBackServiceHandlerThrowable[ 0 ] != null ) {
149
    if( uiCallBackServiceHandlerThrowable[ 0 ] != null ) {
147
      uiCallBackServiceHandlerThrowable[ 0 ].printStackTrace();
150
      uiCallBackServiceHandlerThrowable[ 0 ].printStackTrace();
148
    }
151
    }
Lines 150-156 Link Here
150
    assertTrue( UICallBackManager.getInstance().isCallBackRequestBlocked() );
153
    assertTrue( UICallBackManager.getInstance().isCallBackRequestBlocked() );
151
    assertEquals( RUN_ASYNC_EXEC + RUN_ASYNC_EXEC, log );
154
    assertEquals( RUN_ASYNC_EXEC + RUN_ASYNC_EXEC, log );
152
  }
155
  }
153
  
156
154
  public void testExceptionInAsyncExec() {
157
  public void testExceptionInAsyncExec() {
155
    final RuntimeException exception
158
    final RuntimeException exception
156
      = new RuntimeException( "bad things happen" );
159
      = new RuntimeException( "bad things happen" );
Lines 163-169 Link Here
163
    UICallBackManager.getInstance().addAsync( display, runnable );
166
    UICallBackManager.getInstance().addAsync( display, runnable );
164
    try {
167
    try {
165
      UICallBackManager.getInstance().processNextRunnableInUIThread();
168
      UICallBackManager.getInstance().processNextRunnableInUIThread();
166
      String msg 
169
      String msg
167
        = "Exception that occurs in an asynExec runnable must be wrapped "
170
        = "Exception that occurs in an asynExec runnable must be wrapped "
168
        + "in an SWTException";
171
        + "in an SWTException";
169
      fail( msg );
172
      fail( msg );
Lines 194-200 Link Here
194
    // 'Execute' the null-runnable: must not cause exception
197
    // 'Execute' the null-runnable: must not cause exception
195
    UICallBackManager.getInstance().processNextRunnableInUIThread();
198
    UICallBackManager.getInstance().processNextRunnableInUIThread();
196
  }
199
  }
197
  
200
198
  public void testAddTimer() throws Exception {
201
  public void testAddTimer() throws Exception {
199
    final Display display = new Display();
202
    final Display display = new Display();
200
    Runnable runnable = new Runnable() {
203
    Runnable runnable = new Runnable() {
Lines 211-217 Link Here
211
    assertTrue( callbackManager.processNextRunnableInUIThread() );
214
    assertTrue( callbackManager.processNextRunnableInUIThread() );
212
    assertEquals( RUN_TIMER_EXEC, log.toString() );
215
    assertEquals( RUN_TIMER_EXEC, log.toString() );
213
  }
216
  }
214
  
217
215
  // Ensure that runnables that were added via addTimer but should be executed
218
  // Ensure that runnables that were added via addTimer but should be executed
216
  // in the future are *not* executed on session shutdown
219
  // in the future are *not* executed on session shutdown
217
  public void testShutdown() throws Exception {
220
  public void testShutdown() throws Exception {
Lines 227-233 Link Here
227
    callbackManager.beforeDestroy( null );
230
    callbackManager.beforeDestroy( null );
228
    assertEquals( "", log.toString() );
231
    assertEquals( "", log.toString() );
229
  }
232
  }
230
  
233
231
  public void testRemoveAddedTimer() throws Exception {
234
  public void testRemoveAddedTimer() throws Exception {
232
    final Display display = new Display();
235
    final Display display = new Display();
233
    Runnable runnable = new Runnable() {
236
    Runnable runnable = new Runnable() {
Lines 243-251 Link Here
243
    assertEquals( "", log );
246
    assertEquals( "", log );
244
  }
247
  }
245
248
246
  private static Thread simulateUiCallBackThread( 
249
  // This test ensures that addSync doesn't cause deadlocks
247
    final Throwable[] uiCallBackServiceHandlerThrowable, 
250
  public void testAddSyncBlock() throws Exception {
248
    final ServiceContext[] context ) 
251
    final Display display = new Display();
252
    final ServiceContext context = ContextProvider.getContext();
253
    // the code in bgRunnable simulates a bg-thread that calls Display#addSync
254
    Runnable bgRunnable = new Runnable() {
255
      public void run() {
256
        ContextProvider.setContext( context );
257
        Fixture.fakeResponseWriter();
258
        Runnable doNothing = new Runnable() {
259
          public void run() {
260
          }
261
        };
262
        UICallBackManager.getInstance().addSync( display, doNothing );
263
      }
264
    };
265
    // simulate a lot "parallel" bg-threads to provoke multi-threading problems
266
    List bgThreads = new ArrayList();
267
    for( int i = 0; i < 200; i++ ) {
268
      Thread bgThread = new Thread( bgRunnable, "Test-Bg-Thread " + i );
269
      bgThread.setDaemon( true );
270
      bgThread.start();
271
      UICallBackManager.getInstance().processNextRunnableInUIThread();
272
      bgThreads.add( bgThread );
273
    }
274
    // wait (hopefully long enough) until all bg-threads have done their work
275
    // (i.e. called addSync) and make sure all sync-runnables get executed
276
    Thread.sleep( 20 );
277
    while( UICallBackManager.getInstance().processNextRunnableInUIThread() ) {
278
      Thread.sleep( 20 );
279
    }
280
    // wait for all bgThreads to terminate
281
    for( int i = 0; i < bgThreads.size(); i++ ) {
282
      Thread bgThread = ( Thread )bgThreads.get( i );
283
      bgThread.join();
284
      UICallBackManager.getInstance().processNextRunnableInUIThread();
285
    }
286
    // sanity-check the test itself: all runnables must have been executed 
287
    assertTrue( UICallBackManager.getInstance().runnables.isEmpty() );
288
  }
289
290
  private static Thread simulateUiCallBackThread(
291
    final Throwable[] uiCallBackServiceHandlerThrowable,
292
    final ServiceContext[] context )
249
    throws InterruptedException
293
    throws InterruptedException
250
  {
294
  {
251
    Thread uiCallBackThread = new Thread( new Runnable() {
295
    Thread uiCallBackThread = new Thread( new Runnable() {
Lines 268-277 Link Here
268
    Thread.sleep( SLEEP_TIME );
312
    Thread.sleep( SLEEP_TIME );
269
    return uiCallBackThread;
313
    return uiCallBackThread;
270
  }
314
  }
271
  
315
272
  private static void simulateTimerExecAddition( final Display display, 
316
  private static void simulateTimerExecAddition( final Display display,
273
                                                 final Runnable runnable,
317
                                                 final Runnable runnable,
274
                                                 final long time ) 
318
                                                 final long time )
275
    throws InterruptedException
319
    throws InterruptedException
276
  {
320
  {
277
    final Runnable simulateRunnable = new Runnable() {
321
    final Runnable simulateRunnable = new Runnable() {
Lines 289-301 Link Here
289
    thread.join();
333
    thread.join();
290
  }
334
  }
291
335
292
  private static void simulateBackgroundAddition( 
336
  private static void simulateBackgroundAddition(
293
    final ServiceContext[] context, 
337
    final ServiceContext[] context,
294
    final Display display )
338
    final Display display )
295
    throws InterruptedException
339
    throws InterruptedException
296
  {
340
  {
297
    Thread backgroundThread = new Thread( new Runnable() {
341
    Thread backgroundThread = new Thread( new Runnable() {
298
      public void run() {        
342
      public void run() {
299
        ContextProvider.setContext( context[ 0 ] );
343
        ContextProvider.setContext( context[ 0 ] );
300
        UICallBackManager instance = UICallBackManager.getInstance();
344
        UICallBackManager instance = UICallBackManager.getInstance();
301
        instance.addAsync( display, new Runnable() {
345
        instance.addAsync( display, new Runnable() {
Lines 314-320 Link Here
314
    Thread.sleep( SLEEP_TIME );
358
    Thread.sleep( SLEEP_TIME );
315
  }
359
  }
316
360
317
  private static void simulateBackgroundAdditionDuringLifeCycle( 
361
  private static void simulateBackgroundAdditionDuringLifeCycle(
318
    final ServiceContext[] context,
362
    final ServiceContext[] context,
319
    final Display display )
363
    final Display display )
320
  {
364
  {
Lines 336-342 Link Here
336
        try {
380
        try {
337
          thread.join();
381
          thread.join();
338
        } catch( InterruptedException e ) {
382
        } catch( InterruptedException e ) {
339
          // TODO Auto-generated catch block
340
          e.printStackTrace();
383
          e.printStackTrace();
341
        }
384
        }
342
        lifeCycle.removePhaseListener( this );
385
        lifeCycle.removePhaseListener( this );
Lines 348-355 Link Here
348
      }
391
      }
349
    } );
392
    } );
350
  }
393
  }
351
  
394
352
  private static void simulateUICallBackThreadLockDuringLifeCycle( 
395
  private static void simulateUICallBackThreadLockDuringLifeCycle(
353
    final ServiceContext[] context,
396
    final ServiceContext[] context,
354
    final Throwable[] uiCallBackServiceHandlerThrowable )
397
    final Throwable[] uiCallBackServiceHandlerThrowable )
355
  {
398
  {
Lines 357-369 Link Here
357
      = ( RWTLifeCycle )LifeCycleFactory.getLifeCycle();
400
      = ( RWTLifeCycle )LifeCycleFactory.getLifeCycle();
358
    lifeCycle.addPhaseListener( new PhaseListener() {
401
    lifeCycle.addPhaseListener( new PhaseListener() {
359
      private static final long serialVersionUID = 1L;
402
      private static final long serialVersionUID = 1L;
360
      
403
361
      public void afterPhase( final PhaseEvent event ) {
404
      public void afterPhase( final PhaseEvent event ) {
362
        Thread uiCallBackThread = new Thread( new Runnable() {
405
        Thread uiCallBackThread = new Thread( new Runnable() {
363
          public void run() {
406
          public void run() {
364
            ContextProvider.setContext( context[ 0 ] );
407
            ContextProvider.setContext( context[ 0 ] );
365
            Fixture.fakeResponseWriter();
408
            Fixture.fakeResponseWriter();
366
            UICallBackServiceHandler uiCallBackServiceHandler 
409
            UICallBackServiceHandler uiCallBackServiceHandler
367
              = new UICallBackServiceHandler();
410
              = new UICallBackServiceHandler();
368
            try {
411
            try {
369
              uiCallBackServiceHandler.service();
412
              uiCallBackServiceHandler.service();
Lines 381-387 Link Here
381
        }
424
        }
382
        lifeCycle.removePhaseListener( this );
425
        lifeCycle.removePhaseListener( this );
383
      }
426
      }
384
      
427
385
      public void beforePhase( final PhaseEvent event ) {
428
      public void beforePhase( final PhaseEvent event ) {
386
      }
429
      }
387
      public PhaseId getPhaseId() {
430
      public PhaseId getPhaseId() {
(-)src/org/eclipse/rwt/internal/lifecycle/UICallBackManager.java (-15 / +15 lines)
Lines 110-128 Link Here
110
  }
110
  }
111
  
111
  
112
  public void addSync( final Display display, final Runnable runnable ) {
112
  public void addSync( final Display display, final Runnable runnable ) {
113
    // TODO [fappel]: the synchronized block should synchronize on runnables
113
    if( Thread.currentThread() == display.getThread() ) {
114
    //                not runnable, but by doing so the application may run
114
      runnable.run();
115
    //                into a deadlock. This is because the SyncRunnable blocks
115
    } else {
116
    //                the thread execution on a different lock.
116
      SyncRunnable syncRunnable = new SyncRunnable( runnable );
117
    synchronized( runnable ) {
117
      synchronized( runnablesLock ) {
118
      if( Thread.currentThread() == display.getThread() ) {
119
        runnable.run();
120
      } else {
121
        SyncRunnable syncRunnable = new SyncRunnable( runnable );
122
        runnables.add( syncRunnable );
118
        runnables.add( syncRunnable );
123
        sendUICallBack();
124
        syncRunnable.block();
125
      }
119
      }
120
      sendUICallBack();
121
      syncRunnable.block();
126
    }
122
    }
127
  }
123
  }
128
  
124
  
Lines 278-283 Link Here
278
  
274
  
279
  static final class SyncRunnable extends RunnableBase {
275
  static final class SyncRunnable extends RunnableBase {
280
    private final Object lock;
276
    private final Object lock;
277
    private boolean terminated;
281
    SyncRunnable( final Runnable runnable ) {
278
    SyncRunnable( final Runnable runnable ) {
282
      super( runnable );
279
      super( runnable );
283
      lock = new Object();
280
      lock = new Object();
Lines 285-299 Link Here
285
    void run() {
282
    void run() {
286
      super.run();
283
      super.run();
287
      synchronized( lock ) {
284
      synchronized( lock ) {
285
        terminated = true;
288
        lock.notifyAll();
286
        lock.notifyAll();
289
      }
287
      }
290
    }
288
    }
291
    void block() {
289
    void block() {
292
      synchronized( lock ) {
290
      synchronized( lock ) {
293
        try {
291
        if( !terminated ) {
294
          lock.wait();
292
          try {
295
        } catch( final InterruptedException e ) {
293
            lock.wait();
296
          // stop waiting
294
          } catch( final InterruptedException e ) {
295
            // stop waiting
296
          }
297
        }
297
        }
298
      }
298
      }
299
    }
299
    }

Return to bug 272811