<commit_before>//
//  Scheduler.swift
//  ReactiveSwift
//
//  Created by <NAME> on 2014-06-02.
//  Copyright (c) 2014 GitHub. All rights reserved.
//

import Dispatch
import Foundation

#if os(Linux)
	import let CDispatch.NSEC_PER_SEC
#endif

/// Represents a serial queue of work items.
public protocol Scheduler: class {
	/// Enqueues an action on the scheduler.
	///
	/// When the work is executed depends on the scheduler in use.
	///
	/// - parameters:
	///   - action: The action to be scheduled.
	///
	/// - returns: Optional `Disposable` that can be used to cancel the work
	///            before it begins.
	@discardableResult
	func schedule(_ action: @escaping () -> Void) -> Disposable?
}

/// A particular kind of scheduler that supports enqueuing actions at future
/// dates.
public protocol DateScheduler: Scheduler {
	/// The current date, as determined by this scheduler.
	///
	/// This can be implemented to deterministically return a known date (e.g.,
	/// for testing purposes).
	var currentDate: Date { get }

	/// Schedules an action for execution at or after the given date.
	///
	/// - parameters:
	///   - date: The start date.
	///   - action: A closure of the action to be performed.
	///
	/// - returns: Optional `Disposable` that can be used to cancel the work
	///            before it begins.
	@discardableResult
	func schedule(after date: Date, action: @escaping () -> Void) -> Disposable?

	/// Schedules a recurring action at the given interval, beginning at the
	/// given date.
	///
	/// - parameters:
	///   - date: The start date.
	///   - interval: A repetition interval.
	///   - leeway: Some delta for repetition.
	///   - action: A closure of the action to be performed.
	///
	///	- note: If you plan to specify an `interval` value greater than 200,000
	///			seconds, use `schedule(after:interval:leeway:action)` instead
	///			and specify your own `leeway` value to avoid potential overflow.
	///
	/// - returns: Optional `Disposable` that can be used to cancel the work
	///            before it begins.
	@discardableResult
	func schedule(after date: Date, interval: DispatchTimeInterval, leeway: DispatchTimeInterval, action: @escaping () -> Void) -> Disposable?
}

/// A scheduler that performs all work synchronously.
public final class ImmediateScheduler: Scheduler {
	public init() {}

	/// Immediately calls passed in `action`.
	///
	/// - parameters:
	///   - action: A closure of the action to be performed.
	///
	/// - returns: `nil`.
	@discardableResult
	public func schedule(_ action: @escaping () -> Void) -> Disposable? {
		action()
		return nil
	}
}

/// A scheduler that performs all work on the main queue, as soon as possible.
///
/// If the caller is already running on the main queue when an action is
/// scheduled, it may be run synchronously. However, ordering between actions
/// will always be preserved.
public final class UIScheduler: Scheduler {
	private static let dispatchSpecificKey = DispatchSpecificKey<UInt8>()
	private static let dispatchSpecificValue = UInt8.max
	private static var __once: () = {
			DispatchQueue.main.setSpecific(key: UIScheduler.dispatchSpecificKey,
			                               value: dispatchSpecificValue)
	}()

	#if os(Linux)
	private var queueLength: Atomic<Int32> = Atomic(0)
	#else
	// `inout` references do not guarantee atomicity. Use `UnsafeMutablePointer`
	// instead.
	//
	// https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20161205/004147.html
	private let queueLength: UnsafeMutablePointer<Int32> = {
		let memory = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
		memory.initialize(to: 0)
		return memory
	}()

	deinit {
		queueLength.deinitialize()
		queueLength.deallocate(capacity: 1)
	}
	#endif

	/// Initializes `UIScheduler`
	public init() {
		/// This call is to ensure the main queue has been setup appropriately
		/// for `UIScheduler`. It is only called once during the application
		/// lifetime, since Swift has a `dispatch_once` like mechanism to
		/// lazily initialize global variables and static variables.
		_ = UIScheduler.__once
	}

	/// Queues an action to be performed on main queue. If the action is called
	/// on the main thread and no work is queued, no scheduling takes place and
	/// the action is called instantly.
	///
	/// - parameters:
	///   - action: A closure of the action to be performed on the main thread.
	///
	/// - returns: `Disposable` that can be used to cancel the work before it
	///            begins.
	@discardableResult
	public func schedule(_ action: @escaping () -> Void) -> Disposable? {
		let disposable = AnyDisposable()
		let actionAndDecrement = {
			if !disposable.isDisposed {
				action()
			}

			#if os(Linux)
				self.queueLength.modify { $0 -= 1 }
			#else
				OSAtomicDecrement32(self.queueLength)
			#endif
		}

		#if os(Linux)
			let queued = self.queueLength.modify { value -> Int32 in
				value += 1
				return value
			}
		#else
			let queued = OSAtomicIncrement32(queueLength)
		#endif

		// If we're already running on the main queue, and there isn't work
		// already enqueued, we can skip scheduling and just execute directly.
		if queued == 1 && DispatchQueue.getSpecific(key: UIScheduler.dispatchSpecificKey) == UIScheduler.dispatchSpecificValue {
			actionAndDecrement()
		} else {
			DispatchQueue.main.async(execute: actionAndDecrement)
		}

		return disposable
	}
}

/// A scheduler backed by a serial GCD queue.
public final class QueueScheduler: DateScheduler {
	/// A singleton `QueueScheduler` that always targets the main thread's GCD
	/// queue.
	///
	/// - note: Unlike `UIScheduler`, this scheduler supports scheduling for a
	///         future date, and will always schedule asynchronously (even if <commit_msg>No cancellation token if the action is immediately consumed by UIScheduler.<commit_after>//
//  Scheduler.swift
//  ReactiveSwift
//
//  Created by <NAME> on 2014-06-02.
//  Copyright (c) 2014 GitHub. All rights reserved.
//

import Dispatch
import Foundation

#if os(Linux)
	import let CDispatch.NSEC_PER_SEC
#endif

/// Represents a serial queue of work items.
public protocol Scheduler: class {
	/// Enqueues an action on the scheduler.
	///
	/// When the work is executed depends on the scheduler in use.
	///
	/// - parameters:
	///   - action: The action to be scheduled.
	///
	/// - returns: Optional `Disposable` that can be used to cancel the work
	///            before it begins.
	@discardableResult
	func schedule(_ action: @escaping () -> Void) -> Disposable?
}

/// A particular kind of scheduler that supports enqueuing actions at future
/// dates.
public protocol DateScheduler: Scheduler {
	/// The current date, as determined by this scheduler.
	///
	/// This can be implemented to deterministically return a known date (e.g.,
	/// for testing purposes).
	var currentDate: Date { get }

	/// Schedules an action for execution at or after the given date.
	///
	/// - parameters:
	///   - date: The start date.
	///   - action: A closure of the action to be performed.
	///
	/// - returns: Optional `Disposable` that can be used to cancel the work
	///            before it begins.
	@discardableResult
	func schedule(after date: Date, action: @escaping () -> Void) -> Disposable?

	/// Schedules a recurring action at the given interval, beginning at the
	/// given date.
	///
	/// - parameters:
	///   - date: The start date.
	///   - interval: A repetition interval.
	///   - leeway: Some delta for repetition.
	///   - action: A closure of the action to be performed.
	///
	///	- note: If you plan to specify an `interval` value greater than 200,000
	///			seconds, use `schedule(after:interval:leeway:action)` instead
	///			and specify your own `leeway` value to avoid potential overflow.
	///
	/// - returns: Optional `Disposable` that can be used to cancel the work
	///            before it begins.
	@discardableResult
	func schedule(after date: Date, interval: DispatchTimeInterval, leeway: DispatchTimeInterval, action: @escaping () -> Void) -> Disposable?
}

/// A scheduler that performs all work synchronously.
public final class ImmediateScheduler: Scheduler {
	public init() {}

	/// Immediately calls passed in `action`.
	///
	/// - parameters:
	///   - action: A closure of the action to be performed.
	///
	/// - returns: `nil`.
	@discardableResult
	public func schedule(_ action: @escaping () -> Void) -> Disposable? {
		action()
		return nil
	}
}

/// A scheduler that performs all work on the main queue, as soon as possible.
///
/// If the caller is already running on the main queue when an action is
/// scheduled, it may be run synchronously. However, ordering between actions
/// will always be preserved.
public final class UIScheduler: Scheduler {
	private static let dispatchSpecificKey = DispatchSpecificKey<UInt8>()
	private static let dispatchSpecificValue = UInt8.max
	private static var __once: () = {
			DispatchQueue.main.setSpecific(key: UIScheduler.dispatchSpecificKey,
			                               value: dispatchSpecificValue)
	}()

	#if os(Linux)
	private var queueLength: Atomic<Int32> = Atomic(0)
	#else
	// `inout` references do not guarantee atomicity. Use `UnsafeMutablePointer`
	// instead.
	//
	// https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20161205/004147.html
	private let queueLength: UnsafeMutablePointer<Int32> = {
		let memory = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
		memory.initialize(to: 0)
		return memory
	}()

	deinit {
		queueLength.deinitialize()
		queueLength.deallocate(capacity: 1)
	}
	#endif

	/// Initializes `UIScheduler`
	public init() {
		/// This call is to ensure the main queue has been setup appropriately
		/// for `UIScheduler`. It is only called once during the application
		/// lifetime, since Swift has a `dispatch_once` like mechanism to
		/// lazily initialize global variables and static variables.
		_ = UIScheduler.__once
	}

	/// Queues an action to be performed on main queue. If the action is called
	/// on the main thread and no work is queued, no scheduling takes place and
	/// the action is called instantly.
	///
	/// - parameters:
	///   - action: A closure of the action to be performed on the main thread.
	///
	/// - returns: `Disposable` that can be used to cancel the work before it
	///            begins.
	@discardableResult
	public func schedule(_ action: @escaping () -> Void) -> Disposable? {
		let positionInQueue = enqueue()

		// If we're already running on the main queue, and there isn't work
		// already enqueued, we can skip scheduling and just execute directly.
		if positionInQueue == 1 && DispatchQueue.getSpecific(key: UIScheduler.dispatchSpecificKey) == UIScheduler.dispatchSpecificValue {
			action()
			dequeue()
			return nil
		} else {
			let disposable = AnyDisposable()

			DispatchQueue.main.async {
				defer { self.dequeue() }
				guard !disposable.isDisposed else { return }
				action()
			}

			return disposable
		}
	}

	private func dequeue() {
		#if os(Linux)
			queueLength.modify { $0 -= 1 }
		#else
			OSAtomicDecrement32(queueLength)
		#endif
	}

	private func enqueue() -> Int32 {
		#if os(Linux)
		return queueLength.modify { value -> Int32 in
			value += 1
			return value
		}
		#else
		return OSAtomicIncrement32(queueLength)
		#endif
	}
}

/// A scheduler backed by a serial GCD queue.
public final class QueueScheduler: DateScheduler {
	/// A singleton `QueueScheduler` that always targets the main thread's GCD
	/// queue.
	///
	/// - note: Unlike `UIScheduler`, this scheduler supports scheduling for a
	///         future date, and will always schedule asynchronously (even if 
{'fraction_non_alphanumeric': 0.11118772625409412, 'fraction_numerical': 0.009481123944147562, 'mean_word_length': 2.906734006734007, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 36, 'https://': 2, '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': '319335', 'n_tokens_mistral': 3704, 'n_tokens_neox': 3365, 'n_words': 1266}