diff -r 000000000000 -r b26acd06ea60 sdkcreationmw/sdkruntimes/wsock/src/WinsockSelectThread.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdkcreationmw/sdkruntimes/wsock/src/WinsockSelectThread.cpp Mon Mar 08 12:09:11 2010 +0530 @@ -0,0 +1,401 @@ +/* +* Copyright (c) 2004-2005 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + + +#define TRACE_PREFIX "WSOCK: SelectThread: " +#include "wsock.h" +#include "WinsockSelectThread.h" +#include + +#ifdef EKA2 +// In Series60 version 3.0 they (Symbian) changed the emulator +// threading model from preemptive to cooperative. That is, only +// one emulated Symbian thread is running at any point of time. +// The threads are never interrupted (with exception of interrupt +// simulation) and must explicitely yield control by making a +// kernel call (typically, WaitForAnyRequest or something like +// that). It means that if thread blocks on a Win32 event or +// socket, it prevents other threads from running and the emulator +// gets stuck. The Emulator::Escape() call tells the scheduler to +// temporarily remove current thread from the list of scheduled +// threads, which allows other threads to run while we are blocked +// on select. Emulator::Reenter() waits for the currently active +// Symbian thread to yeild control and makes the current thread +// a normal Symbian thread again. +# define BEGIN_WIN32() Emulator::Escape() +# define END_WIN32() Emulator::Reenter() +# include +#else +# define BEGIN_WIN32() ((void)0) +# define END_WIN32() ((void)0) +#endif // EKA2 + +// Symbian does not support writeable static data, but we don't care because +// this code only runs in the emulator. +static CWinsockSelectThread* instance = NULL; +const TInt ETimeoutInMicroSeconds = 5000000; + +CWinsockSelectThread* CWinsockSelectThread::Static() +{ + CWinsockSelectThread* self = NULL; + TRAPD(err,self = &StaticL()); + return self; +} + +CWinsockSelectThread& CWinsockSelectThread::StaticL() +{ + if (!instance) + { + instance = NewL(); + } + return *instance; +} + +CWinsockSelectThread* CWinsockSelectThread::NewL() +{ + CWinsockSelectThread* self = new(ELeave)CWinsockSelectThread; + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); + return self; +} + +CWinsockSelectThread::CWinsockSelectThread() : +iUnblockSocket(INVALID_SOCKET) +{ +} + +CWinsockSelectThread::~CWinsockSelectThread() +{ + // Normally, this object never gets destroyed + // Only if ConstructL() leaves + ASSERT(!iRequests.Count()); + if (iCriticalSectionCreated) iCriticalSection.Close(); + iRequests.Reset(); + if (iUnblockSocket != INVALID_SOCKET) + { + BEGIN_WIN32(); + closesocket(iUnblockSocket); + END_WIN32(); + } + delete iAddr.iSockAddrIn; + delete iTimeout; +} + +void CWinsockSelectThread::ConstructL() +{ + iTimeout = new(ELeave)(struct timeval); + iTimeout->tv_sec = (ETimeoutInMicroSeconds/1000000); + iTimeout->tv_usec = (ETimeoutInMicroSeconds%1000000); + + iAddr.iSockAddrIn = new(ELeave)(struct sockaddr_in); + TInt addrSize = sizeof(struct sockaddr_in); + Mem::FillZ(iAddr.iSockAddrIn, addrSize); + LEAVE_IF_ERROR(iCriticalSection.CreateLocal()); + + // Create a UDP socket for unblocking the select + BEGIN_WIN32(); + iUnblockSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + END_WIN32(); + + if (iUnblockSocket == INVALID_SOCKET) + { + TRACE1("failed to create socket, err %d", WSAGetLastError()); + LEAVE(KErrGeneral); + } + + iAddr.iSockAddrIn->sin_family = AF_INET; + iAddr.iSockAddrIn->sin_addr.s_addr = inet_addr("127.0.0.1"); + + BEGIN_WIN32(); + int err = bind(iUnblockSocket, iAddr.iSockAddr, addrSize); + END_WIN32(); + + if (err) + { + TRACE1("failed to bind, err %d", WSAGetLastError()); + User::Leave(KErrGeneral); + } + + BEGIN_WIN32(); + err = getsockname(iUnblockSocket, iAddr.iSockAddr, &addrSize); + END_WIN32(); + + if (err) + { + TRACE1("failed to getsockname, err %d", WSAGetLastError()); + LEAVE(KErrGeneral); + } + + TRACE1("notification socket bound to UDP port %d", + (int)ntohs(iAddr.iSockAddrIn->sin_port)); + + // Finally, create and start the thread + _LIT(threadName,"WinsockSelectThread"); + LEAVE_IF_ERROR(iThread.Create(threadName, Run, KDefaultStackSize, + NULL, this, EOwnerProcess)); + iThread.Resume(); +} + +// Wakes up the select thread +TBool CWinsockSelectThread::Wakeup() +{ + char msg[1] = {0}; + + BEGIN_WIN32(); + int bytesSent = sendto(iUnblockSocket,msg,sizeof(msg),0, + iAddr.iSockAddr, sizeof(struct sockaddr_in)); + END_WIN32(); + + if (bytesSent == SOCKET_ERROR) + { + TRACE1("failed to unblock select, err %d", WSAGetLastError()); + return EFalse; + } + return ETrue; +} + +TInt CWinsockSelectThread::Submit(MSelectRequest* aRequest) +{ + TInt err = KErrNone; + iCriticalSection.Wait(); + TInt index = iRequests.FindInAddressOrder(aRequest); + if (index < 0) err = iRequests.InsertInAddressOrder(aRequest); + if (err == KErrNone) Wakeup(); + iCriticalSection.Signal(); + return err; +} + +TBool CWinsockSelectThread::Cancel(MSelectRequest* aRequest) +{ + TBool found = EFalse; + iCriticalSection.Wait(); + TInt index = iRequests.FindInAddressOrder(aRequest); + if (index >= 0) + { + iRequests.Remove(index); + Wakeup(); + found = ETrue; + } + iCriticalSection.Signal(); + return found; +} + +TInt CWinsockSelectThread::Run(TAny *aPtr) +{ + int err; + CWinsockSelectThread* thread = (CWinsockSelectThread*)aPtr; + CTrapCleanup* cleanup = CTrapCleanup::New(); + if (cleanup) { + TRAP(err, thread->RunL()); + delete cleanup; + } else { + err = KErrNoMemory; + } + return err; +} + +void CWinsockSelectThread::RunL() +{ + TRACE("started"); + RPointerArray completedRequests; + struct timeval zeroTimeout; + Mem::FillZ(&zeroTimeout, sizeof(zeroTimeout)); + + // Open handle to our protocol DLL to prevent it from being unloaded + // while this thread is running + RLibrary self; + TBuf<12> libName; + libName.Copy(KWinsockProtocol); + libName.Append(_L(".prt")); + VERIFY_SUCCESS(self.Load(libName)); + + for (;;) + { + TInt i,n,nfd; + TUint maxSock; + + fd_set read_fd_set; + fd_set write_fd_set; + fd_set except_fd_set; + + fd_set * readfs = NULL; + fd_set * writefs = NULL; + fd_set * errfs = NULL; + +#pragma warning(push) // FD_SET macro in winsock.h produces warning +#pragma warning(disable : 4127) // C4127: conditional expression is constant + + // Always listen for notification socket + readfs = &read_fd_set; + FD_ZERO(readfs); + FD_SET(iUnblockSocket,readfs); + maxSock = iUnblockSocket; + + // Construct the fdsets + iCriticalSection.Wait(); + n = iRequests.Count(); + for (i=0; iSocket(); + TInt requestMask = req->SelectMask(); + if (maxSock < sock) maxSock = sock; + if (requestMask & ESelectRead) + { + FD_SET(sock,readfs); + } + if (requestMask & ESelectWrite) + { + if (!writefs) + { + writefs = &write_fd_set; + FD_ZERO(writefs); + } + FD_SET(sock,writefs); + } + if (requestMask & ESelectError) + { + if (!errfs) + { + errfs = &except_fd_set; + FD_ZERO(errfs); + } + FD_SET(sock,errfs); + } + } + +#pragma warning(pop) + + iCriticalSection.Signal(); + + BEGIN_WIN32(); + nfd = select(maxSock+1, readfs, writefs, errfs, iTimeout); + END_WIN32(); + + if (nfd == SOCKET_ERROR) + { + TRACE1("select err %d",WSAGetLastError()); + break; + } + + TBool dataInNotificationSocket = EFalse; + if (FD_ISSET(iUnblockSocket, readfs)) + { + // Don't count the notification socket + nfd--; + + // Read one message from the notification socket + char msg[1]; + DEBUG_ONLY(int nbytes = )recv(iUnblockSocket,msg,sizeof(msg),0); + ASSERT(nbytes != SOCKET_ERROR); + dataInNotificationSocket = ETrue; + } + + if (nfd == 0) + { + // Timeout or request to rebuild fdsets + continue; + } + + iCriticalSection.Wait(); + + // Read all remaining messages from the notification socket + // The fact that we are in a critical section guarantees that + // we won't get stuck here forever + if (dataInNotificationSocket) + { + char msg[1]; + fd_set tmp; + FD_ZERO(&tmp); +#pragma warning(push) // Ignore warning generated by FD_SET macro +#pragma warning(disable : 4127) // C4127: conditional expression is constant + FD_SET(iUnblockSocket,&tmp); + BEGIN_WIN32(); + while (select(iUnblockSocket+1,&tmp,NULL,NULL,&zeroTimeout) == 1) + { + if (recv(iUnblockSocket, msg, sizeof(msg), 0) == SOCKET_ERROR) + { + TRACE1("internal recv socket err %d",WSAGetLastError()); + break; + } + FD_ZERO(&tmp); + FD_SET(iUnblockSocket,&tmp); + } + END_WIN32(); +#pragma warning(pop) + } + + // Move completed requests to completedRequests queue + n = iRequests.Count(); + for (i=n-1; i>=0 && nfd > 0; i--) + { + MSelectRequest* req = iRequests[i]; + TUint sock = req->Socket(); + TInt requestMask = req->SelectMask(); + TInt selectMask = 0; + if (requestMask & ESelectRead && FD_ISSET(sock, readfs)) + { + selectMask |= ESelectRead; + } + if (requestMask & ESelectWrite && FD_ISSET(sock, writefs)) + { + selectMask |= ESelectRead; + } + if (requestMask & ESelectError && FD_ISSET(sock, errfs)) + { + selectMask |= ESelectError; + } + if (selectMask) + { + nfd--; + if (completedRequests.Append(req) == KErrNone) + { + iRequests.Remove(i); + } + } + } + + iCriticalSection.Signal(); + + // Fire completion notifications outside of the critical section + n = completedRequests.Count(); + for (i=n-1; i>=0; i--) + { + MSelectRequest* req = completedRequests[i]; + TUint sock = req->Socket(); + TInt requestMask = req->SelectMask(); + TInt selectMask = 0; + if (requestMask & ESelectRead && FD_ISSET(sock, readfs)) + { + selectMask |= ESelectRead; + } + if (requestMask & ESelectWrite && FD_ISSET(sock, writefs)) + { + selectMask |= ESelectRead; + } + if (requestMask & ESelectError && FD_ISSET(sock, errfs)) + { + selectMask |= ESelectError; + } + ASSERT(selectMask); + req->SelectComplete(selectMask); + completedRequests.Remove(i); + } + } + completedRequests.Reset(); +}