@@ -72,17 +72,23 @@ trait GenTemporal[F[_], E] extends GenConcurrent[F, E] with Clock[F] {
7272 productL(fa)(sleep(time))
7373
7474 /**
75- * Returns an effect that either completes with the result of the source within the specified
76- * time `duration` or otherwise evaluates the `fallback`.
75+ * Returns an effect that either completes with the result of the source or otherwise
76+ * evaluates the `fallback`.
7777 *
78- * The source is canceled in the event that it takes longer than the specified time duration
79- * to complete. Once the source has been successfully canceled (and has completed its
80- * finalizers), the fallback will be sequenced. If the source is uncancelable, the resulting
81- * effect will wait for it to complete before evaluating the fallback.
78+ * The source is raised against the timeout `duration`, and its cancelation is triggered if
79+ * the source doesn't complete within the specified time. The resulting effect will always
80+ * wait for the source effect to complete (and to complete its finalizers), and will return
81+ * the source's outcome over sequencing the `fallback`.
82+ *
83+ * In case source and timeout complete simultaneously, the result of the source will be
84+ * returned over sequencing the `fallback`.
85+ *
86+ * If the source in uncancelable, `fallback` will never be evaluated.
8287 *
8388 * @param duration
84- * The time span for which we wait for the source to complete; in the event that the
85- * specified time has passed without the source completing, the `fallback` gets evaluated
89+ * The time span for which we wait for the source to complete before triggering its
90+ * cancelation; in the event that the specified time has passed without the source
91+ * completing, the `fallback` gets evaluated
8692 *
8793 * @param fallback
8894 * The task evaluated after the duration has passed and the source canceled
@@ -91,33 +97,53 @@ trait GenTemporal[F[_], E] extends GenConcurrent[F, E] with Clock[F] {
9197 handleDuration(duration, fa)(timeoutTo(fa, _, fallback))
9298
9399 protected def timeoutTo [A ](fa : F [A ], duration : FiniteDuration , fallback : F [A ]): F [A ] =
94- flatMap(race(fa, sleep(duration))) {
95- case Left (a) => pure(a)
96- case Right (_) => fallback
100+ uncancelable { poll =>
101+ implicit val F : GenTemporal [F , E ] = this
102+
103+ poll(racePair(fa, sleep(duration))) flatMap {
104+ case Left ((oc, f)) => f.cancel *> oc.embed(poll(F .canceled) *> F .never)
105+ case Right ((f, _)) => f.cancel *> f.join.flatMap { oc => oc.embed(fallback) }
106+ }
97107 }
98108
99109 /**
100- * Returns an effect that either completes with the result of the source within the specified
101- * time `duration` or otherwise raises a `TimeoutException`.
110+ * Returns an effect that either completes with the result of the source or raises a
111+ * `TimeoutException`.
102112 *
103- * The source is canceled in the event that it takes longer than the specified time duration
104- * to complete. Once the source has been successfully canceled (and has completed its
105- * finalizers), the `TimeoutException` will be raised. If the source is uncancelable, the
106- * resulting effect will wait for it to complete before raising the exception.
113+ * The source is raced against the timeout `duration`, and its cancelation is triggered if the
114+ * source doesn't complete within the specified time. The resulting effect will always wait
115+ * for the source effect to complete (and to complete its finalizers), and will return the
116+ * source's outcome over raising a `TimeoutException`.
117+ *
118+ * In case source and timeout complete simultaneously, the result of the source will be
119+ * returned over raising a `TimeoutException`.
120+ *
121+ * If the source effect is uncancelable, a `TimeoutException` will never be raised.
107122 *
108123 * @param duration
109- * The time span for which we wait for the source to complete; in the event that the
110- * specified time has passed without the source completing, a `TimeoutException` is raised
124+ * The time span for which we wait for the source to complete before triggering its
125+ * cancelation; in the event that the specified time has passed without the source
126+ * completing, a `TimeoutException` is raised
127+ * @see
128+ * [[timeoutAndForget[A](fa:F[A],duration:scala\.concurrent\.duration\.Duration)* timeoutAndForget]]
129+ * for a variant which does not wait for cancelation of the source effect to complete.
111130 */
112131 def timeout [A ](fa : F [A ], duration : Duration )(implicit ev : TimeoutException <:< E ): F [A ] = {
113132 handleDuration(duration, fa)(timeout(fa, _))
114133 }
115134
116135 protected def timeout [A ](fa : F [A ], duration : FiniteDuration )(
117136 implicit ev : TimeoutException <:< E ): F [A ] = {
118- flatMap(race(fa, sleep(duration))) {
119- case Left (a) => pure(a)
120- case Right (_) => raiseError[A ](ev(new TimeoutException (duration.toString())))
137+ uncancelable { poll =>
138+ implicit val F : GenTemporal [F , E ] = this
139+
140+ poll(racePair(fa, sleep(duration))) flatMap {
141+ case Left ((oc, f)) => f.cancel *> oc.embed(poll(F .canceled) *> F .never)
142+ case Right ((f, _)) =>
143+ f.cancel *> f.join.flatMap { oc =>
144+ oc.embed(raiseError[A ](ev(new TimeoutException (duration.toString()))))
145+ }
146+ }
121147 }
122148 }
123149
0 commit comments