/* * Copyright (C) 1996-2018 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ /* DEBUG: section 43 Windows AIOPS */ #include "squid.h" #include "DiskIO/DiskThreads/CommIO.h" #include "DiskThreads.h" #include "fd.h" #include "SquidConfig.h" #include "SquidTime.h" #include "Store.h" #include #include #include #include #include #define RIDICULOUS_LENGTH 4096 enum _squidaio_thread_status { _THREAD_STARTING = 0, _THREAD_WAITING, _THREAD_BUSY, _THREAD_FAILED, _THREAD_DONE }; typedef enum _squidaio_thread_status squidaio_thread_status; typedef struct squidaio_request_t { struct squidaio_request_t *next; squidaio_request_type request_type; int cancelled; char *path; int oflag; mode_t mode; int fd; char *bufferp; char *tmpbufp; size_t buflen; off_t offset; int whence; int ret; int err; struct stat *tmpstatp; struct stat *statp; squidaio_result_t *resultp; } squidaio_request_t; typedef struct squidaio_request_queue_t { HANDLE mutex; HANDLE cond; /* See Event objects */ squidaio_request_t *volatile head; squidaio_request_t *volatile *volatile tailp; unsigned long requests; unsigned long blocked; /* main failed to lock the queue */ } squidaio_request_queue_t; typedef struct squidaio_thread_t squidaio_thread_t; struct squidaio_thread_t { squidaio_thread_t *next; HANDLE thread; DWORD dwThreadId; /* thread ID */ squidaio_thread_status status; struct squidaio_request_t *current_req; unsigned long requests; int volatile exit; }; static void squidaio_queue_request(squidaio_request_t *); static void squidaio_cleanup_request(squidaio_request_t *); static DWORD WINAPI squidaio_thread_loop( LPVOID lpParam ); static void squidaio_do_open(squidaio_request_t *); static void squidaio_do_read(squidaio_request_t *); static void squidaio_do_write(squidaio_request_t *); static void squidaio_do_close(squidaio_request_t *); static void squidaio_do_stat(squidaio_request_t *); static void squidaio_do_unlink(squidaio_request_t *); #if AIO_OPENDIR static void *squidaio_do_opendir(squidaio_request_t *); #endif static void squidaio_debug(squidaio_request_t *); static void squidaio_poll_queues(void); static squidaio_thread_t *threads = NULL; static int squidaio_initialised = 0; #define AIO_LARGE_BUFS 16384 #define AIO_MEDIUM_BUFS AIO_LARGE_BUFS >> 1 #define AIO_SMALL_BUFS AIO_LARGE_BUFS >> 2 #define AIO_TINY_BUFS AIO_LARGE_BUFS >> 3 #define AIO_MICRO_BUFS 128 static MemAllocator *squidaio_large_bufs = NULL; /* 16K */ static MemAllocator *squidaio_medium_bufs = NULL; /* 8K */ static MemAllocator *squidaio_small_bufs = NULL; /* 4K */ static MemAllocator *squidaio_tiny_bufs = NULL; /* 2K */ static MemAllocator *squidaio_micro_bufs = NULL; /* 128K */ static int request_queue_len = 0; static MemAllocator *squidaio_request_pool = NULL; static MemAllocator *squidaio_thread_pool = NULL; static squidaio_request_queue_t request_queue; static struct { squidaio_request_t *head, **tailp; } request_queue2 = { NULL, &request_queue2.head }; static squidaio_request_queue_t done_queue; static struct { squidaio_request_t *head, **tailp; } done_requests = { NULL, &done_requests.head }; static HANDLE main_thread; static MemAllocator * squidaio_get_pool(int size) { if (size <= AIO_LARGE_BUFS) { if (size <= AIO_MICRO_BUFS) return squidaio_micro_bufs; else if (size <= AIO_TINY_BUFS) return squidaio_tiny_bufs; else if (size <= AIO_SMALL_BUFS) return squidaio_small_bufs; else if (size <= AIO_MEDIUM_BUFS) return squidaio_medium_bufs; else return squidaio_large_bufs; } return NULL; } void * squidaio_xmalloc(int size) { void *p; MemAllocator *pool; if ((pool = squidaio_get_pool(size)) != NULL) { p = pool->alloc(); } else p = xmalloc(size); return p; } static char * squidaio_xstrdup(const char *str) { char *p; int len = strlen(str) + 1; p = (char *)squidaio_xmalloc(len); strncpy(p, str, len); return p; } void squidaio_xfree(void *p, int size) { MemAllocator *pool; if ((pool = squidaio_get_pool(size)) != NULL) { pool->freeOne(p); } else xfree(p); } static void squidaio_xstrfree(char *str) { MemAllocator *pool; int len = strlen(str) + 1; if ((pool = squidaio_get_pool(len)) != NULL) { pool->freeOne(str); } else xfree(str); } void squidaio_init(void) { int i; squidaio_thread_t *threadp; if (squidaio_initialised) return; if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */ GetCurrentThread(), /* pseudo handle to copy */ GetCurrentProcess(), /* pseudo handle, don't close */ &main_thread, 0, /* required access */ FALSE, /* child process's don't inherit the handle */ DUPLICATE_SAME_ACCESS)) { /* spit errors */ fatal("Couldn't get current thread handle"); } /* Initialize request queue */ if ((request_queue.mutex = CreateMutex(NULL, /* no inheritance */ FALSE, /* start unowned (as per mutex_init) */ NULL) /* no name */ ) == NULL) { fatal("Failed to create mutex"); } if ((request_queue.cond = CreateEvent(NULL, /* no inheritance */ FALSE, /* auto signal reset - which I think is pthreads like ? */ FALSE, /* start non signaled */ NULL) /* no name */ ) == NULL) { fatal("Failed to create condition variable"); } request_queue.head = NULL; request_queue.tailp = &request_queue.head; request_queue.requests = 0; request_queue.blocked = 0; /* Initialize done queue */ if ((done_queue.mutex = CreateMutex(NULL, /* no inheritance */ FALSE, /* start unowned (as per mutex_init) */ NULL) /* no name */ ) == NULL) { fatal("Failed to create mutex"); } if ((done_queue.cond = CreateEvent(NULL, /* no inheritance */ TRUE, /* manually signaled - which I think is pthreads like ? */ FALSE, /* start non signaled */ NULL) /* no name */ ) == NULL) { fatal("Failed to create condition variable"); } done_queue.head = NULL; done_queue.tailp = &done_queue.head; done_queue.requests = 0; done_queue.blocked = 0; // Initialize the thread I/O pipes before creating any threads // see bug 3189 comment 5 about race conditions. CommIO::Initialize(); /* Create threads and get them to sit in their wait loop */ squidaio_thread_pool = memPoolCreate("aio_thread", sizeof(squidaio_thread_t)); assert(NUMTHREADS); for (i = 0; i < NUMTHREADS; ++i) { threadp = (squidaio_thread_t *)squidaio_thread_pool->alloc(); threadp->status = _THREAD_STARTING; threadp->current_req = NULL; threadp->requests = 0; threadp->next = threads; threads = threadp; if ((threadp->thread = CreateThread(NULL, /* no security attributes */ 0, /* use default stack size */ squidaio_thread_loop, /* thread function */ threadp, /* argument to thread function */ 0, /* use default creation flags */ &(threadp->dwThreadId)) /* returns the thread identifier */ ) == NULL) { fprintf(stderr, "Thread creation failed\n"); threadp->status = _THREAD_FAILED; continue; } /* Set the new thread priority above parent process */ SetThreadPriority(threadp->thread,THREAD_PRIORITY_ABOVE_NORMAL); } /* Create request pool */ squidaio_request_pool = memPoolCreate("aio_request", sizeof(squidaio_request_t)); squidaio_large_bufs = memPoolCreate("squidaio_large_bufs", AIO_LARGE_BUFS); squidaio_medium_bufs = memPoolCreate("squidaio_medium_bufs", AIO_MEDIUM_BUFS); squidaio_small_bufs = memPoolCreate("squidaio_small_bufs", AIO_SMALL_BUFS); squidaio_tiny_bufs = memPoolCreate("squidaio_tiny_bufs", AIO_TINY_BUFS); squidaio_micro_bufs = memPoolCreate("squidaio_micro_bufs", AIO_MICRO_BUFS); squidaio_initialised = 1; } void squidaio_shutdown(void) { squidaio_thread_t *threadp; int i; HANDLE * hthreads; if (!squidaio_initialised) return; /* This is the same as in squidaio_sync */ do { squidaio_poll_queues(); } while (request_queue_len > 0); hthreads = (HANDLE *) xcalloc (NUMTHREADS, sizeof (HANDLE)); threadp = threads; for (i = 0; i < NUMTHREADS; ++i) { threadp->exit = 1; hthreads[i] = threadp->thread; threadp = threadp->next; } ReleaseMutex(request_queue.mutex); ResetEvent(request_queue.cond); ReleaseMutex(done_queue.mutex); ResetEvent(done_queue.cond); Sleep(0); WaitForMultipleObjects(NUMTHREADS, hthreads, TRUE, 2000); for (i = 0; i < NUMTHREADS; ++i) { CloseHandle(hthreads[i]); } CloseHandle(main_thread); CommIO::NotifyIOClose(); squidaio_initialised = 0; xfree(hthreads); } static DWORD WINAPI squidaio_thread_loop(LPVOID lpParam) { squidaio_thread_t *threadp = (squidaio_thread_t *)lpParam; squidaio_request_t *request; HANDLE cond; /* local copy of the event queue because win32 event handles * don't atomically release the mutex as cond variables do. */ /* lock the thread info */ if (WAIT_FAILED == WaitForSingleObject(request_queue.mutex, INFINITE)) { fatal("Can't get ownership of mutex\n"); } /* duplicate the handle */ if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */ request_queue.cond, /* handle to copy */ GetCurrentProcess(), /* pseudo handle, don't close */ &cond, 0, /* required access */ FALSE, /* child process's don't inherit the handle */ DUPLICATE_SAME_ACCESS)) fatal("Can't duplicate mutex handle\n"); if (!ReleaseMutex(request_queue.mutex)) { CloseHandle(cond); fatal("Can't release mutex\n"); } Sleep(0); while (1) { DWORD rv; threadp->current_req = request = NULL; request = NULL; /* Get a request to process */ threadp->status = _THREAD_WAITING; if (threadp->exit) { CloseHandle(request_queue.mutex); CloseHandle(cond); return 0; } rv = WaitForSingleObject(request_queue.mutex, INFINITE); if (rv == WAIT_FAILED) { CloseHandle(cond); return 1; } while (!request_queue.head) { if (!ReleaseMutex(request_queue.mutex)) { CloseHandle(cond); threadp->status = _THREAD_FAILED; return 1; } Sleep(0); rv = WaitForSingleObject(cond, INFINITE); if (rv == WAIT_FAILED) { CloseHandle(cond); return 1; } rv = WaitForSingleObject(request_queue.mutex, INFINITE); if (rv == WAIT_FAILED) { CloseHandle(cond); return 1; } } request = request_queue.head; if (request) request_queue.head = request->next; if (!request_queue.head) request_queue.tailp = &request_queue.head; if (!ReleaseMutex(request_queue.mutex)) { CloseHandle(cond); return 1; } Sleep(0); /* process the request */ threadp->status = _THREAD_BUSY; request->next = NULL; threadp->current_req = request; errno = 0; if (!request->cancelled) { switch (request->request_type) { case _AIO_OP_OPEN: squidaio_do_open(request); break; case _AIO_OP_READ: squidaio_do_read(request); break; case _AIO_OP_WRITE: squidaio_do_write(request); break; case _AIO_OP_CLOSE: squidaio_do_close(request); break; case _AIO_OP_UNLINK: squidaio_do_unlink(request); break; #if AIO_OPENDIR /* Opendir not implemented yet */ case _AIO_OP_OPENDIR: squidaio_do_opendir(request); break; #endif case _AIO_OP_STAT: squidaio_do_stat(request); break; default: request->ret = -1; request->err = EINVAL; break; } } else { /* cancelled */ request->ret = -1; request->err = EINTR; } threadp->status = _THREAD_DONE; /* put the request in the done queue */ rv = WaitForSingleObject(done_queue.mutex, INFINITE); if (rv == WAIT_FAILED) { CloseHandle(cond); return 1; } *done_queue.tailp = request; done_queue.tailp = &request->next; if (!ReleaseMutex(done_queue.mutex)) { CloseHandle(cond); return 1; } CommIO::NotifyIOCompleted(); Sleep(0); ++ threadp->requests; } /* while forever */ CloseHandle(cond); return 0; } /* squidaio_thread_loop */ static void squidaio_queue_request(squidaio_request_t * request) { static int high_start = 0; debugs(43, 9, "squidaio_queue_request: " << request << " type=" << request->request_type << " result=" << request->resultp); /* Mark it as not executed (failing result, no error) */ request->ret = -1; request->err = 0; /* Internal housekeeping */ request_queue_len += 1; request->resultp->_data = request; /* Play some tricks with the request_queue2 queue */ request->next = NULL; if (WaitForSingleObject(request_queue.mutex, 0) == WAIT_OBJECT_0) { if (request_queue2.head) { /* Grab blocked requests */ *request_queue.tailp = request_queue2.head; request_queue.tailp = request_queue2.tailp; } /* Enqueue request */ *request_queue.tailp = request; request_queue.tailp = &request->next; if (!SetEvent(request_queue.cond)) fatal("Couldn't push queue"); if (!ReleaseMutex(request_queue.mutex)) { /* unexpected error */ fatal("Couldn't push queue"); } Sleep(0); if (request_queue2.head) { /* Clear queue of blocked requests */ request_queue2.head = NULL; request_queue2.tailp = &request_queue2.head; } } else { /* Oops, the request queue is blocked, use request_queue2 */ *request_queue2.tailp = request; request_queue2.tailp = &request->next; } if (request_queue2.head) { static uint64_t filter = 0; static uint64_t filter_limit = 8196; if (++filter >= filter_limit) { filter_limit += filter; filter = 0; debugs(43, DBG_IMPORTANT, "squidaio_queue_request: WARNING - Queue congestion (growing to " << filter_limit << ")"); } } /* Warn if out of threads */ if (request_queue_len > MAGIC1) { static int last_warn = 0; static int queue_high, queue_low; if (high_start == 0) { high_start = (int)squid_curtime; queue_high = request_queue_len; queue_low = request_queue_len; } if (request_queue_len > queue_high) queue_high = request_queue_len; if (request_queue_len < queue_low) queue_low = request_queue_len; if (squid_curtime >= (last_warn + 15) && squid_curtime >= (high_start + 5)) { debugs(43, DBG_IMPORTANT, "squidaio_queue_request: WARNING - Disk I/O overloading"); if (squid_curtime >= (high_start + 15)) debugs(43, DBG_IMPORTANT, "squidaio_queue_request: Queue Length: current=" << request_queue_len << ", high=" << queue_high << ", low=" << queue_low << ", duration=" << (long int) (squid_curtime - high_start)); last_warn = (int)squid_curtime; } } else { high_start = 0; } /* Warn if seriously overloaded */ if (request_queue_len > RIDICULOUS_LENGTH) { debugs(43, DBG_CRITICAL, "squidaio_queue_request: Async request queue growing uncontrollably!"); debugs(43, DBG_CRITICAL, "squidaio_queue_request: Syncing pending I/O operations.. (blocking)"); squidaio_sync(); debugs(43, DBG_CRITICAL, "squidaio_queue_request: Synced"); } } /* squidaio_queue_request */ static void squidaio_cleanup_request(squidaio_request_t * requestp) { squidaio_result_t *resultp = requestp->resultp; int cancelled = requestp->cancelled; /* Free allocated structures and copy data back to user space if the */ /* request hasn't been cancelled */ switch (requestp->request_type) { case _AIO_OP_STAT: if (!cancelled && requestp->ret == 0) memcpy(requestp->statp, requestp->tmpstatp, sizeof(struct stat)); squidaio_xfree(requestp->tmpstatp, sizeof(struct stat)); squidaio_xstrfree(requestp->path); break; case _AIO_OP_OPEN: if (cancelled && requestp->ret >= 0) /* The open() was cancelled but completed */ close(requestp->ret); squidaio_xstrfree(requestp->path); break; case _AIO_OP_CLOSE: if (cancelled && requestp->ret < 0) /* The close() was cancelled and never got executed */ close(requestp->fd); break; case _AIO_OP_UNLINK: case _AIO_OP_OPENDIR: squidaio_xstrfree(requestp->path); break; case _AIO_OP_READ: break; case _AIO_OP_WRITE: break; default: break; } if (resultp != NULL && !cancelled) { resultp->aio_return = requestp->ret; resultp->aio_errno = requestp->err; } squidaio_request_pool->freeOne(requestp); } /* squidaio_cleanup_request */ int squidaio_cancel(squidaio_result_t * resultp) { squidaio_request_t *request = (squidaio_request_t *)resultp->_data; if (request && request->resultp == resultp) { debugs(43, 9, "squidaio_cancel: " << request << " type=" << request->request_type << " result=" << request->resultp); request->cancelled = 1; request->resultp = NULL; resultp->_data = NULL; resultp->result_type = _AIO_OP_NONE; return 0; } return 1; } /* squidaio_cancel */ int squidaio_open(const char *path, int oflag, mode_t mode, squidaio_result_t * resultp) { squidaio_init(); squidaio_request_t *requestp; requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); requestp->path = (char *) squidaio_xstrdup(path); requestp->oflag = oflag; requestp->mode = mode; requestp->resultp = resultp; requestp->request_type = _AIO_OP_OPEN; requestp->cancelled = 0; resultp->result_type = _AIO_OP_OPEN; squidaio_queue_request(requestp); return 0; } static void squidaio_do_open(squidaio_request_t * requestp) { requestp->ret = open(requestp->path, requestp->oflag, requestp->mode); requestp->err = errno; } int squidaio_read(int fd, char *bufp, size_t bufs, off_t offset, int whence, squidaio_result_t * resultp) { squidaio_request_t *requestp; requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); requestp->fd = fd; requestp->bufferp = bufp; requestp->buflen = bufs; requestp->offset = offset; requestp->whence = whence; requestp->resultp = resultp; requestp->request_type = _AIO_OP_READ; requestp->cancelled = 0; resultp->result_type = _AIO_OP_READ; squidaio_queue_request(requestp); return 0; } static void squidaio_do_read(squidaio_request_t * requestp) { lseek(requestp->fd, requestp->offset, requestp->whence); if (!ReadFile((HANDLE)_get_osfhandle(requestp->fd), requestp->bufferp, requestp->buflen, (LPDWORD)&requestp->ret, NULL)) { WIN32_maperror(GetLastError()); requestp->ret = -1; } requestp->err = errno; } int squidaio_write(int fd, char *bufp, size_t bufs, off_t offset, int whence, squidaio_result_t * resultp) { squidaio_request_t *requestp; requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); requestp->fd = fd; requestp->bufferp = bufp; requestp->buflen = bufs; requestp->offset = offset; requestp->whence = whence; requestp->resultp = resultp; requestp->request_type = _AIO_OP_WRITE; requestp->cancelled = 0; resultp->result_type = _AIO_OP_WRITE; squidaio_queue_request(requestp); return 0; } static void squidaio_do_write(squidaio_request_t * requestp) { if (!WriteFile((HANDLE)_get_osfhandle(requestp->fd), requestp->bufferp, requestp->buflen, (LPDWORD)&requestp->ret, NULL)) { WIN32_maperror(GetLastError()); requestp->ret = -1; } requestp->err = errno; } int squidaio_close(int fd, squidaio_result_t * resultp) { squidaio_request_t *requestp; requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); requestp->fd = fd; requestp->resultp = resultp; requestp->request_type = _AIO_OP_CLOSE; requestp->cancelled = 0; resultp->result_type = _AIO_OP_CLOSE; squidaio_queue_request(requestp); return 0; } static void squidaio_do_close(squidaio_request_t * requestp) { if ((requestp->ret = close(requestp->fd)) < 0) { debugs(43, DBG_CRITICAL, "squidaio_do_close: FD " << requestp->fd << ", errno " << errno); close(requestp->fd); } requestp->err = errno; } int squidaio_stat(const char *path, struct stat *sb, squidaio_result_t * resultp) { squidaio_init(); squidaio_request_t *requestp; requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); requestp->path = (char *) squidaio_xstrdup(path); requestp->statp = sb; requestp->tmpstatp = (struct stat *) squidaio_xmalloc(sizeof(struct stat)); requestp->resultp = resultp; requestp->request_type = _AIO_OP_STAT; requestp->cancelled = 0; resultp->result_type = _AIO_OP_STAT; squidaio_queue_request(requestp); return 0; } static void squidaio_do_stat(squidaio_request_t * requestp) { requestp->ret = stat(requestp->path, requestp->tmpstatp); requestp->err = errno; } int squidaio_unlink(const char *path, squidaio_result_t * resultp) { squidaio_init(); squidaio_request_t *requestp; requestp = (squidaio_request_t *)squidaio_request_pool->alloc(); requestp->path = squidaio_xstrdup(path); requestp->resultp = resultp; requestp->request_type = _AIO_OP_UNLINK; requestp->cancelled = 0; resultp->result_type = _AIO_OP_UNLINK; squidaio_queue_request(requestp); return 0; } static void squidaio_do_unlink(squidaio_request_t * requestp) { requestp->ret = unlink(requestp->path); requestp->err = errno; } #if AIO_OPENDIR /* XXX squidaio_opendir NOT implemented yet.. */ int squidaio_opendir(const char *path, squidaio_result_t * resultp) { squidaio_request_t *requestp; int len; requestp = squidaio_request_pool->alloc(); resultp->result_type = _AIO_OP_OPENDIR; return -1; } static void squidaio_do_opendir(squidaio_request_t * requestp) { /* NOT IMPLEMENTED */ } #endif static void squidaio_poll_queues(void) { /* kick "overflow" request queue */ if (request_queue2.head && (WaitForSingleObject(request_queue.mutex, 0 )== WAIT_OBJECT_0)) { *request_queue.tailp = request_queue2.head; request_queue.tailp = request_queue2.tailp; if (!SetEvent(request_queue.cond)) fatal("couldn't push queue\n"); if (!ReleaseMutex(request_queue.mutex)) { /* unexpected error */ } Sleep(0); request_queue2.head = NULL; request_queue2.tailp = &request_queue2.head; } /* poll done queue */ if (done_queue.head && (WaitForSingleObject(done_queue.mutex, 0)==WAIT_OBJECT_0)) { struct squidaio_request_t *requests = done_queue.head; done_queue.head = NULL; done_queue.tailp = &done_queue.head; if (!ReleaseMutex(done_queue.mutex)) { /* unexpected error */ } Sleep(0); *done_requests.tailp = requests; request_queue_len -= 1; while (requests->next) { requests = requests->next; request_queue_len -= 1; } done_requests.tailp = &requests->next; } } squidaio_result_t * squidaio_poll_done(void) { squidaio_request_t *request; squidaio_result_t *resultp; int cancelled; int polled = 0; AIO_REPOLL: request = done_requests.head; if (request == NULL && !polled) { CommIO::ResetNotifications(); squidaio_poll_queues(); polled = 1; request = done_requests.head; } if (!request) { return NULL; } debugs(43, 9, "squidaio_poll_done: " << request << " type=" << request->request_type << " result=" << request->resultp); done_requests.head = request->next; if (!done_requests.head) done_requests.tailp = &done_requests.head; resultp = request->resultp; cancelled = request->cancelled; squidaio_debug(request); debugs(43, 5, "DONE: " << request->ret << " -> " << request->err); squidaio_cleanup_request(request); if (cancelled) goto AIO_REPOLL; return resultp; } /* squidaio_poll_done */ int squidaio_operations_pending(void) { return request_queue_len + (done_requests.head ? 1 : 0); } int squidaio_sync(void) { /* XXX This might take a while if the queue is large.. */ do { squidaio_poll_queues(); } while (request_queue_len > 0); return squidaio_operations_pending(); } int squidaio_get_queue_len(void) { return request_queue_len; } static void squidaio_debug(squidaio_request_t * request) { switch (request->request_type) { case _AIO_OP_OPEN: debugs(43, 5, "OPEN of " << request->path << " to FD " << request->ret); break; case _AIO_OP_READ: debugs(43, 5, "READ on fd: " << request->fd); break; case _AIO_OP_WRITE: debugs(43, 5, "WRITE on fd: " << request->fd); break; case _AIO_OP_CLOSE: debugs(43, 5, "CLOSE of fd: " << request->fd); break; case _AIO_OP_UNLINK: debugs(43, 5, "UNLINK of " << request->path); break; default: break; } } void squidaio_stats(StoreEntry * sentry) { squidaio_thread_t *threadp; int i; if (!squidaio_initialised) return; storeAppendPrintf(sentry, "\n\nThreads Status:\n"); storeAppendPrintf(sentry, "#\tID\t# Requests\n"); threadp = threads; for (i = 0; i < NUMTHREADS; ++i) { storeAppendPrintf(sentry, "%i\t0x%lx\t%ld\n", i + 1, threadp->dwThreadId, threadp->requests); threadp = threadp->next; } }