Skip to content

Instantly share code, notes, and snippets.

@skeeto
Created September 4, 2020 01:20
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save skeeto/89af673a0a0d24de32ad19ee505c8dbd to your computer and use it in GitHub Desktop.
Save skeeto/89af673a0a0d24de32ad19ee505c8dbd to your computer and use it in GitHub Desktop.
# Asynchronous file open/close
# https://nullprogram.com/blog/2020/09/04/
import asyncio
class _AsyncOpen():
def __init__(self, args, kwargs):
self._args = args
self._kwargs = kwargs
def __aenter__(self):
def thread_open():
return open(*self._args, **self._kwargs)
loop = asyncio.get_event_loop()
self._future = loop.run_in_executor(None, thread_open)
return self._future
async def __aexit__(self, exc_type, exc, tb):
file = await self._future
def thread_close():
file.close()
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, thread_close)
def aopen(*args, **kwargs):
return _AsyncOpen(args, kwargs)
## Test / demo
async def heartbeat():
while True:
await asyncio.sleep(0.5)
print('HEARTBEAT')
async def write(i):
async with aopen(f'/tmp/{i}', 'w') as out:
print(i, file=out)
async def main():
beat = asyncio.create_task(heartbeat())
tasks = [asyncio.create_task(write(i)) for i in range(5)]
await asyncio.gather(*tasks)
beat.cancel()
asyncio.run(main())
/* $ cc -shared -fPIC -o open64.so open64.c */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
int
open64(const char *path, int flags, int mode)
{
if (!strncmp(path, "/tmp/", 5)) {
sleep(3);
}
int (*orig)(const char *, int, int) = dlsym(RTLD_NEXT, "open64");
return orig(path, flags, mode);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment