<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2013 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "OgreD3D11HardwareBuffer.h"
#include "OgreD3D11Mappings.h"
#include "OgreD3D11Device.h"
#include "OgreException.h"
namespace Ogre {

	//---------------------------------------------------------------------
	D3D11HardwareBuffer::D3D11HardwareBuffer(
		BufferType btype, size_t sizeBytes,
		HardwareBuffer::Usage usage, D3D11Device & device,
		bool useSystemMemory, bool useShadowBuffer, bool streamOut)
		: HardwareBuffer(usage, useSystemMemory,  useShadowBuffer),
		mlpD3DBuffer(0),
		mpTempStagingBuffer(0),
		mUseTempStagingBuffer(false),
		mBufferType(btype),
		mDevice(device)
	{
		mSizeInBytes = sizeBytes;
		mDesc.ByteWidth = static_cast<UINT>(sizeBytes);
		mDesc.CPUAccessFlags = D3D11Mappings::_getAccessFlags(mUsage);
		mDesc.MiscFlags = 0;

		if (useSystemMemory)
		{
			mDesc.Usage = D3D11_USAGE_STAGING;
			//A D3D11_USAGE_STAGING Resource cannot be bound to any parts of the graphics pipeline, so therefore cannot have any BindFlags bits set.
			mDesc.BindFlags = 0;
			mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ ;// A D3D11_USAGE_STAGING Resource must have at least one CPUAccessFlag bit set.

		}
		else
		{
			mDesc.Usage = D3D11Mappings::_getUsage(mUsage);
			mDesc.BindFlags = btype == VERTEX_BUFFER ? D3D11_BIND_VERTEX_BUFFER :
							  btype == INDEX_BUFFER  ? D3D11_BIND_INDEX_BUFFER  :
													   D3D11_BIND_CONSTANT_BUFFER;
		}
		// Better check of stream out flag
		if (streamOut && btype != CONSTANT_BUFFER)
		{
			mDesc.BindFlags |= D3D11_BIND_STREAM_OUTPUT;
		}

		if (!useSystemMemory && (usage & HardwareBuffer::HBU_DYNAMIC))
		{
			// We want to be able to map this buffer
			mDesc.CPUAccessFlags |= D3D11_CPU_ACCESS_WRITE;
			mDesc.Usage = D3D11_USAGE_DYNAMIC;
		}

		// Note that in D3D11 you can only directly lock (map) a dynamic resource
		// directly for writing. You can only map for read / write staging resources,
		// which themselves cannot be used for input / output to the GPU. Thus
		// for any locks except write locks on dynamic resources, we have to use
		// temporary staging resources instead and use async copies.
		// We use the 'useSystemMemory' option to indicate a staging resource

		// TODO: we can explicitly initialise the buffer contents here if we like
		// not doing this since OGRE doesn't support this model yet
		HRESULT hr = device->CreateBuffer( &mDesc, NULL, &mlpD3DBuffer );
		if (FAILED(hr) || mDevice.isError())
		{
			String msg = device.getErrorDescription(hr);
			OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
				"Cannot create D3D11 buffer: " + msg,
				"D3D11HardwareBuffer::D3D11HardwareBuffer");
		}

		// Create shadow buffer
		if (mUseShadowBuffer)
		{
			mShadowBuffer = new D3D11HardwareBuffer(mBufferType,
					mSizeInBytes, mUsage, mDevice, true, false, false);
		}

	}
	//---------------------------------------------------------------------
	D3D11HardwareBuffer::~D3D11HardwareBuffer()
	{
#if OGRE_PLATFORM != OGRE_PLATFORM_WINRT
		SAFE_RELEASE(mlpD3DBuffer);
#endif
		SAFE_DELETE(mpTempStagingBuffer); // should never be nonzero unless destroyed while locked
		SAFE_DELETE(mShadowBuffer);
	}
	//---------------------------------------------------------------------
	void* D3D11HardwareBuffer::lockImpl(size_t offset,
		size_t length, LockOptions options)
	{
		if (length > mSizeInBytes)
		{
			// need to realloc
			mDesc.ByteWidth = static_cast<UINT>(mSizeInBytes);
			HRESULT hr = mDevice->CreateBuffer( &mDesc, 0, &mlpD3DBuffer );
			if (FAILED(hr) || mDevice.isError())
			{
				String msg = mDevice.getErrorDescription(hr);
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Cannot create D3D11 buffer: " + msg,
					"D3D11HardwareBuffer::D3D11HardwareBuffer");
			}
		}

		if (mSystemMemory ||
			(mUsage & HardwareBuffer::HBU_DYNAMIC &&
			(options == HardwareBuffer::HBL_DISCARD)))
		{
			// Staging (system memory) buffers or dynamic, write-only buffers
			// are the only case where we can lock directly
			// We have no 'lock for writing' but discard + locking the entire
			// buffer essentially means the same thing, especially since it's
			// not possible to map part of a buffer in Dx11

			// map directly
			D3D11_MAP mapType;
			switch(options)
			{
			case HBL_DISCARD:
				if (!mSystemMemory && (mUsage | HardwareBuffer::HBU_DYNAMIC))
				{
					// Map cannot be called with MAP_WRITE access,
					// because the Resource was created as D3D11_USAGE_DYNAMIC.
					// D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
					// or MAP_WRITE_NO_OVERWRITE with Map.
					mapType = D3D11_MAP_WRITE_DISCARD;
				}
				else
				{
					// Map cannot be called with MAP_WRITE_DISCARD access,
					// because the Resource was not created as D3D11_USAGE_DYNAMIC.
					// D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
					// or MAP_WRITE_NO_OVERWRITE with Map.
					mapType = D3D11_MAP_WRITE;
				}
				break;
			case HBL_NO_OVERWRITE:
				if (mSystemMemory)
				{
					// map cannot be called with MAP_WRITE_NO_OVERWRITE access, because the Resource was not created as D3D11_USAGE_DYNAMIC. D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD or MAP_WRITE_NO_OVERWRITE with Map.
					mapType = D3D11_MAP_READ_WRITE;
				}
				else
				{
					mapType = D3D11_MAP_WRITE_NO_OVERWRITE;
				}
				break;
			case HBL_NORMAL:
				if (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)
				{
					mapType = D3D11_MAP_READ_WRITE;
				}
				else
				{
					mapType = D3D11_MAP_WRITE_DISCARD;
				}
				break;
			case HBL_READ_ONLY:
				mapType = D3D11_MAP_READ;
				break;
			}

			void * pRet = NULL;
			D3D11_MAPPED_SUBRESOURCE mappedSubResource;
			mappedSubResource.pData = NULL;
			mDevice.clearStoredErrorMessages();
			HRESULT hr = mDevice.GetImmediateContext()->Map(mlpD3DBuffer, 0, mapType, 0, &mappedSubResource);
			if (FAILED(hr) || mDevice.isError())
			{
				String msg = mDevice.getErrorDescription(hr);
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Error calling Map: " + msg,
					"D3D11HardwareBuffer::lockImpl");
			}

			pRet = static_cast<void*>(static_cast<char*>(mappedSubResource.pData) + offset);

			return pRet;

		}
		else
		{
			mUseTempStagingBuffer = true;
			if (!mpTempStagingBuffer)
			{
				// create another buffer instance but use system memory
				mpTempStagingBuffer = new D3D11HardwareBuffer(mBufferType,
					mSizeInBytes, mUsage, mDevice, true, false, false);
			}

			// schedule a copy to the staging
			if (options != HBL_DISCARD)
				mpTempStagingBuffer->copyData(*this, 0, 0, mSizeInBytes, true);

			// register whether we'll need to upload on unlock
			mStagingUploadNeeded = (options != HBL_READ_ONLY);

			return mpTempStagingBuffer->lock(offset, length, options);

		}
	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::unlockImpl(void)
	{

		if (mUseTempStagingBuffer)
		{
			mUseTempStagingBuffer = false;

			// ok, we locked the staging buffer
			mpTempStagingBuffer->unlock();

			// copy data if needed
			// this is async but driver should keep reference
			if (mStagingUploadNeeded)
				copyData(*mpTempStagingBuffer, 0, 0, mSizeInBytes, true);

			// delete
			// not that efficient, but we should not be locking often
			SAFE_DELETE(mpTempStagingBuffer);
		}
		else
		{
			// unmap
			mDevice.GetImmediateContext()->Unmap(mlpD3DBuffer, 0);
		}
	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::copyData(HardwareBuffer& srcBuffer, size_t srcOffset,
		size_t dstOffset, size_t length, bool discardWholeBuffer)
	{
		// If we're copying same-size buffers in their entirety...
		if (srcOffset == 0 && dstOffset == 0 &&
			length == mSizeInBytes && mSizeInBytes == srcBuffer.getSizeInBytes())
		{
			// schedule hardware buffer copy
			mDevice.GetImmediateContext()->CopyResource(mlpD3DBuffer, static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer());
			if (mDevice.isError())
			{
				String errorDescription = mDevice.getErrorDescription();
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Cannot copy D3D11 resource\nError Description:" + errorDescription,
					"D3D11HardwareBuffer::copyData");
			}
		}
		else
		{
			// copy subregion
			D3D11_BOX srcBox;
			srcBox.left = (UINT)srcOffset;
			srcBox.right = (UINT)srcOffset + length;
			srcBox.top = 0;
			srcBox.bottom = 1;
			srcBox.front = 0;
			srcBox.back = 1;

			mDevice.GetImmediateContext()->CopySubresourceRegion(mlpD3DBuffer, 0, (UINT)dstOffset, 0, 0,
				static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer(), 0, &srcBox);
			if (mDevice.isError())
			{
				String errorDescription = mDevice.getErrorDescription();
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Cannot copy D3D11 subresource region\nError Description:" + errorDescription,
					"D3D11HardwareBuffer::copyData");
			}
		}
	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::readData(size_t offset, size_t length,
		void* pDest)
	{
		// There is no functional interface in D3D, just do via manual
		// lock, copy & unlock
		void* pSrc = this->lock(offset, length, HardwareBuffer::HBL_READ_ONLY);
		memcpy(pDest, pSrc, length);
		this->unlock();

	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::writeData(size_t offset, size_t length,
		const void* pSource,
		bool discardWholeBuffer)
	{
		// There is no functional interface in D3D, just do via manual
		// lock, copy & unlock
		void* pDst = this->lock(offset, length,
			discardWholeBuffer ? HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL);
		memcpy(pDst, pSource, length);
		this->unlock();

		//What if we try UpdateSubresource
		//mDevice.GetImmediateContext()->UpdateSubresource(mlpD3DBuffer, 0, NULL, pSource, offset, length);
	}
	//---------------------------------------------------------------------

}
<commit_msg>Direct3D11: huge memory leak on WinRT and less frequent on Win32<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2013 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "OgreD3D11HardwareBuffer.h"
#include "OgreD3D11Mappings.h"
#include "OgreD3D11Device.h"
#include "OgreException.h"
namespace Ogre {

	//---------------------------------------------------------------------
	D3D11HardwareBuffer::D3D11HardwareBuffer(
		BufferType btype, size_t sizeBytes,
		HardwareBuffer::Usage usage, D3D11Device & device,
		bool useSystemMemory, bool useShadowBuffer, bool streamOut)
		: HardwareBuffer(usage, useSystemMemory,  useShadowBuffer),
		mlpD3DBuffer(0),
		mpTempStagingBuffer(0),
		mUseTempStagingBuffer(false),
		mBufferType(btype),
		mDevice(device)
	{
		mSizeInBytes = sizeBytes;
		mDesc.ByteWidth = static_cast<UINT>(sizeBytes);
		mDesc.CPUAccessFlags = D3D11Mappings::_getAccessFlags(mUsage);
		mDesc.MiscFlags = 0;

		if (useSystemMemory)
		{
			mDesc.Usage = D3D11_USAGE_STAGING;
			//A D3D11_USAGE_STAGING Resource cannot be bound to any parts of the graphics pipeline, so therefore cannot have any BindFlags bits set.
			mDesc.BindFlags = 0;
			mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ ;// A D3D11_USAGE_STAGING Resource must have at least one CPUAccessFlag bit set.

		}
		else
		{
			mDesc.Usage = D3D11Mappings::_getUsage(mUsage);
			mDesc.BindFlags = btype == VERTEX_BUFFER ? D3D11_BIND_VERTEX_BUFFER :
							  btype == INDEX_BUFFER  ? D3D11_BIND_INDEX_BUFFER  :
													   D3D11_BIND_CONSTANT_BUFFER;
		}
		// Better check of stream out flag
		if (streamOut && btype != CONSTANT_BUFFER)
		{
			mDesc.BindFlags |= D3D11_BIND_STREAM_OUTPUT;
		}

		if (!useSystemMemory && (usage & HardwareBuffer::HBU_DYNAMIC))
		{
			// We want to be able to map this buffer
			mDesc.CPUAccessFlags |= D3D11_CPU_ACCESS_WRITE;
			mDesc.Usage = D3D11_USAGE_DYNAMIC;
		}

		// Note that in D3D11 you can only directly lock (map) a dynamic resource
		// directly for writing. You can only map for read / write staging resources,
		// which themselves cannot be used for input / output to the GPU. Thus
		// for any locks except write locks on dynamic resources, we have to use
		// temporary staging resources instead and use async copies.
		// We use the 'useSystemMemory' option to indicate a staging resource

		// TODO: we can explicitly initialise the buffer contents here if we like
		// not doing this since OGRE doesn't support this model yet
		HRESULT hr = device->CreateBuffer( &mDesc, NULL, &mlpD3DBuffer );
		if (FAILED(hr) || mDevice.isError())
		{
			String msg = device.getErrorDescription(hr);
			OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
				"Cannot create D3D11 buffer: " + msg,
				"D3D11HardwareBuffer::D3D11HardwareBuffer");
		}

		// Create shadow buffer
		if (mUseShadowBuffer)
		{
			mShadowBuffer = new D3D11HardwareBuffer(mBufferType,
					mSizeInBytes, mUsage, mDevice, true, false, false);
		}

	}
	//---------------------------------------------------------------------
	D3D11HardwareBuffer::~D3D11HardwareBuffer()
	{
		SAFE_RELEASE(mlpD3DBuffer);
		SAFE_DELETE(mpTempStagingBuffer); // should never be nonzero unless destroyed while locked
		SAFE_DELETE(mShadowBuffer);
	}
	//---------------------------------------------------------------------
	void* D3D11HardwareBuffer::lockImpl(size_t offset,
		size_t length, LockOptions options)
	{
		if (length > mSizeInBytes)
		{
			// need to realloc
			mDesc.ByteWidth = static_cast<UINT>(mSizeInBytes);
			SAFE_RELEASE(mlpD3DBuffer);
			HRESULT hr = mDevice->CreateBuffer( &mDesc, 0, &mlpD3DBuffer );
			if (FAILED(hr) || mDevice.isError())
			{
				String msg = mDevice.getErrorDescription(hr);
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Cannot create D3D11 buffer: " + msg,
					"D3D11HardwareBuffer::D3D11HardwareBuffer");
			}
		}

		if (mSystemMemory ||
			(mUsage & HardwareBuffer::HBU_DYNAMIC &&
			(options == HardwareBuffer::HBL_DISCARD)))
		{
			// Staging (system memory) buffers or dynamic, write-only buffers
			// are the only case where we can lock directly
			// We have no 'lock for writing' but discard + locking the entire
			// buffer essentially means the same thing, especially since it's
			// not possible to map part of a buffer in Dx11

			// map directly
			D3D11_MAP mapType;
			switch(options)
			{
			case HBL_DISCARD:
				if (!mSystemMemory && (mUsage | HardwareBuffer::HBU_DYNAMIC))
				{
					// Map cannot be called with MAP_WRITE access,
					// because the Resource was created as D3D11_USAGE_DYNAMIC.
					// D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
					// or MAP_WRITE_NO_OVERWRITE with Map.
					mapType = D3D11_MAP_WRITE_DISCARD;
				}
				else
				{
					// Map cannot be called with MAP_WRITE_DISCARD access,
					// because the Resource was not created as D3D11_USAGE_DYNAMIC.
					// D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD
					// or MAP_WRITE_NO_OVERWRITE with Map.
					mapType = D3D11_MAP_WRITE;
				}
				break;
			case HBL_NO_OVERWRITE:
				if (mSystemMemory)
				{
					// map cannot be called with MAP_WRITE_NO_OVERWRITE access, because the Resource was not created as D3D11_USAGE_DYNAMIC. D3D11_USAGE_DYNAMIC Resources must use either MAP_WRITE_DISCARD or MAP_WRITE_NO_OVERWRITE with Map.
					mapType = D3D11_MAP_READ_WRITE;
				}
				else
				{
					mapType = D3D11_MAP_WRITE_NO_OVERWRITE;
				}
				break;
			case HBL_NORMAL:
				if (mDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)
				{
					mapType = D3D11_MAP_READ_WRITE;
				}
				else
				{
					mapType = D3D11_MAP_WRITE_DISCARD;
				}
				break;
			case HBL_READ_ONLY:
				mapType = D3D11_MAP_READ;
				break;
			}

			void * pRet = NULL;
			D3D11_MAPPED_SUBRESOURCE mappedSubResource;
			mappedSubResource.pData = NULL;
			mDevice.clearStoredErrorMessages();
			HRESULT hr = mDevice.GetImmediateContext()->Map(mlpD3DBuffer, 0, mapType, 0, &mappedSubResource);
			if (FAILED(hr) || mDevice.isError())
			{
				String msg = mDevice.getErrorDescription(hr);
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Error calling Map: " + msg,
					"D3D11HardwareBuffer::lockImpl");
			}

			pRet = static_cast<void*>(static_cast<char*>(mappedSubResource.pData) + offset);

			return pRet;

		}
		else
		{
			mUseTempStagingBuffer = true;
			if (!mpTempStagingBuffer)
			{
				// create another buffer instance but use system memory
				mpTempStagingBuffer = new D3D11HardwareBuffer(mBufferType,
					mSizeInBytes, mUsage, mDevice, true, false, false);
			}

			// schedule a copy to the staging
			if (options != HBL_DISCARD)
				mpTempStagingBuffer->copyData(*this, 0, 0, mSizeInBytes, true);

			// register whether we'll need to upload on unlock
			mStagingUploadNeeded = (options != HBL_READ_ONLY);

			return mpTempStagingBuffer->lock(offset, length, options);

		}
	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::unlockImpl(void)
	{

		if (mUseTempStagingBuffer)
		{
			mUseTempStagingBuffer = false;

			// ok, we locked the staging buffer
			mpTempStagingBuffer->unlock();

			// copy data if needed
			// this is async but driver should keep reference
			if (mStagingUploadNeeded)
				copyData(*mpTempStagingBuffer, 0, 0, mSizeInBytes, true);

			// delete
			// not that efficient, but we should not be locking often
			SAFE_DELETE(mpTempStagingBuffer);
		}
		else
		{
			// unmap
			mDevice.GetImmediateContext()->Unmap(mlpD3DBuffer, 0);
		}
	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::copyData(HardwareBuffer& srcBuffer, size_t srcOffset,
		size_t dstOffset, size_t length, bool discardWholeBuffer)
	{
		// If we're copying same-size buffers in their entirety...
		if (srcOffset == 0 && dstOffset == 0 &&
			length == mSizeInBytes && mSizeInBytes == srcBuffer.getSizeInBytes())
		{
			// schedule hardware buffer copy
			mDevice.GetImmediateContext()->CopyResource(mlpD3DBuffer, static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer());
			if (mDevice.isError())
			{
				String errorDescription = mDevice.getErrorDescription();
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Cannot copy D3D11 resource\nError Description:" + errorDescription,
					"D3D11HardwareBuffer::copyData");
			}
		}
		else
		{
			// copy subregion
			D3D11_BOX srcBox;
			srcBox.left = (UINT)srcOffset;
			srcBox.right = (UINT)srcOffset + length;
			srcBox.top = 0;
			srcBox.bottom = 1;
			srcBox.front = 0;
			srcBox.back = 1;

			mDevice.GetImmediateContext()->CopySubresourceRegion(mlpD3DBuffer, 0, (UINT)dstOffset, 0, 0,
				static_cast<D3D11HardwareBuffer&>(srcBuffer).getD3DBuffer(), 0, &srcBox);
			if (mDevice.isError())
			{
				String errorDescription = mDevice.getErrorDescription();
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
					"Cannot copy D3D11 subresource region\nError Description:" + errorDescription,
					"D3D11HardwareBuffer::copyData");
			}
		}
	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::readData(size_t offset, size_t length,
		void* pDest)
	{
		// There is no functional interface in D3D, just do via manual
		// lock, copy & unlock
		void* pSrc = this->lock(offset, length, HardwareBuffer::HBL_READ_ONLY);
		memcpy(pDest, pSrc, length);
		this->unlock();

	}
	//---------------------------------------------------------------------
	void D3D11HardwareBuffer::writeData(size_t offset, size_t length,
		const void* pSource,
		bool discardWholeBuffer)
	{
		// There is no functional interface in D3D, just do via manual
		// lock, copy & unlock
		void* pDst = this->lock(offset, length,
			discardWholeBuffer ? HardwareBuffer::HBL_DISCARD : HardwareBuffer::HBL_NORMAL);
		memcpy(pDst, pSource, length);
		this->unlock();

		//What if we try UpdateSubresource
		//mDevice.GetImmediateContext()->UpdateSubresource(mlpD3DBuffer, 0, NULL, pSource, offset, length);
	}
	//---------------------------------------------------------------------

}
<|endoftext|>


<commit_before>#include "xalt_obfuscate.h"
#include "xalt_syshost.h"
#include "xalt_config.h"
#include "xalt_regex.h"
#include "xalt_version.h"
#include <iostream>
#include <iomanip>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "epoch.h"
#include "Json.h"

const int dateSZ=100;

void displayArray(const char *name, int n, const char **A)
{
  std::cout << "*----------------------*\n";
  std::cout << " Array: " << name << "\n";
  std::cout << "*----------------------*\n";

  for (int i = 0; i < n; ++i)
    std::cout << std::setw(4) << i << ": " << A[i] << "\n";
  std::cout << "\n";
}

int main(int argc, char* argv[])
{
  std::string syshost(xalt_syshost());
  std::string syslog_tag("XALT_LOGGING_");
  syslog_tag.append(syshost);

  char    dateStr[dateSZ];
  time_t  now = (time_t) epoch();
  strftime(dateStr,dateSZ, "%c", localtime(&now));

  const char* transmission = getenv("XALT_TRANSMISSION_STYLE");
  if (transmission == NULL)
    transmission = TRANSMISSION;

  const char* computeSHA1 = getenv("XALT_COMPUTE_SHA1");
  if (computeSHA1 == NULL)
    computeSHA1 = XALT_COMPUTE_SHA1;

  const char* enable_backgrounding = getenv("XALT_ENABLE_BACKGROUNDING");
  if (enable_backgrounding == NULL)
    enable_backgrounding = XALT_ENABLE_BACKGROUNDING;

  const char* xalt_etc_dir = getenv("XALT_ETC_DIR");
  if (xalt_etc_dir == NULL)
    xalt_etc_dir = XALT_ETC_DIR;

  const char* xalt_tracking_mpi_only = getenv("XALT_TRACKING_MPI_ONLY");
  if (xalt_tracking_mpi_only == NULL)
    xalt_tracking_mpi_only = XALT_TRACKING_MPI_ONLY;

  if (argc == 2 && strcmp(argv[1],"--json") == 0)
    {
      Json json;
      json.add("DATE",                        dateStr);
      json.add("XALT_SYSHOST",                syshost);
      json.add("XALT_VERSION",                XALT_VERSION);
      json.add("XALT_GIT_VERSION",            XALT_GIT_VERSION);
      json.add("XALT_VERSION_STR",            XALT_VERSION_STR);
      json.add("XALT_FILE_PREFIX",            XALT_FILE_PREFIX);
      json.add("XALT_ENABLE_BACKGROUNDING",   enable_backgrounding);
      json.add("XALT_TRANSMISSION_STYLE",     transmission);
      if (strcmp(transmission,"syslog") == 0)
        json.add("XALT_LOGGING_TAG",            syslog_tag);
      json.add("XALT_COMPUTE_SHA1",           computeSHA1);
      json.add("XALT_ETC_DIR",                xalt_etc_dir);
      json.add("XALT_CONFIG_PY",              XALT_CONFIG_PY);
      json.add("XALT_SYSTEM_PATH",            XALT_SYSTEM_PATH);
      json.add("XALT_SYSHOST_CONFIG",         SYSHOST_CONFIG);
      json.add("XALT_TRACKING_MPI_ONLY",      xalt_tracking_mpi_only);
      json.add("XALT_SYSLOG_MSG_SZ",          SYSLOG_MSG_SZ);
      json.add("HAVE_32BIT",                  HAVE_32BIT);
      json.add("USING_LIBUUID",               HAVE_WORKING_LIBUUID);
      json.add("BUILT_W_MySQL",               BUILT_W_MySQL);

      json.add("hostnameA",    hostnameSz,    hostnameA);
      json.add("pathPatternA", pathPatternSz, pathPatternA);
      json.add("envPatternA",  envPatternSz,  envPatternA);
      json.fini();

      std::string jsonStr = json.result();
      std::cout << jsonStr << std::endl;
      return 0;
    }

  std::cout << "*------------------------------------------------------------------------------*\n";
  std::cout << "                      XALT Configuration Report\n";
  std::cout << "*------------------------------------------------------------------------------*\n\n";
  std::cout << "Today's DATE:              " << dateStr                << "\n";
  std::cout << "XALT_VERSION:              " << XALT_VERSION           << "\n";
  std::cout << "XALT_GIT_VERSION:          " << XALT_GIT_VERSION       << "\n";
  std::cout << "XALT_VERSION_STR:          " << XALT_VERSION_STR       << "\n";
  std::cout << "*------------------------------------------------------------------------------*\n";
  std::cout << "XALT_SYSHOST:              " << syshost                << "\n";
  std::cout << "XALT_FILE_PREFIX:          " << XALT_FILE_PREFIX       << "\n";
  std::cout << "XALT_TRANSMISSION_STYLE:   " << transmission           << "\n";
  if (strcmp(transmission,"syslog") == 0)
    std::cout << "XALT_LOGGING_TAG:        " << syslog_tag             << "\n";
  std::cout << "XALT_COMPUTE_SHA1:         " << computeSHA1            << "\n";
  std::cout << "XALT_ENABLE_BACKGROUNDING: " << enable_backgrounding   << "\n";
  std::cout << "XALT_ETC_DIR:              " << xalt_etc_dir           << "\n";
  std::cout << "XALT_CONFIG_PY:            " << XALT_CONFIG_PY         << "\n";
  std::cout << "XALT_TRACKING_MPI_ONLY:    " << xalt_tracking_mpi_only << "\n";
  std::cout << "XALT_SYSTEM_PATH:          " << XALT_SYSTEM_PATH       << "\n";
  std::cout << "XALT_SYSHOST_CONFIG:       " << SYSHOST_CONFIG         << "\n";
  std::cout << "XALT_SYSLOG_MSG_SZ:        " << SYSLOG_MSG_SZ          << "\n";
  std::cout << "HAVE_32BIT:                " << HAVE_32BIT             << "\n";
  std::cout << "Using libuuid:             " << HAVE_WORKING_LIBUUID   << "\n";
  std::cout << "Built with MySQL:          " << BUILT_W_MySQL          << "\n";
  std::cout << "*------------------------------------------------------------------------------*\n\n";

  displayArray("hostnameA",    hostnameSz,    hostnameA);
  std::cout << "\nRemember that \"SPSR\" means a scalar program that creates a start record\n";
  displayArray("pathPatternA", pathPatternSz, pathPatternA);
  displayArray("envPatternA",  envPatternSz,  envPatternA);

  return 0;
}
<commit_msg>fix spacing problem with xalt_configure_report<commit_after>#include "xalt_obfuscate.h"
#include "xalt_syshost.h"
#include "xalt_config.h"
#include "xalt_regex.h"
#include "xalt_version.h"
#include <iostream>
#include <iomanip>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "epoch.h"
#include "Json.h"

const int dateSZ=100;

void displayArray(const char *name, int n, const char **A)
{
  std::cout << "*----------------------*\n";
  std::cout << " Array: " << name << "\n";
  std::cout << "*----------------------*\n";

  for (int i = 0; i < n; ++i)
    std::cout << std::setw(4) << i << ": " << A[i] << "\n";
  std::cout << "\n";
}

int main(int argc, char* argv[])
{
  std::string syshost(xalt_syshost());
  std::string syslog_tag("XALT_LOGGING_");
  syslog_tag.append(syshost);

  char    dateStr[dateSZ];
  time_t  now = (time_t) epoch();
  strftime(dateStr,dateSZ, "%c", localtime(&now));

  const char* transmission = getenv("XALT_TRANSMISSION_STYLE");
  if (transmission == NULL)
    transmission = TRANSMISSION;

  const char* computeSHA1 = getenv("XALT_COMPUTE_SHA1");
  if (computeSHA1 == NULL)
    computeSHA1 = XALT_COMPUTE_SHA1;

  const char* enable_backgrounding = getenv("XALT_ENABLE_BACKGROUNDING");
  if (enable_backgrounding == NULL)
    enable_backgrounding = XALT_ENABLE_BACKGROUNDING;

  const char* xalt_etc_dir = getenv("XALT_ETC_DIR");
  if (xalt_etc_dir == NULL)
    xalt_etc_dir = XALT_ETC_DIR;

  const char* xalt_tracking_mpi_only = getenv("XALT_TRACKING_MPI_ONLY");
  if (xalt_tracking_mpi_only == NULL)
    xalt_tracking_mpi_only = XALT_TRACKING_MPI_ONLY;

  if (argc == 2 && strcmp(argv[1],"--json") == 0)
    {
      Json json;
      json.add("DATE",                        dateStr);
      json.add("XALT_SYSHOST",                syshost);
      json.add("XALT_VERSION",                XALT_VERSION);
      json.add("XALT_GIT_VERSION",            XALT_GIT_VERSION);
      json.add("XALT_VERSION_STR",            XALT_VERSION_STR);
      json.add("XALT_FILE_PREFIX",            XALT_FILE_PREFIX);
      json.add("XALT_ENABLE_BACKGROUNDING",   enable_backgrounding);
      json.add("XALT_TRANSMISSION_STYLE",     transmission);
      if (strcmp(transmission,"syslog") == 0)
        json.add("XALT_LOGGING_TAG",          syslog_tag);
      json.add("XALT_COMPUTE_SHA1",           computeSHA1);
      json.add("XALT_ETC_DIR",                xalt_etc_dir);
      json.add("XALT_CONFIG_PY",              XALT_CONFIG_PY);
      json.add("XALT_SYSTEM_PATH",            XALT_SYSTEM_PATH);
      json.add("XALT_SYSHOST_CONFIG",         SYSHOST_CONFIG);
      json.add("XALT_TRACKING_MPI_ONLY",      xalt_tracking_mpi_only);
      json.add("XALT_SYSLOG_MSG_SZ",          SYSLOG_MSG_SZ);
      json.add("HAVE_32BIT",                  HAVE_32BIT);
      json.add("USING_LIBUUID",               HAVE_WORKING_LIBUUID);
      json.add("BUILT_W_MySQL",               BUILT_W_MySQL);

      json.add("hostnameA",    hostnameSz,    hostnameA);
      json.add("pathPatternA", pathPatternSz, pathPatternA);
      json.add("envPatternA",  envPatternSz,  envPatternA);
      json.fini();

      std::string jsonStr = json.result();
      std::cout << jsonStr << std::endl;
      return 0;
    }

  std::cout << "*------------------------------------------------------------------------------*\n";
  std::cout << "                      XALT Configuration Report\n";
  std::cout << "*------------------------------------------------------------------------------*\n\n";
  std::cout << "Today's DATE:              " << dateStr                << "\n";
  std::cout << "XALT_VERSION:              " << XALT_VERSION           << "\n";
  std::cout << "XALT_GIT_VERSION:          " << XALT_GIT_VERSION       << "\n";
  std::cout << "XALT_VERSION_STR:          " << XALT_VERSION_STR       << "\n";
  std::cout << "*------------------------------------------------------------------------------*\n";
  std::cout << "XALT_SYSHOST:              " << syshost                << "\n";
  std::cout << "XALT_FILE_PREFIX:          " << XALT_FILE_PREFIX       << "\n";
  std::cout << "XALT_TRANSMISSION_STYLE:   " << transmission           << "\n";
  if (strcmp(transmission,"syslog") == 0)
    std::cout << "XALT_LOGGING_TAG:          " << syslog_tag           << "\n";
  std::cout << "XALT_COMPUTE_SHA1:         " << computeSHA1            << "\n";
  std::cout << "XALT_ENABLE_BACKGROUNDING: " << enable_backgrounding   << "\n";
  std::cout << "XALT_ETC_DIR:              " << xalt_etc_dir           << "\n";
  std::cout << "XALT_CONFIG_PY:            " << XALT_CONFIG_PY         << "\n";
  std::cout << "XALT_TRACKING_MPI_ONLY:    " << xalt_tracking_mpi_only << "\n";
  std::cout << "XALT_SYSTEM_PATH:          " << XALT_SYSTEM_PATH       << "\n";
  std::cout << "XALT_SYSHOST_CONFIG:       " << SYSHOST_CONFIG         << "\n";
  std::cout << "XALT_SYSLOG_MSG_SZ:        " << SYSLOG_MSG_SZ          << "\n";
  std::cout << "HAVE_32BIT:                " << HAVE_32BIT             << "\n";
  std::cout << "Using libuuid:             " << HAVE_WORKING_LIBUUID   << "\n";
  std::cout << "Built with MySQL:          " << BUILT_W_MySQL          << "\n";
  std::cout << "*------------------------------------------------------------------------------*\n\n";

  displayArray("hostnameA",    hostnameSz,    hostnameA);
  std::cout << "\nRemember that \"SPSR\" means a scalar program that creates a start record\n";
  displayArray("pathPatternA", pathPatternSz, pathPatternA);
  displayArray("envPatternA",  envPatternSz,  envPatternA);

  return 0;
}
<|endoftext|>


<commit_before>#include "client_http.hpp"
#include "server_http.hpp"

#include <cassert>

using namespace std;

#ifndef USE_STANDALONE_ASIO
namespace asio = boost::asio;
#endif

using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;
using HttpClient = SimpleWeb::Client<SimpleWeb::HTTP>;

int main() {
  // Test ScopeRunner
  {
    SimpleWeb::ScopeRunner scope_runner;
    std::thread cancel_thread;
    {
      assert(scope_runner.count == 0);
      auto lock = scope_runner.continue_lock();
      assert(lock);
      assert(scope_runner.count == 1);
      {
        auto lock = scope_runner.continue_lock();
        assert(lock);
        assert(scope_runner.count == 2);
      }
      assert(scope_runner.count == 1);
      cancel_thread = thread([&scope_runner] {
        scope_runner.stop();
        assert(scope_runner.count == -1);
      });
      this_thread::sleep_for(chrono::milliseconds(500));
      assert(scope_runner.count == 1);
    }
    cancel_thread.join();
    assert(scope_runner.count == -1);
    auto lock = scope_runner.continue_lock();
    assert(!lock);
    scope_runner.stop();
    assert(scope_runner.count == -1);

    scope_runner.count = 0;

    vector<thread> threads;
    for(size_t c = 0; c < 100; ++c) {
      threads.emplace_back([&scope_runner] {
        auto lock = scope_runner.continue_lock();
        assert(scope_runner.count > 0);
      });
    }
    for(auto &thread : threads)
      thread.join();
    assert(scope_runner.count == 0);
  }

  HttpServer server;
  server.config.port = 8080;

  server.resource["^/string$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    auto content = request->content.string();

    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n"
              << content;
  };

  server.resource["^/string2$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    response->write(request->content.string());
  };

  server.resource["^/string3$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    stringstream stream;
    stream << request->content.rdbuf();
    response->write(stream);
  };

  server.resource["^/string4$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> /*request*/) {
    response->write(SimpleWeb::StatusCode::client_error_forbidden, {{"Test1", "test2"}, {"tesT3", "test4"}});
  };

  server.resource["^/info$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    stringstream content_stream;
    content_stream << request->method << " " << request->path << " " << request->http_version << " ";
    content_stream << request->header.find("test parameter")->second;

    content_stream.seekp(0, ios::end);

    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content_stream.tellp() << "\r\n\r\n"
              << content_stream.rdbuf();
  };

  server.resource["^/work$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> /*request*/) {
    thread work_thread([response] {
      this_thread::sleep_for(chrono::seconds(5));
      response->write("Work done");
    });
    work_thread.detach();
  };

  server.resource["^/match/([0-9]+)$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    string number = request->path_match[1];
    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n"
              << number;
  };

  server.resource["^/header$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    auto content = request->header.find("test1")->second + request->header.find("test2")->second;

    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n"
              << content;
  };

  server.resource["^/query_string$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    assert(request->path == "/query_string");
    assert(request->query_string == "testing");
    auto queries = request->parse_query_string();
    auto it = queries.find("Testing");
    assert(it != queries.end() && it->first == "testing" && it->second == "");
    response->write(request->query_string);
  };

  thread server_thread([&server]() {
    // Start server
    server.start();
  });

  this_thread::sleep_for(chrono::seconds(1));

  server.stop();
  server_thread.join();

  server_thread = thread([&server]() {
    // Start server
    server.start();
  });

  this_thread::sleep_for(chrono::seconds(1));

  // Test various request types
  {
    HttpClient client("localhost:8080");
    {
      stringstream output;
      auto r = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r->content.string() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string2", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string3", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string4", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::client_error_forbidden);
      assert(r->header.size() == 3);
      assert(r->header.find("test1")->second == "test2");
      assert(r->header.find("tEst3")->second == "test4");
      assert(r->header.find("content-length")->second == "0");
      output << r->content.rdbuf();
      assert(output.str() == "");
    }

    {
      stringstream output;
      stringstream content("A string");
      auto r = client.request("POST", "/string", content);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("GET", "/info", "", {{"Test Parameter", "test value"}});
      output << r->content.rdbuf();
      assert(output.str() == "GET /info 1.1 test value");
    }

    {
      stringstream output;
      auto r = client.request("GET", "/match/123");
      output << r->content.rdbuf();
      assert(output.str() == "123");
    }
  }
  {
    HttpClient client("localhost:8080");

    HttpClient::Connection *connection;
    {
      // test performing the stream version of the request methods first
      stringstream output;
      stringstream content("A string");
      auto r = client.request("POST", "/string", content);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
      assert(client.connections.size() == 1);
      connection = client.connections.begin()->get();
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string", "A string");
      output << r->content.rdbuf();
      assert(output.str() == "A string");
      assert(client.connections.size() == 1);
      assert(connection == client.connections.begin()->get());
    }

    {
      stringstream output;
      auto r = client.request("GET", "/header", "", {{"test1", "test"}, {"test2", "ing"}});
      output << r->content.rdbuf();
      assert(output.str() == "testing");
      assert(client.connections.size() == 1);
      assert(connection == client.connections.begin()->get());
    }

    {
      stringstream output;
      auto r = client.request("GET", "/query_string?testing");
      assert(r->content.string() == "testing");
      assert(client.connections.size() == 1);
      assert(connection == client.connections.begin()->get());
    }
  }

  // Test asynchronous requests
  {
    HttpClient client("localhost:8080");
    bool call = false;
    client.request("GET", "/match/123", [&call](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) {
      assert(!ec);
      stringstream output;
      output << response->content.rdbuf();
      assert(output.str() == "123");
      call = true;
    });
    client.io_service->run();
    assert(call);

    {
      vector<int> calls(100, 0);
      vector<thread> threads;
      for(size_t c = 0; c < 100; ++c) {
        threads.emplace_back([c, &client, &calls] {
          client.request("GET", "/match/123", [c, &calls](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) {
            assert(!ec);
            stringstream output;
            output << response->content.rdbuf();
            assert(output.str() == "123");
            calls[c] = 1;
          });
        });
      }
      for(auto &thread : threads)
        thread.join();
      assert(client.connections.size() == 100);
      client.io_service->reset();
      client.io_service->run();
      assert(client.connections.size() == 1);
      for(auto call : calls)
        assert(call);
    }
  }

  // Test concurrent synchronous request calls
  {
    HttpClient client("localhost:8080");
    {
      vector<int> calls(2, 0);
      vector<thread> threads;
      for(size_t c = 0; c < 2; ++c) {
        threads.emplace_back([c, &client, &calls] {
          try {
            auto r = client.request("GET", "/match/123");
            assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
            assert(r->content.string() == "123");
            calls[c] = 1;
          }
          catch(...) {
            assert(false);
          }
        });
      }
      for(auto &thread : threads)
        thread.join();
      assert(client.connections.size() == 1);
      for(auto call : calls)
        assert(call);
    }
  }

  // Test multiple requests through a persistent connection
  {
    HttpClient client("localhost:8080");
    assert(client.connections.size() == 0);
    for(size_t c = 0; c < 5000; ++c) {
      auto r1 = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r1->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r1->content.string() == "A string");
      assert(client.connections.size() == 1);

      stringstream content("A string");
      auto r2 = client.request("POST", "/string", content);
      assert(SimpleWeb::status_code(r2->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r2->content.string() == "A string");
      assert(client.connections.size() == 1);
    }
  }

  // Test multiple requests through new several client objects
  for(size_t c = 0; c < 100; ++c) {
    {
      HttpClient client("localhost:8080");
      auto r = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r->content.string() == "A string");
      assert(client.connections.size() == 1);
    }

    {
      HttpClient client("localhost:8080");
      stringstream content("A string");
      auto r = client.request("POST", "/string", content);
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r->content.string() == "A string");
      assert(client.connections.size() == 1);
    }
  }

  // Test Client client's stop()
  for(size_t c = 0; c < 40; ++c) {
    auto io_service = make_shared<asio::io_service>();
    bool call = false;
    HttpClient client("localhost:8080");
    client.io_service = io_service;
    client.request("GET", "/work", [&call](shared_ptr<HttpClient::Response> /*response*/, const SimpleWeb::error_code &ec) {
      call = true;
      assert(ec);
    });
    thread thread([io_service] {
      io_service->run();
    });
    this_thread::sleep_for(chrono::milliseconds(100));
    client.stop();
    this_thread::sleep_for(chrono::milliseconds(100));
    thread.join();
    assert(call);
  }

  // Test Client destructor that should cancel the client's request
  for(size_t c = 0; c < 40; ++c) {
    auto io_service = make_shared<asio::io_service>();
    {
      HttpClient client("localhost:8080");
      client.io_service = io_service;
      client.request("GET", "/work", [](shared_ptr<HttpClient::Response> /*response*/, const SimpleWeb::error_code & /*ec*/) {
        assert(false);
      });
      thread thread([io_service] {
        io_service->run();
      });
      thread.detach();
      this_thread::sleep_for(chrono::milliseconds(100));
    }
    this_thread::sleep_for(chrono::milliseconds(100));
  }

  server.stop();
  server_thread.join();

  // Test server destructor
  {
    auto io_service = make_shared<asio::io_service>();
    bool call = false;
    bool client_catch = false;
    {
      HttpServer server;
      server.config.port = 8081;
      server.io_service = io_service;
      server.resource["^/test$"]["GET"] = [&call](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> /*request*/) {
        call = true;
        thread sleep_thread([response] {
          this_thread::sleep_for(chrono::seconds(5));
          response->write(SimpleWeb::StatusCode::success_ok, "test");
          response->send([](const SimpleWeb::error_code & /*ec*/) {
            assert(false);
          });
        });
        sleep_thread.detach();
      };
      server.start();
      thread server_thread([io_service] {
        io_service->run();
      });
      server_thread.detach();
      this_thread::sleep_for(chrono::seconds(1));
      thread client_thread([&client_catch] {
        HttpClient client("localhost:8081");
        try {
          auto r = client.request("GET", "/test");
          assert(false);
        }
        catch(...) {
          client_catch = true;
        }
      });
      client_thread.detach();
      this_thread::sleep_for(chrono::seconds(1));
    }
    this_thread::sleep_for(chrono::seconds(5));
    assert(call);
    assert(client_catch);
    io_service->stop();
  }

  return 0;
}
<commit_msg>Removed unnecessary: return 0;<commit_after>#include "client_http.hpp"
#include "server_http.hpp"

#include <cassert>

using namespace std;

#ifndef USE_STANDALONE_ASIO
namespace asio = boost::asio;
#endif

using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;
using HttpClient = SimpleWeb::Client<SimpleWeb::HTTP>;

int main() {
  // Test ScopeRunner
  {
    SimpleWeb::ScopeRunner scope_runner;
    std::thread cancel_thread;
    {
      assert(scope_runner.count == 0);
      auto lock = scope_runner.continue_lock();
      assert(lock);
      assert(scope_runner.count == 1);
      {
        auto lock = scope_runner.continue_lock();
        assert(lock);
        assert(scope_runner.count == 2);
      }
      assert(scope_runner.count == 1);
      cancel_thread = thread([&scope_runner] {
        scope_runner.stop();
        assert(scope_runner.count == -1);
      });
      this_thread::sleep_for(chrono::milliseconds(500));
      assert(scope_runner.count == 1);
    }
    cancel_thread.join();
    assert(scope_runner.count == -1);
    auto lock = scope_runner.continue_lock();
    assert(!lock);
    scope_runner.stop();
    assert(scope_runner.count == -1);

    scope_runner.count = 0;

    vector<thread> threads;
    for(size_t c = 0; c < 100; ++c) {
      threads.emplace_back([&scope_runner] {
        auto lock = scope_runner.continue_lock();
        assert(scope_runner.count > 0);
      });
    }
    for(auto &thread : threads)
      thread.join();
    assert(scope_runner.count == 0);
  }

  HttpServer server;
  server.config.port = 8080;

  server.resource["^/string$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    auto content = request->content.string();

    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n"
              << content;
  };

  server.resource["^/string2$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    response->write(request->content.string());
  };

  server.resource["^/string3$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    stringstream stream;
    stream << request->content.rdbuf();
    response->write(stream);
  };

  server.resource["^/string4$"]["POST"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> /*request*/) {
    response->write(SimpleWeb::StatusCode::client_error_forbidden, {{"Test1", "test2"}, {"tesT3", "test4"}});
  };

  server.resource["^/info$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    stringstream content_stream;
    content_stream << request->method << " " << request->path << " " << request->http_version << " ";
    content_stream << request->header.find("test parameter")->second;

    content_stream.seekp(0, ios::end);

    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content_stream.tellp() << "\r\n\r\n"
              << content_stream.rdbuf();
  };

  server.resource["^/work$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> /*request*/) {
    thread work_thread([response] {
      this_thread::sleep_for(chrono::seconds(5));
      response->write("Work done");
    });
    work_thread.detach();
  };

  server.resource["^/match/([0-9]+)$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    string number = request->path_match[1];
    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n"
              << number;
  };

  server.resource["^/header$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    auto content = request->header.find("test1")->second + request->header.find("test2")->second;

    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.length() << "\r\n\r\n"
              << content;
  };

  server.resource["^/query_string$"]["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    assert(request->path == "/query_string");
    assert(request->query_string == "testing");
    auto queries = request->parse_query_string();
    auto it = queries.find("Testing");
    assert(it != queries.end() && it->first == "testing" && it->second == "");
    response->write(request->query_string);
  };

  thread server_thread([&server]() {
    // Start server
    server.start();
  });

  this_thread::sleep_for(chrono::seconds(1));

  server.stop();
  server_thread.join();

  server_thread = thread([&server]() {
    // Start server
    server.start();
  });

  this_thread::sleep_for(chrono::seconds(1));

  // Test various request types
  {
    HttpClient client("localhost:8080");
    {
      stringstream output;
      auto r = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r->content.string() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string2", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string3", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string4", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::client_error_forbidden);
      assert(r->header.size() == 3);
      assert(r->header.find("test1")->second == "test2");
      assert(r->header.find("tEst3")->second == "test4");
      assert(r->header.find("content-length")->second == "0");
      output << r->content.rdbuf();
      assert(output.str() == "");
    }

    {
      stringstream output;
      stringstream content("A string");
      auto r = client.request("POST", "/string", content);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
    }

    {
      stringstream output;
      auto r = client.request("GET", "/info", "", {{"Test Parameter", "test value"}});
      output << r->content.rdbuf();
      assert(output.str() == "GET /info 1.1 test value");
    }

    {
      stringstream output;
      auto r = client.request("GET", "/match/123");
      output << r->content.rdbuf();
      assert(output.str() == "123");
    }
  }
  {
    HttpClient client("localhost:8080");

    HttpClient::Connection *connection;
    {
      // test performing the stream version of the request methods first
      stringstream output;
      stringstream content("A string");
      auto r = client.request("POST", "/string", content);
      output << r->content.rdbuf();
      assert(output.str() == "A string");
      assert(client.connections.size() == 1);
      connection = client.connections.begin()->get();
    }

    {
      stringstream output;
      auto r = client.request("POST", "/string", "A string");
      output << r->content.rdbuf();
      assert(output.str() == "A string");
      assert(client.connections.size() == 1);
      assert(connection == client.connections.begin()->get());
    }

    {
      stringstream output;
      auto r = client.request("GET", "/header", "", {{"test1", "test"}, {"test2", "ing"}});
      output << r->content.rdbuf();
      assert(output.str() == "testing");
      assert(client.connections.size() == 1);
      assert(connection == client.connections.begin()->get());
    }

    {
      stringstream output;
      auto r = client.request("GET", "/query_string?testing");
      assert(r->content.string() == "testing");
      assert(client.connections.size() == 1);
      assert(connection == client.connections.begin()->get());
    }
  }

  // Test asynchronous requests
  {
    HttpClient client("localhost:8080");
    bool call = false;
    client.request("GET", "/match/123", [&call](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) {
      assert(!ec);
      stringstream output;
      output << response->content.rdbuf();
      assert(output.str() == "123");
      call = true;
    });
    client.io_service->run();
    assert(call);

    {
      vector<int> calls(100, 0);
      vector<thread> threads;
      for(size_t c = 0; c < 100; ++c) {
        threads.emplace_back([c, &client, &calls] {
          client.request("GET", "/match/123", [c, &calls](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) {
            assert(!ec);
            stringstream output;
            output << response->content.rdbuf();
            assert(output.str() == "123");
            calls[c] = 1;
          });
        });
      }
      for(auto &thread : threads)
        thread.join();
      assert(client.connections.size() == 100);
      client.io_service->reset();
      client.io_service->run();
      assert(client.connections.size() == 1);
      for(auto call : calls)
        assert(call);
    }
  }

  // Test concurrent synchronous request calls
  {
    HttpClient client("localhost:8080");
    {
      vector<int> calls(2, 0);
      vector<thread> threads;
      for(size_t c = 0; c < 2; ++c) {
        threads.emplace_back([c, &client, &calls] {
          try {
            auto r = client.request("GET", "/match/123");
            assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
            assert(r->content.string() == "123");
            calls[c] = 1;
          }
          catch(...) {
            assert(false);
          }
        });
      }
      for(auto &thread : threads)
        thread.join();
      assert(client.connections.size() == 1);
      for(auto call : calls)
        assert(call);
    }
  }

  // Test multiple requests through a persistent connection
  {
    HttpClient client("localhost:8080");
    assert(client.connections.size() == 0);
    for(size_t c = 0; c < 5000; ++c) {
      auto r1 = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r1->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r1->content.string() == "A string");
      assert(client.connections.size() == 1);

      stringstream content("A string");
      auto r2 = client.request("POST", "/string", content);
      assert(SimpleWeb::status_code(r2->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r2->content.string() == "A string");
      assert(client.connections.size() == 1);
    }
  }

  // Test multiple requests through new several client objects
  for(size_t c = 0; c < 100; ++c) {
    {
      HttpClient client("localhost:8080");
      auto r = client.request("POST", "/string", "A string");
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r->content.string() == "A string");
      assert(client.connections.size() == 1);
    }

    {
      HttpClient client("localhost:8080");
      stringstream content("A string");
      auto r = client.request("POST", "/string", content);
      assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok);
      assert(r->content.string() == "A string");
      assert(client.connections.size() == 1);
    }
  }

  // Test Client client's stop()
  for(size_t c = 0; c < 40; ++c) {
    auto io_service = make_shared<asio::io_service>();
    bool call = false;
    HttpClient client("localhost:8080");
    client.io_service = io_service;
    client.request("GET", "/work", [&call](shared_ptr<HttpClient::Response> /*response*/, const SimpleWeb::error_code &ec) {
      call = true;
      assert(ec);
    });
    thread thread([io_service] {
      io_service->run();
    });
    this_thread::sleep_for(chrono::milliseconds(100));
    client.stop();
    this_thread::sleep_for(chrono::milliseconds(100));
    thread.join();
    assert(call);
  }

  // Test Client destructor that should cancel the client's request
  for(size_t c = 0; c < 40; ++c) {
    auto io_service = make_shared<asio::io_service>();
    {
      HttpClient client("localhost:8080");
      client.io_service = io_service;
      client.request("GET", "/work", [](shared_ptr<HttpClient::Response> /*response*/, const SimpleWeb::error_code & /*ec*/) {
        assert(false);
      });
      thread thread([io_service] {
        io_service->run();
      });
      thread.detach();
      this_thread::sleep_for(chrono::milliseconds(100));
    }
    this_thread::sleep_for(chrono::milliseconds(100));
  }

  server.stop();
  server_thread.join();

  // Test server destructor
  {
    auto io_service = make_shared<asio::io_service>();
    bool call = false;
    bool client_catch = false;
    {
      HttpServer server;
      server.config.port = 8081;
      server.io_service = io_service;
      server.resource["^/test$"]["GET"] = [&call](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> /*request*/) {
        call = true;
        thread sleep_thread([response] {
          this_thread::sleep_for(chrono::seconds(5));
          response->write(SimpleWeb::StatusCode::success_ok, "test");
          response->send([](const SimpleWeb::error_code & /*ec*/) {
            assert(false);
          });
        });
        sleep_thread.detach();
      };
      server.start();
      thread server_thread([io_service] {
        io_service->run();
      });
      server_thread.detach();
      this_thread::sleep_for(chrono::seconds(1));
      thread client_thread([&client_catch] {
        HttpClient client("localhost:8081");
        try {
          auto r = client.request("GET", "/test");
          assert(false);
        }
        catch(...) {
          client_catch = true;
        }
      });
      client_thread.detach();
      this_thread::sleep_for(chrono::seconds(1));
    }
    this_thread::sleep_for(chrono::seconds(5));
    assert(call);
    assert(client_catch);
    io_service->stop();
  }
}
<|endoftext|>


<commit_before>/*
 * File:   UpdateWorker.cpp
 * Author: uli
 *
 * Created on 23. April 2013, 10:35
 */

#include "ReadWorker.hpp"
#include <czmq.h>
#include <string>
#include <iostream>
#include "TableOpenHelper.hpp"
#include "Tablespace.hpp"
#include "protocol.hpp"
#include "zutil.hpp"
#include "endpoints.hpp"

using namespace std;
#define DEBUG_READ

/**
 * Read request handler. Shall be called
 *
 * The original message is modified to contain the response.
 *
 * Envelope + Read request Header + Table ID[what zmsg_next() must return] + Payload
 */
static zmsg_t* handleCountRequest(Tablespace& tables, zmsg_t* msg, TableOpenHelper& openHelper) {
    static const char* ackReply = "\x31\x01\x11\x00"; //--> No error
    //Parse the table id
    zframe_t* tableIdFrame = zmsg_next(msg);
    assert(zframe_size(tableIdFrame) == sizeof (uint32_t));
    uint32_t tableId = *((uint32_t*) zframe_data(tableIdFrame));
    //Parse the start/end frame
    zframe_t* rangeStartFrame = zmsg_next(msg);
    //zmsg_next returns a non-NULL frame when calling zmsg_next after the last frame has been next'ed, so we'll have to perform additional checks here
    zframe_t* rangeEndFrame = (rangeStartFrame == NULL ? NULL : zmsg_next(msg));
    bool haveRangeStart = !(rangeStartFrame == NULL || zframe_size(rangeStartFrame) == 0);
    bool haveRangeEnd = !(rangeEndFrame == NULL || zframe_size(rangeEndFrame) == 0);
    cout << " have " << haveRangeStart << "  " << haveRangeEnd << endl;
    std::string rangeStart = (haveRangeStart ? string((char*) zframe_data(rangeStartFrame), zframe_size(rangeStartFrame)) : "");
    std::string rangeEnd = (haveRangeEnd ? string((char*) zframe_data(rangeEndFrame), zframe_size(rangeEndFrame)) : "");
    //We don't need the range frames any more
    if (rangeStartFrame) {
        removeAndDestroyFrame(msg, rangeStartFrame);
    }
    if (rangeEndFrame) {
        removeAndDestroyFrame(msg, rangeEndFrame);
    }
    //Get the table to read from
    leveldb::DB* db = tables.getTable(tableId, openHelper);
    //Create the response object
    leveldb::ReadOptions readOptions;
    string value; //Where the value will be placed
    leveldb::Status status;
    //Create the iterator
    leveldb::Iterator* it = db->NewIterator(readOptions);
    if (haveRangeStart) {
        it->Seek(rangeStart);
    } else {
        it->SeekToFirst();
    }
    uint64_t count = 0;
    //Iterate over all key-values in the range
    for (; it->Valid(); it->Next()) {
        count++;
        string key = it->key().ToString();
        if (haveRangeEnd && key >= rangeEnd) {
            break;
        }
    }
    //TODO handle error properly
    assert(it->status().ok()); // Check for any errors found during the scan
    //Delete the iterator
    delete it;
    //Build the response message
    zframe_reset(tableIdFrame, &count, sizeof (uint64_t));
}

/**
 * Read request handler. Shall be called for Read requests only!
 *
 * The original message is modified to contain the response.
 *
 * Envelope + Read request Header + Table ID[what zmsg_next() must return] + Payload
 */
static void handleReadRequest(Tablespace& tables, zmsg_t* msg, TableOpenHelper& openHelper) {
    //Parse the table id
    zframe_t* tableIdFrame = zmsg_next(msg);
    assert(zframe_size(tableIdFrame) == sizeof (uint32_t));
    uint32_t tableId = *((uint32_t*) zframe_data(tableIdFrame));
    //Parse the
    //Get the table to read from
    leveldb::DB* db = tables.getTable(tableId, openHelper);
    //Create the response object
    leveldb::ReadOptions readOptions;
    leveldb::Status status;
    string value;
    //Read each read request
    zframe_t* keyFrame = NULL;
    while ((keyFrame = zmsg_next(msg)) != NULL) {
        //Build a slice of the key (zero-copy)
        string keystr((char*) zframe_data(keyFrame), zframe_size(keyFrame));
        leveldb::Slice key((char*) zframe_data(keyFrame), zframe_size(keyFrame));
        status = db->Get(readOptions, key, &value);
        if (status.IsNotFound()) {
            //Empty value
            zframe_reset(keyFrame, "", 0);
        } else {
            zframe_reset(keyFrame, value.c_str(), value.length());
        }
    }
    //Now we can remove the table ID frame from the message (doing so before would confuse zmsg_next())
    zmsg_remove(msg, tableIdFrame);
    zframe_destroy(&tableIdFrame);
}

/**
 * The main function for the read worker thread.
 *
 * This function parses the header, calls the appropriate handler function
 * and sends the response for PARTSYNC requests
 */
static void readWorkerThreadFunction(zctx_t* ctx, Tablespace& tablespace) {
    //Create the socket that is used to proxy requests to the external req/rep socket
    void* replyProxySocket = zsocket_new(ctx, ZMQ_PUSH);
    if (zsocket_connect(replyProxySocket, externalRequestProxyEndpoint)) {
        debugZMQError("Connect reply proxy socket", errno);
    }
    assert(replyProxySocket);
    //Create the socket we receive requests from
    void* workPullSocket = zsocket_new(ctx, ZMQ_PULL);
    zsocket_connect(workPullSocket, readWorkerThreadAddr);
    assert(workPullSocket);
    //Create the table open helper (creates a socket that sends table open requests)
    TableOpenHelper tableOpenHelper(ctx);
    //Create the data structure with all info for the poll handler
    while (true) {
        zmsg_t* msg = zmsg_recv(workPullSocket);
        assert(msg);
        assert(zmsg_size(msg) >= 1);
        //Parse the header
        //Contrary to the update message handling, we assume (checked by the request router) that
        // the message contains an envelope. Read requests without envelope (--> without return path)
        // don't really make sense.
        zframe_t* routingFrame = zmsg_first(msg); //Envelope ID
        assert(routingFrame);
        zframe_t* delimiterFrame = zmsg_next(msg); //Envelope delimiter
        assert(delimiterFrame);
        zframe_t* headerFrame = zmsg_next(msg);
        assert(headerFrame);
        assert(isHeaderFrame(headerFrame));
        //Get the request type
        RequestType requestType = getRequestType(headerFrame);
        //Process the rest of the frame
        cout << "Isize " << zmsg_size(msg) << endl;
        if (requestType == ReadRequest) {
            handleReadRequest(tablespace, msg, tableOpenHelper);
        } else if (requestType == CountRequest) {
            handleCountRequest(tablespace, msg, tableOpenHelper);
        } else {
            cerr << "Internal routing error: request type " << requestType << " routed to update worker thread!" << endl;
        }
        //Send reply (the handler function rewrote the original message to contain the reply)
        assert(msg);
        assert(zmsg_size(msg) >= 3); //2 Envelope + 1 Response header (corner case: Nothing to be read)
        cout << "Sentit" << zmsg_size(msg) << endl;
        zmsg_send(&msg, replyProxySocket);
    }
    printf("Stopping update processor\n");
    zsocket_destroy(ctx, workPullSocket);
    zsocket_destroy(ctx, replyProxySocket);
}

ReadWorkerController::ReadWorkerController(zctx_t* context, Tablespace& tablespace) : context(context), tablespace(tablespace), numThreads(3) {
    //Initialize the push socket
    workerPushSocket = zsocket_new(context, ZMQ_PUSH);
    zsocket_bind(workerPushSocket, readWorkerThreadAddr);

}

void ReadWorkerController::start() {
    threads = new std::thread*[numThreads];
    for (int i = 0; i < numThreads; i++) {
        threads[i] = new std::thread(readWorkerThreadFunction, context, std::ref(tablespace));
    }
}

ReadWorkerController::~ReadWorkerController() {

    //Send an empty STOP message for each read worker thread (use a temporary socket)
    void* tempSocket = zsocket_new(context, ZMQ_PUSH); //Create a temporary socket
    zsocket_connect(tempSocket, readWorkerThreadAddr);
    for (int i = 0; i < numThreads; i++) {
        //Send an empty msg (signals the table open thread to stop)
        sendEmptyFrameMessage(tempSocket);
    }
    //Cleanup
    zsocket_destroy(context, &tempSocket);
    //Wait for each thread to exit
    for (int i = 0; i < numThreads; i++) {
        threads[i]->join();
        delete threads[i];
    }
    //Free the array
    if (numThreads > 0) {
        delete[] threads;
    }
}

void ReadWorkerController::send(zmsg_t** msg) {
    zmsg_send(msg, workerPushSocket);
}
<commit_msg>Add count request advanced error handling<commit_after>/*
 * File:   UpdateWorker.cpp
 * Author: uli
 *
 * Created on 23. April 2013, 10:35
 */

#include "ReadWorker.hpp"
#include <czmq.h>
#include <string>
#include <iostream>
#include "TableOpenHelper.hpp"
#include "Tablespace.hpp"
#include "protocol.hpp"
#include "zutil.hpp"
#include "endpoints.hpp"

using namespace std;
#define DEBUG_READ

/**
 * Read request handler. Shall be called
 *
 * The original message is modified to contain the response.
 *
 * Envelope + Read request Header + Table ID[what zmsg_next() must return] + Payload
 */
static zmsg_t* handleCountRequest(Tablespace& tables, zmsg_t* msg, zframe_t* headerFrame, TableOpenHelper& openHelper) {
    static const char* ackReply = "\x31\x01\x11\x00"; //--> No error
    static const char* errorReply = "\x31\x01\x11\x10"; //--> No error
    //Parse the table id
    zframe_t* tableIdFrame = zmsg_next(msg);
    assert(zframe_size(tableIdFrame) == sizeof (uint32_t));
    uint32_t tableId = *((uint32_t*) zframe_data(tableIdFrame));
    //Parse the start/end frame
    zframe_t* rangeStartFrame = zmsg_next(msg);
    //zmsg_next returns a non-NULL frame when calling zmsg_next after the last frame has been next'ed, so we'll have to perform additional checks here
    zframe_t* rangeEndFrame = (rangeStartFrame == NULL ? NULL : zmsg_next(msg));
    bool haveRangeStart = !(rangeStartFrame == NULL || zframe_size(rangeStartFrame) == 0);
    bool haveRangeEnd = !(rangeEndFrame == NULL || zframe_size(rangeEndFrame) == 0);
    cout << " have " << haveRangeStart << "  " << haveRangeEnd << endl;
    std::string rangeStart = (haveRangeStart ? string((char*) zframe_data(rangeStartFrame), zframe_size(rangeStartFrame)) : "");
    std::string rangeEnd = (haveRangeEnd ? string((char*) zframe_data(rangeEndFrame), zframe_size(rangeEndFrame)) : "");
    //We don't need the range frames any more
    if (rangeStartFrame) {
        removeAndDestroyFrame(msg, rangeStartFrame);
    }
    if (rangeEndFrame) {
        removeAndDestroyFrame(msg, rangeEndFrame);
    }
    //Get the table to read from
    leveldb::DB* db = tables.getTable(tableId, openHelper);
    //Create the response object
    leveldb::ReadOptions readOptions;
    string value; //Where the value will be placed
    leveldb::Status status;
    //Create the iterator
    leveldb::Iterator* it = db->NewIterator(readOptions);
    if (haveRangeStart) {
        it->Seek(rangeStart);
    } else {
        it->SeekToFirst();
    }
    uint64_t count = 0;
    //Iterate over all key-values in the range
    for (; it->Valid(); it->Next()) {
        count++;
        string key = it->key().ToString();
        if (haveRangeEnd && key >= rangeEnd) {
            break;
        }
    }
    if (!it->status().ok()) { // Check for any errors found during the scan
        zframe_reset(headerFrame, errorReply, 4);
        //Reuse the table id frame to store the error message
        string status = it->status().ToString();
        zframe_reset(tableIdFrame, status.c_str(), status.length());
    } else { //Nothing happened wrong
        //Set th reply code to success if nothing happened wrong
        zframe_reset(headerFrame, ackReply, 4);
        //Reuse the table id frame to store the count
        zframe_reset(tableIdFrame, &count, sizeof (uint64_t));
    }
    //Cleanup
    delete it;
}

/**
 * Read request handler. Shall be called for Read requests only!
 *
 * The original message is modified to contain the response.
 *
 * Envelope + Read request Header + Table ID[what zmsg_next() must return] + Payload
 */
static void handleReadRequest(Tablespace& tables, zmsg_t* msg, zframe_t* headerFrame, TableOpenHelper& openHelper) {
    //Parse the table id
    zframe_t* tableIdFrame = zmsg_next(msg);
    assert(zframe_size(tableIdFrame) == sizeof (uint32_t));
    uint32_t tableId = *((uint32_t*) zframe_data(tableIdFrame));
    //Parse the
    //Get the table to read from
    leveldb::DB* db = tables.getTable(tableId, openHelper);
    //Create the response object
    leveldb::ReadOptions readOptions;
    leveldb::Status status;
    string value;
    //Read each read request
    zframe_t* keyFrame = NULL;
    while ((keyFrame = zmsg_next(msg)) != NULL) {
        //Build a slice of the key (zero-copy)
        string keystr((char*) zframe_data(keyFrame), zframe_size(keyFrame));
        leveldb::Slice key((char*) zframe_data(keyFrame), zframe_size(keyFrame));
        status = db->Get(readOptions, key, &value);
        if (status.IsNotFound()) {
            //Empty value
            zframe_reset(keyFrame, "", 0);
        } else {
            zframe_reset(keyFrame, value.c_str(), value.length());
        }
    }
    //Now we can remove the table ID frame from the message (doing so before would confuse zmsg_next())
    zmsg_remove(msg, tableIdFrame);
    zframe_destroy(&tableIdFrame);
}

/**
 * The main function for the read worker thread.
 *
 * This function parses the header, calls the appropriate handler function
 * and sends the response for PARTSYNC requests
 */
static void readWorkerThreadFunction(zctx_t* ctx, Tablespace& tablespace) {
    //Create the socket that is used to proxy requests to the external req/rep socket
    void* replyProxySocket = zsocket_new(ctx, ZMQ_PUSH);
    if (zsocket_connect(replyProxySocket, externalRequestProxyEndpoint)) {
        debugZMQError("Connect reply proxy socket", errno);
    }
    assert(replyProxySocket);
    //Create the socket we receive requests from
    void* workPullSocket = zsocket_new(ctx, ZMQ_PULL);
    zsocket_connect(workPullSocket, readWorkerThreadAddr);
    assert(workPullSocket);
    //Create the table open helper (creates a socket that sends table open requests)
    TableOpenHelper tableOpenHelper(ctx);
    //Create the data structure with all info for the poll handler
    while (true) {
        zmsg_t* msg = zmsg_recv(workPullSocket);
        assert(msg);
        assert(zmsg_size(msg) >= 1);
        //Parse the header
        //Contrary to the update message handling, we assume (checked by the request router) that
        // the message contains an envelope. Read requests without envelope (--> without return path)
        // don't really make sense.
        zframe_t* routingFrame = zmsg_first(msg); //Envelope ID
        assert(routingFrame);
        zframe_t* delimiterFrame = zmsg_next(msg); //Envelope delimiter
        assert(delimiterFrame);
        zframe_t* headerFrame = zmsg_next(msg);
        assert(headerFrame);
        assert(isHeaderFrame(headerFrame));
        //Get the request type
        RequestType requestType = getRequestType(headerFrame);
        //Process the rest of the frame
        cout << "Isize " << zmsg_size(msg) << endl;
        if (requestType == ReadRequest) {
            handleReadRequest(tablespace, msg, headerFrame, tableOpenHelper);
        } else if (requestType == CountRequest) {
            handleCountRequest(tablespace, msg, headerFrame, tableOpenHelper);
        } else {
            cerr << "Internal routing error: request type " << requestType << " routed to update worker thread!" << endl;
        }
        //Send reply (the handler function rewrote the original message to contain the reply)
        assert(msg);
        assert(zmsg_size(msg) >= 3); //2 Envelope + 1 Response header (corner case: Nothing to be read)
        cout << "Sentit" << zmsg_size(msg) << endl;
        zmsg_send(&msg, replyProxySocket);
    }
    printf("Stopping update processor\n");
    zsocket_destroy(ctx, workPullSocket);
    zsocket_destroy(ctx, replyProxySocket);
}

ReadWorkerController::ReadWorkerController(zctx_t* context, Tablespace& tablespace) : context(context), tablespace(tablespace), numThreads(3) {
    //Initialize the push socket
    workerPushSocket = zsocket_new(context, ZMQ_PUSH);
    zsocket_bind(workerPushSocket, readWorkerThreadAddr);

}

void ReadWorkerController::start() {
    threads = new std::thread*[numThreads];
    for (int i = 0; i < numThreads; i++) {
        threads[i] = new std::thread(readWorkerThreadFunction, context, std::ref(tablespace));
    }
}

ReadWorkerController::~ReadWorkerController() {

    //Send an empty STOP message for each read worker thread (use a temporary socket)
    void* tempSocket = zsocket_new(context, ZMQ_PUSH); //Create a temporary socket
    zsocket_connect(tempSocket, readWorkerThreadAddr);
    for (int i = 0; i < numThreads; i++) {
        //Send an empty msg (signals the table open thread to stop)
        sendEmptyFrameMessage(tempSocket);
    }
    //Cleanup
    zsocket_destroy(context, &tempSocket);
    //Wait for each thread to exit
    for (int i = 0; i < numThreads; i++) {
        threads[i]->join();
        delete threads[i];
    }
    //Free the array
    if (numThreads > 0) {
        delete[] threads;
    }
}

void ReadWorkerController::send(zmsg_t** msg) {
    zmsg_send(msg, workerPushSocket);
}
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#include "ICUBridgeCollationCompareFunctor.hpp"
#include "ICUBridge.hpp"

#include <PlatformSupport/DOMStringHelper.hpp>

#include <unicode/coll.h>

const StylesheetExecutionContextDefault::DefaultCollationCompareFunctor		ICUBridgeCollationCompareFunctor::s_defaultFunctor;

ICUBridgeCollationCompareFunctor::ICUBridgeCollationCompareFunctor() :
	m_isValid(false),
	m_collator(0)
{
	UErrorCode	theStatus = U_ZERO_ERROR;

	m_collator = Collator::createInstance(theStatus);

	if (theStatus == U_ZERO_ERROR || theStatus == U_USING_DEFAULT_ERROR)
	{
		m_isValid = true;
	}
}

ICUBridgeCollationCompareFunctor::~ICUBridgeCollationCompareFunctor()
{
	delete m_collator;
}

static UChar	dummy = 0;

int
ICUBridgeCollationCompareFunctor::operator()(
			const XalanDOMChar*		theLHS,
			const XalanDOMChar*		theRHS) const
{
	if (isValid() == false)
	{
		return s_defaultFunctor(theLHS, theRHS);
	}
	else
	{
		assert(m_collator != 0);

#if defined(XALAN_ICU_BRIDGE_UCHAR_MISMATCH)
		return m_collator->compare(
					ICUBridge::XalanDOMCharStringToUnicodeString(lhs),
					ICUBridge::XalanDOMCharStringToUnicodeString(rhs));
#else
		// $$$ ToDo: This code is necessary because DOMStrings can
		// have a null representation.
		const XalanDOMChar* const	lhs = theLHS == 0 ? &dummy : theLHS;
		const XalanDOMChar* const	rhs = theRHS == 0 ? &dummy : theRHS;

		return m_collator->compare(
					lhs,
					length(lhs),
					rhs,
					length(rhs));
#endif
	}
}
<commit_msg>Fixed incorrect arguments.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#include "ICUBridgeCollationCompareFunctor.hpp"
#include "ICUBridge.hpp"

#include <PlatformSupport/DOMStringHelper.hpp>

#include <unicode/coll.h>

const StylesheetExecutionContextDefault::DefaultCollationCompareFunctor		ICUBridgeCollationCompareFunctor::s_defaultFunctor;

ICUBridgeCollationCompareFunctor::ICUBridgeCollationCompareFunctor() :
	m_isValid(false),
	m_collator(0)
{
	UErrorCode	theStatus = U_ZERO_ERROR;

	m_collator = Collator::createInstance(theStatus);

	if (theStatus == U_ZERO_ERROR || theStatus == U_USING_DEFAULT_ERROR)
	{
		m_isValid = true;
	}
}

ICUBridgeCollationCompareFunctor::~ICUBridgeCollationCompareFunctor()
{
	delete m_collator;
}

static UChar	dummy = 0;

int
ICUBridgeCollationCompareFunctor::operator()(
			const XalanDOMChar*		theLHS,
			const XalanDOMChar*		theRHS) const
{
	if (isValid() == false)
	{
		return s_defaultFunctor(theLHS, theRHS);
	}
	else
	{
		assert(m_collator != 0);

#if defined(XALAN_ICU_BRIDGE_UCHAR_MISMATCH)
		return m_collator->compare(
					ICUBridge::XalanDOMCharStringToUnicodeString(theLHS),
					ICUBridge::XalanDOMCharStringToUnicodeString(theRHS));
#else
		// $$$ ToDo: This code is necessary because DOMStrings can
		// have a null representation.
		const XalanDOMChar* const	lhs = theLHS == 0 ? &dummy : theLHS;
		const XalanDOMChar* const	rhs = theRHS == 0 ? &dummy : theRHS;

		return m_collator->compare(
					lhs,
					length(lhs),
					rhs,
					length(rhs));
#endif
	}
}
<|endoftext|>