@@ -417,8 +417,13 @@ extension ServiceLifecycle {
417
417
}
418
418
419
419
extension ServiceLifecycle : LifecycleTasksContainer {
420
- public func register( _ tasks: [ LifecycleTask ] ) {
421
- self . underlying. register ( tasks)
420
+ @discardableResult
421
+ public func register( _ tasks: [ LifecycleTask ] ) -> [ RegistrationKey ] {
422
+ return self . underlying. register ( tasks)
423
+ }
424
+
425
+ public func deregister( _ key: RegistrationKey ) {
426
+ self . underlying. deregister ( key)
422
427
}
423
428
}
424
429
@@ -462,7 +467,7 @@ public class ComponentLifecycle: LifecycleTask {
462
467
fileprivate let logger : Logger
463
468
fileprivate let shutdownGroup = DispatchGroup ( )
464
469
465
- private var state = State . idle ( [ ] )
470
+ private var state = State . idle ( Registry ( ) )
466
471
private let stateLock = Lock ( )
467
472
468
473
/// Creates a `ComponentLifecycle` instance.
@@ -492,10 +497,10 @@ public class ComponentLifecycle: LifecycleTask {
492
497
/// - on: `DispatchQueue` to run the handlers callback on
493
498
/// - callback: The handler which is called after the start operation completes. The parameter will be `nil` on success and contain the `Error` otherwise.
494
499
public func start( on queue: DispatchQueue , _ callback: @escaping ( Error ? ) -> Void ) {
495
- guard case . idle( let tasks ) = ( self . stateLock. withLock { self . state } ) else {
500
+ guard case . idle( let registry ) = ( self . stateLock. withLock { self . state } ) else {
496
501
preconditionFailure ( " invalid state, \( self . state) " )
497
502
}
498
- self . _start ( on: queue, tasks : tasks , callback: callback)
503
+ self . _start ( on: queue, registry : registry , callback: callback)
499
504
}
500
505
501
506
/// Starts the provided `LifecycleTask` array and waits (blocking) until `shutdown` is called on another thread.
@@ -530,15 +535,15 @@ public class ComponentLifecycle: LifecycleTask {
530
535
531
536
self . stateLock. lock ( )
532
537
switch self . state {
533
- case . idle( let tasks ) where tasks . isEmpty:
538
+ case . idle( let registry ) where registry . isEmpty:
534
539
self . state = . shutdown( nil )
535
540
self . stateLock. unlock ( )
536
541
defer { self . shutdownGroup. leave ( ) }
537
542
callback ( nil )
538
- case . idle( let tasks ) :
543
+ case . idle( let registry ) :
539
544
self . stateLock. unlock ( )
540
545
// attempt to shutdown any registered tasks
541
- let stoppable = tasks. filter { $0. shutdownIfNotStarted }
546
+ let stoppable = registry . tasks. filter { $0. shutdownIfNotStarted }
542
547
setupShutdownListener ( . global( ) )
543
548
self . _shutdown ( on: . global( ) , tasks: stoppable, callback: self . shutdownGroup. leave)
544
549
case . shutdown:
@@ -552,10 +557,10 @@ public class ComponentLifecycle: LifecycleTask {
552
557
case . shuttingDown( let queue) :
553
558
self . stateLock. unlock ( )
554
559
setupShutdownListener ( queue)
555
- case . started( let queue, let tasks ) :
560
+ case . started( let queue, let registry ) :
556
561
self . stateLock. unlock ( )
557
562
setupShutdownListener ( queue)
558
- self . _shutdown ( on: queue, tasks: tasks, callback: self . shutdownGroup. leave)
563
+ self . _shutdown ( on: queue, tasks: registry . tasks, callback: self . shutdownGroup. leave)
559
564
}
560
565
}
561
566
@@ -576,7 +581,7 @@ public class ComponentLifecycle: LifecycleTask {
576
581
577
582
// MARK: - private
578
583
579
- private func _start( on queue: DispatchQueue , tasks : [ LifecycleTask ] , callback: @escaping ( Error ? ) -> Void ) {
584
+ private func _start( on queue: DispatchQueue , registry : Registry , callback: @escaping ( Error ? ) -> Void ) {
580
585
self . stateLock. withLock {
581
586
guard case . idle = self . state else {
582
587
preconditionFailure ( " invalid state, \( self . state) " )
@@ -587,10 +592,10 @@ public class ComponentLifecycle: LifecycleTask {
587
592
self . logger. info ( " starting " )
588
593
Counter ( label: " \( self . label) .lifecycle.start " ) . increment ( )
589
594
590
- if tasks . count == 0 {
595
+ if registry . isEmpty {
591
596
self . logger. notice ( " no tasks provided " )
592
597
}
593
- self . startTask ( on: queue, tasks: tasks, index: 0 ) { started, error in
598
+ self . startTask ( on: queue, tasks: registry . tasks, index: 0 ) { started, error in
594
599
self . stateLock. lock ( )
595
600
if error != nil {
596
601
self . state = . shuttingDown( queue)
@@ -600,8 +605,8 @@ public class ComponentLifecycle: LifecycleTask {
600
605
self . stateLock. unlock ( )
601
606
// shutdown was called while starting, or start failed, shutdown what we can
602
607
var stoppable = started
603
- if started. count < tasks. count {
604
- let shutdownIfNotStarted = tasks. enumerated ( )
608
+ if started. count < registry . tasks. count {
609
+ let shutdownIfNotStarted = registry . tasks. enumerated ( )
605
610
. filter { $0. offset >= started. count }
606
611
. map { $0. element }
607
612
. filter { $0. shutdownIfNotStarted }
@@ -612,7 +617,7 @@ public class ComponentLifecycle: LifecycleTask {
612
617
self . shutdownGroup. leave ( )
613
618
}
614
619
case . starting:
615
- self . state = . started( queue, tasks )
620
+ self . state = . started( queue, registry )
616
621
self . stateLock. unlock ( )
617
622
callback ( nil )
618
623
default :
@@ -697,70 +702,116 @@ public class ComponentLifecycle: LifecycleTask {
697
702
}
698
703
699
704
private enum State {
700
- case idle( [ LifecycleTask ] )
705
+ case idle( Registry )
701
706
case starting( DispatchQueue )
702
- case started( DispatchQueue , [ LifecycleTask ] )
707
+ case started( DispatchQueue , Registry )
703
708
case shuttingDown( DispatchQueue )
704
709
case shutdown( [ String : Error ] ? )
705
710
}
706
711
}
707
712
708
713
extension ComponentLifecycle : LifecycleTasksContainer {
709
- public func register( _ tasks: [ LifecycleTask ] ) {
714
+ @discardableResult
715
+ public func register( _ newTasks: [ LifecycleTask ] ) -> [ RegistrationKey ] {
716
+ let registrationKeys = self . stateLock. withLock { ( ) -> [ RegistrationKey ] in
717
+ guard case . idle( let registry) = self . state else {
718
+ preconditionFailure ( " invalid state, \( self . state) " )
719
+ }
720
+ return registry. add ( newTasks)
721
+ }
722
+ return registrationKeys
723
+ }
724
+
725
+ public func deregister( _ key: RegistrationKey ) {
726
+ func remove( key: RegistrationKey , tasks: [ LifecycleTask ] , keys: [ RegistrationKey ] ) -> ( [ LifecycleTask ] , [ RegistrationKey ] ) {
727
+ guard let index = keys. firstIndex ( of: key) else {
728
+ return ( tasks, keys)
729
+ }
730
+ var updatedTasks = tasks
731
+ updatedTasks. remove ( at: index)
732
+ var updatedKeys = keys
733
+ updatedKeys. remove ( at: index)
734
+ return ( updatedTasks, updatedKeys)
735
+ }
736
+
710
737
self . stateLock. withLock {
711
- guard case . idle( let existing) = self . state else {
738
+ switch self . state {
739
+ case . idle( let registry) , . started( _, let registry) :
740
+ registry. remove ( key)
741
+ default :
712
742
preconditionFailure ( " invalid state, \( self . state) " )
713
743
}
714
- self . state = . idle( existing + tasks)
715
744
}
716
745
}
717
746
}
718
747
719
748
/// A container of `LifecycleTask`, used to register additional `LifecycleTask`
720
749
public protocol LifecycleTasksContainer {
721
- /// Adds a `LifecycleTask` to a `LifecycleTasks` collection.
750
+ typealias RegistrationKey = String
751
+
752
+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer`.
722
753
///
723
754
/// - parameters:
724
755
/// - tasks: array of `LifecycleTask`.
725
- func register( _ tasks: [ LifecycleTask ] )
756
+ @discardableResult
757
+ func register( _ tasks: [ LifecycleTask ] ) -> [ RegistrationKey ]
758
+
759
+ /// De-register a `LifecycleTask` from a `LifecycleTasksContainer`.
760
+ ///
761
+ /// - parameters:
762
+ /// - registrationKey: The key returned by a register operation.
763
+ func deregister( _ key: RegistrationKey )
726
764
}
727
765
728
766
extension LifecycleTasksContainer {
729
- /// Adds a `LifecycleTask` to a `LifecycleTasks` collection.
767
+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer`.
768
+ ///
769
+ /// - parameters:
770
+ /// - tasks: one or more `LifecycleTask`.
771
+ @discardableResult
772
+ public func register( _ tasks: LifecycleTask ... ) -> [ RegistrationKey ] {
773
+ return self . register ( tasks)
774
+ }
775
+
776
+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer`.
730
777
///
731
778
/// - parameters:
732
779
/// - tasks: one or more `LifecycleTask`.
733
- public func register( _ tasks: LifecycleTask ... ) {
734
- self . register ( tasks)
780
+ @discardableResult
781
+ public func register( _ tasks: LifecycleTask ) -> RegistrationKey {
782
+ return self . register ( tasks) . first! // force the optional on the first in this case is safe
735
783
}
736
784
737
- /// Adds a `LifecycleTask` to a `LifecycleTasks` collection .
785
+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer` .
738
786
///
739
787
/// - parameters:
740
788
/// - label: label of the item, useful for debugging.
741
789
/// - start: `Handler` to perform the startup.
742
790
/// - shutdown: `Handler` to perform the shutdown.
743
- public func register( label: String , start: LifecycleHandler , shutdown: LifecycleHandler , shutdownIfNotStarted: Bool ? = nil ) {
744
- self . register ( _LifecycleTask ( label: label, shutdownIfNotStarted: shutdownIfNotStarted, start: start, shutdown: shutdown) )
791
+ @discardableResult
792
+ public func register( label: String , start: LifecycleHandler , shutdown: LifecycleHandler , shutdownIfNotStarted: Bool ? = nil ) -> RegistrationKey {
793
+ return self . register ( _LifecycleTask ( label: label, shutdownIfNotStarted: shutdownIfNotStarted, start: start, shutdown: shutdown) )
745
794
}
746
795
747
- /// Adds a `LifecycleTask` to a `LifecycleTasks` collection .
796
+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer` .
748
797
///
749
798
/// - parameters:
750
799
/// - label: label of the item, useful for debugging.
751
800
/// - handler: `Handler` to perform the shutdown.
752
- public func registerShutdown( label: String , _ handler: LifecycleHandler ) {
753
- self . register ( label: label, start: . none, shutdown: handler)
801
+ @discardableResult
802
+ public func registerShutdown( label: String , _ handler: LifecycleHandler ) -> RegistrationKey {
803
+ return self . register ( label: label, start: . none, shutdown: handler)
754
804
}
755
805
756
- /// Add a stateful `LifecycleTask` to a `LifecycleTasks` collection .
806
+ /// Register a stateful `LifecycleTask` with a `LifecycleTasksContainer` .
757
807
///
758
808
/// - parameters:
759
809
/// - label: label of the item, useful for debugging.
760
810
/// - start: `LifecycleStartHandler` to perform the startup and return the state.
761
811
/// - shutdown: `LifecycleShutdownHandler` to perform the shutdown given the state.
762
- public func registerStateful< State> ( label: String , start: LifecycleStartHandler < State > , shutdown: LifecycleShutdownHandler < State > ) {
763
- self . register ( StatefulLifecycleTask ( label: label, start: start, shutdown: shutdown) )
812
+ @discardableResult
813
+ public func registerStateful< State> ( label: String , start: LifecycleStartHandler < State > , shutdown: LifecycleShutdownHandler < State > ) -> RegistrationKey {
814
+ return self . register ( StatefulLifecycleTask ( label: label, start: start, shutdown: shutdown) )
764
815
}
765
816
}
766
817
@@ -830,3 +881,42 @@ internal class StatefulLifecycleTask<State>: LifecycleTask {
830
881
831
882
struct UnknownState : Error { }
832
883
}
884
+
885
+ private class Registry {
886
+ typealias RegistrationKey = LifecycleTasksContainer . RegistrationKey
887
+
888
+ private var _tasks : [ LifecycleTask ] = [ ]
889
+ private var keys : [ LifecycleTasksContainer . RegistrationKey ] = [ ]
890
+ private let lock = Lock ( )
891
+
892
+ func add( _ tasks: [ LifecycleTask ] ) -> [ RegistrationKey ] {
893
+ // FIXME: better id generation scheme (cant use UUID)
894
+ let keys : [ RegistrationKey ] = tasks. map { _ in
895
+ let random = UInt64 . random ( in: UInt64 . min ..< UInt64 . max) . addingReportingOverflow ( DispatchTime . now ( ) . uptimeNanoseconds) . partialValue
896
+ return " task- \( random) "
897
+ }
898
+ self . lock. withLock {
899
+ self . _tasks. append ( contentsOf: tasks)
900
+ self . keys. append ( contentsOf: keys)
901
+ }
902
+ return keys
903
+ }
904
+
905
+ func remove( _ key: RegistrationKey ) {
906
+ self . lock. withLock {
907
+ guard let index = self . keys. firstIndex ( of: key) else {
908
+ return
909
+ }
910
+ self . _tasks. remove ( at: index)
911
+ self . keys. remove ( at: index)
912
+ }
913
+ }
914
+
915
+ var tasks : [ LifecycleTask ] {
916
+ return self . lock. withLock { self . _tasks }
917
+ }
918
+
919
+ var isEmpty : Bool {
920
+ return self . lock. withLock { self . _tasks. isEmpty }
921
+ }
922
+ }
0 commit comments