Add io_create_file, io_open_file
This commit is contained in:
@@ -9,14 +9,51 @@
|
||||
* Declare Windows symbols locally to avoid including windows.h
|
||||
*/
|
||||
|
||||
#define GENERIC_READ 0x80000000ULL
|
||||
#define GENERIC_WRITE 0x40000000ULL
|
||||
|
||||
#define CREATE_NEW 1
|
||||
#define CREATE_ALWAYS 2
|
||||
#define OPEN_EXISTING 3
|
||||
#define OPEN_ALWAYS 4
|
||||
|
||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080ULL
|
||||
#define FILE_FLAG_OVERLAPPED 0x40000000ULL
|
||||
|
||||
#define ERROR_IO_PENDING 997l
|
||||
|
||||
extern int ReadFile(void *handle, void *dst, unsigned long max, unsigned long *num, struct io_overlap *ov);
|
||||
extern int WriteFile(void *handle, const void *src, unsigned long max, unsigned long *num, struct io_overlap *ov);
|
||||
extern void *CreateIoCompletionPort(void *handle, void *existing_ioport, unsigned long *ptr, unsigned long num_threads);
|
||||
extern int CloseHandle(void *handle);
|
||||
extern unsigned long GetLastError();
|
||||
extern int GetQueuedCompletionStatus(void *ioport, unsigned long *num, unsigned long *key, struct io_overlap **ov, unsigned long timeout);
|
||||
struct security_attr {
|
||||
unsigned long size;
|
||||
void *desc;
|
||||
int inherit_handle;
|
||||
};
|
||||
|
||||
extern void*
|
||||
CreateFileA(const char *name, unsigned long access,
|
||||
unsigned long share, struct security_attr *sec,
|
||||
unsigned long creation, unsigned long flags,
|
||||
void *template);
|
||||
|
||||
extern int
|
||||
ReadFile(void *handle, void *dst, unsigned long max,
|
||||
unsigned long *num, struct io_overlap *ov);
|
||||
|
||||
extern int
|
||||
WriteFile(void *handle, const void *src, unsigned long max,
|
||||
unsigned long *num, struct io_overlap *ov);
|
||||
|
||||
extern void*
|
||||
CreateIoCompletionPort(void *handle, void *existing_ioport,
|
||||
unsigned long *ptr, unsigned long num_threads);
|
||||
|
||||
extern int
|
||||
CloseHandle(void *handle);
|
||||
|
||||
extern unsigned long
|
||||
GetLastError();
|
||||
|
||||
extern int
|
||||
GetQueuedCompletionStatus(void *ioport, unsigned long *num, unsigned long *key, struct io_overlap **ov, unsigned long timeout);
|
||||
|
||||
bool io_context_init(struct io_context *ioc,
|
||||
struct io_operation *ops,
|
||||
@@ -25,6 +62,9 @@ bool io_context_init(struct io_context *ioc,
|
||||
io_handle handle = CreateIoCompletionPort(IO_INVALID_HANDLE, NULL, 0, 1);
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < max_ops; i++)
|
||||
ops[i].type = IO_VOID;
|
||||
|
||||
ioc->handle = handle;
|
||||
ioc->max_ops = max_ops;
|
||||
@@ -46,8 +86,11 @@ static struct io_operation *alloc_op(struct io_context *ioc)
|
||||
}
|
||||
|
||||
bool io_start_recv(struct io_context *ioc, io_handle handle,
|
||||
void *dst, uint32_t max, void *user)
|
||||
void *dst, uint32_t max)
|
||||
{
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
struct io_operation *op = alloc_op(ioc);
|
||||
if (op == NULL)
|
||||
return false;
|
||||
@@ -58,14 +101,16 @@ bool io_start_recv(struct io_context *ioc, io_handle handle,
|
||||
if (!ok && GetLastError() != ERROR_IO_PENDING)
|
||||
return false;
|
||||
|
||||
op->user = user;
|
||||
op->type = IO_RECV;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool io_start_send(struct io_context *ioc, io_handle handle,
|
||||
void *src, uint32_t num, void *user)
|
||||
void *src, uint32_t num)
|
||||
{
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
struct io_operation *op = alloc_op(ioc);
|
||||
if (op == NULL)
|
||||
return false;
|
||||
@@ -76,20 +121,133 @@ bool io_start_send(struct io_context *ioc, io_handle handle,
|
||||
if (!ok && GetLastError() != ERROR_IO_PENDING)
|
||||
return false;
|
||||
|
||||
op->user = user;
|
||||
op->type = IO_SEND;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool io_start_accept(struct io_context *ioc, io_handle handle, void *user)
|
||||
bool io_start_accept(struct io_context *ioc, io_handle handle)
|
||||
{
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
convert_timeout(int timeout)
|
||||
{
|
||||
if (timeout < 0)
|
||||
return ~0U;
|
||||
else
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static struct io_operation *op_from_ov(struct io_overlap *ov)
|
||||
{
|
||||
return (struct io_operation*) ((char*) ov - offsetof(struct io_operation, ov));
|
||||
}
|
||||
|
||||
void io_wait(struct io_context *ioc, struct io_event *ev)
|
||||
{
|
||||
// TODO
|
||||
void *user;
|
||||
struct io_overlap *ov;
|
||||
unsigned long num;
|
||||
int ok = GetQueuedCompletionStatus(ioc->handle, &num, (unsigned long*) &user, &ov, convert_timeout(-1));
|
||||
|
||||
if (!ok) {
|
||||
|
||||
if (ov == NULL) {
|
||||
/*
|
||||
* General failure
|
||||
*/
|
||||
ev->error = true;
|
||||
ev->user = NULL; // The user must discriminate between general errors and specific operation errors through the user pointer. Not ideal
|
||||
ev->type = IO_VOID;
|
||||
} else {
|
||||
/*
|
||||
* Operation failure
|
||||
*/
|
||||
|
||||
struct io_operation *op = op_from_ov(ov);
|
||||
|
||||
ev->error = true;
|
||||
ev->user = user;
|
||||
ev->type = op->type;
|
||||
|
||||
switch (op->type) {
|
||||
default:break;
|
||||
case IO_RECV: ev->num = 0; break;
|
||||
case IO_SEND: ev->num = 0; break;
|
||||
case IO_ACCEPT: ev->handle = IO_INVALID_HANDLE; break;
|
||||
}
|
||||
|
||||
op->type = IO_VOID; // Mark unused
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct io_operation *op = op_from_ov(ov);
|
||||
|
||||
ev->error = false;
|
||||
ev->user = user;
|
||||
ev->type = op->type;
|
||||
|
||||
switch (op->type) {
|
||||
default:break;
|
||||
case IO_RECV: ev->num = num; break;
|
||||
case IO_SEND: ev->num = num; break;
|
||||
case IO_ACCEPT: /* TODO */ break;
|
||||
}
|
||||
|
||||
op->type = IO_VOID; // Mark unused
|
||||
}
|
||||
|
||||
io_handle io_open_file(struct io_context *ioc,
|
||||
const char *name,
|
||||
int flags, void *user)
|
||||
{
|
||||
unsigned long access = 0;
|
||||
if (flags & IO_ACCESS_RD) access |= GENERIC_READ;
|
||||
if (flags & IO_ACCESS_WR) access |= GENERIC_WRITE;
|
||||
|
||||
io_handle handle = CreateFileA(name, access, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return IO_INVALID_HANDLE;
|
||||
|
||||
if (CreateIoCompletionPort(handle, ioc->handle, user, 0) == NULL) {
|
||||
CloseHandle(handle);
|
||||
return IO_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
io_handle io_create_file(struct io_context *ioc,
|
||||
const char *name,
|
||||
int flags, void *user)
|
||||
{
|
||||
unsigned long flags2 = 0;
|
||||
|
||||
if (flags & IO_CREATE_CANTEXIST)
|
||||
flags2 = CREATE_NEW;
|
||||
else {
|
||||
if (flags & IO_CREATE_OVERWRITE)
|
||||
flags2 = CREATE_ALWAYS;
|
||||
else
|
||||
flags2 = OPEN_ALWAYS;
|
||||
}
|
||||
|
||||
io_handle handle = CreateFileA(name, GENERIC_WRITE, 0, NULL, flags2, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return IO_INVALID_HANDLE;
|
||||
|
||||
if (CreateIoCompletionPort(handle, ioc->handle, user, 0) == NULL) {
|
||||
CloseHandle(handle);
|
||||
return IO_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -97,6 +255,8 @@ void io_wait(struct io_context *ioc, struct io_event *ev)
|
||||
|
||||
#if IO_PLATFORM_LINUX
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -124,8 +284,10 @@ bool io_context_init(struct io_context *ioc,
|
||||
ioc->ops = ops;
|
||||
ioc->max_ops = max_ops;
|
||||
|
||||
for (uint32_t i = 0; i < max_ops; i++)
|
||||
for (uint32_t i = 0; i < max_ops; i++) {
|
||||
ioc->ops[i].type = IO_VOID;
|
||||
ioc->ops[i].handle = IO_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
struct io_uring_params p;
|
||||
void *sq_ptr, *cq_ptr;
|
||||
@@ -228,21 +390,57 @@ static bool start_oper(struct io_context *ioc,
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct io_operation *alloc_op(struct io_context *ioc)
|
||||
static struct io_operation *alloc_op(struct io_context *ioc,
|
||||
io_handle handle, void **user)
|
||||
{
|
||||
for (uint32_t i = 0; i < ioc->max_ops; i++)
|
||||
if (ioc->ops[i].type == IO_VOID)
|
||||
return &ioc->ops[i];
|
||||
return NULL;
|
||||
assert(handle != IO_INVALID_HANDLE);
|
||||
|
||||
/*
|
||||
* Look for an empty struct and the struct associated to
|
||||
* this handle.
|
||||
*/
|
||||
struct io_operation *ref = NULL;
|
||||
struct io_operation *empty = NULL;
|
||||
|
||||
for (uint32_t i = 0; i < ioc->max_ops; i++) {
|
||||
|
||||
struct io_operation *op = &ioc->ops[i];
|
||||
|
||||
if (op->handle == handle) {
|
||||
ref = op;
|
||||
if (empty) break;
|
||||
} else {
|
||||
if (op->type == IO_VOID && op->handle == IO_INVALID_HANDLE) {
|
||||
empty = op;
|
||||
if (ref) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(ref && empty);
|
||||
|
||||
*user = ref->user;
|
||||
|
||||
/*
|
||||
* If the reference slot is unused, use that one,
|
||||
* else use the first empty one.
|
||||
*/
|
||||
if (ref->type == IO_VOID)
|
||||
return ref;
|
||||
else
|
||||
return empty;
|
||||
}
|
||||
|
||||
bool io_start_recv(struct io_context *ioc, io_handle handle,
|
||||
void *dst, uint32_t max, void *user)
|
||||
void *dst, uint32_t max)
|
||||
{
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
void *user;
|
||||
struct io_operation *op;
|
||||
struct io_uring_sqe sqe;
|
||||
|
||||
op = alloc_op(ioc);
|
||||
op = alloc_op(ioc, handle, &user);
|
||||
if (op == NULL)
|
||||
return false;
|
||||
|
||||
@@ -255,19 +453,23 @@ bool io_start_recv(struct io_context *ioc, io_handle handle,
|
||||
|
||||
if (!start_oper(ioc, sqe))
|
||||
return false;
|
||||
|
||||
op->user = user;
|
||||
|
||||
op->user = user;
|
||||
op->type = IO_RECV; // Commit operation structure
|
||||
return true;
|
||||
}
|
||||
|
||||
bool io_start_send(struct io_context *ioc, io_handle handle,
|
||||
void *src, uint32_t num, void *user)
|
||||
void *src, uint32_t num)
|
||||
{
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
void *user;
|
||||
struct io_operation *op;
|
||||
struct io_uring_sqe sqe;
|
||||
|
||||
op = alloc_op(ioc);
|
||||
op = alloc_op(ioc, handle, &user);
|
||||
if (op == NULL)
|
||||
return false;
|
||||
|
||||
@@ -277,21 +479,25 @@ bool io_start_send(struct io_context *ioc, io_handle handle,
|
||||
sqe.addr = (uint64_t) src;
|
||||
sqe.len = num;
|
||||
sqe.user_data = (uint64_t) op;
|
||||
|
||||
|
||||
if (!start_oper(ioc, sqe))
|
||||
return false;
|
||||
|
||||
|
||||
op->user = user;
|
||||
op->type = IO_SEND; // Commit operation structure
|
||||
return true;
|
||||
}
|
||||
|
||||
bool io_start_accept(struct io_context *ioc, io_handle handle, void *user)
|
||||
bool io_start_accept(struct io_context *ioc, io_handle handle)
|
||||
{
|
||||
if (handle == IO_INVALID_HANDLE)
|
||||
return false;
|
||||
|
||||
void *user;
|
||||
struct io_operation *op;
|
||||
struct io_uring_sqe sqe;
|
||||
|
||||
op = alloc_op(ioc);
|
||||
op = alloc_op(ioc, handle, &user);
|
||||
if (op == NULL)
|
||||
return false;
|
||||
|
||||
@@ -351,4 +557,65 @@ void io_wait(struct io_context *ioc, struct io_event *ev)
|
||||
atomic_store(ioc->completions.head, head+1);
|
||||
}
|
||||
|
||||
static struct io_operation*
|
||||
unassociated_operation_struct(struct io_context *ioc)
|
||||
{
|
||||
for (uint32_t i = 0; i < ioc->max_ops; i++) {
|
||||
struct io_operation *op = &ioc->ops[i];
|
||||
if (op->type == IO_VOID && op->handle == IO_INVALID_HANDLE)
|
||||
return op;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io_handle io_open_file(struct io_context *ioc,
|
||||
const char *name,
|
||||
int flags, void *user)
|
||||
{
|
||||
struct io_operation *op;
|
||||
op = unassociated_operation_struct(ioc);
|
||||
if (op == NULL)
|
||||
return IO_INVALID_HANDLE;
|
||||
|
||||
int flags2 = 0;
|
||||
if (flags & IO_ACCESS_RD) flags2 |= O_RDONLY;
|
||||
if (flags & IO_ACCESS_WR) flags2 |= O_WRONLY;
|
||||
|
||||
io_handle fd = open(name, flags2);
|
||||
if (fd < 0)
|
||||
return IO_INVALID_HANDLE;
|
||||
|
||||
op->handle = fd;
|
||||
op->user = user;
|
||||
return fd;
|
||||
}
|
||||
|
||||
io_handle io_create_file(struct io_context *ioc,
|
||||
const char *name,
|
||||
int flags, void *user)
|
||||
{
|
||||
struct io_operation *op;
|
||||
op = unassociated_operation_struct(ioc);
|
||||
if (op == NULL)
|
||||
return IO_INVALID_HANDLE;
|
||||
|
||||
int flags2 = O_CREAT | O_WRONLY;
|
||||
|
||||
if (flags & IO_CREATE_CANTEXIST)
|
||||
flags2 |= O_EXCL;
|
||||
else {
|
||||
if (flags & IO_CREATE_OVERWRITE)
|
||||
flags2 |= O_TRUNC;
|
||||
}
|
||||
|
||||
// TODO: is 0666 ok?
|
||||
int fd = open(name, flags2, 0666);
|
||||
if (fd < 0)
|
||||
return IO_INVALID_HANDLE;
|
||||
|
||||
op->handle = fd;
|
||||
op->user = user;
|
||||
return fd;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user