All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.objectfabric.WorkspaceLoad Maven / Gradle / Ivy

There is a newer version: 0.9.1
Show newest version
/**
 * This file is part of ObjectFabric (http://objectfabric.org).
 *
 * ObjectFabric is licensed under the Apache License, Version 2.0, the terms
 * of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
 * 
 * Copyright ObjectFabric Inc.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

package org.objectfabric;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Workspace state has to be loaded from cache or remote location as JavaScript platform
 * cannot generate reliable UIDs. This unfortunately adds some complexity, but also
 * happens to be an optimization. For eventual consistency it allows reuse of Peer UIDs,
 * and for serialization it allows TObject ids from several application runs to be from
 * the same Range. TODO: formalize this using interval tree clocks or something.
 */
@SuppressWarnings("serial")
abstract class WorkspaceLoad extends AtomicInteger {

    private static final class State {

        long Tick;

        byte[] Range;

        byte Id;
    }

    private static final PlatformConcurrentQueue _saved = new PlatformConcurrentQueue();

    private final URIResolver _resolver;

    WorkspaceLoad(URIResolver resolver) {
        super(1);

        if (resolver == null)
            throw new IllegalArgumentException();

        _resolver = resolver;
    }

    final void run() {
        State state = _saved.poll();

        if (state != null)
            onResponse(state.Tick, state.Range, state.Id);
        else {
            request(_resolver.uriHandlers());
            request(_resolver.caches());

            for (Location location : _resolver.origins().keySet())
                request(location);

            // For initial value 1, makes sure not done before last request sent
            onResponseNull();
        }
    }

    static final void recycle(long tick, byte[] range, byte id) {
        State state = new State();
        state.Tick = tick;
        state.Range = range;
        state.Id = id;
        _saved.add(state);
    }

    private final void request(Object[] array) {
        if (array != null)
            for (int i = 0; i < array.length; i++)
                if (array[i] instanceof Location)
                    request((Location) array[i]);
    }

    private final void request(Location location) {
        for (;;) {
            int value = get();

            if (isDone(value))
                break;

            if (compareAndSet(value, value + 1)) {
                location.start(this);
                break;
            }
        }
    }

    abstract void done(long tick, byte[] range, byte id);

    final boolean isDone() {
        return isDone(get());
    }

    private final boolean isDone(int value) {
        return value < 0;
    }

    final void onResponse(final long tick, final byte[] range, final byte id) {
        if (Debug.ENABLED)
            Debug.assertion(get() == -1 || get() > 0);

        for (;;) {
            int value = get();

            if (isDone(value)) { // Put back
                WorkspaceSave save = new WorkspaceSave() {

                    @Override
                    void run(Callback callback) {
                        callback.run(tick, range, id);
                    }

                    @Override
                    void done() {
                    }
                };

                save.run(_resolver);
                break;
            }

            if (compareAndSet(value, -1)) {
                done(tick, range, id);
                break;
            }
        }
    }

    final void onResponseNull() {
        if (Debug.ENABLED)
            Debug.assertion(get() == -1 || get() > 0);

        for (;;) {
            int value = get();

            if (isDone(value))
                break;

            int update = value - 1;

            if (compareAndSet(value, update)) {
                if (update == 0) {
                    Peer peer = Peer.get(new UID(Platform.get().newUID()));
                    done(Tick.get(peer.index(), 1), Platform.get().newUID(), (byte) 0);
                }

                break;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy