libevent/buffer_iocp.c
2009-04-14 20:11:10 +00:00

349 lines
8.4 KiB
C

#include <windows.h>
#include <assert.h>
#include "event2/buffer.h"
#include "event2/buffer_compat.h"
#include "event2/util.h"
#include "event2/thread.h"
#include "event-config.h"
#include "util-internal.h"
#include "evthread-internal.h"
#include "evbuffer-internal.h"
#include "iocp-internal.h"
#include "mm-internal.h"
#define MAX_WSABUFS 16
struct buffer_overlapped {
struct event_overlapped event_overlapped;
struct evbuffer_chain *first_pinned;
struct evbuffer_overlapped *buf;
int n_buffers;
WSABUF buffers[MAX_WSABUFS];
};
struct evbuffer_overlapped {
struct evbuffer buffer;
evutil_socket_t fd;
unsigned write_in_progress : 1;
unsigned read_in_progress : 1;
struct buffer_overlapped read_info;
struct buffer_overlapped write_info;
};
static inline struct evbuffer_overlapped *
upcast_evbuffer(struct evbuffer *buf)
{
if (!buf || buf->is_overlapped)
return NULL;
return EVUTIL_UPCAST(buf, struct evbuffer_overlapped, buffer);
}
static inline struct buffer_overlapped *
upcast_overlapped(struct event_overlapped *o)
{
return EVUTIL_UPCAST(o, struct buffer_overlapped, event_overlapped);
}
static void
pin_release(struct event_overlapped *eo, unsigned flag)
{
int i;
struct buffer_overlapped *bo = upcast_overlapped(eo);
struct evbuffer_chain *chain = bo->first_pinned;
for (i = 0; i < bo->n_buffers; ++i) {
_evbuffer_chain_unpin(chain, flag);
chain = chain->next;
assert(chain);
}
}
static void
read_completed(struct event_overlapped *eo, uintptr_t _, ssize_t nBytes)
{
struct buffer_overlapped *buf_o = upcast_overlapped(eo);
struct evbuffer_overlapped *buf = buf_o->buf;
struct evbuffer *evbuf = &buf->buffer;
struct evbuffer_chain *chain = buf_o->first_pinned;
EVBUFFER_LOCK(evbuf, EVTHREAD_WRITE);
evbuffer_unfreeze(evbuf, 0);
if (chain == evbuf->previous_to_last) {
size_t n = chain->buffer_len - (chain->misalign + chain->off);
if (n>nBytes)
n=nBytes;
chain->off += n;
nBytes -= n;
evbuf->n_add_for_cb += n;
evbuffer_commit_space(evbuf, nBytes);
} else if (chain == evbuf->last) {
evbuffer_commit_space(evbuf, nBytes);
} else {
assert(0);
}
pin_release(eo, EVBUFFER_MEM_PINNED_R);
buf->read_in_progress = 0;
_evbuffer_decref_and_unlock(evbuf);
}
static void
write_completed(struct event_overlapped *eo, uintptr_t _, ssize_t nBytes)
{
struct buffer_overlapped *buf_o = upcast_overlapped(eo);
struct evbuffer_overlapped *buf = buf_o->buf;
struct evbuffer *evbuf = &buf->buffer;
EVBUFFER_LOCK(evbuf, EVTHREAD_WRITE);
evbuffer_unfreeze(evbuf, 1);
evbuffer_drain(evbuf, nBytes);
pin_release(eo,EVBUFFER_MEM_PINNED_W);
buf->write_in_progress = 0;
_evbuffer_decref_and_unlock(evbuf);
}
struct evbuffer *
evbuffer_overlapped_new(evutil_socket_t fd)
{
struct evbuffer_overlapped *evo;
evo = mm_calloc(1, sizeof(struct evbuffer_overlapped));
TAILQ_INIT(&evo->buffer.callbacks);
evo->buffer.is_overlapped = 1;
return &evo->buffer;
}
int
evbuffer_launch_write(struct evbuffer *buf, ssize_t at_most)
{
struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
int r = -1;
int i;
struct evbuffer_chain *chain;
DWORD bytesSent;
if (!buf) {
/* No buffer, or it isn't overlapped */
return -1;
}
EVBUFFER_LOCK(buf, EVTHREAD_WRITE);
if (buf->freeze_start || buf_o->write_in_progress)
goto done;
if (!buf->total_len) {
/* Nothing to write */
r = 0;
goto done;
} else if (at_most > buf->total_len || at_most < 0) {
at_most = buf->total_len;
}
evbuffer_freeze(buf, 1);
/* XXX we could move much of this into the constructor. */
memset(&buf_o->write_info, 0, sizeof(buf_o->write_info));
buf_o->write_info.buf = buf_o;
buf_o->write_info.event_overlapped.cb = write_completed;
chain = buf_o->write_info.first_pinned = buf->first;
for (i=0; i < MAX_WSABUFS && chain; ++i, chain=chain->next) {
WSABUF *b = &buf_o->write_info.buffers[i];
b->buf = chain->buffer + chain->misalign;
_evbuffer_chain_pin(chain, EVBUFFER_MEM_PINNED_W);
if (at_most > chain->off) {
b->len = chain->off;
at_most -= chain->off;
} else {
b->len = at_most;
++i;
break;
}
}
buf_o->write_info.n_buffers = i;
_evbuffer_incref(buf);
if (WSASend(buf_o->fd, buf_o->write_info.buffers, i, &bytesSent, 0,
&buf_o->write_info.event_overlapped.overlapped, NULL)) {
int error = WSAGetLastError();
if (error != WSA_IO_PENDING) {
/* An actual error. */
pin_release(&buf_o->write_info.event_overlapped, EVBUFFER_MEM_PINNED_W);
evbuffer_unfreeze(buf, 1);
evbuffer_free(buf); /* decref */
goto done;
}
}
buf_o->write_in_progress = 1;
r = 0;
done:
EVBUFFER_UNLOCK(buf, EVTHREAD_WRITE);
return r;
}
int
evbuffer_launch_read(struct evbuffer *buf, size_t at_most)
{
struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
int r = -1;
int nvecs;
struct evbuffer_chain *chain=NULL;
DWORD bytesRead;
DWORD flags = 0;
if (!buf)
return -1;
EVBUFFER_LOCK(buf, EVTHREAD_WRITE);
if (buf->freeze_end || buf_o->read_in_progress)
goto done;
if (_evbuffer_expand_fast(buf, at_most) == -1)
goto done;
evbuffer_freeze(buf, 0);
/* XXX we could move much of this into the constructor. */
memset(&buf_o->read_info, 0, sizeof(buf_o->read_info));
buf_o->read_info.buf = buf_o;
buf_o->read_info.event_overlapped.cb = read_completed;
nvecs = _evbuffer_read_setup_vecs(buf, at_most,
buf_o->read_info.buffers, &chain);
buf_o->read_info.n_buffers = nvecs;
buf_o->read_info.first_pinned = chain;
for ( ; chain; chain = chain->next)
_evbuffer_chain_pin(chain, EVBUFFER_MEM_PINNED_R);
_evbuffer_incref(buf);
if (WSARecv(buf_o->fd, buf_o->read_info.buffers, nvecs, &bytesRead, &flags, &buf_o->read_info.event_overlapped.overlapped, NULL)) {
int error = WSAGetLastError();
if (error != WSA_IO_PENDING) {
/* An actual error. */
pin_release(&buf_o->read_info.event_overlapped, EVBUFFER_MEM_PINNED_R);
evbuffer_unfreeze(buf, 0);
evbuffer_free(buf); /* decref */
goto done;
}
}
buf_o->read_in_progress = 1;
r = 0;
done:
EVBUFFER_UNLOCK(buf, EVTHREAD_WRITE);
return r;
}
/*
C++
typedef struct _OVERLAPPED_ENTRY {
ULONG_PTR lpCompletionKey;
LPOVERLAPPED lpOverlapped;
ULONG_PTR Internal;
DWORD dwNumberOfBytesTransferred;
} OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;
C++
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
} ;
PVOID Pointer;
} ;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
Any unused members of this structure should always be initialized to zero
before the structure is used in a function call. Otherwise, the function may
fail and return ERROR_INVALID_PARAMETER.
You can use the HasOverlappedIoCompleted macro to check whether an
asynchronous I/O operation has completed if GetOverlappedResult is too
cumbersome for your application.
You can use the CancelIo function to cancel an asynchronous I/O operation.
HANDLE WINAPI CreateIoCompletionPort(
__in HANDLE FileHandle,
__in_opt HANDLE ExistingCompletionPort,
__in ULONG_PTR CompletionKey,
__in DWORD NumberOfConcurrentThreads
);
BOOL WINAPI GetQueuedCompletionStatus(
__in HANDLE CompletionPort,
__out LPDWORD lpNumberOfBytes,
__out PULONG_PTR lpCompletionKey,
__out LPOVERLAPPED *lpOverlapped,
__in DWORD dwMilliseconds
);
If the function dequeues a completion packet for a successful I/O operation from the completion port, the return value is nonzero. The function stores information in the variables pointed to by the lpNumberOfBytes, lpCompletionKey, and lpOverlapped parameters.
BOOL WINAPI GetQueuedCompletionStatusEx(
__in HANDLE CompletionPort,
__out LPOVERLAPPED_ENTRY lpCompletionPortEntries,
__in ULONG ulCount,
__out PULONG ulNumEntriesRemoved,
__in DWORD dwMilliseconds,
__in BOOL fAlertable
);
BOOL PASCAL ConnectEx(
__in SOCKET s,
__in const struct sockaddr *name,
__in int namelen,
__in_opt PVOID lpSendBuffer,
__in DWORD dwSendDataLength,
__out LPDWORD lpdwBytesSent,
__in LPOVERLAPPED lpOverlapped
);
typedef void (*LPFN_CONNECTEX)( );
C++
BOOL AcceptEx(
__in SOCKET sListenSocket,
__in SOCKET sAcceptSocket,
__in PVOID lpOutputBuffer,
__in DWORD dwReceiveDataLength,
__in DWORD dwLocalAddressLength,
__in DWORD dwRemoteAddressLength,
__out LPDWORD lpdwBytesReceived,
__in LPOVERLAPPED lpOverlapped
);
C++
BOOL DisconnectEx(
__in SOCKET hSocket,
__in LPOVERLAPPED lpOverlapped,
__in DWORD dwFlags,
__in DWORD reserved
);
*/