Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions docs/release-notes/.FSharp.Core/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,23 @@
* Fix array and string slices with extreme reversed bounds to return correctly shaped empty results. ([Issue #20530](https://github.com/dotnet/fsharp/issues/20530), [PR #20557](https://github.com/dotnet/fsharp/pull/20557))
* Fix printf handling of -0.0 (negative zero) values for float, float32, and decimal values ([Issue #15557](https://github.com/dotnet/fsharp/issues/15557) and [Issue #15558](https://github.com/dotnet/fsharp/issues/15558), [PR #18147](https://github.com/dotnet/fsharp/pull/18147))


### Added

* Add the compiler-recognized `StateMachineHelpers.__runtimeAsyncReturn` intrinsic to the `net10.0` FSharp.Core target for .NET runtime-async methods. ([PR #20235](https://github.com/dotnet/fsharp/pull/20235))
* Add `Unchecked.withNull`, an interop escape hatch that re-types any `'T` to `'T | null` without the usual `not null`/`not struct` constraints, so unconstrained C# nullable-generic APIs (e.g. `T? M<T>()`) can be implemented and consumed from F#. ([Issue #17734](https://github.com/dotnet/fsharp/issues/17734), [PR #20232](https://github.com/dotnet/fsharp/pull/20232))
* Added generic `print` and `printn` functions (`'T -> unit`) to `ExtraTopLevelOperators` for simple value printing to stdout. ([RFC FS-1125](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1125-print-printn-functions.md), [PR #19265](https://github.com/dotnet/fsharp/pull/19265))
* Add generic `print` and `printn` functions (`'T -> unit`) to `ExtraTopLevelOperators` for simple value printing to stdout. ([RFC FS-1125](https://github.com/fsharp/fslang-design/blob/main/RFCs/FS-1125-print-printn-functions.md), [PR #19265](https://github.com/dotnet/fsharp/pull/19265))
* Ship `FSharp.Core` with an additional `net10.0` target framework (next to `netstandard2.0` and `netstandard2.1`). The `net`-TFM assembly is public-surface-identical to the `netstandard2.1` one; the target version is a pinned, deliberately advanced knob. ([PR #20229](https://github.com/dotnet/fsharp/pull/20229))
* Add `Async.Await`, mirroring `Async.AwaitTask` semantics, but elides egregious `AggregateException` wrapping. Includes `ValueTask` support, and a SRTP-based overload accepting any Task-like value that supports the `GetAwaiter` protocol. ([Language Suggestion #840](https://github.com/fsharp/fslang-suggestions/issues/840), [PR #19785](https://github.com/dotnet/fsharp/pull/19785))
* `Async.RunSynchronouslyImmediate`: runs work on the calling thread until the first asynchronous suspension (as opposed to `RunSynchronously`, which immediately offloads if not on a background and/or threadpool thread). ([Issue #1042](https://github.com/fsharp/fslang-suggestions/issues/1042), [PR #19804](https://github.com/dotnet/fsharp/pull/19804))
* Added `AllowOverloadOnReturnTypeAttribute`: applying it to a method causes its return type to be considered during overload resolution, enabling overloads that differ only by return type. ([fslang-suggestions#820](https://github.com/fsharp/fslang-suggestions/issues/820), [PR #19602](https://github.com/dotnet/fsharp/pull/19602))
* Add `Async.RunSynchronouslyImmediate`: runs work on the calling thread until the first asynchronous suspension (as opposed to `RunSynchronously`, which immediately offloads if not on a background and/or threadpool thread). ([Issue #1042](https://github.com/fsharp/fslang-suggestions/issues/1042), [PR #19804](https://github.com/dotnet/fsharp/pull/19804))
* Add `AllowOverloadOnReturnTypeAttribute`: applying it to a method causes its return type to be considered during overload resolution, enabling overloads that differ only by return type. ([fslang-suggestions#820](https://github.com/fsharp/fslang-suggestions/issues/820), [PR #19602](https://github.com/dotnet/fsharp/pull/19602))
* Add `Async.StartTaskImmediate`: passes the ambient `Async.CancellationToken` to a task factory, then await the result using `Async.Await` semantics. Overloads for `Task`, `Task<'T>`, `ValueTask`, `ValueTask<'T>` and task-like `.GetAwaiter()` (via SRTP). ([Language Suggestion #1284](https://github.com/fsharp/fslang-suggestions/issues/1284), [PR #20258](https://github.com/dotnet/fsharp/pull/20258))
* Add modules for `Async`, `Task` and `ValueTask` with consistent `result`, `map`, `bind`, `ignore`, `catchWith`, `catch`, and `empty` functions ([LanguageSuggestion #1466](https://github.com/fsharp/fslang-suggestions/issues/1466), [PR #19844](https://github.com/dotnet/fsharp/pull/19844))
* Add conversion functions `Task.ofValueTask` and `ValueTask.ofTask`. ([LanguageSuggestion #1466](https://github.com/fsharp/fslang-suggestions/issues/1466), [PR #19844](https://github.com/dotnet/fsharp/pull/19844))
* Add `parallelLimit` to `Task` and `Async` modules, for bounded-parallelism execution of async/task computations, flowing cancellation and returning the results as an array. ([LanguageSuggestion #1467](https://github.com/fsharp/fslang-suggestions/issues/1467), [PR #20294](https://github.com/dotnet/fsharp/pull/20294))
* Add `parallelDoLimit` to `Task` and `Async` modules, for bounded-parallelism execution of async/task computations, flowing cancellation. ([LanguageSuggestion #1467](https://github.com/fsharp/fslang-suggestions/issues/1467), [PR #20294](https://github.com/dotnet/fsharp/pull/20294))
* Prototype a compiler-services entry and reference host for direct runtime-async sequences on the `net10.0` target, reusing the first enumerator instance.
* Add `sequentialDo` to `Task` and `Async`, running computations one at a time, flowing cancellation, without yielding an egregious `unit[]`. ([LanguageSuggestion #1467](https://github.com/fsharp/fslang-suggestions/issues/1467), [PR #20294](https://github.com/dotnet/fsharp/pull/20294))
* Add `Task.sequential`, running task computations one at a time, flowing cancellation and returning the results as an array. ([LanguageSuggestion #1467](https://github.com/fsharp/fslang-suggestions/issues/1467), [PR #20294](https://github.com/dotnet/fsharp/pull/20294))
* Add `sequentialDo` to `Task` and `Async`, running computations one at a time, flowing cancellation, without yielding an egregious `unit[]`. ([LanguageSuggestion #1467](https://github.com/fsharp/fslang-suggestions/issues/1467), [PR #20294](https://github.com/dotnet/fsharp/pull/20294), [PR #20695](https://github.com/dotnet/fsharp/pull/20695))
* Add `Task.sequential`, running task computations one at a time, flowing cancellation and returning the results as an array. ([LanguageSuggestion #1467](https://github.com/fsharp/fslang-suggestions/issues/1467), [PR #20294](https://github.com/dotnet/fsharp/pull/20294), [PR #20695](https://github.com/dotnet/fsharp/pull/20695))
* Add `Task.startAsyncImmediate`, starting an `Async<'T>` on the current thread and returning a `Task<'T>`, flowing cancellation. ([LanguageSuggestion #1467](https://github.com/fsharp/fslang-suggestions/issues/1467), [PR #20294](https://github.com/dotnet/fsharp/pull/20294))

### Changed
Expand Down
2 changes: 1 addition & 1 deletion docs/release-notes/.VisualStudio/18.vNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

* Go To Definition no longer blocks the UI thread with a bare `Task.Wait`: the synchronous `IFSharpGoToDefinitionService` call now waits through the cancellable threaded-wait dialog, and the editor's `TaskCompletionSource` bridges run their continuations on the thread pool instead of inline on whichever thread finished the check, so repeated F12 on a large solution no longer starves semantic classification and other main-thread work. ([PR #20482](https://github.com/dotnet/fsharp/pull/20482))
* Peek Definition on an F# symbol whose definition lives in metadata no longer deadlocks Visual Studio. Peek holds the main thread in `JoinableTaskFactory.Run` without pumping messages while it asks the language service for the definition, and generating the metadata document needs that same thread; Peek now stops at definitions that already have a document, and Go To Definition, which owns the wait it makes, still opens the generated one. ([PR #20503](https://github.com/dotnet/fsharp/pull/20503))
* Improve Find All References performance by throttling parallel typechecks. ([PR #20128](https://github.com/dotnet/fsharp/pull/20128))
* Improve Find All References performance by throttling parallel typechecks. ([PR #20128](https://github.com/dotnet/fsharp/pull/20128), [PR #20695](https://github.com/dotnet/fsharp/pull/20695))
* Fixed Rename incorrectly renaming `get` and `set` keywords for properties with explicit accessors. ([Issue #18270](https://github.com/dotnet/fsharp/issues/18270), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
* Fixed Find All References crash when F# project contains non-F# files like `.cshtml`. ([Issue #16394](https://github.com/dotnet/fsharp/issues/16394), [PR #19252](https://github.com/dotnet/fsharp/pull/19252))
* Avoid using `cancellableTask` in `DocumentCache`; the editor cache now uses direct `CancellationToken`-aware `task` wrappers, avoiding the background `Task.Run` offload and a larger wrapper closure from the `cancellableTask` builder. ([Issue #20268](https://github.com/dotnet/fsharp/issues/20268))
Expand Down
112 changes: 61 additions & 51 deletions src/FSharp.Core/tasks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -791,71 +791,81 @@ module Task =

[<CompiledName("Sequential")>]
let sequential (ct: CancellationToken) (computations: seq<CancellationToken -> Task<'T>>) : Task<'T[]> =
task {
let mutable results = ArrayCollector<'T>()
if ct.IsCancellationRequested then
Task.FromCanceled<'T[]> ct
else
task {
let mutable results = ArrayCollector<'T>()

for f in computations do
let! result = f ct
results.Add result
for f in computations do
ct.ThrowIfCancellationRequested()
let! result = f ct
results.Add result

return results.Close()
}
return results.Close()
}

[<CompiledName("SequentialDo")>]
let sequentialDo (ct: CancellationToken) (computations: seq<CancellationToken -> Task<unit>>) : Task<unit> =
task {
for f in computations do
do! f ct
}
if ct.IsCancellationRequested then
Task.FromCanceled<unit> ct
else
task {
for f in computations do
ct.ThrowIfCancellationRequested()
do! f ct
}

[<CompiledName("ParallelLimit")>]
let parallelLimit
(maxDegreeOfParallelism: int)
(ct: CancellationToken)
(computations: seq<CancellationToken -> Task<'T>>)
: Task<'T[]> =
if maxDegreeOfParallelism < 1 then
if ct.IsCancellationRequested then
Task.FromCanceled<'T[]> ct
elif maxDegreeOfParallelism < 1 then
System.String.Format(SR.GetString(SR.maxDegreeOfParallelismNotPositive), maxDegreeOfParallelism)
|> invalidArg (nameof maxDegreeOfParallelism)
// materialize first so exceptions from enumeration can't trigger ObjectDisposedException
// from started children touching semaphore or innerCts
match Seq.toArray computations with
| _ when ct.IsCancellationRequested -> Task.FromCanceled<'T[]> ct
| [||] -> result [||]
| req when maxDegreeOfParallelism = 1 || req.Length = 1 -> sequential ct req
| req ->
task {
let mutable pos = -1
let res = Array.zeroCreate<'T> req.Length

use innerCts = CancellationTokenSource.CreateLinkedTokenSource ct

let worker () =
backgroundTask {
let mutable index = Interlocked.Increment &pos

while index < req.Length && not innerCts.IsCancellationRequested do
let mutable completed = false

try
let! r = req.[index] innerCts.Token
completed <- true
res[index] <- r
finally
if not completed then
innerCts.Cancel()

index <- Interlocked.Increment &pos
}

// Awaits completion of all workers (whether through success, cancellation or faulting)
do! Task.WhenAll [| for _ in 1 .. min req.Length maxDegreeOfParallelism -> worker () :> Task |]
// Where cancellation was requested on the outer ct, but none of the inners saw and/or honored it by throwing TCE,
// res may only be partially complete so we certainly can't return it
// we instead yield a TaskCanceledException to honor standard Task Cancellation semantics
innerCts.Token.ThrowIfCancellationRequested()
return res
}
else
// materialize first so exceptions from enumeration can't trigger ObjectDisposedException
// from started children touching semaphore or innerCts
match Seq.toArray computations with
| [||] -> result [||]
| req when maxDegreeOfParallelism = 1 || req.Length = 1 -> sequential ct req
| req ->
task {
let mutable pos = -1
let res = Array.zeroCreate<'T> req.Length

use innerCts = CancellationTokenSource.CreateLinkedTokenSource ct

let worker () =
backgroundTask {
let mutable index = Interlocked.Increment &pos

while index < req.Length && not innerCts.IsCancellationRequested do
let mutable completed = false

try
let! r = req.[index] innerCts.Token
completed <- true
res[index] <- r
finally
if not completed then
innerCts.Cancel()

index <- Interlocked.Increment &pos
}

// Awaits completion of all workers (whether through success, cancellation or faulting)
do! Task.WhenAll [| for _ in 1 .. min req.Length maxDegreeOfParallelism -> worker () :> Task |]
// Where cancellation was requested on the outer ct, but none of the inners saw and/or honored it by throwing TCE,
// res may only be partially complete so we certainly can't return it
// we instead yield a TaskCanceledException to honor standard Task Cancellation semantics
innerCts.Token.ThrowIfCancellationRequested()
return res
}

[<CompiledName("ParallelDoLimit")>]
let parallelDoLimit
Expand Down
4 changes: 2 additions & 2 deletions src/FSharp.Core/tasks.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ module Task =
/// <param name="ct">A cancellation token to pass to each task factory.</param>
/// <param name="computations">A sequence of task start functions accepting a <see cref="T:System.Threading.CancellationToken"/>.</param>
/// <returns>A task yielding an array of the results of <c>computations</c> in the order they were supplied.</returns>
///
/// <exception cref="OperationCanceledException">Thrown if the provided cancellation token is canceled before the last computation starts, even if the <c>computations</c> ignore cancellation.</exception>
/// <example id="task-sequential-1">
/// <code lang="fsharp">
/// task {
Expand All @@ -598,7 +598,7 @@ module Task =
/// <param name="ct">A cancellation token to pass to each task factory.</param>
/// <param name="computations">A sequence of unit task start functions accepting a <see cref="T:System.Threading.CancellationToken"/>.</param>
/// <returns>A task that runs all inputs in sequence and returns <c>unit</c>.</returns>
///
/// <exception cref="OperationCanceledException">Thrown if the provided cancellation token is canceled before the last computation starts, even if the <c>computations</c> ignore cancellation.</exception>
/// <example id="task-sequentialdo-1">
/// <code lang="fsharp">
/// task {
Expand Down
Loading
Loading