json/src/main/java/org/netbeans/html/json/spi/Observers.java
author Jaroslav Tulach <jtulach@netbeans.org>
Wed, 11 May 2016 06:22:49 +0200
branchConcurrentComputed259132
changeset 1092 9329156bb5d4
parent 838 bdc3d696dd4a
permissions -rw-r--r--
#259132: Ready for multi-threaded access to observables
jtulach@774
     1
/**
jtulach@774
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
jtulach@774
     3
 *
jtulach@774
     4
 * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
jtulach@774
     5
 *
jtulach@774
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
jtulach@774
     7
 * Other names may be trademarks of their respective owners.
jtulach@774
     8
 *
jtulach@774
     9
 * The contents of this file are subject to the terms of either the GNU
jtulach@774
    10
 * General Public License Version 2 only ("GPL") or the Common
jtulach@774
    11
 * Development and Distribution License("CDDL") (collectively, the
jtulach@774
    12
 * "License"). You may not use this file except in compliance with the
jtulach@774
    13
 * License. You can obtain a copy of the License at
jtulach@774
    14
 * http://www.netbeans.org/cddl-gplv2.html
jtulach@774
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
jtulach@774
    16
 * specific language governing permissions and limitations under the
jtulach@774
    17
 * License.  When distributing the software, include this License Header
jtulach@774
    18
 * Notice in each file and include the License file at
jtulach@774
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
jtulach@774
    20
 * particular file as subject to the "Classpath" exception as provided
jtulach@774
    21
 * by Oracle in the GPL Version 2 section of the License file that
jtulach@774
    22
 * accompanied this code. If applicable, add the following below the
jtulach@774
    23
 * License Header, with the fields enclosed by brackets [] replaced by
jtulach@774
    24
 * your own identifying information:
jtulach@774
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
jtulach@774
    26
 *
jtulach@774
    27
 * Contributor(s):
jtulach@774
    28
 *
jtulach@774
    29
 * The Original Software is NetBeans. The Initial Developer of the Original
jtulach@774
    30
 * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
jtulach@774
    31
 *
jtulach@774
    32
 * If you wish your version of this file to be governed by only the CDDL
jtulach@774
    33
 * or only the GPL Version 2, indicate your decision by adding
jtulach@774
    34
 * "[Contributor] elects to include this software in this distribution
jtulach@774
    35
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
jtulach@774
    36
 * single choice of license, a recipient has the option to distribute
jtulach@774
    37
 * your version of this file under either the CDDL, the GPL Version 2 or
jtulach@774
    38
 * to extend the choice of license to its licensees as provided above.
jtulach@774
    39
 * However, if you add GPL Version 2 code and therefore, elected the GPL
jtulach@774
    40
 * Version 2 license, then the option applies only if the new code is
jtulach@774
    41
 * made subject to such option by the copyright holder.
jtulach@774
    42
 */
jtulach@838
    43
package org.netbeans.html.json.spi;
jtulach@774
    44
jtulach@774
    45
import java.lang.ref.WeakReference;
jtulach@782
    46
import java.util.ArrayList;
jtulach@782
    47
import java.util.Iterator;
jtulach@782
    48
import java.util.LinkedList;
jtulach@782
    49
import java.util.List;
jtulach@774
    50
jtulach@774
    51
/**
jtulach@774
    52
 *
jtulach@774
    53
 * @author Jaroslav Tulach
jtulach@774
    54
 */
jtulach@783
    55
final class Observers {
jtulach@782
    56
    private static final LinkedList<Watcher> GLOBAL = new LinkedList<Watcher>();
jtulach@783
    57
    private final List<Watcher> watchers = new ArrayList<Watcher>();
jtulach@783
    58
    private final List<Ref> observers = new ArrayList<Ref>();
jtulach@775
    59
jtulach@785
    60
    Observers() {
jtulach@785
    61
        assert Thread.holdsLock(GLOBAL);
jtulach@774
    62
    }
jtulach@774
    63
    
jtulach@778
    64
    static void beginComputing(Proto p, String name) {
jtulach@782
    65
        synchronized (GLOBAL) {
jtulach@782
    66
            verifyUnlocked(p);
jtulach@778
    67
            final Watcher nw = new Watcher(p, name);
jtulach@782
    68
            GLOBAL.push(nw);
jtulach@778
    69
        }
jtulach@778
    70
    }
jtulach@778
    71
    
jtulach@778
    72
    static void verifyUnlocked(Proto p) {
jtulach@782
    73
        synchronized (GLOBAL) {
jtulach@782
    74
            for (Watcher w : GLOBAL) {
jtulach@782
    75
                if (w.proto == p) {
jtulach@1092
    76
                    if (w.owner == Thread.currentThread()) {
jtulach@1092
    77
                        throw new IllegalStateException("Re-entrant attempt to access " + p);
jtulach@1092
    78
                    }
jtulach@782
    79
                }
jtulach@778
    80
            }
jtulach@778
    81
        }        
jtulach@778
    82
    }
jtulach@778
    83
jtulach@785
    84
    static void accessingValue(Proto p, String propName) {
jtulach@782
    85
        synchronized (GLOBAL) {
jtulach@782
    86
            verifyUnlocked(p);
jtulach@782
    87
            for (Watcher w : GLOBAL) {
jtulach@785
    88
                Observers mine = p.observers(true);
jtulach@785
    89
                mine.add(w, new Ref(w, propName));
jtulach@778
    90
            }
jtulach@778
    91
        }
jtulach@778
    92
    }
jtulach@778
    93
    
jtulach@785
    94
    static void finishComputing(Proto p) {
jtulach@782
    95
        synchronized (GLOBAL) {
jtulach@1092
    96
            boolean found = false;
jtulach@1092
    97
            Iterator<Watcher> it = GLOBAL.iterator();
jtulach@1092
    98
            while (it.hasNext()) {
jtulach@1092
    99
                Watcher w = it.next();
jtulach@1092
   100
                if (w.proto == p && w.owner == Thread.currentThread()) {
jtulach@1092
   101
                    if (w.prop != null) {
jtulach@1092
   102
                        Observers mine = p.observers(true);
jtulach@1092
   103
                        mine.add(w);
jtulach@1092
   104
                    }
jtulach@1092
   105
                    found = true;
jtulach@1092
   106
                    it.remove();
jtulach@1092
   107
                }
jtulach@778
   108
            }
jtulach@1092
   109
            if (!found) {
jtulach@1092
   110
                throw new IllegalStateException("Cannot find " + p + " in " + GLOBAL);
jtulach@778
   111
            }
jtulach@776
   112
        }
jtulach@775
   113
    }
jtulach@774
   114
    
jtulach@782
   115
    private static final class Ref extends WeakReference<Watcher> {
jtulach@774
   116
        private final String prop;
jtulach@774
   117
        
jtulach@774
   118
        public Ref(Watcher ref, String prop) {
jtulach@774
   119
            super(ref);
jtulach@774
   120
            this.prop = prop;
jtulach@774
   121
        }
jtulach@774
   122
        
jtulach@782
   123
        final Watcher watcher() {
jtulach@774
   124
            Watcher w = get();
jtulach@783
   125
            if (w == null) {
jtulach@783
   126
                return null;
jtulach@783
   127
            }
jtulach@785
   128
            final Observers o = w.proto.observers(false);
jtulach@783
   129
            if (o == null) {
jtulach@783
   130
                return null;
jtulach@783
   131
            }
jtulach@783
   132
            if (o.find(w.prop) == w) {
jtulach@774
   133
                return w;
jtulach@774
   134
            }
jtulach@774
   135
            return null;
jtulach@774
   136
        }
jtulach@782
   137
    }
jtulach@782
   138
    
jtulach@783
   139
    private Watcher find(String prop) {
jtulach@783
   140
        if (prop == null) {
jtulach@782
   141
            return null;
jtulach@782
   142
        }
jtulach@783
   143
        for (Watcher w : watchers) {
jtulach@783
   144
            if (prop.equals(w.prop)) {
jtulach@783
   145
                return w;
jtulach@783
   146
            }
jtulach@783
   147
        }
jtulach@783
   148
        return null;
jtulach@783
   149
    }
jtulach@782
   150
jtulach@782
   151
        final void add(Watcher w) {
jtulach@783
   152
        for (int i = 0; i < watchers.size(); i++) {
jtulach@783
   153
            Watcher ith = watchers.get(i);
jtulach@783
   154
            if (w.prop == null) {
jtulach@783
   155
                if (ith.prop == null) {
jtulach@782
   156
                    watchers.set(i, w);
jtulach@782
   157
                    return;
jtulach@782
   158
                }
jtulach@783
   159
            } else if (w.prop.equals(ith.prop)) {
jtulach@783
   160
                watchers.set(i, w);
jtulach@783
   161
                return;
jtulach@782
   162
            }
jtulach@782
   163
        }
jtulach@783
   164
        watchers.add(w);
jtulach@782
   165
    }
jtulach@782
   166
jtulach@785
   167
    static final void valueHasMutated(Proto p, String propName) {
jtulach@783
   168
        List<Watcher> mutated = new LinkedList<Watcher>();
jtulach@783
   169
        synchronized (GLOBAL) {
jtulach@785
   170
            Observers mine = p.observers(false);
jtulach@785
   171
            if (mine == null) {
jtulach@785
   172
                return;
jtulach@785
   173
            }
jtulach@785
   174
            Iterator<Ref> it = mine.observers.iterator();
jtulach@783
   175
            while (it.hasNext()) {
jtulach@783
   176
                Ref ref = it.next();
jtulach@783
   177
                if (ref.get() == null) {
jtulach@783
   178
                    it.remove();
jtulach@783
   179
                    continue;
jtulach@783
   180
                }
jtulach@783
   181
                if (ref.prop.equals(propName)) {
jtulach@783
   182
                    Watcher w = ref.watcher();
jtulach@783
   183
                    if (w != null) {
jtulach@783
   184
                        mutated.add(w);
jtulach@774
   185
                    }
jtulach@774
   186
                }
jtulach@774
   187
            }
jtulach@783
   188
        }
jtulach@785
   189
        for (Watcher w : mutated) {
jtulach@783
   190
            w.proto.valueHasMutated(w.prop);
jtulach@783
   191
        }
jtulach@783
   192
    }
jtulach@783
   193
jtulach@785
   194
    void add(Watcher w, Ref r) {
jtulach@783
   195
        Thread.holdsLock(GLOBAL);
jtulach@783
   196
        if (w == null) {
jtulach@783
   197
            return;
jtulach@783
   198
        }
jtulach@783
   199
        Iterator<Ref> it = observers.iterator();
jtulach@783
   200
        while (it.hasNext()) {
jtulach@783
   201
            Ref ref = it.next();
jtulach@783
   202
            if (r == ref) {
jtulach@783
   203
                return;
jtulach@783
   204
            }
jtulach@783
   205
            final Watcher rw = ref.get();
jtulach@783
   206
            if (rw == null) {
jtulach@783
   207
                it.remove();
jtulach@783
   208
                continue;
jtulach@783
   209
            }
jtulach@783
   210
            if (rw == w && r.prop.equals(r.prop)) {
jtulach@783
   211
                return;
jtulach@782
   212
            }
jtulach@774
   213
        }
jtulach@783
   214
        observers.add(r);
jtulach@783
   215
    }
jtulach@783
   216
    
jtulach@783
   217
    private static final class Watcher {
jtulach@1092
   218
        final Thread owner;
jtulach@783
   219
        final Proto proto;
jtulach@783
   220
        final String prop;
jtulach@774
   221
jtulach@783
   222
        Watcher(Proto proto, String prop) {
jtulach@1092
   223
            this.owner = Thread.currentThread();
jtulach@783
   224
            this.proto = proto;
jtulach@783
   225
            this.prop = prop;
jtulach@783
   226
        }
jtulach@783
   227
        
jtulach@783
   228
        @Override
jtulach@783
   229
        public String toString() {
jtulach@783
   230
            return "Watcher: " + proto + ", " + prop;
jtulach@774
   231
        }
jtulach@774
   232
    }
jtulach@774
   233
}