@@ -705,6 +705,76 @@ final class Tests: XCTestCase {
705705 lifecycle. wait ( )
706706 }
707707
708+ // this is an example of how state can be managed inside a `LifecycleItem`
709+ // note the use of locks in this example since there could be concurrent access issues
710+ // in case shutdown is called (e.g. via signal trap) during the startup sequence
711+ // see also `testExternalState` test case
712+ func testInternalState( ) {
713+ class Item {
714+ enum State : Equatable {
715+ case idle
716+ case starting
717+ case started( String )
718+ case shuttingDown
719+ case shutdown
720+ }
721+
722+ var state = State . idle
723+ let stateLock = Lock ( )
724+
725+ let queue = DispatchQueue ( label: " test " )
726+
727+ let data : String
728+
729+ init ( _ data: String ) {
730+ self . data = data
731+ }
732+
733+ func start( callback: @escaping ( Error ? ) -> Void ) {
734+ self . stateLock. withLock {
735+ self . state = . starting
736+ }
737+ self . queue. asyncAfter ( deadline: . now( ) + Double. random ( in: 0.01 ... 0.1 ) ) {
738+ self . stateLock. withLock {
739+ self . state = . started( self . data)
740+ }
741+ callback ( nil )
742+ }
743+ }
744+
745+ func shutdown( callback: @escaping ( Error ? ) -> Void ) {
746+ self . stateLock. withLock {
747+ self . state = . shuttingDown
748+ }
749+ self . queue. asyncAfter ( deadline: . now( ) + Double. random ( in: 0.01 ... 0.1 ) ) {
750+ self . stateLock. withLock {
751+ self . state = . shutdown
752+ }
753+ callback ( nil )
754+ }
755+ }
756+ }
757+
758+ let expectedData = UUID ( ) . uuidString
759+ let item = Item ( expectedData)
760+ let lifecycle = Lifecycle ( )
761+ lifecycle. register ( label: " test " ,
762+ start: . async( item. start) ,
763+ shutdown: . async( item. shutdown) )
764+
765+ lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { error in
766+ XCTAssertNil ( error, " not expecting error " )
767+ XCTAssertEqual ( item. state, . started( expectedData) , " expected item to be shutdown, but \( item. state) " )
768+ lifecycle. shutdown ( )
769+ }
770+ lifecycle. wait ( )
771+ XCTAssertEqual ( item. state, . shutdown, " expected item to be shutdown, but \( item. state) " )
772+ }
773+
774+ // this is an example of how state can be managed outside the `Lifecycle`
775+ // note the use of locks in this example since there could be concurrent access issues
776+ // in case shutdown is called (e.g. via signal trap) during the startup sequence
777+ // see also `testInternalState` test case, which is the prefered way to manage item's state
708778 func testExternalState( ) {
709779 enum State : Equatable {
710780 case idle
@@ -713,36 +783,48 @@ final class Tests: XCTestCase {
713783 }
714784
715785 class Item {
716- let eventLoopGroup : EventLoopGroup = MultiThreadedEventLoopGroup ( numberOfThreads : 1 )
786+ let queue = DispatchQueue ( label : " test " )
717787
718788 let data : String
789+
719790 init ( _ data: String ) {
720791 self . data = data
721792 }
722793
723- func start( ) -> EventLoopFuture < String > {
724- return self . eventLoopGroup. next ( ) . makeSucceededFuture ( self . data)
794+ func start( callback: @escaping ( String ) -> Void ) {
795+ self . queue. asyncAfter ( deadline: . now( ) + Double. random ( in: 0.01 ... 0.1 ) ) {
796+ callback ( self . data)
797+ }
725798 }
726799
727- func shutdown( ) -> EventLoopFuture < Void > {
728- return self . eventLoopGroup. next ( ) . makeSucceededFuture ( ( ) )
800+ func shutdown( callback: @escaping ( ) -> Void ) {
801+ self . queue. asyncAfter ( deadline: . now( ) + Double. random ( in: 0.01 ... 0.1 ) ) {
802+ callback ( )
803+ }
729804 }
730805 }
731806
732807 var state = State . idle
808+ let stateLock = Lock ( )
733809
734810 let expectedData = UUID ( ) . uuidString
735811 let item = Item ( expectedData)
736812 let lifecycle = Lifecycle ( )
737813 lifecycle. register ( label: " test " ,
738- start: . eventLoopFuture {
739- item. start ( ) . map { data -> Void in
740- state = . started( data)
814+ start: . async { callback in
815+ item. start { data in
816+ stateLock. withLock {
817+ state = . started( data)
818+ }
819+ callback ( nil )
741820 }
742821 } ,
743- shutdown: . eventLoopFuture {
744- item. shutdown ( ) . map { _ -> Void in
745- state = . shutdown
822+ shutdown: . async { callback in
823+ item. shutdown {
824+ stateLock. withLock {
825+ state = . shutdown
826+ }
827+ callback ( nil )
746828 }
747829 } )
748830
0 commit comments