Package rx.internal.operators

Source Code of rx.internal.operators.OnSubscribeRefCount$Token

/**
* Copyright 2014 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package rx.internal.operators;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import rx.Observable.OnSubscribe;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action0;
import rx.observables.ConnectableObservable;
import rx.subscriptions.Subscriptions;

/**
* Returns an observable sequence that stays connected to the source as long
* as there is at least one subscription to the observable sequence.
* @param <T> the value type
*/
public final class OnSubscribeRefCount<T> implements OnSubscribe<T> {
    final ConnectableObservable<? extends T> source;
    final Object guard;
    /** Guarded by guard. */
    int index;
    /** Guarded by guard. */
    boolean emitting;
    /** Guarded by guard. If true, indicates a connection request, false indicates a disconnect request. */
    List<Token> queue;
    /** Manipulated while in the serialized section. */
    int count;
    /** Manipulated while in the serialized section. */
    Subscription connection;
    /** Manipulated while in the serialized section. */
    final Map<Token, Object> connectionStatus;
    /** Occupied indicator. */
    private static final Object OCCUPIED = new Object();
    public OnSubscribeRefCount(ConnectableObservable<? extends T> source) {
        this.source = source;
        this.guard = new Object();
        this.connectionStatus = new WeakHashMap<Token, Object>();
    }

    @Override
    public void call(Subscriber<? super T> t1) {
        int id;
        synchronized (guard) {
            id = ++index;
        }
        final Token t = new Token(id);
        t1.add(Subscriptions.create(new Action0() {
            @Override
            public void call() {
                disconnect(t);
            }
        }));
        source.unsafeSubscribe(t1);
        connect(t);
    }
    private void connect(Token id) {
        List<Token> localQueue;
        synchronized (guard) {
            if (emitting) {
                if (queue == null) {
                    queue = new ArrayList<Token>();
                }
                queue.add(id);
                return;
            }
           
            localQueue = queue;
            queue = null;
            emitting = true;
        }
        boolean once = true;
        do {
            drain(localQueue);
            if (once) {
                once = false;
                doConnect(id);
            }
            synchronized (guard) {
                localQueue = queue;
                queue = null;
                if (localQueue == null) {
                    emitting = false;
                    return;
                }
            }
        } while (true);
    }
    private void disconnect(Token id) {
        List<Token> localQueue;
        synchronized (guard) {
            if (emitting) {
                if (queue == null) {
                    queue = new ArrayList<Token>();
                }
                queue.add(id.toDisconnect()); // negative value indicates disconnect
                return;
            }
           
            localQueue = queue;
            queue = null;
            emitting = true;
        }
        boolean once = true;
        do {
            drain(localQueue);
            if (once) {
                once = false;
                doDisconnect(id);
            }
            synchronized (guard) {
                localQueue = queue;
                queue = null;
                if (localQueue == null) {
                    emitting = false;
                    return;
                }
            }
        } while (true);
    }
    private void drain(List<Token> localQueue) {
        if (localQueue == null) {
            return;
        }
        int n = localQueue.size();
        for (int i = 0; i < n; i++) {
            Token id = localQueue.get(i);
            if (id.isDisconnect()) {
                doDisconnect(id);
            } else {
                doConnect(id);
            }
        }
    }
    private void doConnect(Token id) {
        // this method is called only once per id
        // if add succeeds, id was not yet disconnected
        if (connectionStatus.put(id, OCCUPIED) == null) {
            if (count++ == 0) {
                connection = source.connect();
            }
        } else {
            // connection exists due to disconnect, just remove
            connectionStatus.remove(id);
        }
    }
    private void doDisconnect(Token id) {
        // this method is called only once per id
        // if remove succeeds, id was connected
        if (connectionStatus.remove(id) != null) {
            if (--count == 0) {
                connection.unsubscribe();
                connection = null;
            }
        } else {
            // mark id as if connected
            connectionStatus.put(id, OCCUPIED);
        }
    }
    /** Token that represens a connection request or a disconnection request. */
    private static final class Token {
        final int id;
        public Token(int id) {
            this.id = id;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (obj.getClass() != getClass()) {
                return false;
            }
            int other = ((Token)obj).id;
            return id == other || -id == other;
        }

        @Override
        public int hashCode() {
            return id < 0 ? -id : id;
        }
        public boolean isDisconnect() {
            return id < 0;
        }
        public Token toDisconnect() {
            if (id < 0) {
                return this;
            }
            return new Token(-id);
        }
    }
}
TOP

Related Classes of rx.internal.operators.OnSubscribeRefCount$Token

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.