sdkcreationmw/sdkruntimes/wsock/src/WinsockSelectThread.cpp
changeset 0 b26acd06ea60
child 1 ac50fd48361b
equal deleted inserted replaced
-1:000000000000 0:b26acd06ea60
       
     1 /*
       
     2 * Copyright (c) 2004-2005 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #define TRACE_PREFIX "WSOCK: SelectThread: "
       
    20 #include "wsock.h"
       
    21 #include "WinsockSelectThread.h"
       
    22 #include <winsock2.h>
       
    23 
       
    24 #ifdef EKA2
       
    25 // In Series60 version 3.0 they (Symbian) changed the emulator 
       
    26 // threading model from preemptive to cooperative. That is, only
       
    27 // one emulated Symbian thread is running at any point of time.
       
    28 // The threads are never interrupted (with exception of interrupt
       
    29 // simulation) and must explicitely yield control by making a 
       
    30 // kernel call (typically, WaitForAnyRequest or something like
       
    31 // that). It means that if thread blocks on a Win32 event or
       
    32 // socket, it prevents other threads from running and the emulator
       
    33 // gets stuck. The Emulator::Escape() call tells the scheduler to
       
    34 // temporarily remove current thread from the list of scheduled
       
    35 // threads, which allows other threads to run while we are blocked
       
    36 // on select. Emulator::Reenter() waits for the currently active
       
    37 // Symbian thread to yeild control and makes the current thread
       
    38 // a normal Symbian thread again.
       
    39 #  define BEGIN_WIN32() Emulator::Escape()
       
    40 #  define END_WIN32() Emulator::Reenter()
       
    41 #  include <emulator.h>
       
    42 #else
       
    43 #  define BEGIN_WIN32() ((void)0)
       
    44 #  define END_WIN32() ((void)0)
       
    45 #endif // EKA2
       
    46 
       
    47 // Symbian does not support writeable static data, but we don't care because
       
    48 // this code only runs in the emulator.
       
    49 static CWinsockSelectThread* instance = NULL;
       
    50 const TInt ETimeoutInMicroSeconds = 5000000;
       
    51 
       
    52 CWinsockSelectThread* CWinsockSelectThread::Static()
       
    53 {
       
    54     CWinsockSelectThread* self = NULL;
       
    55     TRAPD(err,self = &StaticL());
       
    56     return self;
       
    57 }
       
    58 
       
    59 CWinsockSelectThread& CWinsockSelectThread::StaticL()
       
    60 {
       
    61     if (!instance)
       
    62     {
       
    63         instance = NewL();
       
    64     }
       
    65     return *instance;
       
    66 }
       
    67 
       
    68 CWinsockSelectThread* CWinsockSelectThread::NewL()
       
    69 {
       
    70     CWinsockSelectThread* self = new(ELeave)CWinsockSelectThread;
       
    71     CleanupStack::PushL(self);
       
    72     self->ConstructL();
       
    73     CleanupStack::Pop();
       
    74     return self;
       
    75 }
       
    76 
       
    77 CWinsockSelectThread::CWinsockSelectThread() : 
       
    78 iUnblockSocket(INVALID_SOCKET)
       
    79 {
       
    80 }
       
    81 
       
    82 CWinsockSelectThread::~CWinsockSelectThread()
       
    83 {
       
    84     // Normally, this object never gets destroyed
       
    85     // Only if ConstructL() leaves
       
    86     ASSERT(!iRequests.Count());
       
    87     if (iCriticalSectionCreated) iCriticalSection.Close();
       
    88     iRequests.Reset();
       
    89     if (iUnblockSocket != INVALID_SOCKET) 
       
    90     {
       
    91         BEGIN_WIN32();
       
    92         closesocket(iUnblockSocket);
       
    93         END_WIN32();
       
    94     }
       
    95     delete iAddr.iSockAddrIn;
       
    96     delete iTimeout;
       
    97 }
       
    98 
       
    99 void CWinsockSelectThread::ConstructL()
       
   100 {
       
   101     iTimeout = new(ELeave)(struct timeval);
       
   102     iTimeout->tv_sec = (ETimeoutInMicroSeconds/1000000);
       
   103     iTimeout->tv_usec = (ETimeoutInMicroSeconds%1000000);
       
   104     
       
   105     iAddr.iSockAddrIn = new(ELeave)(struct sockaddr_in);
       
   106     TInt addrSize = sizeof(struct sockaddr_in);
       
   107     Mem::FillZ(iAddr.iSockAddrIn, addrSize);
       
   108     LEAVE_IF_ERROR(iCriticalSection.CreateLocal());
       
   109 
       
   110     // Create a UDP socket for unblocking the select
       
   111     BEGIN_WIN32();
       
   112     iUnblockSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
       
   113     END_WIN32();
       
   114 
       
   115     if (iUnblockSocket == INVALID_SOCKET)
       
   116     {
       
   117         TRACE1("failed to create socket, err %d", WSAGetLastError());
       
   118         LEAVE(KErrGeneral);
       
   119     }
       
   120 
       
   121     iAddr.iSockAddrIn->sin_family = AF_INET;
       
   122     iAddr.iSockAddrIn->sin_addr.s_addr = inet_addr("127.0.0.1");
       
   123 
       
   124     BEGIN_WIN32();
       
   125     int err = bind(iUnblockSocket, iAddr.iSockAddr, addrSize);
       
   126     END_WIN32();
       
   127 
       
   128     if (err)
       
   129     {
       
   130         TRACE1("failed to bind, err %d", WSAGetLastError());
       
   131         User::Leave(KErrGeneral);
       
   132     }
       
   133 
       
   134     BEGIN_WIN32();
       
   135     err = getsockname(iUnblockSocket, iAddr.iSockAddr, &addrSize);
       
   136     END_WIN32();
       
   137 
       
   138     if (err)
       
   139     {
       
   140         TRACE1("failed to getsockname, err %d", WSAGetLastError());
       
   141         LEAVE(KErrGeneral);
       
   142     }
       
   143 
       
   144     TRACE1("notification socket bound to UDP port %d",
       
   145         (int)ntohs(iAddr.iSockAddrIn->sin_port));
       
   146 
       
   147     // Finally, create and start the thread
       
   148     _LIT(threadName,"WinsockSelectThread");
       
   149     LEAVE_IF_ERROR(iThread.Create(threadName, Run, KDefaultStackSize,
       
   150         NULL, this, EOwnerProcess));
       
   151     iThread.Resume();
       
   152 }
       
   153 
       
   154 // Wakes up the select thread
       
   155 TBool CWinsockSelectThread::Wakeup()
       
   156 {
       
   157     char msg[1] = {0};
       
   158 
       
   159     BEGIN_WIN32();
       
   160     int bytesSent = sendto(iUnblockSocket,msg,sizeof(msg),0,
       
   161         iAddr.iSockAddr, sizeof(struct sockaddr_in));
       
   162     END_WIN32();
       
   163 
       
   164     if (bytesSent == SOCKET_ERROR)
       
   165     {
       
   166         TRACE1("failed to unblock select, err %d", WSAGetLastError());
       
   167         return EFalse;
       
   168     }
       
   169     return ETrue;
       
   170 }
       
   171 
       
   172 TInt CWinsockSelectThread::Submit(MSelectRequest* aRequest)
       
   173 {
       
   174     TInt err = KErrNone;
       
   175     iCriticalSection.Wait();
       
   176     TInt index = iRequests.FindInAddressOrder(aRequest);
       
   177     if (index < 0) err = iRequests.InsertInAddressOrder(aRequest);
       
   178     if (err == KErrNone) Wakeup();
       
   179     iCriticalSection.Signal();
       
   180     return err;
       
   181 }
       
   182 
       
   183 TBool CWinsockSelectThread::Cancel(MSelectRequest* aRequest)
       
   184 {
       
   185     TBool found = EFalse;
       
   186     iCriticalSection.Wait();
       
   187     TInt index = iRequests.FindInAddressOrder(aRequest);
       
   188     if (index >= 0)
       
   189     {
       
   190         iRequests.Remove(index);
       
   191         Wakeup();
       
   192         found = ETrue;
       
   193     }
       
   194     iCriticalSection.Signal();
       
   195     return found;
       
   196 }
       
   197 
       
   198 TInt CWinsockSelectThread::Run(TAny *aPtr)
       
   199 {
       
   200     int err;
       
   201     CWinsockSelectThread* thread = (CWinsockSelectThread*)aPtr;
       
   202     CTrapCleanup* cleanup = CTrapCleanup::New();
       
   203     if (cleanup) {
       
   204         TRAP(err, thread->RunL());
       
   205         delete cleanup;
       
   206     } else {
       
   207         err = KErrNoMemory;
       
   208     }
       
   209     return err;
       
   210 }
       
   211 
       
   212 void CWinsockSelectThread::RunL()
       
   213 {
       
   214     TRACE("started");
       
   215     RPointerArray<MSelectRequest> completedRequests;
       
   216     struct timeval zeroTimeout;
       
   217     Mem::FillZ(&zeroTimeout, sizeof(zeroTimeout));
       
   218 
       
   219     // Open handle to our protocol DLL to prevent it from being unloaded
       
   220     // while this thread is running
       
   221     RLibrary self;
       
   222     TBuf<12> libName; 
       
   223     libName.Copy(KWinsockProtocol);
       
   224     libName.Append(_L(".prt"));
       
   225     VERIFY_SUCCESS(self.Load(libName));
       
   226 
       
   227     for (;;)
       
   228     {
       
   229         TInt i,n,nfd;
       
   230         TUint maxSock;
       
   231 
       
   232         fd_set read_fd_set;
       
   233         fd_set write_fd_set;
       
   234         fd_set except_fd_set;
       
   235 
       
   236         fd_set * readfs = NULL;
       
   237         fd_set * writefs = NULL;
       
   238         fd_set * errfs = NULL;
       
   239         
       
   240 #pragma warning(push)            // FD_SET macro in winsock.h produces warning
       
   241 #pragma warning(disable : 4127)  // C4127: conditional expression is constant
       
   242         
       
   243         // Always listen for notification socket
       
   244         readfs = &read_fd_set;
       
   245         FD_ZERO(readfs);
       
   246         FD_SET(iUnblockSocket,readfs);
       
   247         maxSock = iUnblockSocket;
       
   248 
       
   249         // Construct the fdsets
       
   250         iCriticalSection.Wait();
       
   251         n = iRequests.Count();
       
   252         for (i=0; i<n; i++)
       
   253         {
       
   254             MSelectRequest* req = iRequests[i];
       
   255             TUint sock = req->Socket();
       
   256             TInt requestMask = req->SelectMask();
       
   257             if (maxSock < sock) maxSock = sock;
       
   258             if (requestMask & ESelectRead)
       
   259             {
       
   260                 FD_SET(sock,readfs);
       
   261             }
       
   262             if (requestMask & ESelectWrite)
       
   263             {
       
   264                 if (!writefs)
       
   265                 {
       
   266                     writefs = &write_fd_set;
       
   267                     FD_ZERO(writefs);
       
   268                 }
       
   269                 FD_SET(sock,writefs);
       
   270             }
       
   271             if (requestMask & ESelectError)
       
   272             {
       
   273                 if (!errfs)
       
   274                 {
       
   275                     errfs = &except_fd_set;
       
   276                     FD_ZERO(errfs);
       
   277                 }
       
   278                 FD_SET(sock,errfs);
       
   279             }
       
   280         }
       
   281 
       
   282 #pragma warning(pop) 
       
   283 
       
   284         iCriticalSection.Signal();
       
   285 
       
   286         BEGIN_WIN32();
       
   287         nfd = select(maxSock+1, readfs, writefs, errfs, iTimeout);
       
   288         END_WIN32();
       
   289 
       
   290         if (nfd == SOCKET_ERROR)
       
   291         {
       
   292             TRACE1("select err %d",WSAGetLastError());
       
   293             break;
       
   294         }
       
   295 
       
   296         TBool dataInNotificationSocket = EFalse;
       
   297         if (FD_ISSET(iUnblockSocket, readfs))
       
   298         {
       
   299             // Don't count the notification socket
       
   300             nfd--;
       
   301 
       
   302             // Read one message from the notification socket
       
   303             char msg[1];
       
   304             DEBUG_ONLY(int nbytes = )recv(iUnblockSocket,msg,sizeof(msg),0);
       
   305             ASSERT(nbytes != SOCKET_ERROR);
       
   306             dataInNotificationSocket = ETrue;
       
   307         }
       
   308 
       
   309         if (nfd == 0)
       
   310         {
       
   311             // Timeout or request to rebuild fdsets
       
   312             continue;
       
   313         }
       
   314 
       
   315         iCriticalSection.Wait();
       
   316 
       
   317         // Read all remaining messages from the notification socket
       
   318         // The fact that we are in a critical section guarantees that
       
   319         // we won't get stuck here forever
       
   320         if (dataInNotificationSocket)
       
   321         {
       
   322             char msg[1];
       
   323             fd_set tmp;
       
   324             FD_ZERO(&tmp);
       
   325 #pragma warning(push)            // Ignore warning generated by FD_SET macro
       
   326 #pragma warning(disable : 4127)  // C4127: conditional expression is constant
       
   327             FD_SET(iUnblockSocket,&tmp);
       
   328             BEGIN_WIN32();
       
   329             while (select(iUnblockSocket+1,&tmp,NULL,NULL,&zeroTimeout) == 1)
       
   330             {
       
   331                 if (recv(iUnblockSocket, msg, sizeof(msg), 0) == SOCKET_ERROR)
       
   332                 {
       
   333                     TRACE1("internal recv socket err %d",WSAGetLastError());
       
   334                     break;
       
   335                 }
       
   336                 FD_ZERO(&tmp);
       
   337                 FD_SET(iUnblockSocket,&tmp);
       
   338             }
       
   339             END_WIN32();
       
   340 #pragma warning(pop) 
       
   341         }
       
   342 
       
   343         // Move completed requests to completedRequests queue
       
   344         n = iRequests.Count();
       
   345         for (i=n-1; i>=0 && nfd > 0; i--)
       
   346         {
       
   347             MSelectRequest* req = iRequests[i];
       
   348             TUint sock = req->Socket();
       
   349             TInt requestMask = req->SelectMask();
       
   350             TInt selectMask = 0;
       
   351             if (requestMask & ESelectRead && FD_ISSET(sock, readfs))
       
   352             {
       
   353                 selectMask |= ESelectRead;
       
   354             }
       
   355             if (requestMask & ESelectWrite && FD_ISSET(sock, writefs))
       
   356             {
       
   357                 selectMask |= ESelectRead;
       
   358             }
       
   359             if (requestMask & ESelectError && FD_ISSET(sock, errfs))
       
   360             {
       
   361                 selectMask |= ESelectError;
       
   362             }
       
   363             if (selectMask)
       
   364             {
       
   365                 nfd--;
       
   366                 if (completedRequests.Append(req) == KErrNone)
       
   367                 {
       
   368                     iRequests.Remove(i);
       
   369                 }
       
   370             }
       
   371         }
       
   372 
       
   373         iCriticalSection.Signal();
       
   374 
       
   375         // Fire completion notifications outside of the critical section
       
   376         n = completedRequests.Count();
       
   377         for (i=n-1; i>=0; i--)
       
   378         {
       
   379             MSelectRequest* req = completedRequests[i];
       
   380             TUint sock = req->Socket();
       
   381             TInt requestMask = req->SelectMask();
       
   382             TInt selectMask = 0;
       
   383             if (requestMask & ESelectRead && FD_ISSET(sock, readfs))
       
   384             {
       
   385                 selectMask |= ESelectRead;
       
   386             }
       
   387             if (requestMask & ESelectWrite && FD_ISSET(sock, writefs))
       
   388             {
       
   389                 selectMask |= ESelectRead;
       
   390             }
       
   391             if (requestMask & ESelectError && FD_ISSET(sock, errfs))
       
   392             {
       
   393                 selectMask |= ESelectError;
       
   394             }
       
   395             ASSERT(selectMask);
       
   396             req->SelectComplete(selectMask);
       
   397             completedRequests.Remove(i);
       
   398         }
       
   399     }
       
   400     completedRequests.Reset();
       
   401 }