This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 33467
Collapse All | Expand All

(-)openide/util/src/org/openide/util/RequestProcessor.java (-33 / +108 lines)
Lines 24-30 import java.util.*; Link Here
24
 *
24
 *
25
 * <P><A name="use_cases">There are several use cases for RequestProcessor:</A>
25
 * <P><A name="use_cases">There are several use cases for RequestProcessor:</A>
26
 *
26
 *
27
 * <UL><LI>Having something done asynchronously in some other thread,
27
 * <UL><LI>Having something done asynchronously in some other thread,
28
 *  not insisting on any kind of serialization of the requests:
28
 *  not insisting on any kind of serialization of the requests:
29
 *  Use <CODE>RequestProcessor.{@link RequestProcessor#getDefault
29
 *  Use <CODE>RequestProcessor.{@link RequestProcessor#getDefault
30
 *  }.{@link #post(java.lang.Runnable) post(runnable)}</CODE>
30
 *  }.{@link #post(java.lang.Runnable) post(runnable)}</CODE>
Lines 64-69 import java.util.*; Link Here
64
 * <CODE>RequestProcessor</CODE> instance with limited throughput (probably
64
 * <CODE>RequestProcessor</CODE> instance with limited throughput (probably
65
 * set to 1), the IDE would try to run all your requests in parallel otherwise.
65
 * set to 1), the IDE would try to run all your requests in parallel otherwise.
66
 *
66
 *
67
 * <P>
68
 * Since version JST-PENDING there is a support for interruption of long running tasks.
69
 * There always was a way how to cancel not yet running task using {@link RequestProcessor.Task#cancel }
70
 * but if the task was already running, one was out of luck. Since version 4.6
71
 * the thread running the task is interrupted and the Runnable can check for that
72
 * and terminate its execution sooner. In the runnable one shall check for 
73
 * thread interruption (done from {@link RequestProcessor.Task#cancel }) and 
74
 * if true, return immediatelly as in this example:
75
 * <PRE>
76
 * public void run () {
77
 *     while (veryLongTimeLook) {
78
 *       doAPieceOfIt ();
79
 *
80
 *       if (Thread.interrupted ()) return;
81
 *     }
82
 * }
83
 * </PRE>
84
 * 
67
 * @author Petr Nejedly, Jaroslav Tulach
85
 * @author Petr Nejedly, Jaroslav Tulach
68
 */
86
 */
69
public final class RequestProcessor {
87
public final class RequestProcessor {
Lines 364-384 public final class RequestProcessor { Link Here
364
    }
382
    }
365
383
366
    Task askForWork(Processor worker, String debug) {
384
    Task askForWork(Processor worker, String debug) {
367
        synchronized (processorLock) {
385
        if (stopped || queue.isEmpty()) { // no more work in this burst, return him
368
            if (stopped || queue.isEmpty()) { // no more work in this burst, return him
386
            processors.remove(worker);
369
                processors.remove(worker);
387
            Processor.put(worker, debug);
370
                Processor.put(worker, debug);
388
            running--;
371
                running--;
389
372
390
            return null;
373
                return null;
391
        } else { // we have some work for the worker, pass it
374
            } else { // we have some work for the worker, pass it
392
375
393
            Item i = (Item) queue.remove(0);
376
                Item i = (Item) queue.remove(0);
394
            Task t = i.getTask();
377
                Task t = i.getTask();
395
            i.clear(worker);
378
                i.clear();
379
396
380
                return t;
397
            return t;
381
            }
382
        }
398
        }
383
    }
399
    }
384
400
Lines 458-464 public final class RequestProcessor { Link Here
458
                notifyRunning();
474
                notifyRunning();
459
475
460
                if (item != null) {
476
                if (item != null) {
461
                    item.clear();
477
                    item.clear(null);
462
                }
478
                }
463
479
464
                item = new Item(this, RequestProcessor.this);
480
                item = new Item(this, RequestProcessor.this);
Lines 490-496 public final class RequestProcessor { Link Here
490
        */
506
        */
491
        public boolean cancel() {
507
        public boolean cancel() {
492
            synchronized (processorLock) {
508
            synchronized (processorLock) {
493
                boolean success = (item == null) ? false : item.clear();
509
                boolean success;
510
511
                if (item == null) {
512
                    success = false;
513
                } else {
514
                    Processor p = item.getProcessor();
515
                    success = item.clear(null);
516
517
                    if (p != null) {
518
                        p.interruptTask(this);
519
                        item = null;
520
                    }
521
                }
494
522
495
                if (success) {
523
                if (success) {
496
                    notifyFinished(); // mark it as finished
524
                    notifyFinished(); // mark it as finished
Lines 541-557 public final class RequestProcessor { Link Here
541
        */
569
        */
542
        public void waitFinished() {
570
        public void waitFinished() {
543
            if (isRequestProcessorThread()) { //System.err.println("Task.waitFinished on " + this + " from other task in RP: " + Thread.currentThread().getName());
571
            if (isRequestProcessorThread()) { //System.err.println("Task.waitFinished on " + this + " from other task in RP: " + Thread.currentThread().getName());
544
545
                boolean toRun;
572
                boolean toRun;
546
573
547
                synchronized (processorLock) {
574
                synchronized (processorLock) {
548
                    // correct line:    toRun = (item == null) ? !isFinished (): (item.clear() && !isFinished ());
575
                    // correct line:    toRun = (item == null) ? !isFinished (): (item.clear() && !isFinished ());
549
                    // the same:        toRun = !isFinished () && (item == null ? true : item.clear ());
576
                    // the same:        toRun = !isFinished () && (item == null ? true : item.clear ());
550
                    toRun = !isFinished() && ((item == null) || item.clear());
577
                    toRun = !isFinished() && ((item == null) || item.clear(null));
551
                }
578
                }
552
579
553
                if (toRun) { //System.err.println("    ## running it synchronously");
580
                if (toRun) { //System.err.println("    ## running it synchronously");
554
                    run();
581
                    Processor processor = (Processor)Thread.currentThread();
582
                    processor.doEvaluate (this, processorLock);
555
                } else { // it is already running in other thread of this RP
583
                } else { // it is already running in other thread of this RP
556
584
557
                    if (lastThread != Thread.currentThread()) {
585
                    if (lastThread != Thread.currentThread()) {
Lines 586-592 public final class RequestProcessor { Link Here
586
                boolean toRun;
614
                boolean toRun;
587
615
588
                synchronized (processorLock) {
616
                synchronized (processorLock) {
589
                    toRun = !isFinished() && ((item == null) || item.clear());
617
                    toRun = !isFinished() && ((item == null) || item.clear(null));
590
                }
618
                }
591
619
592
                if (toRun) {
620
                if (toRun) {
Lines 614-620 public final class RequestProcessor { Link Here
614
    /* One item representing the task pending in the pending queue */
642
    /* One item representing the task pending in the pending queue */
615
    private static class Item extends Exception {
643
    private static class Item extends Exception {
616
        private final RequestProcessor owner;
644
        private final RequestProcessor owner;
617
        private Task action;
645
        private Object action;
618
        private boolean enqueued;
646
        private boolean enqueued;
619
647
620
        Item(Task task, RequestProcessor rp) {
648
        Item(Task task, RequestProcessor rp) {
Lines 624-645 public final class RequestProcessor { Link Here
624
        }
652
        }
625
653
626
        Task getTask() {
654
        Task getTask() {
627
            return action;
655
            Object a = action;
656
657
            return (a instanceof Task) ? (Task) a : null;
628
        }
658
        }
629
659
630
        /** Annulate this request iff still possible.
660
        /** Annulate this request iff still possible.
631
         * @returns true if it was possible to skip this item, false
661
         * @returns true if it was possible to skip this item, false
632
         * if the item was/is already processed */
662
         * if the item was/is already processed */
633
        boolean clear() {
663
        boolean clear(Processor processor) {
634
            synchronized (owner.processorLock) {
664
            synchronized (owner.processorLock) {
635
                action = null;
665
                action = processor;
636
666
637
                return enqueued ? owner.queue.remove(this) : true;
667
                return enqueued ? owner.queue.remove(this) : true;
638
            }
668
            }
639
        }
669
        }
640
670
671
        Processor getProcessor() {
672
            Object a = action;
673
674
            return (a instanceof Processor) ? (Processor) a : null;
675
        }
676
641
        int getPriority() {
677
        int getPriority() {
642
            return action.getPriority();
678
            return getTask().getPriority();
643
        }
679
        }
644
680
645
        public Throwable fillInStackTrace() {
681
        public Throwable fillInStackTrace() {
Lines 669-674 public final class RequestProcessor { Link Here
669
705
670
        //private Item task;
706
        //private Item task;
671
        private RequestProcessor source;
707
        private RequestProcessor source;
708
709
        /** task we are working on */
710
        private RequestProcessor.Task todo;
672
        private boolean idle = true;
711
        private boolean idle = true;
673
712
674
        /** Waiting lock */
713
        /** Waiting lock */
Lines 771-777 public final class RequestProcessor { Link Here
771
                    }
810
                    }
772
                }
811
                }
773
812
774
                Task todo;
775
                String debug = null;
813
                String debug = null;
776
814
777
                ErrorManager em = logger();
815
                ErrorManager em = logger();
Lines 782-789 public final class RequestProcessor { Link Here
782
                }
820
                }
783
821
784
                // while we have something to do
822
                // while we have something to do
785
                while ((todo = current.askForWork(this, debug)) != null) {
823
                for (;;) {
786
                    // if(todo != null) {
824
                    // need the same sync as interruptTask
825
                    synchronized (current.processorLock) {
826
                        todo = current.askForWork(this, debug);
827
                        if (todo == null) break;
828
                    }
787
                    setPrio(todo.getPriority());
829
                    setPrio(todo.getPriority());
788
830
789
                    try {
831
                    try {
Lines 813-828 public final class RequestProcessor { Link Here
813
                        doNotify(todo, t);
855
                        doNotify(todo, t);
814
                    }
856
                    }
815
857
816
                    // to improve GC
858
                    // need the same sync as interruptTask
817
                    todo = null;
859
                    synchronized (current.processorLock) {
818
860
                        // to improve GC
819
                    // }
861
                        todo = null;
862
                        // and to clear any possible interrupted state
863
                        // set by calling Task.cancel ()
864
                        Thread.interrupted();
865
                    }
820
                }
866
                }
821
867
822
                if (loggable) {
868
                if (loggable) {
823
                    logger().log(ErrorManager.INFORMATIONAL, "Work finished " + getName()); // NOI18N
869
                    logger().log(ErrorManager.INFORMATIONAL, "Work finished " + getName()); // NOI18N
824
                }
870
                }
825
            }
871
            }
872
        }
873
        
874
        /** Evaluates given task directly.
875
         */
876
        final void doEvaluate (Task t, Object processorLock) {
877
            Task previous = todo;
878
            boolean interrupted = Thread.interrupted();
879
            try {
880
                todo = t;
881
                t.run ();
882
            } finally {
883
                synchronized (processorLock) {
884
                    todo = previous;
885
                    if (interrupted || todo.item == null) {
886
                        Thread.currentThread().interrupt();
887
                    }
888
                }
889
            }
890
        }
891
892
        /** Called under the processorLock */
893
        public void interruptTask(Task t) {
894
            if (t != todo) {
895
                // not running this task so
896
                return;
897
            }
898
899
            // otherwise interrupt this thread
900
            interrupt();
826
        }
901
        }
827
902
828
        /** @see "#20467" */
903
        /** @see "#20467" */
(-)openide/util/test/unit/src/org/openide/util/RequestProcessorTest.java (+251 lines)
Lines 682-687 public class RequestProcessorTest extend Link Here
682
                     (error ? "error" : "exception"), 1);
682
                     (error ? "error" : "exception"), 1);
683
    }
683
    }
684
    
684
    
685
    public void testCancelInterruptsTheRunningThread () throws Exception {
686
        RequestProcessor rp = new RequestProcessor ("Cancellable");
687
        
688
        class R implements Runnable {
689
            public boolean checkBefore;
690
            public boolean checkAfter;
691
            public boolean interrupted;
692
            
693
            public synchronized void run () {
694
                checkBefore = Thread.interrupted();
695
                
696
                notifyAll ();
697
                
698
                try {
699
                    wait ();
700
                    interrupted = false;
701
                } catch (InterruptedException ex) {
702
                    interrupted = true;
703
                }
704
                
705
                notifyAll ();
706
                
707
                try {
708
                    wait ();
709
                } catch (InterruptedException ex) {
710
                }
711
                
712
                checkAfter = Thread.interrupted();
713
                
714
                notifyAll ();
715
            }
716
        }
717
        
718
        R r = new R ();
719
        synchronized (r) {
720
            RequestProcessor.Task t = rp.post (r);
721
            r.wait ();
722
            assertTrue ("The task is already running", !t.cancel ());
723
            r.wait ();
724
            r.notifyAll ();
725
            r.wait ();
726
            assertTrue ("The task has been interrupted", r.interrupted);
727
            assertTrue ("Not before", !r.checkBefore);
728
            assertTrue ("Not after - as the notification was thru InterruptedException", !r.checkAfter);
729
        }
730
        
731
        // interrupt after the task has finished
732
        r = new R ();
733
        synchronized (r) {
734
            RequestProcessor.Task t = rp.post (r);
735
            r.wait ();
736
            r.notifyAll ();
737
            r.wait ();
738
            assertTrue ("The task is already running", !t.cancel ());
739
            r.notifyAll ();
740
            r.wait ();
741
            assertTrue ("The task has not been interrupted by exception", !r.interrupted);
742
            assertTrue ("Not interupted before", !r.checkBefore);
743
            assertTrue ("But interupted after", r.checkAfter);
744
        }
745
    }
746
    
747
    public void testInterruptedStatusIsClearedBetweenTwoTaskExecution () throws Exception {
748
        RequestProcessor rp = new RequestProcessor ("testInterruptedStatusIsClearedBetweenTwoTaskExecution");
749
        
750
        final RequestProcessor.Task[] task = new RequestProcessor.Task[1];
751
        // test interrupted status is cleared after task ends
752
        class Fail implements Runnable {
753
            public boolean checkBefore;
754
            public Thread runIn;
755
            public boolean goodThread;
756
            
757
            public synchronized void run () {
758
                if (runIn == null) {
759
                    runIn = Thread.currentThread();
760
                    task[0].schedule (0);
761
                } else {
762
                    goodThread = Thread.currentThread () == runIn;
763
                }
764
                    
765
                checkBefore = runIn.isInterrupted();
766
                // set the flag for next execution
767
                runIn.interrupt();
768
                
769
                notifyAll ();
770
            }
771
        }
772
        
773
        Fail f = new Fail ();
774
        synchronized (f) {
775
            task[0] = rp.post (f);
776
            
777
            // wait for the first execution
778
            f.wait ();
779
        }
780
        // wait for the second
781
        task[0].waitFinished ();
782
        
783
        assertTrue ("This shall be always true, but if not, than it does not mean too much"
784
            + " just that the tasks were not executed in the same thread. In such case it "
785
            + " this test does not do anything useful as it needs to execute the task twice "
786
            + " in the same thread", f.goodThread);
787
        
788
        assertTrue ("Interrupted state has been cleared between two executions of the task", !f.checkBefore);
789
            
790
    }
791
    
792
    public void testInterruptedStatusWorksInInversedTasks() throws Exception {
793
        RequestProcessor rp = new RequestProcessor ("testInterruptedStatusWorksInInversedTasks");
794
        
795
        class Fail implements Runnable {
796
            public Fail (String n) {
797
                name = n;
798
            }
799
            
800
            private String name;
801
            public RequestProcessor.Task wait;
802
            public Object lock;
803
            
804
            public boolean checkBefore;
805
            public boolean checkAfter;
806
            
807
            public void run () {
808
                synchronized (this) {
809
                    checkBefore = Thread.interrupted();
810
                    notifyAll();
811
                }
812
                if (lock != null) {
813
                    synchronized (lock) {
814
                        lock.notify();
815
                        try {
816
                            lock.wait();
817
                        } catch (InterruptedException ex) {
818
                            ex.printStackTrace();
819
                            fail ("No InterruptedException");
820
                        }
821
                    }
822
                }
823
                
824
                if (wait != null) {
825
                    wait.schedule(100);
826
                    wait.waitFinished();
827
                }
828
                
829
                synchronized (this) {
830
                    checkAfter = Thread.interrupted();
831
                    notifyAll();
832
                }
833
            }
834
            
835
            public String toString () {
836
                return name;
837
            }
838
        }
839
        
840
        Object initLock = new Object();
841
        
842
        Fail smaller = new Fail("smaller");
843
        smaller.lock = initLock;
844
        Fail bigger = new Fail("BIGGER");
845
        RequestProcessor.Task smallerTask, biggerTask;
846
        
847
        
848
        smallerTask = rp.create (smaller);
849
        biggerTask = rp.create (bigger);
850
        
851
        bigger.wait = smallerTask;
852
        
853
        synchronized (initLock) {
854
            biggerTask.schedule(0);
855
            initLock.wait();
856
            initLock.notifyAll();
857
            assertFalse ("Already running", biggerTask.cancel());
858
        }
859
860
        biggerTask.waitFinished();
861
        
862
        assertFalse("bigger not interrupted at begining", bigger.checkBefore);
863
        assertFalse("smaller not interrupted at all", smaller.checkBefore);
864
        assertFalse("smaller not interrupted at all2", smaller.checkAfter);
865
        assertTrue("bigger interrupted at end", bigger.checkAfter);
866
        
867
    }
868
869
    public void testInterruptedStatusWorksInInversedTasksWhenInterruptedSoon() throws Exception {
870
        RequestProcessor rp = new RequestProcessor ("testInterruptedStatusWorksInInversedTasksWhenInterruptedSoon");
871
        
872
        class Fail implements Runnable {
873
            public RequestProcessor.Task wait;
874
            public Object lock;
875
            
876
            public boolean checkBefore;
877
            public boolean checkAfter;
878
            
879
            public void run () {
880
                synchronized (this) {
881
                    checkBefore = Thread.interrupted();
882
                    notifyAll();
883
                }
884
                if (lock != null) {
885
                    synchronized (lock) {
886
                        lock.notify();
887
                    }
888
                }
889
                
890
                if (wait != null) {
891
                    // we cannot call Thread.sleep, so lets slow things own 
892
                    // in other way
893
                    for (int i = 0; i < 10; i++) {
894
                        System.gc ();
895
                    }
896
                    
897
                    wait.waitFinished();
898
                }
899
                
900
                synchronized (this) {
901
                    checkAfter = Thread.interrupted();
902
                    notifyAll();
903
                }
904
            }
905
        }
906
        
907
        Object initLock = new Object();
908
        
909
        Fail smaller = new Fail();
910
        Fail bigger = new Fail();
911
        RequestProcessor.Task smallerTask, biggerTask;
912
        
913
        
914
        smallerTask = rp.create (smaller);
915
        biggerTask = rp.create (bigger);
916
917
        
918
        bigger.lock = initLock;
919
        bigger.wait = smallerTask;
920
        
921
        synchronized (initLock) {
922
            biggerTask.schedule(0);
923
            initLock.wait();
924
            assertFalse ("Already running", biggerTask.cancel());
925
        }
926
927
        biggerTask.waitFinished();
928
        
929
        assertFalse("bigger not interrupted at begining", bigger.checkBefore);
930
        assertFalse("smaller not interrupted at all", smaller.checkBefore);
931
        assertFalse("smaller not interrupted at all2", smaller.checkAfter);
932
        assertTrue("bigger interrupted at end", bigger.checkAfter);
933
        
934
    }
935
    
685
    private static void doGc (int count, Reference toClear) {
936
    private static void doGc (int count, Reference toClear) {
686
        java.util.ArrayList l = new java.util.ArrayList (count);
937
        java.util.ArrayList l = new java.util.ArrayList (count);
687
        while (count-- > 0) {
938
        while (count-- > 0) {

Return to bug 33467