Question: I'm in a situation where I need to use a C library that calls a python function for IO purposes so I don't have to create a C equivalent for the IO. The python code base makes extensive use of asyncio and all the IO goes through queues. And the C code is loaded as a dll using ctypes The problem is you can't await from a C callback. Is there anyway to use an async python function as a C callback? Below is essentially how it will work. Is my only option to do something like the non_async_callback or is there someway to await from C using ctypes. python import asyncio import time import ctypes DLL_PATH="./bin/test.dll" dll = ctypes.CDLL(DLL_PATH) incoming_queue=asyncio.Queue() outgoing_queue=asyncio.Queue() c_func = ctypes.CFUNCTYPE(ctypes.c_uint8, ctypes.c_uint8) async def python_callback(test_num): await outgoing_queue.put(test_num) test_num = await incoming_queue.get() return test_num def non_async_callback(test_num): while True: try: outgoing_queue.put_nowait(test_num) break except asyncio.QueueFull: time.sleep(0.1) continue while True: try: test_num = incoming_queue.get_nowait(test_num) break except asyncio.QueueFull: time.sleep(0.1) continue return test_num # Called at some point during initialization def setup_callback(): dll.setup_callback(c_func(python_callback))  C uint8_t (CALLBACK*)(uint8_t); CALLBACK py_callback void setup_callbacks(void* callback) { py_callback = callback; } // Called from somewhere else in the C code. uint8_t python_callback(uint8_t value) { uint8_t result = py_callback(value); // Do something with result } 

Answer: From a bit of searching, I believe async def python_callback(test_num): await outgoing_queue.put(test_num) test_num = await incoming_queue.get() return test_num  can be wrapped with something like loop = None # No loop has been created initially def non_async_callback(test_num): global loop if not loop: # Only create a new loop on first callback loop = asyncio.new_event_loop() test_num = loop.run_until_complete(python_callback(test_num)) return test_num  If non_async_callback might be called concurrently from multiple threads, then just use: def non_async_callback(test_num): test_num = asyncio.run(python_callback(test_num)) return test_num  Update Based on the OP's comment it appears that we initially have a Python script calling a C function and that C function ultimately results in a callback being invoked. Thus the callback is occurring on the same thread that the original Python script (and thus its event loop) is executing. Consequently, the problem becomes calling a Python coroutine from a regular Python function without blocking the current event loop. This requires creating a separate thread running a separate event loop: import asyncio import threading # Create a new thread running a new event_loop: _new_event_loop = asyncio.new_event_loop() # Run the new event loop on a new daemon thread: threading.Thread(target=_new_event_loop.run_forever, name="Async Runner", daemon=True).start() async def python_callback(test_num): print('starting') await asyncio.sleep(1) # Emulate doing something print('ending') return test_num * test_num def non_async_callback(test_num): # Invoke the callback on a different thread, since no # further awaits can be done on this thread until this # funtion returns: return asyncio.run_coroutine_threadsafe(python_callback(test_num), _new_event_loop).result() async def main(): ... # Ultimately our callback gets invoked on the same thread: test_num = non_async_callback(4) print(test_num) asyncio.run(main())  Prints: starting ending 16