Skip to content

Commit cfa57d3

Browse files
authored
Run timeout constraint (#239)
1 parent 00590dc commit cfa57d3

15 files changed

+190
-243
lines changed

Sources/SwiftQueue.h

Whitespace-only changes.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
import Foundation
19+
20+
internal final class TimeoutConstraint: JobConstraint {
21+
22+
func willSchedule(queue: SqOperationQueue, operation: SqOperation) throws {
23+
// Nothing to do
24+
}
25+
26+
func willRun(operation: SqOperation) throws {
27+
// Nothing to do
28+
}
29+
30+
func run(operation: SqOperation) -> Bool {
31+
guard let timeout = operation.info.timeout else {
32+
return true
33+
}
34+
35+
operation.dispatchQueue.runAfter(timeout) {
36+
if (operation.isExecuting && !operation.isFinished) {
37+
operation.cancel(with: SwiftQueueError.timeout)
38+
}
39+
}
40+
41+
return true
42+
}
43+
44+
}

Sources/SwiftQueue/JobBuilder.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ public final class JobBuilder {
133133
return self
134134
}
135135

136+
/// Maximum time in second that the job is allowed to run
137+
public func timeout(value: TimeInterval) -> Self {
138+
info.timeout = value
139+
return self
140+
}
141+
136142
/// Get the JobInfo built
137143
public func build() -> JobInfo {
138144
return info

Sources/SwiftQueue/JobInfo.swift

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,36 @@ public struct JobInfo {
8080
/// The relative amount of importance for granting system resources to the operation.
8181
var qualityOfService: QualityOfService
8282

83+
var timeout: TimeInterval?
84+
85+
func buildConstraints() -> [JobConstraint] {
86+
var constraints = [JobConstraint]()
87+
88+
constraints.append(UniqueUUIDConstraint())
89+
90+
if (requireCharging) {
91+
constraints.append(BatteryChargingConstraint())
92+
}
93+
94+
if (deadline != nil) {
95+
constraints.append(DeadlineConstraint())
96+
}
97+
98+
if (delay != nil) {
99+
constraints.append(DelayConstraint())
100+
}
101+
102+
if (requireNetwork != NetworkType.any) {
103+
constraints.append(NetworkConstraint())
104+
}
105+
106+
if (timeout != nil) {
107+
constraints.append(TimeoutConstraint())
108+
}
109+
110+
return constraints
111+
}
112+
83113
init(type: String) {
84114
self.init(
85115
type: type,
@@ -100,29 +130,31 @@ public struct JobInfo {
100130
runCount: 0,
101131
requireCharging: false,
102132
priority: .normal,
103-
qualityOfService: .utility
133+
qualityOfService: .utility,
134+
timeout: nil
104135
)
105136
}
106137

107-
init(type: String,
108-
queueName: String,
109-
uuid: String,
110-
override: Bool,
111-
includeExecutingJob: Bool,
112-
tags: Set<String>,
113-
delay: TimeInterval?,
114-
deadline: Date?,
115-
requireNetwork: NetworkType,
116-
isPersisted: Bool,
117-
params: [String: Any],
118-
createTime: Date,
119-
interval: TimeInterval,
120-
maxRun: Limit,
121-
retries: Limit,
122-
runCount: Double,
123-
requireCharging: Bool,
124-
priority: Operation.QueuePriority,
125-
qualityOfService: QualityOfService
138+
internal init(type: String,
139+
queueName: String,
140+
uuid: String,
141+
override: Bool,
142+
includeExecutingJob: Bool,
143+
tags: Set<String>,
144+
delay: TimeInterval?,
145+
deadline: Date?,
146+
requireNetwork: NetworkType,
147+
isPersisted: Bool,
148+
params: [String: Any],
149+
createTime: Date,
150+
interval: TimeInterval,
151+
maxRun: Limit,
152+
retries: Limit,
153+
runCount: Double,
154+
requireCharging: Bool,
155+
priority: Operation.QueuePriority,
156+
qualityOfService: QualityOfService,
157+
timeout: TimeInterval?
126158
) {
127159

128160
self.type = type
@@ -144,6 +176,7 @@ public struct JobInfo {
144176
self.requireCharging = requireCharging
145177
self.priority = priority
146178
self.qualityOfService = qualityOfService
179+
self.timeout = timeout
147180

148181
/// Transient
149182
self.currentRepetition = 0

Sources/SwiftQueue/JobInfoSerializer+Decodable.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ extension JobInfo: Decodable {
7474
case requireCharging = "requireCharging"
7575
case priority = "priority"
7676
case qualityOfService = "qualityOfService"
77+
case timeout = "timeout"
7778
}
7879

7980
public init(from decoder: Decoder) throws {
@@ -98,6 +99,7 @@ extension JobInfo: Decodable {
9899
let requireCharging: Bool = try container.decode(Bool.self, forKey: .requireCharging)
99100
let priority: Int? = try container.decode(Int?.self, forKey: .priority)
100101
let qualityOfService: Int? = try container.decode(Int?.self, forKey: .qualityOfService)
102+
let timeout: TimeInterval? = try container.decode(TimeInterval?.self, forKey: .timeout)
101103

102104
self.init(
103105
type: type,
@@ -118,7 +120,8 @@ extension JobInfo: Decodable {
118120
runCount: runCount,
119121
requireCharging: requireCharging,
120122
priority: Operation.QueuePriority(fromValue: priority),
121-
qualityOfService: QualityOfService(fromValue: qualityOfService)
123+
qualityOfService: QualityOfService(fromValue: qualityOfService),
124+
timeout: timeout
122125
)
123126
}
124127
}
@@ -146,6 +149,7 @@ extension JobInfo: Encodable {
146149
try container.encode(requireCharging, forKey: .requireCharging)
147150
try container.encode(priority.rawValue, forKey: .priority)
148151
try container.encode(qualityOfService.rawValue, forKey: .qualityOfService)
152+
try container.encode(timeout, forKey: .timeout)
149153
}
150154
}
151155

Sources/SwiftQueue/SqOperation.swift

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,7 @@ internal final class SqOperation: Operation {
6363
self.logger = logger
6464
self.listener = listener
6565
self.dispatchQueue = dispatchQueue
66-
67-
self.constraints = [
68-
DeadlineConstraint(),
69-
DelayConstraint(),
70-
UniqueUUIDConstraint(),
71-
NetworkConstraint(),
72-
BatteryChargingConstraint()
73-
]
66+
self.constraints = info.buildConstraints()
7467

7568
super.init()
7669
}

Sources/SwiftQueue/SwiftQueue.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,7 @@ public enum SwiftQueueError: Swift.Error {
187187
/// Job canceled inside onError. Parameter contains the origin error
188188
case onRetryCancel(Error)
189189

190-
}
190+
/// Job took too long to run
191+
case timeout
191192

193+
}

Sources/SwiftQueue/SwiftQueueManager.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,13 @@ public final class SwiftQueueManager {
6060
let queue = getQueue(queueName: info.queueName)
6161
let job = queue.createHandler(type: info.type, params: info.params)
6262

63-
let operation = SqOperation(job: job, info: info, logger: params.logger, listener: params.listener, dispatchQueue: params.dispatchQueue)
63+
let operation = SqOperation(job: job,
64+
info: info,
65+
logger: params.logger,
66+
listener: params.listener,
67+
dispatchQueue: params.dispatchQueue
68+
)
69+
6470
queue.addOperation(operation)
6571
}
6672

Sources/SwiftQueue/Utils.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ internal extension QualityOfService {
113113

114114
}
115115

116-
117116
#if !swift(>=4.1)
118117
extension Sequence {
119118
func compactMap<T>(_ fn: (Self.Iterator.Element) throws -> T?) rethrows -> [T] {

SwiftQueue.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
1E68ED598FA15EF36B431571 /* Constraint+Deadline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E68EF96F1DE038FFF94FCD8 /* Constraint+Deadline.swift */; };
3434
1E68EDC2230D2FACC61E7868 /* Constraint+Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E68E940C8373221E3EDC771 /* Constraint+Delay.swift */; };
3535
1E68EE914B5B51CDB2BD7C4D /* Constraint+UniqueUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E68E002DC39E9BBE4A0A06A /* Constraint+UniqueUUID.swift */; };
36+
61A65B7722ABD172009AC9B7 /* Constraint+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A65B7622ABD172009AC9B7 /* Constraint+Timeout.swift */; };
37+
61A65B7822ABD175009AC9B7 /* Constraint+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A65B7622ABD172009AC9B7 /* Constraint+Timeout.swift */; };
38+
61A65B7922ABD175009AC9B7 /* Constraint+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A65B7622ABD172009AC9B7 /* Constraint+Timeout.swift */; };
39+
61A65B7A22ABD176009AC9B7 /* Constraint+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A65B7622ABD172009AC9B7 /* Constraint+Timeout.swift */; };
3640
95A210264F99EAC94629D786 /* SwiftQueueLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */; };
3741
95A213CFEA10D6FD5672A64A /* JobInfoSerializer+V1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A210DDE517DF37DE5A7EC1 /* JobInfoSerializer+V1.swift */; };
3842
95A2142AD4929198F8C2534D /* Constraint+Charging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A21C01EDB773B7F17134AF /* Constraint+Charging.swift */; };
@@ -114,6 +118,7 @@
114118
1E68E82C1E071C7AA1AC4BB6 /* Constraint+Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Constraint+Network.swift"; path = "SwiftQueue/Constraint+Network.swift"; sourceTree = "<group>"; };
115119
1E68E940C8373221E3EDC771 /* Constraint+Delay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Constraint+Delay.swift"; path = "SwiftQueue/Constraint+Delay.swift"; sourceTree = "<group>"; };
116120
1E68EF96F1DE038FFF94FCD8 /* Constraint+Deadline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Constraint+Deadline.swift"; path = "SwiftQueue/Constraint+Deadline.swift"; sourceTree = "<group>"; };
121+
61A65B7622ABD172009AC9B7 /* Constraint+Timeout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Constraint+Timeout.swift"; path = "SwiftQueue/Constraint+Timeout.swift"; sourceTree = "<group>"; };
117122
95A210DDE517DF37DE5A7EC1 /* JobInfoSerializer+V1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "JobInfoSerializer+V1.swift"; path = "SwiftQueue/JobInfoSerializer+V1.swift"; sourceTree = "<group>"; };
118123
95A2169E6968B5F155D5B612 /* JobInfoSerializer+Decodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "JobInfoSerializer+Decodable.swift"; path = "SwiftQueue/JobInfoSerializer+Decodable.swift"; sourceTree = "<group>"; };
119124
95A217FDB8197696002CE25C /* SwiftQueueLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftQueueLogger.swift; path = SwiftQueue/SwiftQueueLogger.swift; sourceTree = "<group>"; };
@@ -227,6 +232,7 @@
227232
OBJ_7 /* Sources */ = {
228233
isa = PBXGroup;
229234
children = (
235+
61A65B7622ABD172009AC9B7 /* Constraint+Timeout.swift */,
230236
1D8479A61F82A84B00B3BBFB /* Constraint.swift */,
231237
1D8479A21F82A84A00B3BBFB /* SwiftQueue.swift */,
232238
1D8479A41F82A84B00B3BBFB /* SqOperation.swift */,
@@ -394,6 +400,7 @@
394400
files = (
395401
B06405031FA04AA000086C69 /* UserDefaultsPersister.swift in Sources */,
396402
B06405041FA04AA000086C69 /* SwiftQueue.swift in Sources */,
403+
61A65B7A22ABD176009AC9B7 /* Constraint+Timeout.swift in Sources */,
397404
B06405061FA04AA000086C69 /* Constraint.swift in Sources */,
398405
B06405071FA04AA000086C69 /* Utils.swift in Sources */,
399406
B06405081FA04AA000086C69 /* SqOperation.swift in Sources */,
@@ -418,6 +425,7 @@
418425
files = (
419426
B06405121FA04AAC00086C69 /* UserDefaultsPersister.swift in Sources */,
420427
B06405131FA04AAC00086C69 /* SwiftQueue.swift in Sources */,
428+
61A65B7922ABD175009AC9B7 /* Constraint+Timeout.swift in Sources */,
421429
B06405151FA04AAC00086C69 /* Constraint.swift in Sources */,
422430
B06405161FA04AAC00086C69 /* Utils.swift in Sources */,
423431
B06405171FA04AAC00086C69 /* SqOperation.swift in Sources */,
@@ -442,6 +450,7 @@
442450
files = (
443451
B06405211FA04CCF00086C69 /* UserDefaultsPersister.swift in Sources */,
444452
B06405221FA04CCF00086C69 /* SwiftQueue.swift in Sources */,
453+
61A65B7822ABD175009AC9B7 /* Constraint+Timeout.swift in Sources */,
445454
B06405241FA04CCF00086C69 /* Constraint.swift in Sources */,
446455
B06405251FA04CCF00086C69 /* Utils.swift in Sources */,
447456
B06405261FA04CCF00086C69 /* SqOperation.swift in Sources */,
@@ -466,6 +475,7 @@
466475
files = (
467476
1D8479A81F82A84B00B3BBFB /* UserDefaultsPersister.swift in Sources */,
468477
1D8479A91F82A84B00B3BBFB /* SwiftQueue.swift in Sources */,
478+
61A65B7722ABD172009AC9B7 /* Constraint+Timeout.swift in Sources */,
469479
1D8479AD1F82A84B00B3BBFB /* Constraint.swift in Sources */,
470480
1D8479AA1F82A84B00B3BBFB /* Utils.swift in Sources */,
471481
1D8479AB1F82A84B00B3BBFB /* SqOperation.swift in Sources */,

Tests/LinuxMain.swift

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)