diff --git a/deps/hiredis/hiredis.c b/deps/hiredis/hiredis.c index ae73a78e..49dd807d 100644 --- a/deps/hiredis/hiredis.c +++ b/deps/hiredis/hiredis.c @@ -44,6 +44,7 @@ #include "sds.h" #ifdef _WIN32 #include "../../src/win32fixes.h" + #include "../../src/win32_socketmap.h" #endif static redisReply *createReplyObject(int type); @@ -1025,6 +1026,7 @@ void redisFree(redisContext *c) { if (c->fd > 0) #ifdef _WIN32 closesocket(c->fd); + smRemoveSocket(c->fd); #else close(c->fd); #endif diff --git a/deps/hiredis/net.c b/deps/hiredis/net.c index 028d9d13..81cdad98 100644 --- a/deps/hiredis/net.c +++ b/deps/hiredis/net.c @@ -60,6 +60,7 @@ #include "sds.h" #ifdef _WIN32 #include "../../src/win32fixes.h" + #include "../../src/win32_socketmap.h" #endif /* Defined in hiredis.c */ @@ -104,6 +105,7 @@ static int redisCreateSocket(redisContext *c, int type) { if (setsockopt((int)s, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)) == -1) { __redisSetError(c,REDIS_ERR_IO,NULL); closesocket(s); + smRemoveSocket(s); return REDIS_ERR; } } @@ -143,6 +145,7 @@ static int redisSetBlocking(redisContext *c, int fd, int blocking) { __redisSetError(c,REDIS_ERR_IO, sdscatprintf(sdsempty(), "ioctlsocket(FIONBIO): %d\n", errno)); closesocket(fd); + smRemoveSocket(fd); return REDIS_ERR; }; @@ -183,6 +186,7 @@ static int redisSetTcpNoDelay(redisContext *c, int fd) { __redisSetError(c,REDIS_ERR_IO, sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %d", (int)GetLastError())); closesocket(fd); + smRemoveSocket(fd); return REDIS_ERR; } return REDIS_OK; @@ -218,6 +222,7 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval * if (select(FD_SETSIZE, NULL, &wfd, NULL, toptr) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"select(2)"); closesocket(fd); + smRemoveSocket(fd); return REDIS_ERR; } @@ -225,6 +230,7 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval * errno = WSAGetLastError(); __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); closesocket(fd); + smRemoveSocket(fd); return REDIS_ERR; } @@ -236,6 +242,7 @@ static int redisContextWaitReady(redisContext *c, int fd, const struct timeval * __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); closesocket(fd); + smRemoveSocket(fd); return REDIS_ERR; } #else @@ -298,6 +305,7 @@ int redisCheckSocketError(redisContext *c, int fd) { errno = WSAGetLastError(); __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)"); closesocket(fd); + smRemoveSocket(fd); return REDIS_ERR; } #else @@ -374,6 +382,7 @@ int redisContextPreConnectTcp(redisContext *c, const char *addr, int port, __redisSetError(c,REDIS_ERR_OTHER, sdscatprintf(sdsempty(),"can't resolve: %s\n", addr)); closesocket(s); + smRemoveSocket(s); return REDIS_ERR; } memcpy(&sa->sin_addr, he->h_addr, sizeof(struct in_addr)); @@ -415,6 +424,7 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct t __redisSetError(c,REDIS_ERR_OTHER, sdscatprintf(sdsempty(),"can't resolve: %s\n", addr)); closesocket(s); + smRemoveSocket(s); return REDIS_ERR; } memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); diff --git a/msvs/RedisBenchmark/RedisBenchmark.vcxproj b/msvs/RedisBenchmark/RedisBenchmark.vcxproj index 263bc8a2..00e5e10c 100644 --- a/msvs/RedisBenchmark/RedisBenchmark.vcxproj +++ b/msvs/RedisBenchmark/RedisBenchmark.vcxproj @@ -167,8 +167,10 @@ + + diff --git a/msvs/RedisLog.h b/msvs/RedisLog.h new file mode 100644 index 00000000..13ba4f7f --- /dev/null +++ b/msvs/RedisLog.h @@ -0,0 +1,37 @@ +/* + * Copyright (c), Microsoft Open Technologies, Inc. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +/* Log levels */ +#define REDIS_DEBUG 0 +#define REDIS_VERBOSE 1 +#define REDIS_NOTICE 2 +#define REDIS_WARNING 3 +#define REDIS_LOG_RAW (1<<10) /* Modifier to log without timestamp */ + +#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */ + +void redisLog(int level, const char *fmt, ...); +void redisLogRaw(int level, const char *msg); +void redisLogFromHandler(int level, const char *msg); + +void SetVerbosity(int verbosity); \ No newline at end of file diff --git a/msvs/RedisServer.sln b/msvs/RedisServer.sln index af1fa3f3..46742399 100644 --- a/msvs/RedisServer.sln +++ b/msvs/RedisServer.sln @@ -2,6 +2,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RedisServer", "RedisServer.vcxproj", "{46842776-68A5-EC98-6A09-1859BBFC73AA}" + ProjectSection(ProjectDependencies) = postProject + {170B0909-5F75-467F-9501-C99DEC16C6DC} = {170B0909-5F75-467F-9501-C99DEC16C6DC} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hiredis", "hiredis\hiredis.vcxproj", "{13E85053-54B3-487B-8DDB-3430B1C1B3BF}" EndProject diff --git a/msvs/RedisServer.vcxproj b/msvs/RedisServer.vcxproj index 3750804d..8450400c 100644 --- a/msvs/RedisServer.vcxproj +++ b/msvs/RedisServer.vcxproj @@ -177,6 +177,7 @@ + @@ -196,6 +197,7 @@ + @@ -218,6 +220,7 @@ + @@ -229,6 +232,7 @@ + diff --git a/msvs/hiredis/hiredis.vcxproj b/msvs/hiredis/hiredis.vcxproj index 00278ee5..88e7973b 100644 --- a/msvs/hiredis/hiredis.vcxproj +++ b/msvs/hiredis/hiredis.vcxproj @@ -151,6 +151,8 @@ + + diff --git a/src/RedisLog.c b/src/RedisLog.c new file mode 100644 index 00000000..9109e940 --- /dev/null +++ b/src/RedisLog.c @@ -0,0 +1,116 @@ +/* + * Copyright (c), Microsoft Open Technologies, Inc. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "redisLog.h" +#include +#include +#include +#include +#include +#include "win32Fixes.h" +#include + + +static int verbosity = REDIS_WARNING; +static char* logFile = NULL; + +void setLogVerbosityLevel(int level) +{ + verbosity = level; +} + +void setLogFile(const char* logFileName) +{ + if (logFile != NULL) { + free((void*)logFile); + logFile = NULL; + } + logFile = (char*)malloc(strlen(logFile)); + if (logFile==NULL) { + redisLog(REDIS_WARNING, "memory allocation failure"); + return; + } + strcpy (logFile,logFileName); +} + +void redisLogRaw(int level, const char *msg) { +#ifndef _WIN32 + const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING }; +#endif + const char *c = ".-*#"; + FILE *fp; + char buf[64]; + int rawmode = (level & REDIS_LOG_RAW); + + level &= 0xff; /* clear flags */ + if (level < verbosity) return; + + fp = (logFile == NULL) ? stdout : fopen(logFile,"a"); + if (!fp) return; + + if (rawmode) { + fprintf(fp,"%s",msg); + } else { + int off; +#ifdef _WIN32 + time_t secs; + unsigned int usecs; + struct tm * now ; + + secs = gettimeofdaysecs(&usecs); + now = localtime(&secs); + off = (int)strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",now); + snprintf(buf+off,sizeof(buf)-off,"%03d",usecs/1000); +#else + struct timeval tv; + + gettimeofday(&tv,NULL); + off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec)); + snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000); +#endif + fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg); + } + fflush(fp); + + if (logFile) fclose(fp); +#ifndef _WIN32 + if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg); +#endif +} + + +/* Like redisLogRaw() but with printf-alike support. This is the function that + * is used across the code. The raw version is only used in order to dump + * the INFO output on crash. */ +void redisLog(int level, const char *fmt, ...) { + va_list ap; + char msg[REDIS_MAX_LOGMSG_LEN]; + + if ((level&0xff) < verbosity) return; + + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + redisLogRaw(level,msg); +} diff --git a/src/ae.c b/src/ae.c index ca22b58f..d5a8c039 100644 --- a/src/ae.c +++ b/src/ae.c @@ -36,6 +36,8 @@ #include #include #include +#else +#include "win32_socketmap.h" #endif #include #include @@ -46,6 +48,8 @@ #include "zmalloc.h" #include "config.h" + + /* Include the best multiplexing layer supported by this system. * The following should be ordered by performances, descending. */ #ifdef _WIN32 @@ -112,11 +116,26 @@ int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData) { aeFileEvent *fe; + + +#ifdef _WIN32 + if (smGetSocketCount() >= eventLoop->setsize) { + errno = ERANGE; + return AE_ERR; + } + + if(smLookupFD(fd) == -1 ) { + errno = ERANGE; + return AE_ERR; + } + fe = &eventLoop->events[smLookupFD(fd)]; +#else if (fd >= eventLoop->setsize) { errno = ERANGE; return AE_ERR; } fe = &eventLoop->events[fd]; +#endif if (aeApiAddEvent(eventLoop, fd, mask) == -1) return AE_ERR; @@ -132,8 +151,17 @@ int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) { aeFileEvent *fe; +#ifdef _WIN32 + if (smGetSocketCount() >= eventLoop->setsize) return; + + if(smLookupFD(fd) == -1 ) return; + + fe = &eventLoop->events[smLookupFD(fd)]; +#else if (fd >= eventLoop->setsize) return; + fe = &eventLoop->events[fd]; +#endif if (fe->mask == AE_NONE) return; fe->mask = fe->mask & (~mask); @@ -150,8 +178,17 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) int aeGetFileEvents(aeEventLoop *eventLoop, int fd) { aeFileEvent *fe; +#ifdef _WIN32 + if(smLookupFD(fd) == -1 ) DebugBreak(); + + if (smGetSocketCount() >= eventLoop->setsize) return 0; + + fe = &eventLoop->events[smLookupFD(fd)]; +#else if (fd >= eventLoop->setsize) return 0; + fe = &eventLoop->events[fd]; +#endif return fe->mask; } @@ -382,11 +419,24 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags) numevents = aeApiPoll(eventLoop, tvp); for (j = 0; j < numevents; j++) { - aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; + aeFileEvent *fe; int mask = eventLoop->fired[j].mask; int fd = eventLoop->fired[j].fd; int rfired = 0; +#ifdef _WIN32 + if(smLookupFD(eventLoop->fired[j].fd) == -1 ) { + // We may have already processed a socket close request + // before a subscription notification gets sent out + processed++; + continue; + } + + fe = &eventLoop->events[smLookupFD(eventLoop->fired[j].fd)]; +#else + fe = &eventLoop->events[eventLoop->fired[j].fd]; +#endif + /* note the fe->mask & mask & ... code: maybe an already processed * event removed an element that fired and we still didn't * processed, so we check if the event is still valid. */ diff --git a/src/anet.c b/src/anet.c index 90bb9235..a3bb2e47 100644 --- a/src/anet.c +++ b/src/anet.c @@ -49,6 +49,7 @@ #ifdef _WIN32 #include "win32fixes.h" #define ANET_NOTUSED(V) ((void) V) +#include "win32_socketmap.h" #endif #include "anet.h" @@ -245,9 +246,12 @@ static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) { struct sockaddr_in sa; unsigned long inAddress; - if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) + if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) { return ANET_ERR; - + } + else { + smAddSocket(s); + } sa.sin_family = AF_INET; sa.sin_port = htons((u_short)port); inAddress = inet_addr(addr); @@ -258,6 +262,7 @@ static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) { if (he == NULL) { anetSetError(err, "can't resolve: %s\n", addr); closesocket(s); + smRemoveSocket(s); return ANET_ERR; } memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); @@ -274,6 +279,7 @@ static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) { anetSetError(err, "connect: %d\n", errno); closesocket(s); + smRemoveSocket(s); return ANET_ERR; } @@ -435,12 +441,14 @@ static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { errno = WSAGetLastError(); anetSetError(err, "bind error: %d\n", errno); closesocket((SOCKET)s); + smRemoveSocket(s); return ANET_ERR; } if (aeWinListen((SOCKET)s, 511) == SOCKET_ERROR) { /* the magic 511 constant is from nginx */ errno = WSAGetLastError(); anetSetError(err, "listen error: %d\n", errno); closesocket((SOCKET)s); + smRemoveSocket(s); return ANET_ERR; } return ANET_OK; @@ -457,6 +465,8 @@ int anetTcpServer(char *err, int port, char *bindaddr) if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) return ANET_ERR; + smAddSocket(s); + /* Override for SO_REUSEADDR for windows server socks */ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == SOCKET_ERROR) { errno = WSAGetLastError(); diff --git a/src/config.c b/src/config.c index 97e6f6ae..14de1a1b 100644 --- a/src/config.c +++ b/src/config.c @@ -157,6 +157,9 @@ void loadServerConfigFromString(char *config) { "Can't open the log file: %s", strerror(errno)); goto loaderr; } + else { + setLogFile( server.logfile ); + } fclose(logfp); } } else if (!strcasecmp(argv[0],"syslog-enabled") && argc == 2) { @@ -688,6 +691,7 @@ void configSetCommand(redisClient *c) { } else { goto badfmt; } + setLogVerbosityLevel(server.verbosity); } else if (!strcasecmp(c->argv[2]->ptr,"client-output-buffer-limit")) { int vlen, j; sds *v = sdssplitlen(o->ptr,(int)sdslen(o->ptr)," ",1,&vlen); diff --git a/src/redis.c b/src/redis.c index 2bbf6b65..ac8d9cb9 100644 --- a/src/redis.c +++ b/src/redis.c @@ -36,6 +36,7 @@ #ifdef _WIN32 #include #define LOG_LOCAL0 0 +#include "win32_socketmap.h" #else #include #include @@ -259,67 +260,6 @@ struct redisCommand redisCommandTable[] = { /*============================ Utility functions ============================ */ -/* Low level logging. To use only for very big messages, otherwise - * redisLog() is to prefer. */ -void redisLogRaw(int level, const char *msg) { -#ifndef _WIN32 - const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING }; -#endif - const char *c = ".-*#"; - FILE *fp; - char buf[64]; - int rawmode = (level & REDIS_LOG_RAW); - - level &= 0xff; /* clear flags */ - if (level < server.verbosity) return; - - fp = (server.logfile == NULL) ? stdout : fopen(server.logfile,"a"); - if (!fp) return; - - if (rawmode) { - fprintf(fp,"%s",msg); - } else { - int off; -#ifdef _WIN32 - time_t secs; - unsigned int usecs; - - secs = gettimeofdaysecs(&usecs); - off = (int)strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&secs)); - snprintf(buf+off,sizeof(buf)-off,"%03d",usecs/1000); -#else - struct timeval tv; - - gettimeofday(&tv,NULL); - off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec)); - snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000); -#endif - fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg); - } - fflush(fp); - - if (server.logfile) fclose(fp); -#ifndef _WIN32 - if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg); -#endif -} - -/* Like redisLogRaw() but with printf-alike support. This is the function that - * is used across the code. The raw version is only used in order to dump - * the INFO output on crash. */ -void redisLog(int level, const char *fmt, ...) { - va_list ap; - char msg[REDIS_MAX_LOGMSG_LEN]; - - if ((level&0xff) < server.verbosity) return; - - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - - redisLogRaw(level,msg); -} - #ifdef _WIN32 /* Misc Windows house keeping */ void win32Cleanup(void) { @@ -1214,6 +1154,7 @@ void initServerConfig() { server.sofd = -1; server.dbnum = REDIS_DEFAULT_DBNUM; server.verbosity = REDIS_NOTICE; + setLogVerbosityLevel(server.verbosity); server.maxidletime = REDIS_MAXIDLETIME; server.tcpkeepalive = 0; server.active_expire_enabled = 1; @@ -1918,8 +1859,14 @@ int prepareForShutdown(int flags) { } /* Close the listening sockets. Apparently this allows faster restarts. */ #ifdef _WIN32 - if (server.ipfd != -1) closesocket(server.ipfd); - if (server.sofd != -1) closesocket(server.sofd); + if (server.ipfd != -1) { + closesocket(server.ipfd); + smRemoveSocket(server.ipfd); + } + if (server.sofd != -1) { + closesocket(server.sofd); + smRemoveSocket(server.sofd); + } #else if (server.ipfd != -1) close(server.ipfd); if (server.sofd != -1) close(server.sofd); diff --git a/src/redis.h b/src/redis.h index baccf948..a03a885d 100644 --- a/src/redis.h +++ b/src/redis.h @@ -70,6 +70,7 @@ #include "util.h" /* Misc functions useful in many places */ #include "win32_cow.h" /* Windows copy on write */ +#include "redisLog.h" /* moved logging for hiredis and RedisCli usage /* /* Error codes */ #define REDIS_OK 0 @@ -90,7 +91,6 @@ #define REDIS_SHARED_SELECT_CMDS 10 #define REDIS_SHARED_INTEGERS 10000 #define REDIS_SHARED_BULKHDR_LEN 32 -#define REDIS_MAX_LOGMSG_LEN 1024 /* Default maximum length of syslog messages */ #define REDIS_AOF_REWRITE_PERC 100 #define REDIS_AOF_REWRITE_MIN_SIZE (1024*1024) #define REDIS_AOF_REWRITE_ITEMS_PER_CMD 64 @@ -244,13 +244,6 @@ #define REDIS_SORT_DESC 2 #define REDIS_SORTKEY_MAX 1024 -/* Log levels */ -#define REDIS_DEBUG 0 -#define REDIS_VERBOSE 1 -#define REDIS_NOTICE 2 -#define REDIS_WARNING 3 -#define REDIS_LOG_RAW (1<<10) /* Modifier to log without timestamp */ - /* Anti-warning macro... */ #define REDIS_NOTUSED(V) ((void) V) @@ -1008,9 +1001,6 @@ void call(redisClient *c, int flags); void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags); void alsoPropagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int target); int prepareForShutdown(); -void redisLog(int level, const char *fmt, ...); -void redisLogRaw(int level, const char *msg); -void redisLogFromHandler(int level, const char *msg); void usage(); void updateDictResizePolicy(void); int htNeedsResize(dict *dict); diff --git a/src/win32_socketmap.cpp b/src/win32_socketmap.cpp new file mode 100644 index 00000000..78bd9cd7 --- /dev/null +++ b/src/win32_socketmap.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c), Microsoft Open Technologies, Inc. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "win32fixes.h" +#include "win32_socketmap.h" +#include +#include +using namespace std; + +extern "C" { +#include "redisLog.h" +} + +typedef map SocketToFDMapType; +typedef map FDToSocketMapType; +typedef queue FDRecyclePoolType; +typedef SocketToFDMapType::iterator S2FDIterator; +typedef FDToSocketMapType::iterator FD2SIterator; + +/* In UNIX File Descriptors increment by one for each new one. Windows handles + * do not follow the same rule. Additionally UNIX uses a 32-bit int to + * represent a FD while Windows_x64 uses a 64-bit value to represent a handle. + * There is no documented guarantee that a Windows SOCKET value will be + * entirely constrained in 32-bits (though it seems to be currently). SOCKETs + * should be treated as an opaque value and not be cast to a 32-bit int. In + * order to not break existing code that relies on the maximum FD value to + * indicate the number of handles that have been created (and other UNIXisms), + * this code maps SOCKET handles to a virtual FD number starting at 3 (0,1 and + * 2 are reserved for stdin, stdout and stderr). + */ +class SocketMap { +public: + static SocketMap& getInstance() { + static SocketMap instance; // Instantiated on first use. Guaranteed to be destroyed. + return instance; + } +private: + SocketMap() {}; + SocketMap(SocketMap const&); // Don't implement to guarantee singleton semantics + void operator=(SocketMap const&); // Don't implement to guarantee singleton semantics + +private: + SocketToFDMapType SocketToFDMap; + FDToSocketMapType FDToSocketMap; + FDRecyclePoolType FDRecyclePool; + +public: + const static int minFD = 3; + const static int invalidFD = -1; + +private: + /* Gets the next available File Descriptor. File Descriptors are always + non-negative integers, with the first three being reserved for stdin(0), + stdout(1) and stderr(2). */ + int getNextFDAvailable() { + if( FDRecyclePool.empty() == false ) { + int FD = FDRecyclePool.front(); + FDRecyclePool.pop(); + return FD; + } else { + return (int)(minFD + SocketToFDMap.size()); + } + } + +public: + /* Adds a socket to the socket map. Returns the file descriptor value for + the socket. Returns invalidFD if the socket is already added to the + collection. */ + int addSocket(SOCKET s) { + if (SocketToFDMap.find(s) != SocketToFDMap.end()) { + redisLog( REDIS_DEBUG, "SocketMap::addSocket() - socket already exists!" ); + return invalidFD; + } + + int FD = getNextFDAvailable(); + SocketToFDMap[s] = FD; + FDToSocketMap[FD] = s; + return FD; + } + + /* Removes a socket from the list of sockets. Also removes the associated + file descriptor. */ + void removeSocket(SOCKET s) { + S2FDIterator smit = SocketToFDMap.find(s); + if(smit == SocketToFDMap.end()) { + redisLog( REDIS_DEBUG, "SocketMap::removeSocket() - failed to find socket!" ); + } + int FD = (*smit).second; + FDRecyclePool.push(FD); + SocketToFDMap.erase(s); + } + + /* Returns the socket associated with a file descriptor. */ + SOCKET lookupSocket(int FD) { + if (FDToSocketMap.find(FD) != FDToSocketMap.end()) { + return FDToSocketMap[FD]; + } else { + redisLog( REDIS_DEBUG, "SocketMap::lookupSocket() - failed to find socket!" ); + return invalidFD; + } + } + + /* Returns the file descriptor associated with a socket. */ + int lookupFD(SOCKET s) { + if (SocketToFDMap.find(s) != SocketToFDMap.end()) { + return SocketToFDMap[s]; + } else { + redisLog( REDIS_DEBUG, "SocketMap::lookupFD() - failed to find FD!" ); + return invalidFD; + } + } + + /* Returns the number of socket->FD mappings allocated. */ + int getCount() { + return (int)(SocketToFDMap.size()); + } +}; + +extern "C" { + int smAddSocket(SOCKET s) { + return SocketMap::getInstance().addSocket(s); + } + + void smRemoveSocket(SOCKET s) { + SocketMap::getInstance().removeSocket(s); + } + + SOCKET smLookupSocket(int fd) { + return SocketMap::getInstance().lookupSocket(fd); + } + + int smLookupFD(SOCKET s) { + return SocketMap::getInstance().lookupFD(s); + } + + int smGetSocketCount() { + return SocketMap::getInstance().getCount(); + } +} \ No newline at end of file diff --git a/src/win32_socketmap.h b/src/win32_socketmap.h new file mode 100644 index 00000000..d93becbd --- /dev/null +++ b/src/win32_socketmap.h @@ -0,0 +1,39 @@ +/* + * Copyright (c), Microsoft Open Technologies, Inc. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int smAddSocket( SOCKET s ); +void smRemoveSocket( SOCKET s ); +SOCKET smLookupSocket( int FD ); +int smLookupFD( SOCKET s ); +int smGetSocketCount(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/win32_wsiocp.c b/src/win32_wsiocp.c index 16b037d1..4f7468d7 100644 --- a/src/win32_wsiocp.c +++ b/src/win32_wsiocp.c @@ -27,6 +27,7 @@ #include #include #include "win32_wsiocp.h" +#include "win32_socketmap.h" static void *iocpState; @@ -64,6 +65,10 @@ int aeWinQueueAccept(SOCKET listensock) { errno = WSAEINVAL; return -1; } + else { + smAddSocket(acceptsock); + } + accsockstate = aeGetSockState(iocpState, (int)acceptsock); if (accsockstate == NULL) { @@ -90,6 +95,7 @@ int aeWinQueueAccept(SOCKET listensock) { errno = WSAGetLastError(); sockstate->masks &= ~ACCEPT_PENDING; closesocket(acceptsock); + smRemoveSocket(acceptsock); accsockstate->masks = 0; zfree(areq); return -1; @@ -439,6 +445,7 @@ int aeWinCloseSocket(int fd) { if ((sockstate = aeGetSockState(iocpState, fd)) == NULL) { closesocket((SOCKET)fd); + smRemoveSocket(fd); return 0; } @@ -448,6 +455,7 @@ int aeWinCloseSocket(int fd) { if (sockstate->wreqs == 0 && (sockstate->masks & (READ_QUEUED | CONNECT_PENDING | SOCKET_ATTACHED)) == 0) { closesocket((SOCKET)fd); + smRemoveSocket(fd); } else { sockstate->masks |= CLOSE_PENDING; } diff --git a/src/win32fixes.h b/src/win32fixes.h index 5a978c74..523edf5a 100644 --- a/src/win32fixes.h +++ b/src/win32fixes.h @@ -40,7 +40,7 @@ #define fseeko fseeko64 #define ftello ftello64 -#define inline __inline +//#define inline __inline #undef ftruncate #define ftruncate replace_ftruncate