Move the operation queue from Account to ArticlesDatabase, which is the rightful owner.

This commit is contained in:
Brent Simmons 2020-02-05 22:17:32 -08:00
parent d40eaed1f5
commit 01fc60916e
5 changed files with 64 additions and 69 deletions

View File

@ -231,14 +231,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return delegate.refreshProgress
}
private var operationQueue = MainThreadOperationQueue()
private enum OperationName {
static let FetchAllUnreadCounts = "FetchAllUnreadCounts"
static let FetchFeedUnreadCount = "FetchFeedUnreadCount"
static let FetchUnreadCountsForFeeds = "FetchUnreadCountsForFeeds"
}
private static let discardableOperationNames = [OperationName.FetchAllUnreadCounts, OperationName.FetchFeedUnreadCount, OperationName.FetchUnreadCountsForFeeds]
init?(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) {
switch type {
case .onMyMac:
@ -422,9 +414,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
public func suspendDatabase() {
operationQueue.suspend()
cancelDiscardableOperations()
database.suspend()
database.cancelAndSuspend()
save()
metadataFile.suspend()
webFeedMetadataFile.suspend()
@ -436,7 +426,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
public func resumeDatabaseAndDelegate() {
database.resume()
delegate.resume()
operationQueue.resume()
}
/// Reload OPML, etc.
@ -460,12 +449,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
delegate.accountWillBeDeleted(self)
}
public func cancelDiscardableOperations() {
for operationName in Self.discardableOperationNames {
operationQueue.cancelOperations(named: operationName)
}
}
func loadOPMLItems(_ items: [RSOPMLItem], parentFolder: Folder?) {
var feedsToAdd = Set<WebFeed>()
@ -1242,43 +1225,34 @@ private extension Account {
fetchUnreadCounts(feeds, completion)
}
else {
fetchAllUnreadCounts()
fetchAllUnreadCounts(completion)
}
}
func fetchUnreadCount(_ feed: WebFeed, _ completion: VoidCompletionBlock?) {
let operation = database.createFetchFeedUnreadCountOperation(feedID: feed.webFeedID)
operation.name = OperationName.FetchFeedUnreadCount
operation.completionBlock = { operation in
let fetchOperation = operation as! FetchFeedUnreadCountOperation
if let unreadCount = fetchOperation.unreadCount {
database.fetchUnreadCount(feed.webFeedID) { result in
if let unreadCount = try? result.get() {
feed.unreadCount = unreadCount
}
completion?()
}
operationQueue.add(operation)
}
func fetchUnreadCounts(_ feeds: Set<WebFeed>, _ completion: VoidCompletionBlock?) {
let feedIDs = feeds.map { $0.webFeedID }
let operation = database.createFetchUnreadCountsForFeedsOperation(feedIDs: Set(feedIDs))
operation.name = OperationName.FetchUnreadCountsForFeeds
operation.completionBlock = { operation in
let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation
if let unreadCountDictionary = fetchOperation.unreadCountDictionary {
let webFeedIDs = Set(feeds.map { $0.webFeedID })
database.fetchUnreadCounts(for: webFeedIDs) { result in
if let unreadCountDictionary = try? result.get() {
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds)
}
completion?()
}
operationQueue.add(operation)
}
func fetchAllUnreadCounts() {
func fetchAllUnreadCounts(_ completion: VoidCompletionBlock? = nil) {
fetchingAllUnreadCounts = true
database.fetchAllUnreadCounts { (result) in
database.fetchAllUnreadCounts { result in
guard let unreadCountDictionary = try? result.get() else {
completion?()
return
}
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedWebFeeds())
@ -1290,6 +1264,7 @@ private extension Account {
self.isUnreadCountsInitialized = true
self.postUnreadCountDidInitializeNotification()
}
completion?()
}
}

View File

@ -149,6 +149,26 @@ public final class ArticlesDatabase {
operationQueue.add(operation)
}
/// Fetch unread count for a single feed.
public func fetchUnreadCount(_ webFeedID: String, _ completion: @escaping SingleUnreadCountCompletionBlock) {
let operation = FetchFeedUnreadCountOperation(webFeedID: webFeedID, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate)
operation.completionBlock = { operation in
let fetchOperation = operation as! FetchFeedUnreadCountOperation
completion(fetchOperation.result)
}
operationQueue.add(operation)
}
/// Fetch non-zero unread counts for given webFeedIDs.
public func fetchUnreadCounts(for webFeedIDs: Set<String>, _ completion: @escaping UnreadCountDictionaryCompletionBlock) {
let operation = FetchUnreadCountsForFeedsOperation(webFeedIDs: webFeedIDs, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate)
operation.completionBlock = { operation in
let fetchOperation = operation as! FetchUnreadCountsForFeedsOperation
completion(fetchOperation.result)
}
operationQueue.add(operation)
}
public func fetchUnreadCountForToday(for webFeedIDs: Set<String>, completion: @escaping SingleUnreadCountCompletionBlock) {
fetchUnreadCount(for: webFeedIDs, since: todayCutoffDate(), completion: completion)
}
@ -199,33 +219,25 @@ public final class ArticlesDatabase {
articlesTable.createStatusesIfNeeded(articleIDs, completion)
}
// MARK: - Operations
public func cancelOperations() {
operationQueue.cancelAllOperations()
}
/// Create an operation that fetches the unread count for a single given feedID.
public func createFetchFeedUnreadCountOperation(feedID: String) -> FetchFeedUnreadCountOperation {
return FetchFeedUnreadCountOperation(feedID: feedID, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate)
}
/// Create an operation that fetches unread counts for a number of feedIDs.
public func createFetchUnreadCountsForFeedsOperation(feedIDs: Set<String>) -> FetchUnreadCountsForFeedsOperation {
return FetchUnreadCountsForFeedsOperation(feedIDs: feedIDs, databaseQueue: queue, cutoffDate: articlesTable.articleCutoffDate)
}
// MARK: - Suspend and Resume (for iOS)
/// Cancel current operations and close the database.
public func cancelAndSuspend() {
cancelOperations()
suspend()
}
/// Close the database and stop running database calls.
/// Any pending calls will complete first.
public func suspend() {
operationQueue.suspend()
queue.suspend()
}
/// Open the database and allow for running database calls again.
public func resume() {
queue.resume()
operationQueue.resume()
}
// MARK: - Caches
@ -272,4 +284,10 @@ private extension ArticlesDatabase {
// 24 hours previous. This is used by the Today smart feed, which should not actually empty out at midnight.
return Date(timeIntervalSinceNow: -(60 * 60 * 24)) // This does not need to be more precise.
}
// MARK: - Operations
func cancelOperations() {
operationQueue.cancelAllOperations()
}
}

View File

@ -12,7 +12,7 @@ import RSDatabase
public final class FetchAllUnreadCountsOperation: MainThreadOperation {
public var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended)
var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended)
// MainThreadOperation
public var isCanceled = false

View File

@ -13,8 +13,7 @@ import RSDatabase
/// Fetch the unread count for a single feed.
public final class FetchFeedUnreadCountOperation: MainThreadOperation {
public var unreadCount: Int?
public let feedID: String
var result: SingleUnreadCountResult = .failure(.isSuspended)
// MainThreadOperation
public var isCanceled = false
@ -25,9 +24,10 @@ public final class FetchFeedUnreadCountOperation: MainThreadOperation {
private let queue: DatabaseQueue
private let cutoffDate: Date
private let webFeedID: String
init(feedID: String, databaseQueue: DatabaseQueue, cutoffDate: Date) {
self.feedID = feedID
init(webFeedID: String, databaseQueue: DatabaseQueue, cutoffDate: Date) {
self.webFeedID = webFeedID
self.queue = databaseQueue
self.cutoffDate = cutoffDate
}
@ -54,7 +54,7 @@ private extension FetchFeedUnreadCountOperation {
func fetchUnreadCount(_ database: FMDatabase) {
let sql = "select count(*) from articles natural join statuses where feedID=? and read=0 and userDeleted=0 and (starred=1 or dateArrived>?);"
guard let resultSet = database.executeQuery(sql, withArgumentsIn: [feedID, cutoffDate]) else {
guard let resultSet = database.executeQuery(sql, withArgumentsIn: [webFeedID, cutoffDate]) else {
informOperationDelegateOfCompletion()
return
}
@ -64,7 +64,8 @@ private extension FetchFeedUnreadCountOperation {
}
if resultSet.next() {
unreadCount = resultSet.long(forColumnIndex: 0)
let unreadCount = resultSet.long(forColumnIndex: 0)
result = .success(unreadCount)
}
resultSet.close()

View File

@ -13,8 +13,7 @@ import RSDatabase
/// Fetch the unread counts for a number of feeds.
public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation {
public var unreadCountDictionary: UnreadCountDictionary?
public let feedIDs: Set<String>
var result: UnreadCountDictionaryCompletionResult = .failure(.isSuspended)
// MainThreadOperation
public var isCanceled = false
@ -25,9 +24,10 @@ public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation {
private let queue: DatabaseQueue
private let cutoffDate: Date
private let webFeedIDs: Set<String>
init(feedIDs: Set<String>, databaseQueue: DatabaseQueue, cutoffDate: Date) {
self.feedIDs = feedIDs
init(webFeedIDs: Set<String>, databaseQueue: DatabaseQueue, cutoffDate: Date) {
self.webFeedIDs = webFeedIDs
self.queue = databaseQueue
self.cutoffDate = cutoffDate
}
@ -52,11 +52,11 @@ public final class FetchUnreadCountsForFeedsOperation: MainThreadOperation {
private extension FetchUnreadCountsForFeedsOperation {
func fetchUnreadCounts(_ database: FMDatabase) {
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))!
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(webFeedIDs.count))!
let sql = "select distinct feedID, count(*) from articles natural join statuses where feedID in \(placeholders) and read=0 and userDeleted=0 and (starred=1 or dateArrived>?) group by feedID;"
var parameters = [Any]()
parameters += Array(feedIDs) as [Any]
parameters += Array(webFeedIDs) as [Any]
parameters += [cutoffDate] as [Any]
guard let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) else {
@ -64,11 +64,12 @@ private extension FetchUnreadCountsForFeedsOperation {
return
}
if isCanceled {
resultSet.close()
informOperationDelegateOfCompletion()
return
}
var d = UnreadCountDictionary()
var unreadCountDictionary = UnreadCountDictionary()
while resultSet.next() {
if isCanceled {
resultSet.close()
@ -77,12 +78,12 @@ private extension FetchUnreadCountsForFeedsOperation {
}
let unreadCount = resultSet.long(forColumnIndex: 1)
if let webFeedID = resultSet.string(forColumnIndex: 0) {
d[webFeedID] = unreadCount
unreadCountDictionary[webFeedID] = unreadCount
}
}
resultSet.close()
unreadCountDictionary = d
result = .success(unreadCountDictionary)
informOperationDelegateOfCompletion()
}
}