@@ -417,8 +417,13 @@ extension ServiceLifecycle {
417417}
418418
419419extension 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)
422427 }
423428}
424429
@@ -462,7 +467,7 @@ public class ComponentLifecycle: LifecycleTask {
462467 fileprivate let logger : Logger
463468 fileprivate let shutdownGroup = DispatchGroup ( )
464469
465- private var state = State . idle ( [ ] )
470+ private var state = State . idle ( Registry ( ) )
466471 private let stateLock = Lock ( )
467472
468473 /// Creates a `ComponentLifecycle` instance.
@@ -492,10 +497,10 @@ public class ComponentLifecycle: LifecycleTask {
492497 /// - on: `DispatchQueue` to run the handlers callback on
493498 /// - callback: The handler which is called after the start operation completes. The parameter will be `nil` on success and contain the `Error` otherwise.
494499 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 {
496501 preconditionFailure ( " invalid state, \( self . state) " )
497502 }
498- self . _start ( on: queue, tasks : tasks , callback: callback)
503+ self . _start ( on: queue, registry : registry , callback: callback)
499504 }
500505
501506 /// Starts the provided `LifecycleTask` array and waits (blocking) until `shutdown` is called on another thread.
@@ -530,15 +535,15 @@ public class ComponentLifecycle: LifecycleTask {
530535
531536 self . stateLock. lock ( )
532537 switch self . state {
533- case . idle( let tasks ) where tasks . isEmpty:
538+ case . idle( let registry ) where registry . isEmpty:
534539 self . state = . shutdown( nil )
535540 self . stateLock. unlock ( )
536541 defer { self . shutdownGroup. leave ( ) }
537542 callback ( nil )
538- case . idle( let tasks ) :
543+ case . idle( let registry ) :
539544 self . stateLock. unlock ( )
540545 // attempt to shutdown any registered tasks
541- let stoppable = tasks. filter { $0. shutdownIfNotStarted }
546+ let stoppable = registry . tasks. filter { $0. shutdownIfNotStarted }
542547 setupShutdownListener ( . global( ) )
543548 self . _shutdown ( on: . global( ) , tasks: stoppable, callback: self . shutdownGroup. leave)
544549 case . shutdown:
@@ -552,10 +557,10 @@ public class ComponentLifecycle: LifecycleTask {
552557 case . shuttingDown( let queue) :
553558 self . stateLock. unlock ( )
554559 setupShutdownListener ( queue)
555- case . started( let queue, let tasks ) :
560+ case . started( let queue, let registry ) :
556561 self . stateLock. unlock ( )
557562 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)
559564 }
560565 }
561566
@@ -576,7 +581,7 @@ public class ComponentLifecycle: LifecycleTask {
576581
577582 // MARK: - private
578583
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 ) {
580585 self . stateLock. withLock {
581586 guard case . idle = self . state else {
582587 preconditionFailure ( " invalid state, \( self . state) " )
@@ -587,10 +592,10 @@ public class ComponentLifecycle: LifecycleTask {
587592 self . logger. info ( " starting " )
588593 Counter ( label: " \( self . label) .lifecycle.start " ) . increment ( )
589594
590- if tasks . count == 0 {
595+ if registry . isEmpty {
591596 self . logger. notice ( " no tasks provided " )
592597 }
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
594599 self . stateLock. lock ( )
595600 if error != nil {
596601 self . state = . shuttingDown( queue)
@@ -600,8 +605,8 @@ public class ComponentLifecycle: LifecycleTask {
600605 self . stateLock. unlock ( )
601606 // shutdown was called while starting, or start failed, shutdown what we can
602607 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 ( )
605610 . filter { $0. offset >= started. count }
606611 . map { $0. element }
607612 . filter { $0. shutdownIfNotStarted }
@@ -612,7 +617,7 @@ public class ComponentLifecycle: LifecycleTask {
612617 self . shutdownGroup. leave ( )
613618 }
614619 case . starting:
615- self . state = . started( queue, tasks )
620+ self . state = . started( queue, registry )
616621 self . stateLock. unlock ( )
617622 callback ( nil )
618623 default :
@@ -697,70 +702,116 @@ public class ComponentLifecycle: LifecycleTask {
697702 }
698703
699704 private enum State {
700- case idle( [ LifecycleTask ] )
705+ case idle( Registry )
701706 case starting( DispatchQueue )
702- case started( DispatchQueue , [ LifecycleTask ] )
707+ case started( DispatchQueue , Registry )
703708 case shuttingDown( DispatchQueue )
704709 case shutdown( [ String : Error ] ? )
705710 }
706711}
707712
708713extension 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+
710737 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 :
712742 preconditionFailure ( " invalid state, \( self . state) " )
713743 }
714- self . state = . idle( existing + tasks)
715744 }
716745 }
717746}
718747
719748/// A container of `LifecycleTask`, used to register additional `LifecycleTask`
720749public protocol LifecycleTasksContainer {
721- /// Adds a `LifecycleTask` to a `LifecycleTasks` collection.
750+ typealias RegistrationKey = String
751+
752+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer`.
722753 ///
723754 /// - parameters:
724755 /// - 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 )
726764}
727765
728766extension 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`.
730777 ///
731778 /// - parameters:
732779 /// - 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
735783 }
736784
737- /// Adds a `LifecycleTask` to a `LifecycleTasks` collection .
785+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer` .
738786 ///
739787 /// - parameters:
740788 /// - label: label of the item, useful for debugging.
741789 /// - start: `Handler` to perform the startup.
742790 /// - 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) )
745794 }
746795
747- /// Adds a `LifecycleTask` to a `LifecycleTasks` collection .
796+ /// Register a `LifecycleTask` with a `LifecycleTasksContainer` .
748797 ///
749798 /// - parameters:
750799 /// - label: label of the item, useful for debugging.
751800 /// - 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)
754804 }
755805
756- /// Add a stateful `LifecycleTask` to a `LifecycleTasks` collection .
806+ /// Register a stateful `LifecycleTask` with a `LifecycleTasksContainer` .
757807 ///
758808 /// - parameters:
759809 /// - label: label of the item, useful for debugging.
760810 /// - start: `LifecycleStartHandler` to perform the startup and return the state.
761811 /// - 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) )
764815 }
765816}
766817
@@ -830,3 +881,42 @@ internal class StatefulLifecycleTask<State>: LifecycleTask {
830881
831882 struct UnknownState : Error { }
832883}
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