<commit_before>/**
 * Copyright (C) 2010-12 Brookhaven National Laboratory
 * All rights reserved. Use is subject to license terms.
 */
package org.epics.pvmanager;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * An object representing the PVReader. It contains all elements that are common
 * to all PVs of all type. The payload is specified by the generic type,
 * and is returned by {@link #getValue()}. Changes in
 * values are notified through the {@link PVReaderListener}. Listeners
 * can be registered from any thread.
 * <p>
 * The value/connection/exception can be accessed from any the thread,
 * but there is no guarantee on the atomicity. The only way to work on a consistent
 * snapshot is to use a listener.
 *
 * @author carcassi
 * @param <T> the type of the PVReader.
 */
class PVReaderImpl<T> implements PVReader<T> {

    static <T> PVReaderImpl<T> implOf(PVReader<T> pvReader) {
        if (pvReader instanceof PVReaderImpl) {
            return (PVReaderImpl<T>) pvReader;
        }
        
        throw new IllegalArgumentException("PVReader must be implemented using PVReaderImpl");
    }
    
    /**
     * Factory methods for PVReader objects. The class is used to initialize
     * the value of the PVReader.
     *
     * @param name of the PVReader
     * @param notifyFirstListener true if, when the first listener is registered
     * and a previous event was generated (value or exception),
     * it should be fired so that it is not lost
     */
    PVReaderImpl(String name, boolean notifyFirstListener) {
        this.name = name;
        this.notifyFirstListener = notifyFirstListener;
    }

    // PVReader state
    
    // Immutable part, no need to syncronize
    private final String name;
    private final boolean notifyFirstListener;

    // ReaderListener have their own syncronization, which allows
    // adding/removing listeners while iterating.
    private List<PVReaderListener<T>> pvReaderListeners = new CopyOnWriteArrayList<>();

    // guarded by this
    private boolean closed = false;
    private boolean paused = false;
    private boolean connected = false;
    private T value;
    private PVReader<T> readerForNotification = this;
    private Exception lastException;
    
    private boolean missedNotification = false;
    private boolean exceptionToNotify = false;
    private boolean connectionToNotify = false;
    private boolean valueToNotify = false;

    void setReaderForNotification(PVReader<T> readerForNotification) {
        this.readerForNotification = readerForNotification;
    }

    synchronized void firePvValueChanged() {
        int notificationMask = 0;
        if (connectionToNotify) {
            notificationMask += PVReaderEvent.CONNECTION_MASK;
        }
        if (valueToNotify) {
            notificationMask += PVReaderEvent.VALUE_MASK;
        }
        if (exceptionToNotify) {
            notificationMask += PVReaderEvent.EXCEPTION_MASK;
        }
        connectionToNotify = false;
        valueToNotify = false;
        exceptionToNotify = false;
        boolean missed = true;
        PVReaderEvent<T> event = new PVReaderEvent<>(notificationMask, readerForNotification);
        for (PVReaderListener<T> listener : pvReaderListeners) {
            listener.pvChanged(event);
            missed = false;
        }
        if (missed)
            missedNotification = true;
    }

    /**
     * Adds a listener to the value. This method is thread safe.
     *
     * @param listener a new listener
     */
    @Override
    public synchronized void addPVReaderListener(PVReaderListener<? super T> listener) {
        if (isClosed())
            throw new IllegalStateException("Can't add listeners to a closed PV");
        
        // Check whether to notify when the first listener is added.
        // This is done to make sure that exceptions thrown at pv creation
        // are not lost since the listener is added after the pv is created.
        // If the notification is done on a separate thread, the context switch
        // is enough to make sure the listener is registerred before the event
        // arrives, but if the notification is done on the same thread
        // the notification would be lost.
        boolean notify = notifyFirstListener && missedNotification;
        @SuppressWarnings("unchecked")
        PVReaderListener<T> convertedListener = (PVReaderListener<T>) listener;
        pvReaderListeners.add(convertedListener);
        if (notify)
            firePvValueChanged();
    }
    
    /**
     * Adds a listener to the value, which is notified only if the value is
     * of a given type. This method is thread safe.
     *<commit_msg>o.c.u.pvmanager: reduced locking so listener notification to not hold the lock. Hopefully will prevent certain types of deadlock to appear, without adverse consequences.<commit_after>/**
 * Copyright (C) 2010-12 Brookhaven National Laboratory
 * All rights reserved. Use is subject to license terms.
 */
package org.epics.pvmanager;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * An object representing the PVReader. It contains all elements that are common
 * to all PVs of all type. The payload is specified by the generic type,
 * and is returned by {@link #getValue()}. Changes in
 * values are notified through the {@link PVReaderListener}. Listeners
 * can be registered from any thread.
 * <p>
 * The value/connection/exception can be accessed from any the thread,
 * but there is no guarantee on the atomicity. The only way to work on a consistent
 * snapshot is to use a listener.
 *
 * @author carcassi
 * @param <T> the type of the PVReader.
 */
class PVReaderImpl<T> implements PVReader<T> {

    static <T> PVReaderImpl<T> implOf(PVReader<T> pvReader) {
        if (pvReader instanceof PVReaderImpl) {
            return (PVReaderImpl<T>) pvReader;
        }
        
        throw new IllegalArgumentException("PVReader must be implemented using PVReaderImpl");
    }
    
    /**
     * Factory methods for PVReader objects. The class is used to initialize
     * the value of the PVReader.
     *
     * @param name of the PVReader
     * @param notifyFirstListener true if, when the first listener is registered
     * and a previous event was generated (value or exception),
     * it should be fired so that it is not lost
     */
    PVReaderImpl(String name, boolean notifyFirstListener) {
        this.name = name;
        this.notifyFirstListener = notifyFirstListener;
    }

    // PVReader state
    
    // Immutable part, no need to syncronize
    private final String name;
    private final boolean notifyFirstListener;

    // ReaderListener have their own syncronization, which allows
    // adding/removing listeners while iterating.
    private List<PVReaderListener<T>> pvReaderListeners = new CopyOnWriteArrayList<>();

    // guarded by this
    private boolean closed = false;
    private boolean paused = false;
    private boolean connected = false;
    private T value;
    private PVReader<T> readerForNotification = this;
    private Exception lastException;
    
    private boolean missedNotification = false;
    private boolean exceptionToNotify = false;
    private boolean connectionToNotify = false;
    private boolean valueToNotify = false;

    void setReaderForNotification(PVReader<T> readerForNotification) {
        this.readerForNotification = readerForNotification;
    }

    void firePvValueChanged() {
        int notificationMask = 0;
        synchronized(this) {
            if (connectionToNotify) {
                notificationMask += PVReaderEvent.CONNECTION_MASK;
            }
            if (valueToNotify) {
                notificationMask += PVReaderEvent.VALUE_MASK;
            }
            if (exceptionToNotify) {
                notificationMask += PVReaderEvent.EXCEPTION_MASK;
            }
            connectionToNotify = false;
            valueToNotify = false;
            exceptionToNotify = false;
        }
        boolean missed = true;
        PVReaderEvent<T> event = new PVReaderEvent<>(notificationMask, readerForNotification);
        for (PVReaderListener<T> listener : pvReaderListeners) {
            listener.pvChanged(event);
            missed = false;
        }
        synchronized(this) {
            if (missed)
                missedNotification = true;
        }
    }

    /**
     * Adds a listener to the value. This method is thread safe.
     *
     * @param listener a new listener
     */
    @Override
    public synchronized void addPVReaderListener(PVReaderListener<? super T> listener) {
        if (isClosed())
            throw new IllegalStateException("Can't add listeners to a closed PV");
        
        // Check whether to notify when the first listener is added.
        // This is done to make sure that exceptions thrown at pv creation
        // are not lost since the listener is added after the pv is created.
        // If the notification is done on a separate thread, the context switch
        // is enough to make sure the listener is registerred before the event
        // arrives, but if the notification is done on the same thread
        // the notification would be lost.
        boolean notify = notifyFirstListener && missedNotification;
        @SuppressWarnings("unchecked")
        PVReaderListener<T> convertedListener = (PVReaderListener<T>) listener;
        pvReaderListeners.add(convertedListener);
        if (notify)
            firePvValueChanged();
    }
    
    /**
     * Adds a listener to the value, which is notified only if the value is
     * of a given type. This method is thread safe.
     *
{'fraction_non_alphanumeric': 0.06970040152373108, 'fraction_numerical': 0.0014413672397817357, 'mean_word_length': 2.6450281425891182, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3820607', 'n_tokens_mistral': 2486, 'n_tokens_neox': 2390, 'n_words': 943}