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