<commit_before>// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/
import Foundation

/// Class to handle updating via CADisplayLink
public class CallbackLoop: NSObject {
    private var internalHandler: () -> Void = {}

    /// Period of loop in seconds
    public var duration = 1.0

    /// Frequency of callbacks in Hertz
    public var frequency: Double {
        get {
            1.0 / duration
        }
        set {
            duration = 1.0 / newValue
        }
    }
    private var isRunning = false

    /// Repeat this loop at a given period with a code block
    ///
    /// - parameter period: Interval between block executions
    /// - parameter handler: Code block to execute
    ///
    public init(every period: Double, handler: @escaping () -> Void) {
        duration = period
        internalHandler = handler
        super.init()
        update()
    }

    /// Repeat this loop at a given frequency with a code block
    ///
    /// - parameter frequency: Frequency of block executions in Hz
    /// - parameter handler: Code block to execute
    ///
    public init(frequency: Double, handler: @escaping () -> Void) {
        duration = 1.0 / frequency
        internalHandler = handler
        super.init()
        update()
    }

    /// Start the loop
    public func start() {
        isRunning = true
        update()
    }

    /// Stop the loop
    public func stop() {
        isRunning = false
    }

    /// Callback function
    @objc func update() {
        if isRunning {
            self.internalHandler()
            self.perform(#selector(update),
                         with: nil,
                         afterDelay: duration,
                         inModes: [.common])
        }
    }
}<commit_msg>Fixing issue where callback loop only fires once if started on a thread other than main.<commit_after>// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/
import Foundation

/// Class to handle updating via CADisplayLink
public class CallbackLoop: NSObject {
    private var internalHandler: () -> Void = {}

    /// Period of loop in seconds
    public var duration = 1.0

    /// Frequency of callbacks in Hertz
    public var frequency: Double {
        get {
            1.0 / duration
        }
        set {
            duration = 1.0 / newValue
        }
    }
    private var isRunning = false

    /// Repeat this loop at a given period with a code block
    ///
    /// - parameter period: Interval between block executions
    /// - parameter handler: Code block to execute
    ///
    public init(every period: Double, handler: @escaping () -> Void) {
        duration = period
        internalHandler = handler
        super.init()
        update()
    }

    /// Repeat this loop at a given frequency with a code block
    ///
    /// - parameter frequency: Frequency of block executions in Hz
    /// - parameter handler: Code block to execute
    ///
    public init(frequency: Double, handler: @escaping () -> Void) {
        duration = 1.0 / frequency
        internalHandler = handler
        super.init()
        update()
    }

    /// Start the loop
    public func start() {
        isRunning = true
        update()
    }

    /// Stop the loop
    public func stop() {
        isRunning = false
    }

    /// Callback function
    @objc func update() {
        if isRunning {
            if !Thread.isMainThread {
                DispatchQueue.main.async { [self] in
                    self.updateInternal()
                }
            }
            else {
                self.updateInternal()
            }
        }
    }
    
    /// Call the callback function.
    /// self.perform only works when executed on the main thread. Called from self.update()
    @objc internal func updateInternal() {
        if isRunning {
            self.internalHandler()
            self.perform(#selector(updateInternal),
                         with: nil,
                         afterDelay: duration,
                         inModes: [.common])
        }
    }
}
{'fraction_non_alphanumeric': 0.09293320425943853, 'fraction_numerical': 0.003872216844143272, 'mean_word_length': 1.5912225705329153, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, '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': '6983404', 'n_tokens_mistral': 1068, 'n_tokens_neox': 1008, 'n_words': 367}