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

org.objectfabric.Watcher Maven / Gradle / Ivy

The 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 org.objectfabric.Actor.Flush;
import org.objectfabric.CloseCounter.Callback;
import org.objectfabric.Counter.CounterRead;
import org.objectfabric.Counter.CounterSharedVersion;
import org.objectfabric.Counter.CounterVersion;
import org.objectfabric.Resource.NewBlock;
import org.objectfabric.Resource.ResourceRead;
import org.objectfabric.Resource.ResourceVersion;
import org.objectfabric.TObject.Version;
import org.objectfabric.ThreadAssert.AllowSharedRead;
import org.objectfabric.ThreadAssert.SingleThreaded;

/**
 * Interface between a workspace and the IO systems. Listen to updates and handles
 * serialization/deserialization.
 */
@SuppressWarnings("serial")
@SingleThreaded
final class Watcher extends Extension {

    @AllowSharedRead
    private final Run _run = new Run();

    private final Writer _writer = new Writer(this);

    private final List _buffs = new List();

    private final Queue _added = new Queue();

    private final PlatformMap _hasPendingAcks = new PlatformMap();

    private final List _flushes = new List();

    private Clock _clock;

    private Version[] _versions;

    Watcher(Workspace workspace) {
        super(workspace, true);

        if (Debug.THREADS) {
            ThreadAssert.exchangeGiveList(_run, _writer.getThreadContextObjects());
            ThreadAssert.exchangeGive(_run, this);
        }
    }

    final Actor actor() {
        return _run;
    }

    final Clock clock() {
        return _clock;
    }

    final void start() {
        if (Debug.THREADS)
            ThreadAssert.exchangeTake(_run);

        workspace().register(this, _run);

        if (Debug.THREADS) {
            ThreadAssert.exchangeGiveList(_run, _writer.getThreadContextObjects());
            ThreadAssert.exchangeGive(_run, this);
        }

        _run.onStarted();
    }

    @Override
    final boolean casSnapshotWithoutThis(Snapshot expected, Snapshot update, Exception exception) {
        boolean value = super.casSnapshotWithoutThis(expected, update, exception);

        if (value) {
            while (_buffs.size() > 0) {
                Buff buff = _buffs.removeLast();
                buff.recycle();
            }

            if (Debug.ENABLED)
                assertIdle();
        }

        return value;
    }

    final void cleanThreadContext() {
        Object key;

        if (Debug.ENABLED) {
            ThreadAssert.suspend(key = new Object());
            ThreadAssert.resume(_run, false);
        }

        if (Debug.THREADS) {
            if (_clock != null)
                ThreadAssert.removePrivate(_clock);

            ThreadAssert.exchangeTake(_run);
            ThreadAssert.removePrivateList(_writer.getThreadContextObjects());
            ThreadAssert.removePrivate(Watcher.this);
        }

        if (Debug.ENABLED)
            ThreadAssert.resume(key);
    }

    final void run() {
        if (Debug.ENABLED)
            Debug.assertion(Platform.get().value() == Platform.GWT);

        _run.run();
    }

    private final class Run extends Actor implements Runnable {

        @Override
        protected void enqueue() {
            Platform.get().execute(this);
        }

        private static final int WALK = 0;

        private static final int COMMIT = 1;

        @Override
        public void run() {
            if (Debug.ENABLED)
                ThreadAssert.resume(this, false);

            if (Debug.THREADS)
                ThreadAssert.exchangeTake(this);

            onRunStarting();
            runMessages(interrupted());

            int step = WALK;

            if (interrupted())
                step = resumeInt();
            else {
                if (_clock != null)
                    _clock.start();
            }

            switch (step) {
                case WALK: {
                    walk();

                    if (interrupted()) {
                        interruptInt(WALK);
                        break;
                    }
                }
                case COMMIT: {
                    if (_clock != null)
                        _clock.commit();

                    if (interrupted()) {
                        interruptInt(COMMIT);
                        break;
                    }
                }
            }

            boolean interrupted = interrupted();

            if (Debug.ENABLED)
                ThreadAssert.suspend(this);

            onRunEnded(interrupted);
        }

        @Override
        void onClose(Callback closeCallback) {
            super.onClose(null);
            Object key;

            if (Debug.ENABLED) {
                ThreadAssert.suspend(key = new Object());
                ThreadAssert.resume(Run.this, false);
            }

            workspace().unregister(Watcher.this, _run, null);

            if (Debug.ENABLED)
                ThreadAssert.suspend(Run.this);

            closeCallback.call();

            if (Debug.ENABLED)
                ThreadAssert.resume(key);
        }
    }

    //

    private final Clock createClock() {
        Clock clock = request(workspace().caches());

        if (clock == null)
            clock = request(workspace().uriHandlers());

        if (clock == null) {
            for (Location location : workspace().resolver().origins().keySet()) {
                clock = location.newClock(this);

                if (clock != null)
                    break;
            }
        }

        if (clock == null)
            clock = workspace().newDefaultClock();

        return clock;
    }

    private final Clock request(Object[] array) {
        for (int i = 0; array != null && i < array.length; i++) {
            if (array[i] instanceof Location) {
                Clock clock = ((Location) array[i]).newClock(this);

                if (clock != null)
                    return clock;
            }
        }

        return null;
    }

    //

    @Override
    final Action onVisitingTObject(TObject object) {
        Action action = super.onVisitingTObject(object);

        if (action == Action.VISIT)
            if (object.isReferencedByURI())
                return Action.VISIT;

        return Action.SKIP;
    }

    @Override
    final Action onVisitingMap(int mapIndex) {
        Action action = super.onVisitingMap(mapIndex);

        if (snapshot().getVersionMaps()[mapIndex].isRemote())
            action = Action.SKIP;

        return action;
    }

    @Override
    void onVisitingResources(Resources resources) {
        super.onVisitingResources(resources);

        if (resources.size() > 0) {
            if (_clock == null)
                _clock = createClock();

            _clock.writing(resources);
        }
    }

    @Override
    final void onVisitingResource(Resource resource) {
        super.onVisitingResource(resource);

        if (interrupted())
            resume();

        if (_clock.peer() == null) {
            interrupt(null);
            return;
        }

        _writer.reset();

        if (_buffs.size() == 0) {
            Buff buff = addBuffer();
            buff.putByte(TObject.SERIALIZATION_VERSION);
        } else {
            if (Debug.ENABLED)
                Debug.assertion(_buffs.size() == 1 && _buffs.get(0).position() == 1);
        }
    }

    @Override
    final void onVisitingVersion(Version version) {
        super.onVisitingVersion(version);

        if (_versions == null)
            _versions = new Version[OpenMap.CAPACITY];

        _versions = TransactionBase.putVersion(_versions, version);
    }

    final void onWriting(TObject object) {
        if (!object.isReferencedByURI()) {
            object.setReferencedByURI();
            _added.add(object);
        }
    }

    @Override
    final void onVisitedResource(Resource resource) {
        super.onVisitedResource(resource);

        if (_added.size() > 0) {
            visitingNewObject(true);
            int map1 = mapIndex1();
            int map2 = mapIndex2();
            mapIndex1(TransactionManager.OBJECTS_VERSIONS_INDEX);
            mapIndex2(snapshot().writes().length);

            for (;;) {
                TObject object = _added.poll();

                if (object == null)
                    break;

                if (Debug.ENABLED)
                    Debug.assertion(object.resource() == resource);

                visit(object);
            }

            mapIndex1(map1);
            mapIndex2(map2);
            visitingNewObject(false);
        }

        if (_buffs.size() > 1 || _buffs.get(0).position() > 1)
            _clock.onBlock(resource, _versions);

        _versions = null;
    }

    //

    final void writeDependency(long tick) {
        for (;;) {
            _writer.writePeerTick(Writer.COMMAND_DEPENDENCY, tick);

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    final void writeHappenedBefore(long[] ticks) {
        for (int i = 0; i < ticks.length; i++) {
            if (!Tick.isNull(ticks[i]) && Tick.peer(ticks[i]) != _clock.peer().index()) {
                for (;;) {
                    _writer.writePeerTick(Writer.COMMAND_HAPPENED_BEFORE, ticks[i]);

                    if (!interrupted())
                        break;

                    addBuffer();
                }
            }
        }
    }

    final Buff[] finishTick() {
        for (;;) {
            _writer.writeCommand(Writer.COMMAND_TICK);

            if (!interrupted())
                break;

            addBuffer();
        }

        Buff[] buffs = new Buff[_buffs.size()];
        _buffs.copyToFixed(buffs);
        _buffs.clear();

        for (int i = 0; i < buffs.length; i++) {
            buffs[i].limit(buffs[i].position());
            buffs[i].position(0);
            buffs[i].mark();

            if (Debug.ENABLED)
                buffs[i].lock(buffs[i].limit());
        }

        return buffs;
    }

    private final Buff addBuffer() {
        Buff buff = Buff.getOrCreate();
        _buffs.add(buff);
        _writer.setBuff(buff);
        return buff;
    }

    //

    final boolean hasPendingAcks(Resource resource) {
        return _hasPendingAcks.containsKey(resource);
    }

    final void addHasPendingAcks(Resource resource) {
        Resource previous = _hasPendingAcks.put(resource, resource);

        if (Debug.ENABLED)
            Debug.assertion(previous == null);
    }

    final boolean removeHasPendingAcks(Resource resource, boolean checkChanged) {
        Resource previous = _hasPendingAcks.remove(resource);

        if (Debug.ENABLED && checkChanged)
            Debug.assertion(previous != null);

        return previous != null;
    }

    //

    final void startFlush(FutureWithCallback future) {
        _run.addAndRun(new WriteFlush(future));
    }

    final void onBlockAck(NewBlock block) {
        for (int i = _flushes.size() - 1; i >= 0; i--)
            _flushes.get(i).onBlockAck(block, i);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private final class WriteFlush extends Flush {

        private final FutureWithCallback _future;

        private PlatformMap _pending;

        WriteFlush(FutureWithCallback future) {
            _future = future;
        }

        @Override
        final void done() {
            if (Debug.ENABLED)
                ThreadAssert.resume(_run);

            if (_hasPendingAcks.size() == 0)
                _future.set(null);
            else {
                _flushes.add(this);
                _pending = new PlatformMap();

                for (Resource resource : _hasPendingAcks.keySet()) {
                    if (Debug.ENABLED)
                        Debug.assertion(resource.pendingAcks().size() > 0);

                    for (NewBlock block : resource.pendingAcks().values())
                        _pending.put(block, block);
                }
            }

            if (Debug.ENABLED)
                ThreadAssert.suspend(_run);
        }

        final void onBlockAck(NewBlock block, int index) {
            NewBlock removed = _pending.remove(block);

            if (Debug.ENABLED)
                Debug.assertion(removed != null);

            if (_pending.size() == 0) {
                _future.set(null);
                _flushes.remove(index);
            }
        }
    }

    // Resource

    @Override
    final void visit(ResourceRead version) {
        for (;;) {
            _writer.writeRootRead();

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    @Override
    final void visit(ResourceVersion version) {
        for (;;) {
            _writer.writeRootVersion(version.getValue() != Resource.NULL ? version.getValue() : null);

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    // Indexed32

    @Override
    final void visit(TIndexed32Read version) {
        for (;;) {
            _writer.write(version);

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    // IndexedN

    @Override
    final void visit(TIndexedNRead version) {
        for (;;) {
            _writer.write(version);

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    // TKeyed

    @Override
    final void visit(TKeyedRead version) {
        TObject object = version.object();

        for (;;) {
            _writer.writeTKeyed(object, version.getEntries(), false, version.getFullyRead());

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    @Override
    final void visit(TKeyedVersion version) {
        TObject object = version.object();

        for (;;) {
            _writer.writeTKeyed(object, version.getEntries(), version.getCleared(), false);

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    @Override
    final void visit(TKeyedSharedVersion shared) {
        throw new IllegalStateException();
    }

    // Counter

    @Override
    final void visit(CounterRead version) {
        for (;;) {
            _writer.writeCounter(version.object(), false, 0, false);

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    @Override
    final void visit(CounterVersion version) {
        for (;;) {
            _writer.writeCounter(version.object(), true, version.getDelta(), version.getReset());

            if (!interrupted())
                break;

            addBuffer();
        }
    }

    @Override
    final void visit(CounterSharedVersion shared) {
        throw new IllegalStateException();
    }

    // Debug

    private final void assertIdle() {
        if (!Debug.ENABLED)
            throw new IllegalStateException();

        if (_writer != null) {
            Debug.assertion(_buffs.size() == 0);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy