Skip to content

Conversation

NinoFloris
Copy link
Member

Fixes #5715

@@ -353,6 +314,58 @@ public Continuation(object handle, delegate*<Task, object, int[], void> continua

public void Invoke(Task task, object collection, int[] indices) => _continuation(task, collection, indices);
}
}

abstract class ArrayConverter<T> : PgStreamingConverter<T> where T : notnull
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No more class constraint, should make ROM easier to implement too now

SetValue(collection, indices, new ValueTask<TElement>((Task<TElement>)task).Result);
// Justification: exact type Unsafe.As used to reduce generic duplication cost.
Debug.Assert(task is Task<TElement>);
// Using .Result on ValueTask is equivalent to GetAwaiter().GetResult(), this removes TaskAwaiter<T> rooting.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not new in this PR, but I seem to remember that .Result causes AggregateException to be thrown on failure, wrapping the actual exceptionn (whereas GetAwaiter().GetResult() unwraps)... May be worth making sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand ValueTask doesn't do so due to the expectation that it's only used for sequential async flows (single consumer single producer). As opposed to Task supporting Dataflow, Task.WhenAll and friends, where exceptions from multiple tasks can come together in one.

// We're using ValueTask.Result here to avoid rooting any TaskAwaiter or ValueTaskAwaiter types.
// On ValueTask calling .Result is equivalent to GetAwaiter().GetResult() w.r.t. exception wrapping.
return new ValueTask<T>(task: (Task<T>)task).Result!;
// Justification: exact type Unsafe.As used to reduce generic duplication cost.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed this assert and justification in #5554

SetValue(collection, indices, new ValueTask<TElement>((Task<TElement>)task).Result);
// Justification: exact type Unsafe.As used to reduce generic duplication cost.
Debug.Assert(task is Task<TElement>);
// Using .Result on ValueTask is equivalent to GetAwaiter().GetResult(), this removes TaskAwaiter<T> rooting.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand ValueTask doesn't do so due to the expectation that it's only used for sequential async flows (single consumer single producer). As opposed to Task supporting Dataflow, Task.WhenAll and friends, where exceptions from multiple tasks can come together in one.

@NinoFloris NinoFloris merged commit 81e2f6e into npgsql:main Jun 24, 2024
NinoFloris added a commit that referenced this pull request Jun 24, 2024
(cherry picked from commit 81e2f6e)
@NinoFloris
Copy link
Member Author

Backported to 8.0.4 via 1312c64

@NinoFloris NinoFloris deleted the fix-unsafe-cast branch June 28, 2024 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NpgsqlBinaryExporter.ReadAsync<string[,]>() throws EntryPointNotFoundException
2 participants