Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for async functions #313

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open

Conversation

tonybaloney
Copy link
Owner

@tonybaloney tonybaloney commented Nov 14, 2024

Concept:

  • A Coroutine[T_Yield, T_Send, T_Return] will be mapped to an ICoroutine<Tyield, TSend, TReturn>, similar to Generator
  • The Coroutine class has an implementation for AsTask() which returns a Task<TYield> by executing the following:
  • Creating a Python event loop if one doesn't exist. Event loops are 1:1 with threads, since .NET reuses threads for tasks from a thread pool, the event loops are reused using thread local statics. All of the event loops are closed at shutdown
  • Generating a task from the Python coroutine object
  • Scheduling the task
  • All of this is done within a thread in .NET Task

The code generator has been updated to reflect the function to Task<TYield> as moves the AsTask() execution outside of the initial GIL block to ensure they don't get deadlocked.

@tonybaloney tonybaloney changed the title [WIP] Add async enumerator Add support for async functions Nov 15, 2024
@tonybaloney
Copy link
Owner Author

Tests are hanging because Py_Finalize is called from a different thread than Py_Initialize. See python/cpython#122517

@tonybaloney tonybaloney marked this pull request as ready for review November 29, 2024 06:52

while (!initialized) // Wait for startup
{
continue;
Copy link
Owner Author

Choose a reason for hiding this comment

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

I'm sure there's a better pattern for doing this, also we should maybe add a 50ms sleep here

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could also use some event (for example, ManualResetEventSlim or AutoResetEvent) instead of a loop in the above initialization thread.

// Clean-up interns
NewEventLoopFactory?.Dispose();
AsyncioModule?.Dispose();
// TODO: Add more cleanup code here
Copy link
Owner Author

Choose a reason for hiding this comment

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

This TODO is to clean up the currently static globals which we never dispose of, but are cleaned up automatically. We should dispose of them incase people recycle environments.


public EventLoop()
{
loop = NewEventLoopFactory!.Call();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This call can return a null? If so, why not simply throw here if null?

close?.Call();
}

public void Dispose()
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should ensure that Dispose() can be called multiple times. Not sure if this implementation handles that.

IsDisposed = true;
}

public PyObject RunTaskUntilComplete(PyObject coroutine)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Public APIs on a type that implement IDisposable typically throw an ObjectDisposedException when they are disposed.


while (!initialized) // Wait for startup
{
continue;
Copy link
Collaborator

Choose a reason for hiding this comment

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


while (!initialized) // Wait for startup
{
continue;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could also use some event (for example, ManualResetEventSlim or AutoResetEvent) instead of a loop in the above initialization thread.

@richardu
Copy link

richardu commented Jan 21, 2025

Hi @tonybaloney et. al,
Thanks for this great tool!
RE: async functionality - what's the current status of it? alpha / beta / RC, etc.?
Thx, Rich

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.

3 participants