2006-04-12 01:59:08 +02:00
|
|
|
/**
|
|
|
|
* =========================================================================
|
|
|
|
* File : waio.cpp
|
|
|
|
* Project : 0 A.D.
|
|
|
|
* Description : emulate POSIX asynchronous I/O on Windows.
|
|
|
|
*
|
|
|
|
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
|
|
|
* =========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2003-2004 Jan Wassenberg
|
|
|
|
*
|
|
|
|
* Redistribution and/or modification are also permitted under the
|
|
|
|
* terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation (version 2 or later, at your option).
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*/
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-08 03:11:51 +02:00
|
|
|
#include "precompiled.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "lib/lib.h"
|
|
|
|
#include "lib/posix.h"
|
2004-03-03 00:56:51 +01:00
|
|
|
#include "win_internal.h"
|
|
|
|
|
2005-06-28 06:06:25 +02:00
|
|
|
|
2004-06-04 14:41:53 +02:00
|
|
|
#include <stdlib.h>
|
2004-09-21 15:40:14 +02:00
|
|
|
#include <malloc.h> // _aligned_malloc
|
2004-06-04 14:41:53 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
#define lock() win_lock(WAIO_CS)
|
|
|
|
#define unlock() win_unlock(WAIO_CS)
|
|
|
|
|
|
|
|
|
2005-06-21 18:31:55 +02:00
|
|
|
#pragma data_seg(WIN_CALLBACK_PRE_LIBC(c))
|
2004-06-13 18:06:23 +02:00
|
|
|
WIN_REGISTER_FUNC(waio_init);
|
2005-06-21 18:31:55 +02:00
|
|
|
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(x))
|
2004-06-13 18:06:23 +02:00
|
|
|
WIN_REGISTER_FUNC(waio_shutdown);
|
|
|
|
#pragma data_seg()
|
|
|
|
|
2006-04-03 03:00:45 +02:00
|
|
|
// return the largest sector size [bytes] of any storage medium
|
|
|
|
// (HD, optical, etc.) in the system.
|
|
|
|
//
|
|
|
|
// this may be a bit slow to determine (iterates over all drives),
|
|
|
|
// but caches the result so subsequent calls are free.
|
|
|
|
// (caveat: device changes won't be noticed during this program run)
|
|
|
|
//
|
|
|
|
// sector size is relevant because Windows aio requires all IO
|
|
|
|
// buffers, offsets and lengths to be a multiple of it. this requirement
|
|
|
|
// is also carried over into the vfs / file.cpp interfaces for efficiency
|
|
|
|
// (avoids the need for copying to/from align buffers).
|
|
|
|
//
|
|
|
|
// waio uses the sector size to (in some cases) align IOs if
|
|
|
|
// they aren't already, but it's also needed by user code when
|
|
|
|
// aligning their buffers to meet the requirements.
|
|
|
|
//
|
|
|
|
// the largest size is used so that we can read from any drive. while this
|
|
|
|
// is a bit wasteful (more padding) and requires iterating over all drives,
|
|
|
|
// it is the only safe way: this may be called before we know which
|
|
|
|
// drives will be needed, and hardlinks may confuse things.
|
|
|
|
size_t sys_max_sector_size()
|
2006-01-24 09:16:29 +01:00
|
|
|
{
|
2006-04-03 03:00:45 +02:00
|
|
|
// users may call us more than once, so cache the results.
|
|
|
|
static size_t cached_sector_size;
|
|
|
|
if(cached_sector_size)
|
|
|
|
return cached_sector_size;
|
|
|
|
|
|
|
|
// currently disabled: DVDs have 2..4KB, but this causes
|
|
|
|
// waio to unnecessarily align some file transfers (when at EOF)
|
|
|
|
// this means that we might not be able to read from CD/DVD drives
|
|
|
|
// (ReadFile will return error)
|
|
|
|
// reactivated for correctness.
|
|
|
|
|
2006-01-24 09:16:29 +01:00
|
|
|
// temporarily disable the "insert disk into drive" error box; we are
|
|
|
|
// only interested in fixed drives anyway.
|
|
|
|
//
|
|
|
|
// note: use SetErrorMode (crappy interface, grr) twice so as not to
|
|
|
|
// stomp on other flags (e.g. alignment exception).
|
|
|
|
const UINT old_err_mode = SetErrorMode(0);
|
|
|
|
SetErrorMode(old_err_mode|SEM_FAILCRITICALERRORS);
|
|
|
|
|
2006-04-03 03:00:45 +02:00
|
|
|
// find maximum of all drive's sector sizes.
|
2006-01-24 09:16:29 +01:00
|
|
|
const DWORD drives = GetLogicalDrives();
|
|
|
|
char drive_str[4] = "?:\\";
|
2006-04-19 23:55:51 +02:00
|
|
|
for(int drive = 2; drive <= 25; drive++) // C: .. Z:
|
2006-01-24 09:16:29 +01:00
|
|
|
{
|
|
|
|
// avoid BoundsChecker warning by skipping invalid drives
|
|
|
|
if(!(drives & BIT(drive)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
drive_str[0] = (char)('A'+drive);
|
|
|
|
|
|
|
|
DWORD spc, nfc, tnc; // don't need these
|
2006-04-03 03:00:45 +02:00
|
|
|
DWORD cur_sector_size;
|
|
|
|
if(GetDiskFreeSpace(drive_str, &spc, &cur_sector_size, &nfc, &tnc))
|
|
|
|
cached_sector_size = MAX(cached_sector_size, cur_sector_size);
|
2006-01-24 09:16:29 +01:00
|
|
|
// otherwise, it's probably an empty CD drive. ignore the
|
|
|
|
// BoundsChecker error; GetDiskFreeSpace seems to be the
|
|
|
|
// only way of getting at the sector size.
|
|
|
|
}
|
|
|
|
|
|
|
|
SetErrorMode(old_err_mode);
|
|
|
|
|
2006-04-03 03:00:45 +02:00
|
|
|
// sanity check; believed to be the case for all drives.
|
|
|
|
debug_assert(cached_sector_size % 512 == 0);
|
|
|
|
|
|
|
|
return cached_sector_size;
|
2006-01-24 09:16:29 +01:00
|
|
|
}
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2004-05-27 19:30:06 +02:00
|
|
|
// associate async-capable handle with POSIX file descriptor (int)
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// current implementation: open file again for async access on each open();
|
|
|
|
// wastes 1 HANDLE per file, but that's less overhead than storing the
|
|
|
|
// filename/mode for every file and re-opening that on demand.
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
2004-09-21 15:40:14 +02:00
|
|
|
// note: current Windows lowio file descriptor limit is 2k
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
static HANDLE* aio_hs;
|
|
|
|
// array; expanded when needed in aio_h_set
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
static int aio_hs_size;
|
2004-05-27 19:30:06 +02:00
|
|
|
// often compared against fd => int
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// aio_h: no init needed.
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
static void aio_h_cleanup()
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-05-27 19:30:06 +02:00
|
|
|
lock();
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
for(int i = 0; i < aio_hs_size; i++)
|
|
|
|
if(aio_hs[i] != INVALID_HANDLE_VALUE)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-05-27 19:30:06 +02:00
|
|
|
if(!CloseHandle(aio_hs[i]))
|
|
|
|
debug_warn("CloseHandle failed");
|
2004-05-06 19:14:30 +02:00
|
|
|
aio_hs[i] = INVALID_HANDLE_VALUE;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
free(aio_hs);
|
|
|
|
aio_hs = 0;
|
|
|
|
|
|
|
|
aio_hs_size = 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
unlock();
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
static bool is_valid_file_handle(const HANDLE h)
|
2004-03-03 16:16:20 +01:00
|
|
|
{
|
2005-03-18 23:15:49 +01:00
|
|
|
const bool valid = (GetFileSize(h, 0) != INVALID_FILE_SIZE);
|
|
|
|
if(!valid)
|
|
|
|
debug_warn("waio: invalid file handle");
|
2004-03-03 16:16:20 +01:00
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2005-03-18 23:15:49 +01:00
|
|
|
// return true iff an aio-capable HANDLE has been attached to <fd>.
|
|
|
|
// used by aio_close.
|
|
|
|
static bool aio_h_is_set(const int fd)
|
|
|
|
{
|
|
|
|
bool is_set = false;
|
|
|
|
|
|
|
|
lock();
|
|
|
|
|
|
|
|
if(0 <= fd && fd < aio_hs_size)
|
|
|
|
if(aio_hs[fd] != INVALID_HANDLE_VALUE)
|
|
|
|
is_set = true;
|
|
|
|
|
|
|
|
unlock();
|
|
|
|
|
|
|
|
return is_set;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// return async-capable handle associated with file <fd>
|
2005-01-07 02:13:48 +01:00
|
|
|
static HANDLE aio_h_get(const int fd)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
HANDLE h = INVALID_HANDLE_VALUE;
|
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
lock();
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
if(0 <= fd && fd < aio_hs_size)
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
2004-05-27 19:30:06 +02:00
|
|
|
h = aio_hs[fd];
|
|
|
|
if(!is_valid_file_handle(h))
|
|
|
|
h = INVALID_HANDLE_VALUE;
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
2004-05-27 19:30:06 +02:00
|
|
|
else
|
|
|
|
debug_warn("aio_h_get: fd's aio handle not set");
|
2005-03-18 23:15:49 +01:00
|
|
|
// h is already INVALID_HANDLE_VALUE
|
2004-03-03 16:16:20 +01:00
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
unlock();
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// associate h (an async-capable file handle) with fd;
|
|
|
|
// returned by subsequent aio_h_get(fd) calls.
|
|
|
|
// setting h = INVALID_HANDLE_VALUE removes the association.
|
2005-12-11 23:23:55 +01:00
|
|
|
static LibError aio_h_set(const int fd, const HANDLE h)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2006-04-03 03:00:45 +02:00
|
|
|
if(fd < 0)
|
|
|
|
WARN_RETURN(ERR_INVALID_PARAM);
|
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
lock();
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-04-03 03:00:45 +02:00
|
|
|
LibError err;
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
// grow hs array to at least fd+1 entries
|
2004-05-06 19:14:30 +02:00
|
|
|
if(fd >= aio_hs_size)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-05-27 19:30:06 +02:00
|
|
|
const uint size2 = (uint)round_up(fd+8, 8);
|
2004-12-10 00:12:02 +01:00
|
|
|
HANDLE* hs2 = (HANDLE*)realloc(aio_hs, size2*sizeof(HANDLE));
|
2004-03-03 00:56:51 +01:00
|
|
|
if(!hs2)
|
2006-04-03 03:00:45 +02:00
|
|
|
{
|
|
|
|
err = ERR_NO_MEM;
|
2004-03-03 00:56:51 +01:00
|
|
|
goto fail;
|
2006-04-03 03:00:45 +02:00
|
|
|
}
|
2004-05-27 19:30:06 +02:00
|
|
|
// don't assign directly from realloc -
|
|
|
|
// we'd leak the previous array if realloc fails.
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
for(uint i = aio_hs_size; i < size2; i++)
|
2004-03-03 00:56:51 +01:00
|
|
|
hs2[i] = INVALID_HANDLE_VALUE;
|
2004-05-06 19:14:30 +02:00
|
|
|
aio_hs = hs2;
|
|
|
|
aio_hs_size = size2;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2006-04-03 03:00:45 +02:00
|
|
|
|
2004-03-03 16:16:20 +01:00
|
|
|
if(h == INVALID_HANDLE_VALUE)
|
2006-04-03 03:00:45 +02:00
|
|
|
{
|
|
|
|
// nothing to do; will set aio_hs[fd] to INVALID_HANDLE_VALUE below.
|
|
|
|
}
|
2004-03-03 16:16:20 +01:00
|
|
|
else
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-09-21 15:40:14 +02:00
|
|
|
// already set
|
2004-05-06 19:14:30 +02:00
|
|
|
if(aio_hs[fd] != INVALID_HANDLE_VALUE)
|
2006-04-03 03:00:45 +02:00
|
|
|
{
|
|
|
|
err = ERR_LOGIC;
|
2004-03-03 16:16:20 +01:00
|
|
|
goto fail;
|
2006-04-03 03:00:45 +02:00
|
|
|
}
|
2004-09-21 15:40:14 +02:00
|
|
|
// setting invalid handle
|
2004-03-03 16:16:20 +01:00
|
|
|
if(!is_valid_file_handle(h))
|
2006-04-03 03:00:45 +02:00
|
|
|
{
|
2006-05-17 16:48:18 +02:00
|
|
|
err = ERR_TNODE_WRONG_TYPE;
|
2004-03-03 16:16:20 +01:00
|
|
|
goto fail;
|
2006-04-03 03:00:45 +02:00
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
aio_hs[fd] = h;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
unlock();
|
2006-06-05 00:27:40 +02:00
|
|
|
return INFO_OK;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
fail:
|
2004-05-27 19:30:06 +02:00
|
|
|
unlock();
|
2006-04-03 03:00:45 +02:00
|
|
|
WARN_RETURN(err);
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
// open fn in async mode; associate with fd (retrieve via aio_h(fd))
|
|
|
|
int aio_reopen(int fd, const char* fn, int oflag, ...)
|
|
|
|
{
|
|
|
|
// interpret oflag
|
|
|
|
DWORD access = GENERIC_READ; // assume O_RDONLY
|
|
|
|
DWORD share = FILE_SHARE_READ;
|
|
|
|
DWORD create = OPEN_EXISTING;
|
|
|
|
if(oflag & O_WRONLY)
|
|
|
|
{
|
|
|
|
access = GENERIC_WRITE;
|
|
|
|
share = FILE_SHARE_WRITE;
|
|
|
|
}
|
|
|
|
else if(oflag & O_RDWR)
|
|
|
|
{
|
|
|
|
access |= GENERIC_WRITE;
|
|
|
|
share |= FILE_SHARE_WRITE;
|
|
|
|
}
|
|
|
|
if(oflag & O_CREAT)
|
|
|
|
create = (oflag & O_EXCL)? CREATE_NEW : CREATE_ALWAYS;
|
|
|
|
|
|
|
|
// open file
|
|
|
|
DWORD flags = FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN;
|
|
|
|
WIN_SAVE_LAST_ERROR; // CreateFile
|
|
|
|
HANDLE h = CreateFile(fn, access, share, 0, create, flags, 0);
|
|
|
|
WIN_RESTORE_LAST_ERROR;
|
|
|
|
if(h == INVALID_HANDLE_VALUE)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
if(aio_h_set(fd, h) < 0)
|
|
|
|
{
|
|
|
|
CloseHandle(h);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("failed");
|
2004-09-21 15:40:14 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int aio_close(int fd)
|
|
|
|
{
|
2005-03-18 23:15:49 +01:00
|
|
|
// early out for files that were never re-opened for AIO.
|
2005-08-13 19:09:57 +02:00
|
|
|
// since there is no way for wposix close to know this, we mustn't
|
|
|
|
// return an error (which would cause it to WARN_ERR).
|
2005-03-18 23:15:49 +01:00
|
|
|
if(!aio_h_is_set(fd))
|
2005-08-13 19:09:57 +02:00
|
|
|
return 0;
|
2005-03-18 23:15:49 +01:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
HANDLE h = aio_h_get(fd);
|
2005-03-18 23:15:49 +01:00
|
|
|
// out of bounds or already closed
|
|
|
|
if(h == INVALID_HANDLE_VALUE)
|
2005-01-07 02:13:48 +01:00
|
|
|
goto fail;
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
if(!CloseHandle(h))
|
2005-01-07 02:13:48 +01:00
|
|
|
goto fail;
|
2006-04-22 23:21:42 +02:00
|
|
|
RETURN_ERR(aio_h_set(fd, INVALID_HANDLE_VALUE));
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
return 0;
|
2005-01-07 02:13:48 +01:00
|
|
|
|
|
|
|
fail:
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("failed");
|
2005-01-07 02:13:48 +01:00
|
|
|
return -1;
|
2004-09-21 15:40:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2004-03-03 16:16:20 +01:00
|
|
|
// Req
|
2004-03-03 00:56:51 +01:00
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
// information about active transfers (reused)
|
|
|
|
struct Req
|
|
|
|
{
|
2004-09-21 15:40:14 +02:00
|
|
|
// used to identify this request; != 0 <==> request valid.
|
|
|
|
// set by req_alloc.
|
2004-03-03 00:56:51 +01:00
|
|
|
aiocb* cb;
|
|
|
|
|
|
|
|
OVERLAPPED ovl;
|
2004-09-21 15:40:14 +02:00
|
|
|
// hEvent signals when transfer complete
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// align buffer - unaligned reads are padded to sector boundaries and
|
|
|
|
// go here; the desired data is then copied into the user's buffer.
|
|
|
|
// reused, since the Req has global lifetime; resized if too small.
|
|
|
|
void* buf;
|
2004-03-03 00:56:51 +01:00
|
|
|
size_t buf_size;
|
2004-09-21 13:58:22 +02:00
|
|
|
|
|
|
|
HANDLE hFile;
|
2004-09-21 15:40:14 +02:00
|
|
|
// needed to GetOverlappedResult in aio_return
|
2004-09-21 13:58:22 +02:00
|
|
|
|
|
|
|
size_t pad; // offset from starting sector
|
2004-09-21 15:40:14 +02:00
|
|
|
bool read_into_align_buffer;
|
2004-03-03 00:56:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2004-08-17 15:40:55 +02:00
|
|
|
// an aiocb is used to pass the request from caller to aio,
|
|
|
|
// and serves as a "token" identifying the IO - its address is unique.
|
|
|
|
// Req holds some state needed for the Windows AIO calls (OVERLAPPED).
|
|
|
|
//
|
|
|
|
// cb -> req (e.g. in aio_return) is accomplished by searching reqs
|
|
|
|
// for the given cb (no problem since MAX_REQS is small).
|
2004-09-21 15:40:14 +02:00
|
|
|
// req stores a pointer to its associated cb.
|
2004-03-03 00:56:51 +01:00
|
|
|
|
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
const int MAX_REQS = 8;
|
2004-05-06 19:14:30 +02:00
|
|
|
static Req reqs[MAX_REQS];
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
static void req_cleanup(void)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
Req* r = reqs;
|
2004-05-06 19:14:30 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
for(int i = 0; i < MAX_REQS; i++, r++)
|
|
|
|
{
|
|
|
|
HANDLE& h = r->ovl.hEvent;
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert(h != INVALID_HANDLE_VALUE);
|
2004-09-21 15:40:14 +02:00
|
|
|
CloseHandle(h);
|
|
|
|
h = INVALID_HANDLE_VALUE;
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
_aligned_free(r->buf);
|
2004-03-03 00:56:51 +01:00
|
|
|
r->buf = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-27 19:30:06 +02:00
|
|
|
static void req_init()
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-05-06 19:14:30 +02:00
|
|
|
for(int i = 0; i < MAX_REQS; i++)
|
|
|
|
reqs[i].ovl.hEvent = CreateEvent(0,1,0,0); // manual reset
|
2004-05-27 19:30:06 +02:00
|
|
|
|
|
|
|
// buffers are allocated on-demand.
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-08-17 15:40:55 +02:00
|
|
|
// return first Req with given cb field
|
2004-09-21 15:40:14 +02:00
|
|
|
// (0 if searching for a free Req)
|
2004-08-17 15:40:55 +02:00
|
|
|
static Req* req_find(const aiocb* cb)
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
|
|
|
Req* r = reqs;
|
2004-03-03 00:56:51 +01:00
|
|
|
for(int i = 0; i < MAX_REQS; i++, r++)
|
2004-08-17 15:40:55 +02:00
|
|
|
if(r->cb == cb)
|
2004-09-21 15:40:14 +02:00
|
|
|
return r;
|
2004-05-14 23:20:23 +02:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// not found
|
|
|
|
return 0;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-17 15:40:55 +02:00
|
|
|
static Req* req_alloc(aiocb* cb)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert(cb);
|
2004-08-17 15:40:55 +02:00
|
|
|
|
|
|
|
// first free Req, or 0
|
|
|
|
Req* r = req_find(0);
|
2004-09-21 15:40:14 +02:00
|
|
|
// .. found one: mark it in-use
|
2004-08-17 15:40:55 +02:00
|
|
|
if(r)
|
|
|
|
r->cb = cb;
|
|
|
|
|
|
|
|
return r;
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
static LibError req_free(Req* r)
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert(r->cb != 0 && "req_free: not currently in use");
|
2004-05-06 19:14:30 +02:00
|
|
|
r->cb = 0;
|
2006-06-05 00:27:40 +02:00
|
|
|
return INFO_OK;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-03 16:16:20 +01:00
|
|
|
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
// called by aio_read, aio_write, and lio_listio
|
|
|
|
// cb->aio_lio_opcode specifies desired operation
|
|
|
|
//
|
|
|
|
// if cb->aio_fildes doesn't support seeking (e.g. a socket),
|
|
|
|
// cb->aio_offset must be 0.
|
|
|
|
static int aio_rw(struct aiocb* cb)
|
|
|
|
{
|
2004-09-21 13:58:22 +02:00
|
|
|
int ret = -1;
|
|
|
|
Req* r = 0;
|
|
|
|
|
2004-07-12 16:25:39 +02:00
|
|
|
WIN_SAVE_LAST_ERROR;
|
|
|
|
|
2004-08-17 15:40:55 +02:00
|
|
|
// no-op from lio_listio
|
|
|
|
if(!cb || cb->aio_lio_opcode == LIO_NOP)
|
2004-03-03 00:56:51 +01:00
|
|
|
return 0;
|
2004-08-17 15:40:55 +02:00
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
// fail if aiocb is already in use (forbidden by SUSv3)
|
2004-08-17 15:40:55 +02:00
|
|
|
if(req_find(cb))
|
2004-05-06 19:14:30 +02:00
|
|
|
{
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("aiocb is already in use");
|
2004-09-21 13:58:22 +02:00
|
|
|
goto fail;
|
2004-05-06 19:14:30 +02:00
|
|
|
}
|
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
// extract aiocb fields for convenience
|
|
|
|
const bool is_write = (cb->aio_lio_opcode == LIO_WRITE);
|
|
|
|
const int fd = cb->aio_fildes;
|
|
|
|
const size_t size = cb->aio_nbytes;
|
|
|
|
const off_t ofs = cb->aio_offset;
|
2004-12-10 00:12:02 +01:00
|
|
|
void* buf = (void*)cb->aio_buf; // from volatile void*
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert(buf);
|
2004-09-21 13:58:22 +02:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// allocate IO request
|
|
|
|
r = req_alloc(cb);
|
|
|
|
if(!r)
|
|
|
|
{
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("cannot allocate a Req (too many concurrent IOs)");
|
2004-09-21 15:40:14 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
HANDLE h = aio_h_get(fd);
|
|
|
|
if(h == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("associated handle is invalid");
|
2004-09-21 13:58:22 +02:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto fail;
|
2004-03-03 16:16:20 +01:00
|
|
|
}
|
2004-09-21 15:40:14 +02:00
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
|
|
|
|
r->hFile = h;
|
|
|
|
r->pad = 0;
|
2004-09-21 15:40:14 +02:00
|
|
|
r->read_into_align_buffer = false;
|
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
//
|
|
|
|
// align
|
|
|
|
//
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-04-03 03:00:45 +02:00
|
|
|
// Win32 requires transfers to be sector aligned.
|
|
|
|
// we check if the transfer is aligned to sector size (the max of
|
|
|
|
// all drives in the system) and copy to/from align buffer if not.
|
|
|
|
|
|
|
|
// actual transfer parameters (possibly rounded up/down)
|
2004-09-21 13:58:22 +02:00
|
|
|
size_t actual_ofs = 0;
|
|
|
|
// assume socket; if file, set below
|
|
|
|
size_t actual_size = size;
|
|
|
|
void* actual_buf = buf;
|
2004-07-08 16:21:14 +02:00
|
|
|
|
2006-04-03 03:00:45 +02:00
|
|
|
const size_t sector_size = sys_max_sector_size();
|
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
// leave offset 0 if h is a socket (don't support seeking);
|
2004-09-21 15:40:14 +02:00
|
|
|
// otherwise, calculate aligned offset/size
|
|
|
|
const bool is_file = (GetFileType(h) == FILE_TYPE_DISK);
|
2004-09-21 13:58:22 +02:00
|
|
|
if(is_file)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-09-21 18:59:50 +02:00
|
|
|
// round offset down to start of previous sector, and total
|
|
|
|
// transfer size up to an integral multiple of sector_size.
|
|
|
|
r->pad = ofs % sector_size;
|
|
|
|
actual_ofs = ofs - r->pad;
|
|
|
|
actual_size = round_up(size + r->pad, sector_size);
|
|
|
|
|
|
|
|
// and whether it was ofs or buf in particular
|
|
|
|
// (needed for unaligned write handling below).
|
|
|
|
const bool ofs_misaligned = r->pad != 0;
|
2004-09-21 15:40:14 +02:00
|
|
|
const bool buf_misaligned = (uintptr_t)buf % sector_size != 0;
|
2004-09-21 18:59:50 +02:00
|
|
|
const bool misaligned = ofs_misaligned || buf_misaligned || actual_size != size;
|
|
|
|
// note: actual_size != size if ofs OR size is unaligned
|
2004-09-21 13:58:22 +02:00
|
|
|
|
2004-09-21 18:59:50 +02:00
|
|
|
// misaligned => will need to go through align buffer
|
|
|
|
// (we fail some types of misalignment for convenience; see below).
|
2004-09-21 15:40:14 +02:00
|
|
|
if(misaligned)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-07-08 16:21:14 +02:00
|
|
|
// expand current align buffer if too small
|
2004-09-21 13:58:22 +02:00
|
|
|
if(r->buf_size < actual_size)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-09-21 15:40:14 +02:00
|
|
|
void* buf2 = _aligned_realloc(r->buf, actual_size, sector_size);
|
2004-03-03 00:56:51 +01:00
|
|
|
if(!buf2)
|
2004-09-21 13:58:22 +02:00
|
|
|
{
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
r->buf = buf2;
|
2004-09-21 13:58:22 +02:00
|
|
|
r->buf_size = actual_size;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
if(!is_write)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
2004-09-21 15:40:14 +02:00
|
|
|
actual_buf = r->buf;
|
|
|
|
r->read_into_align_buffer = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-03 03:00:45 +02:00
|
|
|
// unaligned write offset: not supported.
|
2004-09-21 15:40:14 +02:00
|
|
|
// (we'd have to read padding, then write our data. ugh.)
|
2004-09-21 18:59:50 +02:00
|
|
|
if(ofs_misaligned)
|
2004-09-21 13:58:22 +02:00
|
|
|
{
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
// unaligned buffer: copy to align buffer and write from there.
|
|
|
|
if(buf_misaligned)
|
|
|
|
{
|
2005-10-30 17:19:20 +01:00
|
|
|
memcpy2(r->buf, buf, size);
|
2004-09-21 15:40:14 +02:00
|
|
|
actual_buf = r->buf;
|
2006-04-03 03:00:45 +02:00
|
|
|
// clear previous contents at end of align buf
|
|
|
|
memset((char*)r->buf + size, 0, actual_size - size);
|
2004-09-21 15:40:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// unaligned size: already taken care of (we round up)
|
|
|
|
}
|
|
|
|
} // misaligned
|
|
|
|
} // is_file
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
// set OVERLAPPED fields
|
2006-01-24 09:16:29 +01:00
|
|
|
// note: Read-/WriteFile reset ovl.hEvent - no need to do that.
|
2004-09-21 13:58:22 +02:00
|
|
|
r->ovl.Internal = r->ovl.InternalHigh = 0;
|
2006-04-03 03:00:45 +02:00
|
|
|
// note: OVERLAPPED.Pointer is more convenient but not defined on VC6.
|
|
|
|
r->ovl.Offset = u64_lo(actual_ofs);
|
|
|
|
r->ovl.OffsetHigh = u64_hi(actual_ofs);
|
2004-03-03 16:16:20 +01:00
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
DWORD size32 = (DWORD)(actual_size & 0xffffffff);
|
2004-07-12 16:25:39 +02:00
|
|
|
BOOL ok;
|
2006-01-24 09:16:29 +01:00
|
|
|
|
|
|
|
DWORD bytes_transferred;
|
2004-09-21 13:58:22 +02:00
|
|
|
if(is_write)
|
2006-01-24 09:16:29 +01:00
|
|
|
ok = WriteFile(h, actual_buf, size32, &bytes_transferred, &r->ovl);
|
2004-07-12 16:25:39 +02:00
|
|
|
else
|
2006-01-24 09:16:29 +01:00
|
|
|
ok = ReadFile(h, actual_buf, size32, &bytes_transferred, &r->ovl);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2006-04-10 22:12:25 +02:00
|
|
|
// check result.
|
|
|
|
// .. "pending" isn't an error
|
|
|
|
if(!ok && GetLastError() == ERROR_IO_PENDING)
|
|
|
|
ok = TRUE;
|
|
|
|
// .. translate from Win32 result code to POSIX
|
|
|
|
LibError err = LibError_from_win32(ok);
|
2006-06-05 00:27:40 +02:00
|
|
|
if(err == INFO_OK)
|
2004-09-21 13:58:22 +02:00
|
|
|
ret = 0;
|
2006-04-10 22:12:25 +02:00
|
|
|
LibError_set_errno(err);
|
2004-09-21 13:58:22 +02:00
|
|
|
|
|
|
|
done:
|
2004-07-12 16:25:39 +02:00
|
|
|
WIN_RESTORE_LAST_ERROR;
|
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
fail:
|
2006-01-24 09:16:29 +01:00
|
|
|
debug_warn("waio failure");
|
2004-09-21 13:58:22 +02:00
|
|
|
req_free(r);
|
|
|
|
goto done;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// return status of transfer
|
|
|
|
int aio_error(const struct aiocb* cb)
|
|
|
|
{
|
2005-01-07 02:13:48 +01:00
|
|
|
// must not pass 0 to req_find - we'd look for a free cb!
|
|
|
|
if(!cb)
|
|
|
|
{
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("invalid cb");
|
2005-01-07 02:13:48 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-12-10 00:12:02 +01:00
|
|
|
Req* r = req_find(cb);
|
2004-03-03 00:56:51 +01:00
|
|
|
if(!r)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
switch(r->ovl.Internal) // I/O status
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 0;
|
|
|
|
case STATUS_PENDING:
|
2004-04-09 14:38:47 +02:00
|
|
|
return EINPROGRESS;
|
2004-03-03 00:56:51 +01:00
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// get bytes transferred. call exactly once for each op.
|
|
|
|
ssize_t aio_return(struct aiocb* cb)
|
|
|
|
{
|
2005-01-07 02:13:48 +01:00
|
|
|
// must not pass 0 to req_find - we'd look for a free cb!
|
|
|
|
if(!cb)
|
|
|
|
{
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("invalid cb");
|
2005-01-07 02:13:48 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-12-10 00:12:02 +01:00
|
|
|
Req* r = req_find(cb);
|
2004-03-03 00:56:51 +01:00
|
|
|
if(!r)
|
2004-08-17 15:40:55 +02:00
|
|
|
{
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("cb not found (already called aio_return?)");
|
2004-03-03 00:56:51 +01:00
|
|
|
return -1;
|
2004-08-17 15:40:55 +02:00
|
|
|
}
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert(r->ovl.Internal == 0 && "aio_return with transfer in progress");
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
const BOOL wait = FALSE; // should already be done!
|
2004-09-21 13:58:22 +02:00
|
|
|
DWORD bytes_transferred;
|
2004-09-21 15:40:14 +02:00
|
|
|
if(!GetOverlappedResult(r->hFile, &r->ovl, &bytes_transferred, wait))
|
|
|
|
{
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("GetOverlappedResult failed");
|
2004-09-21 15:40:14 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-09-21 13:58:22 +02:00
|
|
|
|
|
|
|
// we read into align buffer - copy to user's buffer
|
2004-09-21 15:40:14 +02:00
|
|
|
if(r->read_into_align_buffer)
|
2005-10-30 17:19:20 +01:00
|
|
|
memcpy2((void*)cb->aio_buf, (u8*)r->buf + r->pad, cb->aio_nbytes);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-07-08 16:21:14 +02:00
|
|
|
// TODO: this copies data back into original buffer from align buffer
|
|
|
|
// when writing from unaligned buffer. unnecessarily slow.
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
req_free(r);
|
2004-03-03 00:56:51 +01:00
|
|
|
|
2004-09-21 13:58:22 +02:00
|
|
|
return (ssize_t)bytes_transferred;
|
2004-03-03 00:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-10 01:14:10 +01:00
|
|
|
int aio_suspend(const struct aiocb* const cbs[], int n, const struct timespec* ts)
|
2004-03-03 00:56:51 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(n <= 0 || n > MAXIMUM_WAIT_OBJECTS)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
int cnt = 0; // actual number of valid cbs
|
|
|
|
HANDLE hs[MAXIMUM_WAIT_OBJECTS];
|
|
|
|
|
|
|
|
for(i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
// ignore NULL list entries
|
|
|
|
if(!cbs[i])
|
|
|
|
continue;
|
|
|
|
|
2004-05-06 19:14:30 +02:00
|
|
|
Req* r = req_find(cbs[i]);
|
2004-03-03 00:56:51 +01:00
|
|
|
if(r)
|
|
|
|
{
|
|
|
|
if(r->ovl.Internal == STATUS_PENDING)
|
|
|
|
hs[cnt++] = r->ovl.hEvent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// no valid, pending transfers - done
|
|
|
|
if(!cnt)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// timeout: convert timespec to ms (NULL ptr -> no timeout)
|
|
|
|
DWORD timeout = INFINITE;
|
|
|
|
if(ts)
|
|
|
|
timeout = (DWORD)(ts->tv_sec*1000 + ts->tv_nsec/1000000);
|
|
|
|
|
2004-09-21 15:40:14 +02:00
|
|
|
const BOOL wait_all = FALSE;
|
|
|
|
DWORD result = WaitForMultipleObjects(cnt, hs, wait_all, timeout);
|
2004-09-19 20:44:21 +02:00
|
|
|
|
2004-03-03 00:56:51 +01:00
|
|
|
for(i = 0; i < cnt; i++)
|
|
|
|
ResetEvent(hs[i]);
|
|
|
|
|
|
|
|
if(result == WAIT_TIMEOUT)
|
|
|
|
{
|
|
|
|
//errno = -EAGAIN;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return (result == WAIT_FAILED)? -1 : 0;
|
|
|
|
}
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
int aio_cancel(int fd, struct aiocb* cb)
|
|
|
|
{
|
2005-01-07 02:13:48 +01:00
|
|
|
// Win32 limitation: can't cancel single transfers -
|
|
|
|
// all pending reads on this file are cancelled.
|
2005-08-09 18:23:19 +02:00
|
|
|
UNUSED2(cb);
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
const HANDLE h = aio_h_get(fd);
|
|
|
|
if(h == INVALID_HANDLE_VALUE)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
CancelIo(h);
|
|
|
|
return AIO_CANCELED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int aio_read(struct aiocb* cb)
|
|
|
|
{
|
|
|
|
cb->aio_lio_opcode = LIO_READ;
|
2005-01-07 02:13:48 +01:00
|
|
|
return aio_rw(cb); // checks for cb == 0
|
2004-09-21 15:40:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int aio_write(struct aiocb* cb)
|
|
|
|
{
|
|
|
|
cb->aio_lio_opcode = LIO_WRITE;
|
2005-01-07 02:13:48 +01:00
|
|
|
return aio_rw(cb); // checks for cb == 0
|
2004-09-21 15:40:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int lio_listio(int mode, struct aiocb* const cbs[], int n, struct sigevent* se)
|
|
|
|
{
|
2005-08-09 18:23:19 +02:00
|
|
|
UNUSED2(se);
|
2004-09-21 15:40:14 +02:00
|
|
|
|
2005-01-07 02:13:48 +01:00
|
|
|
int err = 0;
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
for(int i = 0; i < n; i++)
|
|
|
|
{
|
2005-01-07 02:13:48 +01:00
|
|
|
int ret = aio_rw(cbs[i]); // checks for cbs[i] == 0
|
2006-04-22 23:21:42 +02:00
|
|
|
// don't RETURN_ERR yet - we want to try to issue each one
|
2005-01-07 02:13:48 +01:00
|
|
|
if(ret < 0 && !err)
|
2004-09-21 15:40:14 +02:00
|
|
|
err = ret;
|
|
|
|
}
|
|
|
|
|
2006-04-22 23:21:42 +02:00
|
|
|
RETURN_ERR(err);
|
2004-09-21 15:40:14 +02:00
|
|
|
|
|
|
|
if(mode == LIO_WAIT)
|
|
|
|
return aio_suspend(cbs, n, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int aio_fsync(int, struct aiocb*)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// init / cleanup
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
static LibError waio_init()
|
2004-09-21 15:40:14 +02:00
|
|
|
{
|
|
|
|
req_init();
|
2006-06-05 00:27:40 +02:00
|
|
|
return INFO_OK;
|
2004-09-21 15:40:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
static LibError waio_shutdown()
|
2004-09-21 15:40:14 +02:00
|
|
|
{
|
|
|
|
req_cleanup();
|
|
|
|
aio_h_cleanup();
|
2006-06-05 00:27:40 +02:00
|
|
|
return INFO_OK;
|
2004-09-21 15:40:14 +02:00
|
|
|
}
|