@ -475,10 +475,7 @@ public final class JobRunner: JobRunnerType {
return
}
queues . mutate {
$0 [ updatedJob . variant ] ?
. add ( updatedJob , canStartJob : canStartJob , dependencies : dependencies )
}
queues . wrappedValue [ updatedJob . variant ] ? . add ( db , job : updatedJob , canStartJob : canStartJob , dependencies : dependencies )
// D o n ' t s t a r t t h e q u e u e i f t h e j o b c a n ' t b e s t a r t e d
guard canStartJob else { return }
@ -501,7 +498,7 @@ public final class JobRunner: JobRunnerType {
return
}
queues . wrappedValue [ job . variant ] ? . upsert ( job, canStartJob : canStartJob , dependencies : dependencies )
queues . wrappedValue [ job . variant ] ? . upsert ( db, job : job, canStartJob : canStartJob , dependencies : dependencies )
// D o n ' t s t a r t t h e q u e u e i f t h e j o b c a n ' t b e s t a r t e d
guard canStartJob else { return }
@ -618,14 +615,28 @@ public final class JobQueue {
// / o n o u r r a n d o m q u e u e t h r e a d s r e s u l t s i n t h e t i m e r n e v e r f i r i n g , t h e ` s t a r t ` m e t h o d w i l l r e d i r e c t i t s e l f t o
// / t h e c o r r e c t t h r e a d
let trigger : Trigger = Trigger ( )
trigger . fireTimestamp = max ( 1 , ( timestamp - Date ( ) . timeIntervalSince1970 ) )
trigger . timer = Timer . scheduledTimerOnMainThread (
withTimeInterval : trigger . fireTimestamp ,
repeats : false ,
block : { [ weak queue ] _ in
queue ? . start ( dependencies : dependencies )
}
)
trigger . fireTimestamp = max ( 1 , ( timestamp - dependencies . date . timeIntervalSince1970 ) )
switch HasAppContext ( ) && CurrentAppContext ( ) . isRunningTests {
case true :
// W h e n r u n n i n g u n i t t e s t s d o n ' t s c h e d u l e a p r o p e r T i m e r , u s e a w h i l e l o o p i n s t e a d
DispatchQueue . global ( qos : . default ) . async { [ weak queue ] in
while timestamp < dependencies . date . timeIntervalSince1970 {
Thread . sleep ( forTimeInterval : 0.01 ) // W a i t f o r 1 0 m s
}
queue ? . start ( dependencies : dependencies )
}
case false :
trigger . timer = Timer . scheduledTimerOnMainThread (
withTimeInterval : trigger . fireTimestamp ,
repeats : false ,
block : { [ weak queue ] _ in
queue ? . start ( dependencies : dependencies )
}
)
}
return trigger
}
@ -711,12 +722,12 @@ public final class JobQueue {
// MARK: - E x e c u t i o n
fileprivate func add ( _ job: Job , canStartJob : Bool = true , dependencies : Dependencies ) {
fileprivate func add ( _ db: Database , job: Job , canStartJob : Bool = true , dependencies : Dependencies ) {
// C h e c k i f t h e j o b s h o u l d b e a d d e d t o t h e q u e u e
guard
canStartJob ,
job . behaviour != . runOnceNextLaunch ,
job . nextRunTimestamp <= Date( ) . timeIntervalSince1970
job . nextRunTimestamp <= dependencies. date . timeIntervalSince1970
else { return }
guard job . id != nil else {
SNLog ( " [JobRunner] Prevented attempt to add \( job . variant ) job without id to queue " )
@ -724,6 +735,15 @@ public final class JobQueue {
}
pendingJobsQueue . mutate { $0 . append ( job ) }
// I f t h i s i s a c o n c u r r e n t q u e u e t h e n w e s h o u l d i m m e d i a t e l y s t a r t t h e n e x t j o b
guard executionType = = . concurrent else { return }
// E n s u r e t h a t t h e d a t a b a s e c o m m i t h a s c o m p l e t e d a n d t h e n t r i g g e r t h e n e x t j o b t o r u n ( n e e d
// t o e n s u r e a n y i n t e r a c t i o n s h a v e b e e n c o r r e c t l y i n s e r t e d f i r s t )
db . afterNextTransactionNestedOnce ( dedupeId : " JobRunner-Add: \( job . variant ) " ) { [ weak self ] _ in
self ? . runNextJob ( dependencies : dependencies )
}
}
// / U p s e r t a j o b o n t o t h e q u e u e , i f t h e q u e u e i s n ' t c u r r e n t l y r u n n i n g a n d ' c a n S t a r t J o b ' i s t r u e t h e n t h i s w i l l s t a r t
@ -731,7 +751,7 @@ public final class JobQueue {
// /
// / * * N o t e : * * I f t h e j o b h a s a ` b e h a v i o u r ` o f ` r u n O n c e N e x t L a u n c h ` o r t h e ` n e x t R u n T i m e s t a m p `
// / i s i n t h e f u t u r e t h e n t h e j o b w o n ' t b e s t a r t e d
fileprivate func upsert ( _ job: Job , canStartJob : Bool = true , dependencies : Dependencies ) {
fileprivate func upsert ( _ db: Database , job: Job , canStartJob : Bool = true , dependencies : Dependencies ) {
guard let jobId : Int64 = job . id else {
SNLog ( " [JobRunner] Prevented attempt to upsert \( job . variant ) job without id to queue " )
return
@ -754,7 +774,7 @@ public final class JobQueue {
// I f w e d i d n ' t u p d a t e a n e x i s t i n g j o b t h e n w e n e e d t o a d d i t t o t h e p e n d i n g J o b s Q u e u e
guard ! didUpdateExistingJob else { return }
add ( job, canStartJob : canStartJob , dependencies : dependencies )
add ( db, job : job, canStartJob : canStartJob , dependencies : dependencies )
}
fileprivate func insert ( _ job : Job , before otherJob : Job , dependencies : Dependencies ) {
@ -790,7 +810,11 @@ public final class JobQueue {
}
}
fileprivate func appDidBecomeActive ( with jobs : [ Job ] , canStart : Bool ) {
fileprivate func appDidBecomeActive (
with jobs : [ Job ] ,
canStart : Bool ,
dependencies : Dependencies
) {
let currentlyRunningJobIds : Set < Int64 > = jobsCurrentlyRunning . wrappedValue
pendingJobsQueue . mutate { queue in
@ -826,7 +850,7 @@ public final class JobQueue {
fileprivate func hasPendingOrRunningJob ( with detailsData : Data ? ) -> Bool {
guard let detailsData : Data = detailsData else { return false }
let pendingJobs : [ Job ] = q ueue. wrappedValue
let pendingJobs : [ Job ] = pendingJobsQ ueue. wrappedValue
guard ! pendingJobs . contains ( where : { job in job . details = = detailsData } ) else { return true }
@ -1013,7 +1037,7 @@ public final class JobQueue {
// /
// / * * N o t e : * * W e d o n ' t a d d t h e c u r r e n t j o b b a c k t h e t h e q u e u e b e c a u s e i t s h o u l d o n l y b e r e - a d d e d i f i t ' s d e p e n d e n c i e s
// / a r e s u c c e s s f u l l y c o m p l e t e d
let currentlyRunningJobIds : [ Int64 ] = Array ( detailsForCurrentlyRunningJobs . wrappedValue . keys )
let currentlyRunningJobIds : [ Int64 ] = Array ( detailsForCurrentlyRunningJobs . wrappedValue . keys .map { $0 . id } )
let dependencyJobsNotCurrentlyRunning : [ Job ] = dependencyInfo . jobs
. filter { job in ! currentlyRunningJobIds . contains ( job . id ? ? - 1 ) }
. sorted { lhs , rhs in ( lhs . id ? ? - 1 ) < ( rhs . id ? ? - 1 ) }
@ -1023,7 +1047,7 @@ public final class JobQueue {
. filter { ! dependencyJobsNotCurrentlyRunning . contains ( $0 ) }
. inserting ( contentsOf : dependencyJobsNotCurrentlyRunning , at : 0 )
}
handleJobDeferred ( nextJob )
handleJobDeferred ( nextJob , dependencies : dependencies )
return
}
@ -1105,7 +1129,7 @@ public final class JobQueue {
}
// I f t h e n e x t j o b i s n ' t s c h e d u l e d i n t h e f u t u r e t h e n j u s t r e s t a r t t h e J o b R u n n e r i m m e d i a t e l y
let secondsUntilNextJob : TimeInterval = ( nextJobTimestamp - Date( ) . timeIntervalSince1970 )
let secondsUntilNextJob : TimeInterval = ( nextJobTimestamp - dependencies. date . timeIntervalSince1970 )
guard secondsUntilNextJob > 0 else {
// O n l y l o g t h a t t h e q u e u e i s g e t t i n g r e s t a r t e d i f t h i s q u e u e h a d a c t u a l l y b e e n a b o u t t o s t o p
@ -1218,7 +1242,7 @@ public final class JobQueue {
// / * * N o t e : * * I f a n y o f t h e s e ` d e p e n d a n t J o b s ` h a v e o t h e r d e p e n d e n c i e s t h e n w h e n t h e y a t t e m p t t o s t a r t t h e y w i l l b e
// / r e m o v e d f r o m t h e q u e u e , r e p l a c e d b y t h e i r d e p e n d e n c i e s
if ! dependantJobs . isEmpty {
let currentlyRunningJobIds : [ Int64 ] = Array ( detailsForCurrentlyRunningJobs . wrappedValue . keys )
let currentlyRunningJobIds : [ Int64 ] = Array ( detailsForCurrentlyRunningJobs . wrappedValue . keys .map { $0 . id } )
let dependantJobsNotCurrentlyRunning : [ Job ] = dependantJobs
. filter { job in ! currentlyRunningJobIds . contains ( job . id ? ? - 1 ) }
. sorted { lhs , rhs in ( lhs . id ? ? - 1 ) < ( rhs . id ? ? - 1 ) }
@ -1288,7 +1312,7 @@ public final class JobQueue {
// G e t t h e m a x f a i l u r e c o u n t f o r t h e j o b ( a v a l u e o f ' - 1 ' m e a n s i t w i l l r e t r y i n d e f i n i t e l y )
let maxFailureCount : Int = ( executorMap . wrappedValue [ job . variant ] ? . maxFailureCount ? ? 0 )
let nextRunTimestamp : TimeInterval = ( Date( ) . timeIntervalSince1970 + JobRunner . getRetryInterval ( for : job ) )
let nextRunTimestamp : TimeInterval = ( dependencies. date . timeIntervalSince1970 + JobRunner . getRetryInterval ( for : job ) )
dependencies . storage . write { db in
// / R e m o v e a n y d e p e n d a n t j o b s f r o m t h e q u e u e ( s h o u l d n ' t b e i n t h e r e b u t f i l t e r t h e q u e u e j u s t i n c a s e s o w e d o n ' t t r y
@ -1313,7 +1337,7 @@ public final class JobQueue {
updatedFailureCount <= maxFailureCount
)
else {
SNLog ( " [JobRunner] \( queueContext ) \( job . variant ) failed permanently \( maxFailureCount >= 0 ? " ; too many retries " : " " ) " )
SNLog ( " [JobRunner] \( queueContext ) \( job . variant ) failed permanently \( maxFailureCount >= 0 && updatedFailureCount > maxFailureCount ? " ; too many retries " : " " ) " )
// I f t h e j o b p e r m a n e n t l y f a i l e d o r w e h a v e p e r f o r m e d a l l o f o u r r e t r y a t t e m p t s
// t h e n d e l e t e t h e j o b a n d a l l o f i t ' s d e p e n d a n t j o b s ( i t ' l l p r o b a b l y n e v e r s u c c e e d )
@ -1364,12 +1388,12 @@ public final class JobQueue {
guard let lastRecord : ( count : Int , times : [ TimeInterval ] ) = $0 [ job . id ] else {
$0 = $0 . setting (
job . id ,
( 1 , [ Date( ) . timeIntervalSince1970 ] )
( 1 , [ dependencies. date . timeIntervalSince1970 ] )
)
return
}
let timeNow : TimeInterval = Date( ) . timeIntervalSince1970
let timeNow : TimeInterval = dependencies. date . timeIntervalSince1970
stuckInDeferLoop = (
lastRecord . count >= JobQueue . deferralLoopThreshold &&
( timeNow - lastRecord . times [ 0 ] ) < CGFloat ( lastRecord . count )