| /* |
| * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "udp_socket2_manager_windows.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #include "aligned_malloc.h" |
| #include "udp_socket2_windows.h" |
| |
| namespace webrtc { |
| WebRtc_UWord32 UdpSocket2ManagerWindows::_numOfActiveManagers = 0; |
| bool UdpSocket2ManagerWindows::_wsaInit = false; |
| |
| UdpSocket2ManagerWindows::UdpSocket2ManagerWindows() |
| : UdpSocketManager(), |
| _id(-1), |
| _stopped(false), |
| _init(false), |
| _pCrit(CriticalSectionWrapper::CreateCriticalSection()), |
| _ioCompletionHandle(NULL), |
| _numActiveSockets(0), |
| _event(EventWrapper::Create()) |
| { |
| _managerNumber = _numOfActiveManagers++; |
| |
| if(_numOfActiveManagers == 1) |
| { |
| WORD wVersionRequested = MAKEWORD(2, 2); |
| WSADATA wsaData; |
| _wsaInit = WSAStartup(wVersionRequested, &wsaData) == 0; |
| // TODO (hellner): seems safer to use RAII for this. E.g. what happens |
| // if a UdpSocket2ManagerWindows() created and destroyed |
| // without being initialized. |
| } |
| } |
| |
| UdpSocket2ManagerWindows::~UdpSocket2ManagerWindows() |
| { |
| WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, |
| "UdpSocket2ManagerWindows(%d)::~UdpSocket2ManagerWindows()", |
| _managerNumber); |
| |
| if(_init) |
| { |
| _pCrit->Enter(); |
| if(_numActiveSockets) |
| { |
| _pCrit->Leave(); |
| _event->Wait(INFINITE); |
| } |
| else |
| { |
| _pCrit->Leave(); |
| } |
| StopWorkerThreads(); |
| |
| // All threads are stopped. Safe to delete them. |
| ListItem* pItem = NULL; |
| while((pItem = _workerThreadsList.First()) != NULL) |
| { |
| delete static_cast<UdpSocket2WorkerWindows*>(pItem->GetItem()); |
| _workerThreadsList.PopFront(); |
| } |
| |
| _ioContextPool.Free(); |
| |
| _numOfActiveManagers--; |
| if(_ioCompletionHandle) |
| { |
| CloseHandle(_ioCompletionHandle); |
| } |
| if (_numOfActiveManagers == 0) |
| { |
| if(_wsaInit) |
| { |
| WSACleanup(); |
| } |
| } |
| } |
| if(_pCrit) |
| { |
| delete _pCrit; |
| } |
| if(_event) |
| { |
| delete _event; |
| } |
| } |
| |
| bool UdpSocket2ManagerWindows::Init(WebRtc_Word32 id, |
| WebRtc_UWord8& numOfWorkThreads) { |
| CriticalSectionScoped cs(_pCrit); |
| if ((_id != -1) || (_numOfWorkThreads != 0)) { |
| assert(_id != -1); |
| assert(_numOfWorkThreads != 0); |
| return false; |
| } |
| _id = id; |
| _numOfWorkThreads = numOfWorkThreads; |
| return true; |
| } |
| |
| WebRtc_Word32 UdpSocket2ManagerWindows::ChangeUniqueId(const WebRtc_Word32 id) |
| { |
| _id = id; |
| return 0; |
| } |
| |
| bool UdpSocket2ManagerWindows::Start() |
| { |
| WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, |
| "UdpSocket2ManagerWindows(%d)::Start()",_managerNumber); |
| if(!_init) |
| { |
| StartWorkerThreads(); |
| } |
| |
| if(!_init) |
| { |
| return false; |
| } |
| _pCrit->Enter(); |
| // Start worker threads. |
| _stopped = false; |
| WebRtc_Word32 i = 0; |
| WebRtc_Word32 error = 0; |
| ListItem* pItem = _workerThreadsList.First(); |
| UdpSocket2WorkerWindows* pWorker; |
| while(pItem != NULL && !error) |
| { |
| pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); |
| if(!pWorker->Start()) |
| error = 1; |
| pItem = _workerThreadsList.Next(pItem); |
| } |
| if(error) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::Start() error starting worker\ |
| threads", |
| _managerNumber); |
| _pCrit->Leave(); |
| return false; |
| } |
| _pCrit->Leave(); |
| return true; |
| } |
| |
| bool UdpSocket2ManagerWindows::StartWorkerThreads() |
| { |
| if(!_init) |
| { |
| _pCrit->Enter(); |
| |
| _ioCompletionHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, |
| 0, 0); |
| if(_ioCompletionHandle == NULL) |
| { |
| WebRtc_Word32 error = GetLastError(); |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::StartWorkerThreads()" |
| "_ioCompletioHandle == NULL: error:%d", |
| _managerNumber,error); |
| _pCrit->Leave(); |
| return false; |
| } |
| |
| // Create worker threads. |
| WebRtc_UWord32 i = 0; |
| bool error = false; |
| while(i < _numOfWorkThreads && !error) |
| { |
| UdpSocket2WorkerWindows* pWorker = |
| new UdpSocket2WorkerWindows(_ioCompletionHandle); |
| if(pWorker->Init() != 0) |
| { |
| error = true; |
| delete pWorker; |
| break; |
| } |
| _workerThreadsList.PushFront(pWorker); |
| i++; |
| } |
| if(error) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::StartWorkerThreads() error " |
| "creating work threads", |
| _managerNumber); |
| // Delete worker threads. |
| ListItem* pItem = NULL; |
| while((pItem = _workerThreadsList.First()) != NULL) |
| { |
| delete static_cast<UdpSocket2WorkerWindows*>(pItem->GetItem()); |
| _workerThreadsList.PopFront(); |
| } |
| _pCrit->Leave(); |
| return false; |
| } |
| if(_ioContextPool.Init()) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::StartWorkerThreads() error " |
| "initiating _ioContextPool", |
| _managerNumber); |
| _pCrit->Leave(); |
| return false; |
| } |
| _init = true; |
| WEBRTC_TRACE( |
| kTraceDebug, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows::StartWorkerThreads %d number of work " |
| "threads created and initialized", |
| _numOfWorkThreads); |
| _pCrit->Leave(); |
| } |
| return true; |
| } |
| |
| bool UdpSocket2ManagerWindows::Stop() |
| { |
| WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, |
| "UdpSocket2ManagerWindows(%d)::Stop()",_managerNumber); |
| |
| if(!_init) |
| { |
| return false; |
| } |
| _pCrit->Enter(); |
| _stopped = true; |
| if(_numActiveSockets) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::Stop() there is still active\ |
| sockets", |
| _managerNumber); |
| _pCrit->Leave(); |
| return false; |
| } |
| // No active sockets. Stop all worker threads. |
| bool result = StopWorkerThreads(); |
| _pCrit->Leave(); |
| return result; |
| } |
| |
| bool UdpSocket2ManagerWindows::StopWorkerThreads() |
| { |
| WebRtc_Word32 error = 0; |
| WEBRTC_TRACE( |
| kTraceDebug, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() Worker\ |
| threadsStoped, numActicve Sockets=%d", |
| _managerNumber, |
| _numActiveSockets); |
| UdpSocket2WorkerWindows* pWorker; |
| ListItem* pItem = _workerThreadsList.First(); |
| |
| // Set worker threads to not alive so that they will stop calling |
| // UdpSocket2WorkerWindows::Run(). |
| while(pItem != NULL) |
| { |
| pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); |
| pWorker->SetNotAlive(); |
| pItem = _workerThreadsList.Next(pItem); |
| } |
| // Release all threads waiting for GetQueuedCompletionStatus(..). |
| if(_ioCompletionHandle) |
| { |
| WebRtc_UWord32 i = 0; |
| for(i = 0; i < _workerThreadsList.GetSize(); i++) |
| { |
| PostQueuedCompletionStatus(_ioCompletionHandle, 0 ,0 , NULL); |
| } |
| } |
| pItem = _workerThreadsList.First(); |
| |
| while(pItem != NULL) |
| { |
| pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); |
| if(pWorker->Stop() == false) |
| { |
| error = -1; |
| WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, |
| "failed to stop worker thread"); |
| } |
| pItem = _workerThreadsList.Next(pItem); |
| } |
| |
| if(error) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() error stopping\ |
| worker threads", |
| _managerNumber); |
| return false; |
| } |
| return true; |
| } |
| |
| bool UdpSocket2ManagerWindows::AddSocketPrv(UdpSocket2Windows* s) |
| { |
| WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, |
| "UdpSocket2ManagerWindows(%d)::AddSocketPrv()",_managerNumber); |
| if(!_init) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::AddSocketPrv() manager not\ |
| initialized", |
| _managerNumber); |
| return false; |
| } |
| _pCrit->Enter(); |
| if(s == NULL) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket == NULL", |
| _managerNumber); |
| _pCrit->Leave(); |
| return false; |
| } |
| if(s->GetFd() == NULL || s->GetFd() == INVALID_SOCKET) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket->GetFd() ==\ |
| %d", |
| _managerNumber, |
| (WebRtc_Word32)s->GetFd()); |
| _pCrit->Leave(); |
| return false; |
| |
| } |
| _ioCompletionHandle = CreateIoCompletionPort((HANDLE)s->GetFd(), |
| _ioCompletionHandle, |
| (ULONG_PTR)(s), 0); |
| if(_ioCompletionHandle == NULL) |
| { |
| WebRtc_Word32 error = GetLastError(); |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::AddSocketPrv() Error adding to IO\ |
| completion: %d", |
| _managerNumber, |
| error); |
| _pCrit->Leave(); |
| return false; |
| } |
| _numActiveSockets++; |
| _pCrit->Leave(); |
| return true; |
| } |
| bool UdpSocket2ManagerWindows::RemoveSocketPrv(UdpSocket2Windows* s) |
| { |
| if(!_init) |
| { |
| return false; |
| } |
| _pCrit->Enter(); |
| _numActiveSockets--; |
| if(_numActiveSockets == 0) |
| { |
| _event->Set(); |
| } |
| _pCrit->Leave(); |
| return true; |
| } |
| |
| PerIoContext* UdpSocket2ManagerWindows::PopIoContext() |
| { |
| if(!_init) |
| { |
| return NULL; |
| } |
| |
| PerIoContext* pIoC = NULL; |
| if(!_stopped) |
| { |
| pIoC = _ioContextPool.PopIoContext(); |
| }else |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| _id, |
| "UdpSocket2ManagerWindows(%d)::PopIoContext() Manager Not started", |
| _managerNumber); |
| } |
| return pIoC; |
| } |
| |
| WebRtc_Word32 UdpSocket2ManagerWindows::PushIoContext(PerIoContext* pIoContext) |
| { |
| return _ioContextPool.PushIoContext(pIoContext); |
| } |
| |
| IoContextPool::IoContextPool() |
| : _pListHead(NULL), |
| _init(false), |
| _size(0), |
| _inUse(0) |
| { |
| } |
| |
| IoContextPool::~IoContextPool() |
| { |
| Free(); |
| assert(_size.Value() == 0); |
| AlignedFree(_pListHead); |
| } |
| |
| WebRtc_Word32 IoContextPool::Init(WebRtc_UWord32 /*increaseSize*/) |
| { |
| if(_init) |
| { |
| return 0; |
| } |
| |
| _pListHead = (PSLIST_HEADER)AlignedMalloc(sizeof(SLIST_HEADER), |
| MEMORY_ALLOCATION_ALIGNMENT); |
| if(_pListHead == NULL) |
| { |
| return -1; |
| } |
| InitializeSListHead(_pListHead); |
| _init = true; |
| return 0; |
| } |
| |
| PerIoContext* IoContextPool::PopIoContext() |
| { |
| if(!_init) |
| { |
| return NULL; |
| } |
| |
| PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); |
| if(pListEntry == NULL) |
| { |
| IoContextPoolItem* item = (IoContextPoolItem*) |
| AlignedMalloc( |
| sizeof(IoContextPoolItem), |
| MEMORY_ALLOCATION_ALIGNMENT); |
| if(item == NULL) |
| { |
| return NULL; |
| } |
| memset(&item->payload.ioContext,0,sizeof(PerIoContext)); |
| item->payload.base = item; |
| pListEntry = &(item->itemEntry); |
| ++_size; |
| } |
| ++_inUse; |
| return &((IoContextPoolItem*)pListEntry)->payload.ioContext; |
| } |
| |
| WebRtc_Word32 IoContextPool::PushIoContext(PerIoContext* pIoContext) |
| { |
| // TODO (hellner): Overlapped IO should be completed at this point. Perhaps |
| // add an assert? |
| const bool overlappedIOCompleted = HasOverlappedIoCompleted( |
| (LPOVERLAPPED)pIoContext); |
| |
| IoContextPoolItem* item = ((IoContextPoolItemPayload*)pIoContext)->base; |
| |
| const WebRtc_Word32 usedItems = --_inUse; |
| const WebRtc_Word32 totalItems = _size.Value(); |
| const WebRtc_Word32 freeItems = totalItems - usedItems; |
| if(freeItems < 0) |
| { |
| assert(false); |
| AlignedFree(item); |
| return -1; |
| } |
| if((freeItems >= totalItems>>1) && |
| overlappedIOCompleted) |
| { |
| AlignedFree(item); |
| --_size; |
| return 0; |
| } |
| InterlockedPushEntrySList(_pListHead, &(item->itemEntry)); |
| return 0; |
| } |
| |
| WebRtc_Word32 IoContextPool::Free() |
| { |
| if(!_init) |
| { |
| return 0; |
| } |
| |
| WebRtc_Word32 itemsFreed = 0; |
| PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); |
| while(pListEntry != NULL) |
| { |
| IoContextPoolItem* item = ((IoContextPoolItem*)pListEntry); |
| AlignedFree(item); |
| --_size; |
| itemsFreed++; |
| pListEntry = InterlockedPopEntrySList(_pListHead); |
| } |
| return itemsFreed; |
| } |
| |
| WebRtc_Word32 UdpSocket2WorkerWindows::_numOfWorkers = 0; |
| |
| UdpSocket2WorkerWindows::UdpSocket2WorkerWindows(HANDLE ioCompletionHandle) |
| : _ioCompletionHandle(ioCompletionHandle), |
| _pThread(NULL), |
| _init(false) |
| { |
| _workerNumber = _numOfWorkers++; |
| WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, |
| "UdpSocket2WorkerWindows created"); |
| } |
| |
| UdpSocket2WorkerWindows::~UdpSocket2WorkerWindows() |
| { |
| if(_pThread) |
| { |
| delete _pThread; |
| } |
| WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, |
| "UdpSocket2WorkerWindows deleted"); |
| } |
| |
| bool UdpSocket2WorkerWindows::Start() |
| { |
| unsigned int id = 0; |
| WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, |
| "Start UdpSocket2WorkerWindows"); |
| return _pThread->Start(id); |
| } |
| |
| bool UdpSocket2WorkerWindows::Stop() |
| { |
| WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, |
| "Stop UdpSocket2WorkerWindows"); |
| return _pThread->Stop(); |
| } |
| |
| void UdpSocket2WorkerWindows::SetNotAlive() |
| { |
| WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, |
| "SetNotAlive UdpSocket2WorkerWindows"); |
| _pThread->SetNotAlive(); |
| } |
| |
| WebRtc_Word32 UdpSocket2WorkerWindows::Init() |
| { |
| if(!_init) |
| { |
| const WebRtc_Word8* threadName = "UdpSocket2ManagerWindows_thread"; |
| _pThread = ThreadWrapper::CreateThread(Run, this, kRealtimePriority, |
| threadName); |
| if(_pThread == NULL) |
| { |
| WEBRTC_TRACE( |
| kTraceError, |
| kTraceTransport, |
| -1, |
| "UdpSocket2WorkerWindows(%d)::Init(), error creating thread!", |
| _workerNumber); |
| return -1; |
| } |
| _init = true; |
| } |
| return 0; |
| } |
| |
| bool UdpSocket2WorkerWindows::Run(ThreadObj obj) |
| { |
| UdpSocket2WorkerWindows* pWorker = |
| static_cast<UdpSocket2WorkerWindows*>(obj); |
| return pWorker->Process(); |
| } |
| |
| // Process should always return true. Stopping the worker threads is done in |
| // the UdpSocket2ManagerWindows::StopWorkerThreads() function. |
| bool UdpSocket2WorkerWindows::Process() |
| { |
| WebRtc_Word32 success = 0; |
| DWORD ioSize = 0; |
| UdpSocket2Windows* pSocket = NULL; |
| PerIoContext* pIOContext = 0; |
| OVERLAPPED* pOverlapped = 0; |
| success = GetQueuedCompletionStatus(_ioCompletionHandle, |
| &ioSize, |
| (ULONG_PTR*)&pSocket, &pOverlapped, 200); |
| |
| WebRtc_UWord32 error = 0; |
| if(!success) |
| { |
| error = GetLastError(); |
| if(error == WAIT_TIMEOUT) |
| { |
| return true; |
| } |
| // This may happen if e.g. PostQueuedCompletionStatus() has been called. |
| // The IO context still needs to be reclaimed or re-used which is done |
| // in UdpSocket2Windows::IOCompleted(..). |
| } |
| if(pSocket == NULL) |
| { |
| WEBRTC_TRACE( |
| kTraceDebug, |
| kTraceTransport, |
| -1, |
| "UdpSocket2WorkerWindows(%d)::Process(), pSocket == 0, end thread", |
| _workerNumber); |
| return true; |
| } |
| pIOContext = (PerIoContext*)pOverlapped; |
| pSocket->IOCompleted(pIOContext,ioSize,error); |
| return true; |
| } |
| } // namespace webrtc |