Giter VIP home page Giter VIP logo

Comments (5)

timcassell avatar timcassell commented on May 28, 2024

Enumerables usually act as enumerator-generating factories, so they can be re-used for multiple iterations. This is problematic for us, because we have no knowledge of when the user is finished with the enumerable, so we can't use object pooling conveniently. We could add a Dispose call to re-pool it, but enumerables are not usually used that way, and that adds extra boilerplate code.

I'm thinking to solve this issue, just make PromiseEnumerable single-use, so when GetAsyncEnumerator is called, it is invalidated and cannot be legally called a second time. This matches how enumerables are usually consumed. It is unusual (though not rare) to consume an enumerable more than once. If an enumerator needs to be consumed multiple times, then just call the same function again that built it the first time. This makes it so we only need 1 allocation, and it can be re-pooled when the iteration is complete.

Because "just call the same function again" is not quite the same thing as directly consuming the same enumerable multiple times, due to side effects, I'm also contemplating adding Preserve and Forget APIs (to match the Promise APIs) to allow consuming the same enumerable more than once. But I'm not 100% on that, because it complicates nested enumerables scenarios (when 1 is preserved, it will need to preserve its child). I think it's best to just do the single-use only, and revisit preserve/forget in the future if it's really needed.

from protopromise.

timcassell avatar timcassell commented on May 28, 2024

Or another option is to only implement PromiseEnumerator which is single-use by contract, and don't implement PromiseEnumerable at all (PromiseEnumerator.GetAsyncEnumerator would just return itself to still work with await foreach syntax).

I think that's a better option. And then revisit PromiseEnumerable in the future if it makes sense.

from protopromise.

timcassell avatar timcassell commented on May 28, 2024

After some more investigation, I've found that I cannot safely implement the DisposeAsync pattern without compiler support.

Consider this implementation:

static PromiseEnumerable<int> IterateAsync()
{
    return PromiseEnumerable.Create<int>(async (writer, _) =>
    {
        try
        {
            await writer.YieldAsync(0);
            await writer.YieldAsync(1);
        }
        finally
        {
            Console.WriteLine("Finally block");
        }
    });
}

Then consumed with a break:

await foreach (var elem in IterateAsync())
{
    Console.WriteLine("elem: " + elem);
    break;
}

We have no way of safely running that finally block while skipping over the rest of the code. We could try to reject or cancel the promise returned by writer.YieldAsync, but that's not safe, because the user could catch it. Even if we try to use a special exception, it could still be caught by a general catch clause.

from protopromise.

timcassell avatar timcassell commented on May 28, 2024

I realized that C# prevents usage of yield inside a try block that has an associated catch block. https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs1626

If we extend that restriction to this, it alleviates the issue in my previous comment. The only thing left to make this feasible then is to investigate whether a Roslyn analyzer can be added to prevent using await writer.YieldAsync in such a try/catch block.

from protopromise.

timcassell avatar timcassell commented on May 28, 2024

I was able to prototype an analyzer that successfully shows an error if it's used incorrectly. Unfortunately, Unity doesn't have a way to include analyzers in packages that just work out-of-the-box, so I guess the only course of action is to create an analyzer nuget package, and recommend users to install it separately.

from protopromise.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.