From a9048c21ce478e5f6ce8549488f59b764dbdd015 Mon Sep 17 00:00:00 2001 From: tzachid Date: Mon, 19 Dec 2005 09:03:04 +0000 Subject: [PATCH] [SDP] Fixed a race in which data was not promised to be sent on shutdown. (rev 825) git-svn-id: svn://openib.tc.cornell.edu/gen1@207 ad392aa1-c5ef-ae45-8dd8-e69d62a5ef86 --- trunk/ulp/sdp/kernel/SdpBufferPool.cpp | 33 +- trunk/ulp/sdp/kernel/SdpBufferPool.h | 9 +- trunk/ulp/sdp/kernel/SdpRecvPool.cpp | 14 +- trunk/ulp/sdp/kernel/SdpRecvPool.h | 11 +- trunk/ulp/sdp/kernel/SdpSocket.cpp | 59 +- trunk/ulp/sdp/kernel/SdpSocket.h | 2 + trunk/ulp/sdp/kernel/SdpUserFile.cpp | 5 +- trunk/ulp/sdp/tests/basic/SdpConnect.cpp | 1109 +++++++++++----------- 8 files changed, 658 insertions(+), 584 deletions(-) diff --git a/trunk/ulp/sdp/kernel/SdpBufferPool.cpp b/trunk/ulp/sdp/kernel/SdpBufferPool.cpp index f8ccc62b..17c93df3 100644 --- a/trunk/ulp/sdp/kernel/SdpBufferPool.cpp +++ b/trunk/ulp/sdp/kernel/SdpBufferPool.cpp @@ -112,6 +112,10 @@ BufferPool::BufferPool() m_CreditdBufferDescriptor = NULL; m_pSdpSocket = NULL; + + m_NumberOfBytesSent = 0; + m_NumberOfBytesSentAndAcked = 0; + } NTSTATUS @@ -283,7 +287,7 @@ BufferPool::ReturnBuffer(BufferDescriptor *pBufferDescriptor) if ((RemainingToCopy(pIrp) < m_MaxMessageSize * m_FreePackets.Size())) { rc = m_pSdpSocket->RequestCallBack(); if (!NT_SUCCESS(rc)) { - SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("PostCredits failed rc = 0x%x\n", rc )); + SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("RequestCallBack failed rc = 0x%x\n", rc )); goto Cleanup; } m_CallBackPending = true; @@ -295,9 +299,21 @@ BufferPool::ReturnBuffer(BufferDescriptor *pBufferDescriptor) m_pSdpSocket->DisconectSentEvent(); } } + /* + We allow buffers to be sent here since it is possible that the + socket is already closed, and the user thread is not present. + */ + rc = SendBuffersIfCan(); + if (!NT_SUCCESS(rc)) { + SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("SendBuffersIfCan failed rc = 0x%x\n", rc )); + goto Cleanup; + } + + Cleanup: ASSERT(m_CurrentlySentBuffers != 0); m_CurrentlySentBuffers--; + return rc; } @@ -493,11 +509,19 @@ Cleanup: VOID BufferPool::CloseSocket() { + LIST_ENTRY *item = NULL; + IRP *pIrp = NULL; + SDP_PRINT(SDP_TRACE, SDP_BUFFER_POOL, ("this = 0x%p \n")); AssertLocked(); - - //??? Should we do something here - + // All IRP's that were not compleated, will be compleated as cancelled + while (m_UserPackets.Size() > 0 ) { + item = m_UserPackets.RemoveHeadList(); + pIrp = CONTAINING_RECORD(item, IRP ,Tail.Overlay.ListEntry); + pIrp->IoStatus.Status = STATUS_CANCELLED; + pIrp->IoStatus.Information = 0; + IoCompleteRequest (pIrp, IO_NO_INCREMENT); + } } @@ -592,6 +616,7 @@ BufferPool::SendBuffer(BufferDescriptor *pBufferDescriptor) rc = IB2Status(ib_status); goto Cleanup; } + m_NumberOfBytesSent++; //????? Should we clear the post credits here ???????? m_CurrentlySentBuffers ++; m_rRecvBuf--; diff --git a/trunk/ulp/sdp/kernel/SdpBufferPool.h b/trunk/ulp/sdp/kernel/SdpBufferPool.h index f7f79898..bfa8a8bb 100644 --- a/trunk/ulp/sdp/kernel/SdpBufferPool.h +++ b/trunk/ulp/sdp/kernel/SdpBufferPool.h @@ -256,7 +256,7 @@ public: VOID SetRemoteRecvBuf (uint16_t rRecvBuf) { if (m_rRecvBuf == 2) { - SDP_PRINT(SDP_ALL, SDP_BUFFER_POOL,("m_rRecvBuf = %d, it is being set to %d seqnum = %d\n", m_rRecvBuf, rRecvBuf, m_SendSeq)); + SDP_PRINT(SDP_DEBUG, SDP_BUFFER_POOL,("m_rRecvBuf = %d, it is being set to %d seqnum = %d\n", m_rRecvBuf, rRecvBuf, m_SendSeq)); } ASSERT(rRecvBuf < 1000); m_rRecvBuf = rRecvBuf; @@ -318,6 +318,13 @@ private: //????????? bool m_CallBackPending; // Set to true if we have requesetd a callback from // the users thread + // TODO: The two counters bellow are for debug only. move them to be + // declared as such +public: + uint32_t m_NumberOfBytesSent; + uint32_t m_NumberOfBytesSentAndAcked; + + VOID AssertLocked(); }; diff --git a/trunk/ulp/sdp/kernel/SdpRecvPool.cpp b/trunk/ulp/sdp/kernel/SdpRecvPool.cpp index f0442e4c..333f7f48 100644 --- a/trunk/ulp/sdp/kernel/SdpRecvPool.cpp +++ b/trunk/ulp/sdp/kernel/SdpRecvPool.cpp @@ -42,6 +42,8 @@ RecvPool::RecvPool() m_DisConnRecieved = false; m_LocaleAdvertisedBuffers = 0; m_pSdpSocket = NULL; + m_NumberOfBuffersRecievedWithError = 0; + m_NumberOfBuffersRecievedSuccessfully = 0; } NTSTATUS @@ -55,7 +57,7 @@ RecvPool::Init( SdpSocket *pSdpSocket ) { - SDP_PRINT(SDP_TRACE, SDP_BUFFER_POOL, ("this = 0x%p \n", m_pSdpSocket)); + SDP_PRINT(SDP_TRACE, SDP_BUFFER_POOL, ("this = 0x%p \n", pSdpSocket)); m_MaxBuffers = MaxBuffers; m_MaxConcurrentRecieves = MaxConcurrentRecieves; m_MaxMessageSize = MaxMessageSize; @@ -117,8 +119,11 @@ RecvPool::RecievedBuffer(BufferDescriptor *pBufferDescriptor, bool error) if (error) { // Not much that we can do in this case (only return the packet) m_FreePackets.InsertTailList(&pBufferDescriptor->BuffersList); + m_NumberOfBuffersRecievedWithError++; goto Cleanup; } + ASSERT(m_NumberOfBuffersRecievedWithError == 0); + m_NumberOfBuffersRecievedSuccessfully++; // We have recieved a "RAW" buffer, we have to make sure that the buffer // descriptor is OK. @@ -175,7 +180,7 @@ RecvPool::RecievedBuffer(BufferDescriptor *pBufferDescriptor, bool error) } else { // This is an empty buffer ASSERT(pHeader->size == sizeof msg_hdr_bsdh); - m_FreePackets.InsertTailList(&pBufferDescriptor->BuffersList); + m_FreePackets.InsertTailList(&pBufferDescriptor->BuffersList); } } @@ -206,8 +211,7 @@ RecvPool::RecievedBuffer(BufferDescriptor *pBufferDescriptor, bool error) SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("RecieveIfCan failed rc = 0x%x\n", rc )); goto Cleanup; } - -Cleanup: +Cleanup: return rc; } @@ -261,7 +265,7 @@ RecvPool::GetData( m_ClientBeingServed = true; } - // Can we supply data to the userd right now ? + // Can we supply data to the user right now ? while (*Copied < CopySize) { if (m_FullPackets.Size()) { // We have a buffer, we can use it to copy data to the user diff --git a/trunk/ulp/sdp/kernel/SdpRecvPool.h b/trunk/ulp/sdp/kernel/SdpRecvPool.h index 38528052..40e0c44b 100644 --- a/trunk/ulp/sdp/kernel/SdpRecvPool.h +++ b/trunk/ulp/sdp/kernel/SdpRecvPool.h @@ -118,9 +118,16 @@ private: SdpSocket *m_pSdpSocket; // This signals that the remote side will not be sending any data any more - bool m_DisConnRecieved; + bool m_DisConnRecieved; -VOID AssertLocked(); + // TODO: The two counters bellow are for debug only. move them to be + // declared as such + public: + uint32_t m_NumberOfBuffersRecievedWithError; + uint32_t m_NumberOfBuffersRecievedSuccessfully; + + + VOID AssertLocked(); }; diff --git a/trunk/ulp/sdp/kernel/SdpSocket.cpp b/trunk/ulp/sdp/kernel/SdpSocket.cpp index bf0d37e5..65118007 100644 --- a/trunk/ulp/sdp/kernel/SdpSocket.cpp +++ b/trunk/ulp/sdp/kernel/SdpSocket.cpp @@ -1097,13 +1097,6 @@ SdpSocket::WSPCloseSocket( SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("this = 0x%p state = %s \n",this, SS2String(m_state))); OBJECT_ATTRIBUTES attr; HANDLE ThreadHandle; - bool sleep = false; -restart: - - if (sleep) { - Sleep(1*1000*1000);//??????? - } - sleep = true; if (!m_Lock.Lock()) { SDP_PRINT(SDP_ERR, SDP_SOCKET, ("Failed to lock this = 0x%p \n",this)); @@ -1121,19 +1114,13 @@ restart: goto Cleanup; } - - //????????? - if (m_SendBufferPool.m_UserPackets.Size() > 0) { - m_Lock.Unlock(); - goto restart; - } - // This will force that no more calls will be allowed ASSERT(m_CloseSocketCalled == FALSE); // If this is not the case // We shouldn't be able to take the lock m_CloseSocketCalled = true; // notify to all "subclients" to free all waiting clients + // and also return cancell on all IRPs m_RecvBufferPool.CloseSocket(); m_SendBufferPool.CloseSocket(); m_ConnectionList.CloseSocket(); @@ -1231,6 +1218,7 @@ Cleanup: VOID SdpSocket::DisconectSentEvent() { + SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("this = 0x%p\n",this)); KeSetEvent( &m_DisconectSentEvent, IO_NO_INCREMENT, FALSE ); } @@ -1257,7 +1245,7 @@ VOID SdpSocket::CloseSocketThread() ); if (rc == STATUS_TIMEOUT) { - SDP_PRINT(SDP_ERR, SDP_SOCKET, ("Wait failed with time out\n")); + SDP_PRINT(SDP_ERR, SDP_SOCKET, ("Wait failed with time out this = 0x%p\n", this)); } ASSERT(NT_SUCCESS(rc)); @@ -1857,7 +1845,7 @@ SdpSocket::__recv_cb1( IN const ib_cq_handle_t h_cq, IN void *cq_context ) { - SDP_PRINT(SDP_ALL, SDP_SOCKET, ("__recv_cb1\n")); + SDP_PRINT(SDP_DEBUG, SDP_SOCKET, ("__recv_cb1\n")); SdpSocket *pSocket = (SdpSocket *) cq_context; pSocket->m_Lock.SignalCB(RECV_CB_CALLED); @@ -2033,6 +2021,8 @@ NTSTATUS SdpSocket::send_cb() { ASSERT( p_wc->wc_type == IB_WC_SEND ); pBufferDescriptor = (BufferDescriptor*)(uintn_t)p_wc->wr_id; + m_SendBufferPool.m_NumberOfBytesSentAndAcked++; + rc2 = m_SendBufferPool.ReturnBuffer(pBufferDescriptor); UpdateRc(&rc1, rc2); // We remember the error here and continue to avoid leaks switch( p_wc->status ) @@ -2429,7 +2419,7 @@ VOID WaitForShutdownEvent(KEVENT *ShutdownCompleteEvent) ); if (rc == STATUS_TIMEOUT) { - SDP_PRINT(SDP_ERR, SDP_SOCKET, ("Wait failed with time out\n")); + SDP_PRINT(SDP_ERR, SDP_SOCKET, ("Wait failed with time out \n")); ASSERT(FALSE); } @@ -2439,17 +2429,46 @@ VOID WaitForShutdownEvent(KEVENT *ShutdownCompleteEvent) KeClearEvent(ShutdownCompleteEvent); } +/* + This function is called when the usercallback thread has detected + that the user thread is dead, or when we have a IRP_MJ_CLEANUP. + In this case, if CloseSocket was called, we don't do anything + (since there is alreadya thread that is garantied to close it) and on + all other cases, we close it abortively. +*/ + +VOID SdpSocket::DyingProcessDetected() +{ + SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("called this = 0x%p\n", this)); + m_Lock.Lock(true); //????? verify must succeed + if (m_CloseSocketCalled) { + SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("this = 0x%p m_CloseSocketCalled, letting it finish his job\n", this)); + m_Lock.Unlock(); // Error is ignored since this is already + // shutdown call + return; + } + m_Lock.Unlock(); // Error is ignored since this is already + // shutdown call + Shutdown(); +} + VOID SdpSocket::Shutdown() { - SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("called this = 0x%p\n", this)); + SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("called this = 0x%p\n", this)); + SDP_PRINT(SDP_ERR, SDP_SOCKET, ("this = 0x%p m_NumberOfBytesSent = %d m_NumberOfBytesSentAndAcked = %d " + "m_NumberOfBuffersRecievedSuccessfully = %d, m_NumberOfBuffersRecievedWithError = %d\n", + this, m_SendBufferPool.m_NumberOfBytesSent, m_SendBufferPool.m_NumberOfBytesSentAndAcked, + m_RecvBufferPool.m_NumberOfBuffersRecievedSuccessfully, m_RecvBufferPool.m_NumberOfBuffersRecievedWithError)); + NTSTATUS rc = STATUS_SUCCESS; ib_api_status_t ib_status; ib_qp_mod_t qp_mod; - if (m_ShutdownCalled) { // Since this variable is always changing from false to true - // we can check it without the lock + // we can check it without the lock. Since there is no memory + // barier, this test is not true for multiple enviorments, but it + // still makes the shutodwn reentrent return; } // now take the lock and test again diff --git a/trunk/ulp/sdp/kernel/SdpSocket.h b/trunk/ulp/sdp/kernel/SdpSocket.h index 3b1dbf43..b44c36cc 100644 --- a/trunk/ulp/sdp/kernel/SdpSocket.h +++ b/trunk/ulp/sdp/kernel/SdpSocket.h @@ -223,6 +223,8 @@ public: WspSocketCloseOut *pWspSocketCloseOut ); + VOID DyingProcessDetected(); + VOID Shutdown(); static VOID ShutdownCB(VOID* pContext); diff --git a/trunk/ulp/sdp/kernel/SdpUserFile.cpp b/trunk/ulp/sdp/kernel/SdpUserFile.cpp index 5a62b452..e4932cc7 100644 --- a/trunk/ulp/sdp/kernel/SdpUserFile.cpp +++ b/trunk/ulp/sdp/kernel/SdpUserFile.cpp @@ -81,7 +81,9 @@ void SdpUserFile::Shutdown() // Don't call release with the lock being hold Lock.Unlock(); - pSdpSocket->Shutdown(); + // We currently call this both when the process is dead as well as + // when the device is closed + pSdpSocket->DyingProcessDetected(); pSdpSocket->Release(); // It seems that we shoule be protected by the m_shutdown // flag, but will take the lock just in case @@ -227,7 +229,6 @@ SdpUserFile::UsersThread() pSdpSocket = CONTAINING_RECORD(item, SdpSocket , m_CallBackRequestList); Lock.Unlock(); - // Do the call back if (!ShutdownCalled) { pSdpSocket->UsersThreadCallBack(true); diff --git a/trunk/ulp/sdp/tests/basic/SdpConnect.cpp b/trunk/ulp/sdp/tests/basic/SdpConnect.cpp index c0cdcf6d..257db86b 100644 --- a/trunk/ulp/sdp/tests/basic/SdpConnect.cpp +++ b/trunk/ulp/sdp/tests/basic/SdpConnect.cpp @@ -1,550 +1,559 @@ - -#ifdef _WIN32 -#include -#include -#include -#include - -#define AF_INET_FAMILY PF_INET - -#define sleep(x) Sleep(1000 * (x)); -#define CloseSocket closesocket - -#define GetTimeMs() GetTickCount() -#else - -#define AF_INET_FAMILY 27 - -#include -#include -#include -#include -#include - -#define _stricmp strcasecmp -#define WSAGetLastError() errno -#define CloseSocket close - - -#endif -// Windows linux - -const int NumberOfThreads = 1; - -HANDLE g_ComplitionPort; -HANDLE *g_pThreads; - -#define ASSERT assert - -struct sockaddr addressFromString(char *address, int port) -{ - unsigned int b1,b2,b3,b4; - - struct sockaddr_in socket_address; - struct sockaddr *socket_address_ptr = (struct sockaddr*)&socket_address; - - memset(&socket_address, 0, sizeof(socket_address)); - socket_address.sin_family = AF_INET; - socket_address.sin_port = htons((u_short)port); - - - sscanf(address, "%d.%d.%d.%d",&b1, &b2, &b3, &b4); - socket_address.sin_addr.s_addr = b4 * 256 * 256 *256 + - b3 * 256 * 256 + - b2 * 256 + - b1; - - return *socket_address_ptr; -} - - -struct OverlappedSend { - OVERLAPPED Overlapped; - SOCKET Socket; - void *Buffer; // The data to send - int DataSize; - DWORD NumberOfBytesSent; - int RemainingSends; - int SendsInAir; // Curently sent data - CRITICAL_SECTION Lock; - HANDLE SendDoneEvent; - -}; - - -DWORD WINAPI WorkerThreadFunc( LPVOID lpParam ) -{ - BOOL ret; - DWORD NumberOfBytes; - ULONG_PTR CompletionKey; - OVERLAPPED* lpOverlapped; - OverlappedSend *pOverLappedSend; - BOOL ContinueLoop = true; - int iRet; - - while (TRUE) { - ret = GetQueuedCompletionStatus( - g_ComplitionPort, - &NumberOfBytes, - &CompletionKey, - &lpOverlapped, - INFINITE); - ASSERT(ret != 0); - pOverLappedSend = CONTAINING_RECORD(lpOverlapped, OverlappedSend, Overlapped); - // Work on the object itself: - EnterCriticalSection(&pOverLappedSend->Lock); - pOverLappedSend->SendsInAir--; - ASSERT(pOverLappedSend->SendsInAir ==0); // Only one thread - if (pOverLappedSend->RemainingSends > 0) { - // do the next send - WSABUF Buffers; - Buffers.len = pOverLappedSend->DataSize; - Buffers.buf = (char *) pOverLappedSend->Buffer; - pOverLappedSend->SendsInAir++; - pOverLappedSend->RemainingSends--; - - iRet = WSASend( - pOverLappedSend->Socket, - &Buffers, - 1, - &pOverLappedSend->NumberOfBytesSent, - 0, - &pOverLappedSend->Overlapped, - NULL - ); - ASSERT((iRet ==0) || (iRet == SOCKET_ERROR && WSAGetLastError() == WSA_IO_PENDING )); - - } else { - // Nothing more to send - signal compleation and exit - SetEvent(pOverLappedSend->SendDoneEvent); - ContinueLoop = false; - } - LeaveCriticalSection(&pOverLappedSend->Lock); - if (!ContinueLoop) { - break; - } - - } - return 0; -} - -void CreateComplitionPort(int newfd) -{ - g_pThreads = new HANDLE [NumberOfThreads]; - ASSERT(g_pThreads != NULL); - - g_ComplitionPort = CreateIoCompletionPort((HANDLE)newfd, NULL, NULL, 2); - if(g_ComplitionPort == NULL) { - printf("Create complition port failed err=%d", GetLastError()); - exit(1); - } - - // Create the threads that will work on the complitions - g_pThreads[0] = CreateThread(NULL, 0 ,WorkerThreadFunc ,NULL, 0,NULL ); - - -} - -void CloseComplitionPort() -{ - //??? Post a complition thread end message - - WaitForMultipleObjects(NumberOfThreads,g_pThreads, TRUE, INFINITE); - CloseHandle(g_ComplitionPort); - -} - -void SendDataOverlapped(int newfd, double NumberOfBuffers, double BufferSize, BOOL SendPacketNumber) -{ - // We will start with one operations simultaniously - int i ; - double d; - VOID *buffer = NULL; - double elapsed, acc = 0; - WSAOVERLAPPED wsa; - OverlappedSend *pOverLappedSend = NULL; - BOOL ret; - memset(&wsa, 0, sizeof wsa); - - buffer = malloc ((size_t)BufferSize); - if (buffer == NULL) { - printf("Error allocating buffer\n"); - exit(1); - } - - CreateComplitionPort(newfd); - - pOverLappedSend = new OverlappedSend; - - // Set it's fields - memset(&pOverLappedSend->Overlapped, 0, sizeof (pOverLappedSend->Overlapped)); - pOverLappedSend->Socket = newfd; - pOverLappedSend->Buffer = buffer; - pOverLappedSend->DataSize = (size_t)BufferSize; - pOverLappedSend->RemainingSends = (size_t)NumberOfBuffers; - pOverLappedSend->SendsInAir = 1; // To force a simulate of the compleation - InitializeCriticalSection(&pOverLappedSend->Lock); - pOverLappedSend->SendDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - - double Start = GetTimeMs(); - // Post the message that will do the first send - ret = PostQueuedCompletionStatus( - g_ComplitionPort, - (DWORD)BufferSize, - NULL, - &pOverLappedSend->Overlapped - ); - ASSERT(ret != 0); - - // wait for the send - WaitForSingleObject(pOverLappedSend->SendDoneEvent, INFINITE); - elapsed = (GetTimeMs() - Start) / 1000; - printf("Finishd sending correctly %f mbytes/sec\n", NumberOfBuffers * BufferSize /elapsed/1024/1024 ); - CloseComplitionPort(); -//Cleanup: - free(buffer); - - -} - - - -void SendData(int newfd, double NumberOfBuffers, double BufferSize, BOOL SendPacketNumber) -{ - int i ; - double d; - CHAR *buffer = NULL; - double elapsed, acc = 0; - - buffer = new char [(size_t)BufferSize]; - if (buffer == NULL) { - printf("Error allocating buffer\n"); - exit(1); - } - - printf("Starting to send %lf messages of size %lf\n", NumberOfBuffers, BufferSize ); - - i = 0; - - for (i = 0; i < 10; i++) { - buffer[i] = 'a' + i; - } - double Start = GetTimeMs(); - - for (d=0; d < NumberOfBuffers; d++) { - int j; - if (SendPacketNumber) { - memset(buffer, (char) d, (size_t)BufferSize); - } - - j = send(newfd, buffer, (size_t)BufferSize, 0 ); - acc += j; - if (j!=BufferSize) { - printf("Error send not compleated sent %lf\n", acc); - goto Cleanup; - } - } - elapsed = (GetTimeMs() - Start) / 1000; - printf("Finishd sending correctly %f mbytes/sec (%f seconfs)\n", acc/elapsed/1024/1024, elapsed ); -Cleanup: - free(buffer); -} - - -void RecvData(int newfd, double NumberOfBuffers, double BufferSize ) -{ - int read1 = 1; - unsigned char *p; - char buffer[380000]; - double elapsed, acc = 0; - int i; - double Start = GetTimeMs(); - - while ((read1 != -1) /*&& (acc < (double)BufferSize * NumberOfBuffers)*/) { - read1 = recv(newfd, buffer, sizeof buffer, 0); - if (read1 == -1 || read1 == 0) { - printf("Finsihed reading, total read %lf bytes last read =%d\n", acc, read1 ); - if (acc != BufferSize * NumberOfBuffers) { - printf("Error, expected to read %lf but read %lf\n", - (BufferSize * NumberOfBuffers), acc); - } - break; - } - else { - acc += read1; - -// printf("read returned %d \"%c%c%c%c\"\n",read1, -// buffer[0],buffer[1], buffer[2], buffer[3]); - } - } - - if (acc != ((double)BufferSize * NumberOfBuffers)) { - printf("Error, expected to read %lf but read %lf", - ((double)BufferSize * NumberOfBuffers), acc); - } else { - elapsed = (GetTimeMs() - Start) / 1000; - printf("Finishd reading correctly %f mbytes/sec (time = %f)\n", acc / elapsed/1024/1024, elapsed); - } -} - -void PrintUsage(char *name) -{ - printf("The program might be used in client or server mode\n"); - printf("usage is %s \n", name); - printf("usage is %s