@@ -55,36 +55,39 @@ private[jetty] object JettyLifeCycle {
55
55
* internally, e.g. due to some internal error occurring.
56
56
*/
57
57
private [this ] def stopLifeCycle [F [_]](lifeCycle : LifeCycle )(implicit F : Async [F ]): F [Unit ] =
58
- F .async_[Unit ] { cb =>
59
- lifeCycle.addLifeCycleListener(
60
- new LifeCycle .Listener {
61
- override def lifeCycleStopped (a : LifeCycle ): Unit =
62
- cb(Right (()))
63
- override def lifeCycleFailure (a : LifeCycle , error : Throwable ): Unit =
64
- cb(Left (error))
65
- }
66
- )
58
+ F .async[Unit ] { cb =>
59
+ F .delay {
60
+ val listener =
61
+ new LifeCycle .Listener {
62
+ override def lifeCycleStopped (a : LifeCycle ): Unit =
63
+ cb(Right (()))
64
+ override def lifeCycleFailure (a : LifeCycle , error : Throwable ): Unit =
65
+ cb(Left (error))
66
+ }
67
+ lifeCycle.addLifeCycleListener(listener)
67
68
68
- // In the general case, it is not sufficient to merely call stop(). For
69
- // example, the concrete implementation of stop() for the canonical
70
- // Jetty Server instance will shortcut to a `return` call taking no
71
- // action if the server is "stopping". This method _can't_ return until
72
- // we are _actually stopped_, so we have to check three different states
73
- // here.
69
+ // In the general case, it is not sufficient to merely call stop(). For
70
+ // example, the concrete implementation of stop() for the canonical
71
+ // Jetty Server instance will shortcut to a `return` call taking no
72
+ // action if the server is "stopping". This method _can't_ return until
73
+ // we are _actually stopped_, so we have to check three different states
74
+ // here.
74
75
75
- if (lifeCycle.isStopped) {
76
- // If the first case, we are already stopped, so our listener won't be
77
- // called and we just return.
78
- cb(Right (()))
79
- } else if (lifeCycle.isStopping()) {
80
- // If it is stopping, we need to wait for our listener to get invoked.
81
- ()
82
- } else {
83
- // If it is neither stopped nor stopping, we need to request a stop
84
- // and then wait for the event. It is imperative that we add the
85
- // listener beforehand here. Otherwise we have some very annoying race
86
- // conditions.
87
- lifeCycle.stop()
76
+ if (lifeCycle.isStopped) {
77
+ // If the first case, we are already stopped, so our listener won't be
78
+ // called and we just return.
79
+ cb(Right (()))
80
+ } else if (lifeCycle.isStopping()) {
81
+ // If it is stopping, we need to wait for our listener to get invoked.
82
+ ()
83
+ } else {
84
+ // If it is neither stopped nor stopping, we need to request a stop
85
+ // and then wait for the event. It is imperative that we add the
86
+ // listener beforehand here. Otherwise we have some very annoying race
87
+ // conditions.
88
+ lifeCycle.stop()
89
+ }
90
+ Some (F .delay(lifeCycle.removeLifeCycleListener(listener)))
88
91
}
89
92
}
90
93
@@ -95,51 +98,53 @@ private[jetty] object JettyLifeCycle {
95
98
* (or starting) this will fail.
96
99
*/
97
100
private [this ] def startLifeCycle [F [_]](lifeCycle : LifeCycle )(implicit F : Async [F ]): F [Unit ] =
98
- F .async_ [Unit ] { cb =>
99
- lifeCycle.addLifeCycleListener(
100
- new LifeCycle .Listener {
101
+ F .async [Unit ] { cb =>
102
+ F .delay {
103
+ val listener = new LifeCycle .Listener {
101
104
override def lifeCycleStarted (a : LifeCycle ): Unit =
102
105
cb(Right (()))
103
106
override def lifeCycleFailure (a : LifeCycle , error : Throwable ): Unit =
104
107
cb(Left (error))
105
108
}
106
- )
109
+ lifeCycle.addLifeCycleListener(listener )
107
110
108
- // Sanity check to ensure the LifeCycle component is not already
109
- // started. A couple of notes here.
110
- //
111
- // - There is _always_ going to be a small chance of a race condition
112
- // here in the final branch where we invoke `lifeCycle.start()` _if_
113
- // something else has a reference to the `LifeCycle`
114
- // value. Thankfully, unlike the stopLifeCycle function, this is
115
- // completely in the control of the caller. As long as the caller
116
- // doesn't leak the reference (or call .start() themselves) nothing
117
- // internally to Jetty should ever invoke .start().
118
- // - Jetty components allow for reuse in many cases, unless the
119
- // .destroy() method is invoked (and the particular type implements
120
- // `Destroyable`, it's not part of `LifeCycle`). Jetty uses this for
121
- // "soft" resets of the `LifeCycle` component. Thus it is possible
122
- // that this `LifeCycle` component has been started before, though I
123
- // don't recommend this and we don't (at this time) do that in the
124
- // http4s codebase.
125
- if (lifeCycle.isStarted) {
126
- cb(
127
- Left (
128
- new IllegalStateException (
129
- " Attempting to start Jetty LifeCycle component, but it is already started."
111
+ // Sanity check to ensure the LifeCycle component is not already
112
+ // started. A couple of notes here.
113
+ //
114
+ // - There is _always_ going to be a small chance of a race condition
115
+ // here in the final branch where we invoke `lifeCycle.start()` _if_
116
+ // something else has a reference to the `LifeCycle`
117
+ // value. Thankfully, unlike the stopLifeCycle function, this is
118
+ // completely in the control of the caller. As long as the caller
119
+ // doesn't leak the reference (or call .start() themselves) nothing
120
+ // internally to Jetty should ever invoke .start().
121
+ // - Jetty components allow for reuse in many cases, unless the
122
+ // .destroy() method is invoked (and the particular type implements
123
+ // `Destroyable`, it's not part of `LifeCycle`). Jetty uses this for
124
+ // "soft" resets of the `LifeCycle` component. Thus it is possible
125
+ // that this `LifeCycle` component has been started before, though I
126
+ // don't recommend this and we don't (at this time) do that in the
127
+ // http4s codebase.
128
+ if (lifeCycle.isStarted) {
129
+ cb(
130
+ Left (
131
+ new IllegalStateException (
132
+ " Attempting to start Jetty LifeCycle component, but it is already started."
133
+ )
130
134
)
131
135
)
132
- )
133
- } else if (lifeCycle.isStarting) {
134
- cb (
135
- Left (
136
- new IllegalStateException (
137
- " Attempting to start Jetty LifeCycle component, but it is already starting. "
136
+ } else if (lifeCycle.isStarting) {
137
+ cb(
138
+ Left (
139
+ new IllegalStateException (
140
+ " Attempting to start Jetty LifeCycle component, but it is already starting. "
141
+ )
138
142
)
139
143
)
140
- )
141
- } else {
142
- lifeCycle.start()
144
+ } else {
145
+ lifeCycle.start()
146
+ }
147
+ Some (F .delay(lifeCycle.removeLifeCycleListener(listener)))
143
148
}
144
149
}
145
150
}
0 commit comments