Merging and documenting for for #259132
authorJaroslav Tulach <jtulach@netbeans.org>
Wed, 11 May 2016 07:18:59 +0200
changeset 10937d3990311255
parent 1087 7865ad598184
parent 1092 9329156bb5d4
child 1094 47cd2110ed8d
child 1099 833829b33779
Merging and documenting for for #259132
src/main/javadoc/overview.html
     1.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Observers.java	Tue May 03 04:44:10 2016 +0200
     1.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Observers.java	Wed May 11 07:18:59 2016 +0200
     1.3 @@ -73,7 +73,9 @@
     1.4          synchronized (GLOBAL) {
     1.5              for (Watcher w : GLOBAL) {
     1.6                  if (w.proto == p) {
     1.7 -                    throw new IllegalStateException("Re-entrant attempt to access " + p);
     1.8 +                    if (w.owner == Thread.currentThread()) {
     1.9 +                        throw new IllegalStateException("Re-entrant attempt to access " + p);
    1.10 +                    }
    1.11                  }
    1.12              }
    1.13          }        
    1.14 @@ -91,13 +93,21 @@
    1.15      
    1.16      static void finishComputing(Proto p) {
    1.17          synchronized (GLOBAL) {
    1.18 -            Watcher w = GLOBAL.pop();
    1.19 -            if (w.proto != p) {
    1.20 -                throw new IllegalStateException("Inconsistency: " + w.proto + " != " + p);
    1.21 +            boolean found = false;
    1.22 +            Iterator<Watcher> it = GLOBAL.iterator();
    1.23 +            while (it.hasNext()) {
    1.24 +                Watcher w = it.next();
    1.25 +                if (w.proto == p && w.owner == Thread.currentThread()) {
    1.26 +                    if (w.prop != null) {
    1.27 +                        Observers mine = p.observers(true);
    1.28 +                        mine.add(w);
    1.29 +                    }
    1.30 +                    found = true;
    1.31 +                    it.remove();
    1.32 +                }
    1.33              }
    1.34 -            if (w.prop != null) {
    1.35 -                Observers mine = p.observers(true);
    1.36 -                mine.add(w);
    1.37 +            if (!found) {
    1.38 +                throw new IllegalStateException("Cannot find " + p + " in " + GLOBAL);
    1.39              }
    1.40          }
    1.41      }
    1.42 @@ -205,10 +215,12 @@
    1.43      }
    1.44      
    1.45      private static final class Watcher {
    1.46 +        final Thread owner;
    1.47          final Proto proto;
    1.48          final String prop;
    1.49  
    1.50          Watcher(Proto proto, String prop) {
    1.51 +            this.owner = Thread.currentThread();
    1.52              this.proto = proto;
    1.53              this.prop = prop;
    1.54          }
     2.1 --- a/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java	Tue May 03 04:44:10 2016 +0200
     2.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/DeepChangeTest.java	Wed May 11 07:18:59 2016 +0200
     2.3 @@ -521,6 +521,17 @@
     2.4          void set(Object v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
     2.5              pb.setValue(v);
     2.6          }
     2.7 +
     2.8 +        void assertNoChange(String msg) {
     2.9 +            assertEquals(changes, 0, msg);
    2.10 +        }
    2.11 +
    2.12 +        void assertChange(String msg) {
    2.13 +            if (changes == 0) {
    2.14 +                fail(msg);
    2.15 +            }
    2.16 +            changes = 0;
    2.17 +        }
    2.18      }
    2.19  
    2.20      static final class MapTechnology
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/ParallelChangeTest.java	Wed May 11 07:18:59 2016 +0200
     3.3 @@ -0,0 +1,199 @@
     3.4 +/**
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     3.8 + *
     3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.10 + * Other names may be trademarks of their respective owners.
    3.11 + *
    3.12 + * The contents of this file are subject to the terms of either the GNU
    3.13 + * General Public License Version 2 only ("GPL") or the Common
    3.14 + * Development and Distribution License("CDDL") (collectively, the
    3.15 + * "License"). You may not use this file except in compliance with the
    3.16 + * License. You can obtain a copy of the License at
    3.17 + * http://www.netbeans.org/cddl-gplv2.html
    3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.19 + * specific language governing permissions and limitations under the
    3.20 + * License.  When distributing the software, include this License Header
    3.21 + * Notice in each file and include the License file at
    3.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    3.23 + * particular file as subject to the "Classpath" exception as provided
    3.24 + * by Oracle in the GPL Version 2 section of the License file that
    3.25 + * accompanied this code. If applicable, add the following below the
    3.26 + * License Header, with the fields enclosed by brackets [] replaced by
    3.27 + * your own identifying information:
    3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.29 + *
    3.30 + * Contributor(s):
    3.31 + *
    3.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    3.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    3.34 + *
    3.35 + * If you wish your version of this file to be governed by only the CDDL
    3.36 + * or only the GPL Version 2, indicate your decision by adding
    3.37 + * "[Contributor] elects to include this software in this distribution
    3.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    3.39 + * single choice of license, a recipient has the option to distribute
    3.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    3.41 + * to extend the choice of license to its licensees as provided above.
    3.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    3.43 + * Version 2 license, then the option applies only if the new code is
    3.44 + * made subject to such option by the copyright holder.
    3.45 + */
    3.46 +package org.netbeans.html.json.impl;
    3.47 +
    3.48 +import java.util.Map;
    3.49 +import java.util.concurrent.CountDownLatch;
    3.50 +import java.util.concurrent.ExecutorService;
    3.51 +import java.util.concurrent.Executors;
    3.52 +import java.util.concurrent.TimeUnit;
    3.53 +import net.java.html.BrwsrCtx;
    3.54 +import net.java.html.json.ComputedProperty;
    3.55 +import net.java.html.json.Model;
    3.56 +import net.java.html.json.Models;
    3.57 +import net.java.html.json.Property;
    3.58 +import org.netbeans.html.context.spi.Contexts;
    3.59 +import org.netbeans.html.json.impl.DeepChangeTest.One;
    3.60 +import org.netbeans.html.json.spi.Technology;
    3.61 +import org.netbeans.html.json.spi.Transfer;
    3.62 +import org.testng.annotations.BeforeMethod;
    3.63 +import org.testng.annotations.Test;
    3.64 +import static org.testng.Assert.assertEquals;
    3.65 +
    3.66 +public class ParallelChangeTest {
    3.67 +    private DeepChangeTest.MapTechnology t;
    3.68 +    private BrwsrCtx c;
    3.69 +
    3.70 +    @BeforeMethod public void initTechnology() {
    3.71 +        t = new DeepChangeTest.MapTechnology();
    3.72 +        c = Contexts.newBuilder().register(Technology.class, t, 1).
    3.73 +            register(Transfer.class, t, 1).build();
    3.74 +    }
    3.75 +
    3.76 +    @Test
    3.77 +    public void multipleValues() throws InterruptedException {
    3.78 +        doTest(true);
    3.79 +    }
    3.80 +
    3.81 +    @Test
    3.82 +    public void singleValue() throws InterruptedException {
    3.83 +        doTest(false);
    3.84 +    }
    3.85 +
    3.86 +    private void doTest(boolean multipleValues) throws InterruptedException {
    3.87 +        class Test implements Runnable {
    3.88 +            final int offset;
    3.89 +            final Depending dep;
    3.90 +            private Error error;
    3.91 +
    3.92 +            public Test(int index, Depending dep) {
    3.93 +                this.offset = index;
    3.94 +                this.dep = dep;
    3.95 +            }
    3.96 +
    3.97 +            @Override
    3.98 +            public void run() {
    3.99 +                try {
   3.100 +                    int value = dep.getValuePlusAdd();
   3.101 +                    assertEquals(value, offset + 11, "Offset " + offset + " plus one plus ten");
   3.102 +                } catch (Error err) {
   3.103 +                    this.error = err;
   3.104 +                }
   3.105 +            }
   3.106 +
   3.107 +            private void assertException() {
   3.108 +                if (error != null) {
   3.109 +                    throw error;
   3.110 +                }
   3.111 +            }
   3.112 +        }
   3.113 +
   3.114 +        Depending[] deps = new Depending[2];
   3.115 +        BlockingValue[] values = new BlockingValue[deps.length];
   3.116 +        CountDownLatch blockInCall = new CountDownLatch(deps.length);
   3.117 +        Test[] runs = new Test[deps.length];
   3.118 +        ExecutorService exec = Executors.newFixedThreadPool(deps.length);
   3.119 +        for (int i = 0; i < deps.length; i++) {
   3.120 +            if (multipleValues) {
   3.121 +                values[i] = BlockingValueCntrl.create(c);
   3.122 +            } else {
   3.123 +                values[i] = i == 0 ? BlockingValueCntrl.create(c) : values[0];
   3.124 +            }
   3.125 +            deps[i] = DependingCntrl.create(c, values[i], 10);
   3.126 +            runs[i] = new Test(0, deps[i]);
   3.127 +        }
   3.128 +        BlockingValueCntrl.initialize(blockInCall);
   3.129 +        for (int i = 0; i < deps.length; i++) {
   3.130 +            exec.execute(runs[i]);
   3.131 +        }
   3.132 +
   3.133 +        exec.awaitTermination(1, TimeUnit.SECONDS);
   3.134 +
   3.135 +        for (int i = 0; i < deps.length; i++) {
   3.136 +            Map raw = (Map) Models.toRaw(deps[i]);
   3.137 +            One value = (One) raw.get("valuePlusAdd");
   3.138 +            value.assertNoChange("No changes yet for index " + i);
   3.139 +        }
   3.140 +
   3.141 +        for (int i = 0; i < deps.length; i++) {
   3.142 +            runs[i].assertException();
   3.143 +            values[i].setValue(30);
   3.144 +        }
   3.145 +
   3.146 +        for (int i = 0; i < deps.length; i++) {
   3.147 +            Map raw = (Map) Models.toRaw(deps[i]);
   3.148 +            One value = (One) raw.get("valuePlusAdd");
   3.149 +            value.assertChange("A change for index " + i);
   3.150 +        }
   3.151 +
   3.152 +        for (int i = 0; i < deps.length; i++) {
   3.153 +            assertEquals(deps[i].getValuePlusAdd(), 41, "[" + i + "] = 0 plus 30 plus one plus 10");
   3.154 +        }
   3.155 +    }
   3.156 +
   3.157 +    @Model(className="BlockingValue", properties = {
   3.158 +        @Property(name = "value", type = int.class)
   3.159 +    })
   3.160 +    static class BlockingValueCntrl {
   3.161 +        private static CountDownLatch latch;
   3.162 +
   3.163 +        static void initialize(CountDownLatch l) {
   3.164 +            latch = l;
   3.165 +        }
   3.166 +
   3.167 +        @ComputedProperty
   3.168 +        static int plusOne(int value)  {
   3.169 +            if (latch != null) {
   3.170 +                latch.countDown();
   3.171 +                try {
   3.172 +                    latch.await();
   3.173 +                } catch (InterruptedException ex) {
   3.174 +                    throw new IllegalStateException(ex);
   3.175 +                }
   3.176 +            }
   3.177 +            return value + 1;
   3.178 +        }
   3.179 +
   3.180 +        static BlockingValue create(BrwsrCtx c) {
   3.181 +            return Models.bind(new BlockingValue(), c);
   3.182 +        }
   3.183 +    }
   3.184 +
   3.185 +    @Model(className = "Depending", properties = {
   3.186 +        @Property(name = "add", type = int.class),
   3.187 +        @Property(name = "dep", type = BlockingValue.class)
   3.188 +    })
   3.189 +    static class DependingCntrl {
   3.190 +        @ComputedProperty
   3.191 +        static int valuePlusAdd(BlockingValue dep, int add) {
   3.192 +            return dep.getPlusOne() + add;
   3.193 +        }
   3.194 +
   3.195 +        static Depending create(BrwsrCtx c, BlockingValue value, int add) {
   3.196 +            Depending d = Models.bind(new Depending(add, null), c);
   3.197 +            d.setDep(value);
   3.198 +            return d;
   3.199 +        }
   3.200 +    }
   3.201 +
   3.202 +}
     4.1 --- a/src/main/javadoc/overview.html	Tue May 03 04:44:10 2016 +0200
     4.2 +++ b/src/main/javadoc/overview.html	Wed May 11 07:18:59 2016 +0200
     4.3 @@ -79,6 +79,8 @@
     4.4  
     4.5          Both values <code>null</code> and <code>undefined</code> are
     4.6          <a href="net/java/html/js/package-summary.html#undefined">treated as null</a>.
     4.7 +        Better behavior under <a target="_blank" href="https://netbeans.org/bugzilla/show_bug.cgi?id=259132">
     4.8 +        multi-threaded load</a>.
     4.9  
    4.10          <h3>Improvements in version 1.3</h3>
    4.11