<commit_before>#include <ting/debug.hpp>
#include <algorithm>
#include <ting/Buffer.hpp>

#include "../src/cliser/ServerThread.hpp"
#include "../src/cliser/ClientThread.hpp"

namespace{
const ting::u32 DMaxCnt = 16384;
const ting::u16 DPort = 13666;
const char* DIpAddress = "127.0.0.1";
}

class Connection : public cliser::Connection{
public:

	ting::StaticBuffer<ting::u8, sizeof(ting::u32)> rbuf;
	ting::Inited<unsigned, 0> rbufBytes;

	ting::Inited<ting::u32, 0> cnt;

	ting::Inited<ting::u32, 0> rcnt;

	ting::Inited<bool, false> isConnected;

	Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	~Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	void SendPortion(){
		ASSERT_INFO(this->cnt <= DMaxCnt, "this->cnt = " << this->cnt)

		if(this->cnt == DMaxCnt){
			if(this->rcnt == DMaxCnt){
				this->Disconnect_ts();
			}
			return;
		}

		ting::Array<ting::u8> buf(sizeof(ting::u32) * ( (std::min)(ting::u32(0xfffff / 4), DMaxCnt - this->cnt)) );

		ASSERT(buf.Size() > 0)

		for(ting::u8* p = buf.Begin(); p != buf.End(); p += sizeof(ting::u32)){
			ting::Serialize32(this->cnt, p);
			++this->cnt;
		}

		this->Send_ts(buf);
	}

	void HandleReceivedData(const ting::Buffer<ting::u8>& d){
		for(const ting::u8* p = d.Begin(); p != d.End(); ++p){
			this->rbuf[this->rbufBytes] = *p;
			++this->rbufBytes;

			if(this->rbufBytes == this->rbuf.Size()){
				this->rbufBytes = 0;
				ting::u32 num = ting::Deserialize32(this->rbuf.Begin());
				ASSERT_INFO_ALWAYS(this->rcnt == num, "num = " << num << " rcnt = " << this->rcnt)
				++this->rcnt;
			}
		}

		ASSERT_INFO(this->rcnt <= DMaxCnt, "this->rcnt = " << this->rcnt)

		if(this->rcnt == DMaxCnt){
			if(this->cnt == DMaxCnt){
				this->Disconnect_ts();
			}
		}
	}

	static ting::Ref<Connection> New(){
		return ting::Ref<Connection>(new Connection());
	}
};

class Server : private cliser::Listener, public cliser::ServerThread{
public:
	Server() :
			cliser::Listener(),
			cliser::ServerThread(DPort, 2, this)
	{}

private:
	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		//do nothing
	}

	class HandleDataMessage : public ting::Message{
		ting::Ref<Connection> conn;
	public:
		HandleDataMessage(const ting::Ref<Connection>& conn) :
				conn(conn)
		{}

		//override
		void Handle(){
			if(ting::Array<ting::u8> d = this->conn->GetReceivedData_ts()){
				this->conn->HandleReceivedData(d);
			}
		}
	};

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		TRACE_ALWAYS(<< "Client: data received" << std::endl)
		this->PushMessage(
				ting::Ptr<ting::Message>(
						new HandleDataMessage(c.StaticCast<Connection>())
					)
			);

		return false;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

class Client : private cliser::Listener, public cliser::ClientThread{
public:
	Client() :
			cliser::Listener(),
			cliser::ClientThread(63, this) //max connections
	{}

private:
	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_ALWAYS(!conn->isConnected)
		conn->isConnected = true;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		conn->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		ting::Ref<Connection> conn = c.StaticCast<Connection>();

		if(conn->isConnected){
			conn->isConnected = false;
		}else{
			//if we get here then it is a connect request failure
			ASSERT_ALWAYS(false)
		}

		this->Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		ting::Ref<Connection> con = c.StaticCast<Connection>();

		con->HandleReceivedData(d);
		TRACE_ALWAYS(<< "Server: data received" << std::endl)
		return true;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

int main(int argc, char *argv[]){
	TRACE_ALWAYS(<< "Cliser test" << std::endl)

	ting::SocketLib socketsLib;

	Server server;
	server.Start();

	ting::Thread::Sleep(100);//give server thread some time to start waiting on the socket

	Client client;
	client.Start();

	for(unsigned i = 0; i < client.MaxConnections(); ++i){
		client.Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	ting::Thread::Sleep(20000);

    client.PushQuitMessage();
    client.Join();

	server.PushQuitMessage();
	server.Join();

	return 0;
}
<commit_msg>infinite running of connect/disconnect test<commit_after>#include <ting/debug.hpp>
#include <algorithm>
#include <ting/Buffer.hpp>

#include "../src/cliser/ServerThread.hpp"
#include "../src/cliser/ClientThread.hpp"

namespace{
const ting::u32 DMaxCnt = 16384;
const ting::u16 DPort = 13666;
const char* DIpAddress = "127.0.0.1";
}

class Connection : public cliser::Connection{
public:

	ting::StaticBuffer<ting::u8, sizeof(ting::u32)> rbuf;
	ting::Inited<unsigned, 0> rbufBytes;

	ting::Inited<ting::u32, 0> cnt;

	ting::Inited<ting::u32, 0> rcnt;

	ting::Inited<bool, false> isConnected;

	Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	~Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	void SendPortion(){
		ASSERT_INFO(this->cnt <= DMaxCnt, "this->cnt = " << this->cnt)

		if(this->cnt == DMaxCnt){
			if(this->rcnt == DMaxCnt){
				this->Disconnect_ts();
			}
			return;
		}

		ting::Array<ting::u8> buf(sizeof(ting::u32) * ( (std::min)(ting::u32(0xfffff / 4), DMaxCnt - this->cnt)) );

		ASSERT(buf.Size() > 0)

		for(ting::u8* p = buf.Begin(); p != buf.End(); p += sizeof(ting::u32)){
			ting::Serialize32(this->cnt, p);
			++this->cnt;
		}

		this->Send_ts(buf);
	}

	void HandleReceivedData(const ting::Buffer<ting::u8>& d){
		for(const ting::u8* p = d.Begin(); p != d.End(); ++p){
			this->rbuf[this->rbufBytes] = *p;
			++this->rbufBytes;

			if(this->rbufBytes == this->rbuf.Size()){
				this->rbufBytes = 0;
				ting::u32 num = ting::Deserialize32(this->rbuf.Begin());
				ASSERT_INFO_ALWAYS(this->rcnt == num, "num = " << num << " rcnt = " << this->rcnt)
				++this->rcnt;
			}
		}

		ASSERT_INFO(this->rcnt <= DMaxCnt, "this->rcnt = " << this->rcnt)

		if(this->rcnt == DMaxCnt){
			if(this->cnt == DMaxCnt){
				this->Disconnect_ts();
			}
		}
	}

	static ting::Ref<Connection> New(){
		return ting::Ref<Connection>(new Connection());
	}
};

class Server : private cliser::Listener, public cliser::ServerThread{
public:
	Server() :
			cliser::Listener(),
			cliser::ServerThread(DPort, 2, this)
	{}

private:
	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		//do nothing
	}

	class HandleDataMessage : public ting::Message{
		ting::Ref<Connection> conn;
	public:
		HandleDataMessage(const ting::Ref<Connection>& conn) :
				conn(conn)
		{}

		//override
		void Handle(){
			if(ting::Array<ting::u8> d = this->conn->GetReceivedData_ts()){
				this->conn->HandleReceivedData(d);
			}
		}
	};

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		TRACE_ALWAYS(<< "Client: data received" << std::endl)
		this->PushMessage(
				ting::Ptr<ting::Message>(
						new HandleDataMessage(c.StaticCast<Connection>())
					)
			);

		return false;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

class Client : private cliser::Listener, public cliser::ClientThread{
public:
	Client() :
			cliser::Listener(),
			cliser::ClientThread(63, this) //max connections
	{}

private:
	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_ALWAYS(!conn->isConnected)
		conn->isConnected = true;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		conn->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		ting::Ref<Connection> conn = c.StaticCast<Connection>();

		if(conn->isConnected){
			conn->isConnected = false;
		}else{
			//if we get here then it is a connect request failure
			ASSERT_ALWAYS(false)
		}

		this->Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		ting::Ref<Connection> con = c.StaticCast<Connection>();

		con->HandleReceivedData(d);
		TRACE_ALWAYS(<< "Server: data received" << std::endl)
		return true;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

int main(int argc, char *argv[]){
	TRACE_ALWAYS(<< "Cliser test" << std::endl)

	unsigned msec = 20000;

	if(argc >= 2){
		if(std::string("0") == argv[1]){
			msec = 0;
		}
	}

	ting::SocketLib socketsLib;

	Server server;
	server.Start();

	ting::Thread::Sleep(100);//give server thread some time to start waiting on the socket

	Client client;
	client.Start();

	for(unsigned i = 0; i < client.MaxConnections(); ++i){
		client.Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	if(msec == 0){
		while(true){
			ting::Thread::Sleep(1000000);
		}
	}else{
		ting::Thread::Sleep(20000);
	}

    client.PushQuitMessage();
    client.Join();

	server.PushQuitMessage();
	server.Join();

	return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
 * EXPRESS 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 <copyright holder> 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.
 */

#include "CppUTest/TestHarness.h"
#include "CppUTest/MemoryLeakDetector.h"
#include "CppUTest/PlatformSpecificFunctions.h"

class MemoryLeakFailureForTest : public MemoryLeakFailure
{
public:
   virtual ~MemoryLeakFailureForTest(){};
   virtual void fail(char* fail_string) {*message = fail_string;}

   SimpleString *message;
};

TEST_GROUP(MemoryLeakDetectorTest)
{
   MemoryLeakDetector* detector;
   MemoryLeakFailureForTest *reporter;

   void setup()
   {
	  detector = new MemoryLeakDetector;
	  reporter = new MemoryLeakFailureForTest;
      detector->init(reporter);
      detector->enable();
      detector->startChecking();
      reporter->message = new SimpleString();
   }
   void teardown()
   {
	  delete reporter->message;
	  delete detector;
	  delete reporter;
   }
};

TEST(MemoryLeakDetectorTest, OneLeak)
{
   char* mem = detector->allocOperatorNew(4);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains(MEM_LEAK_HEADER));
   CHECK(output.contains("size: 4"));
   CHECK(output.contains("new"));
   CHECK(output.contains(MEM_LEAK_FOOTER));
   PlatformSpecificFree(mem);
}

TEST(MemoryLeakDetectorTest, OneLeakOutsideCheckingPeriod)
{
   detector->stopChecking();
   char* mem = detector->allocOperatorNew(4);
   SimpleString output = detector->report(mem_leak_period_all);
   CHECK(output.contains(MEM_LEAK_HEADER));
   CHECK(output.contains("size: 4"));
   CHECK(output.contains("new"));
   CHECK(output.contains(MEM_LEAK_FOOTER));
   PlatformSpecificFree(mem);
}

TEST(MemoryLeakDetectorTest, NoLeaksWhatsoever)
{
   detector->stopChecking();
   STRCMP_EQUAL(MEM_LEAK_NONE, detector->report(mem_leak_period_checking));
   STRCMP_EQUAL(MEM_LEAK_NONE, detector->report(mem_leak_period_all));
}

TEST(MemoryLeakDetectorTest, TwoLeaksUsingOperatorNew)
{
   char* mem = detector->allocOperatorNew(4);
   char* mem2 = detector->allocOperatorNew(8);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_checking));
   CHECK(output.contains("size: 8"));
   CHECK(output.contains("size: 4"));
   PlatformSpecificFree(mem);
   PlatformSpecificFree(mem2);
}

TEST(MemoryLeakDetectorTest, OneAllocButNoLeak)
{
   char* mem = detector->allocOperatorNew(4);
   detector->freeOperatorDelete(mem);
   detector->stopChecking();
   STRCMP_EQUAL(MEM_LEAK_NONE, detector->report(mem_leak_period_checking));
}

TEST(MemoryLeakDetectorTest, TwoAllocOneFreeOneLeak)
{
   char* mem = detector->allocOperatorNew(4);
   char* mem2 = detector->allocOperatorNew(12);
   detector->freeOperatorDelete(mem);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   CHECK(output.contains("size: 12"));
   CHECK(!output.contains("size: 4"));
   PlatformSpecificFree (mem2);
}

TEST(MemoryLeakDetectorTest, TwoAllocOneFreeOneLeakReverseOrder)
{
   char* mem = detector->allocOperatorNew(4);
   char* mem2 = detector->allocOperatorNew(12);
   detector->freeOperatorDelete(mem2);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   CHECK(!output.contains("size: 12"));
   CHECK(output.contains("size: 4"));
   PlatformSpecificFree (mem);
}

TEST(MemoryLeakDetectorTest, DeleteNonAlocatedMemory)
{
   char a;
   char* pa = &a;
   detector->freeFree(pa, "FREE.c", 100);
   detector->stopChecking();
   CHECK(reporter->message->contains(MEM_LEAK_DEALLOC_NON_ALLOCATED));
   CHECK(reporter->message->contains("   allocated at file: <unknown> line: 0 size: 0 type: unknown"));
   CHECK(reporter->message->contains("   dealllocated at file: FREE.c line: 100 type: malloc"));
}

TEST(MemoryLeakDetectorTest, IgnoreMemoryAllocatedOutsideCheckingPeriod)
{
   detector->stopChecking();
   char* mem = detector->allocOperatorNew(4);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->freeOperatorDelete(mem);
}

TEST(MemoryLeakDetectorTest, IgnoreMemoryAllocatedOutsideCheckingPeriodComplicatedCase)
{
   char* mem = detector->allocOperatorNew(4);
   detector->stopChecking();

   char* mem2 = detector->allocOperatorNew(8);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->clearAllAccounting(mem_leak_period_checking);
   PlatformSpecificFree(mem);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));

   detector->startChecking();
   char* mem3 = detector->allocOperatorNew(4);
   detector->stopChecking();
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));

   detector->clearAllAccounting(mem_leak_period_checking);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));

   detector->clearAllAccounting(mem_leak_period_all);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   PlatformSpecificFree(mem2);
   PlatformSpecificFree(mem3);
}

TEST(MemoryLeakDetectorTest, OneLeakUsingOperatorNewWithFileLine)
{
   char* mem = detector->allocOperatorNew(4, "file.cpp", 1234);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("file.cpp"));
   CHECK(output.contains("1234"));
   PlatformSpecificFree(mem);
}

TEST(MemoryLeakDetectorTest, OneAllocAndFreeUsingArrayNew)
{
   char* mem = detector->allocOperatorNewArray(10, "file.cpp", 1234);
   char* mem2 = detector->allocOperatorNewArray(12);
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("new []"));
   detector->freeOperatorDeleteArray(mem);
   detector->freeOperatorDeleteArray(mem2);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->stopChecking();
}

TEST(MemoryLeakDetectorTest, OneAllocAndFreeUsingMalloc)
{
   char* mem = detector->allocMalloc(10, "file.cpp", 1234);
   char* mem2 = detector->allocMalloc(12);
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_checking));
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("malloc"));
   detector->freeFree(mem);
   detector->freeFree(mem2, "file.c", 5678);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->stopChecking();
}

TEST(MemoryLeakDetectorTest, OneRealloc)
{
   char* mem1 = detector->allocMalloc(10, "file.cpp", 1234);

   char* mem2 = detector->allocRealloc(mem1, 1000, "other.cpp", 5678);

   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("other.cpp"));

   detector->freeFree(mem2);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->stopChecking();
}

TEST(MemoryLeakDetectorTest, AllocOneTypeFreeAnotherType)
{
   char* mem = detector->allocOperatorNewArray(100, "ALLOC.c", 10);
   detector->freeFree(mem, "FREE.c", 100);
   detector->stopChecking();
   CHECK(reporter->message->contains(MEM_LEAK_ALLOC_DEALLOC_MISMATCH));
   CHECK(reporter->message->contains("   allocated at file: ALLOC.c line: 10 size: 100 type: new []"));
   CHECK(reporter->message->contains("   dealllocated at file: FREE.c line: 100 type: malloc"));
}

TEST(MemoryLeakDetectorTest, AllocOneTypeFreeAnotherTypeWithCheckingDisabled)
{
   detector->disableAllocationTypeChecking ();
   char* mem = detector->allocOperatorNewArray(100, "ALLOC.c", 10);
   detector->freeFree(mem, "FREE.c", 100);
   detector->stopChecking();
   STRCMP_EQUAL("", reporter->message->asCharString());
   detector->enableAllocationTypeChecking ();
}

TEST(MemoryLeakDetectorTest, MarkCheckingPeriodLeaksAsNonCheckingPeriod)
{
   char* mem = detector->allocOperatorNewArray(100);
   char* mem2 = detector->allocOperatorNewArray(100);
   detector->stopChecking();
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->markCheckingPeriodLeaksAsNonCheckingPeriod();
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));
   PlatformSpecificFree(mem);
   PlatformSpecificFree(mem2);
}

TEST(MemoryLeakDetectorTest, memoryCorruption)
{
   char* mem = detector->allocMalloc(10, "ALLOC.c", 10);
   mem[10] = 'O';
   mem[11] = 'H';
   detector->freeFree(mem, "FREE.c", 100);
   detector->stopChecking();
   CHECK(reporter->message->contains(MEM_LEAK_MEMORY_CORRUPTION));
   CHECK(reporter->message->contains("   allocated at file: ALLOC.c line: 10 size: 10 type: malloc"));
   CHECK(reporter->message->contains("   dealllocated at file: FREE.c line: 100 type: malloc"));
}

TEST(MemoryLeakDetectorTest, safelyDeleteNULL)
{
   detector->freeOperatorDelete(0);
   STRCMP_EQUAL("", reporter->message->asCharString());
}

TEST(MemoryLeakDetectorTest, periodDisabled)
{
   detector->disable();
   char* mem = detector->allocMalloc(2);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_disabled));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_enabled));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->freeFree(mem);
}

TEST(MemoryLeakDetectorTest, periodEnabled)
{
   detector->enable();
   char* mem = detector->allocMalloc(2);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_disabled));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_enabled));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->freeFree(mem);
}

TEST(MemoryLeakDetectorTest, periodChecking)
{
   char* mem = detector->allocMalloc(2);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_disabled));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_enabled));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->freeFree(mem);
}

<commit_msg><commit_after>/*
 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
 * EXPRESS 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 <copyright holder> 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.
 */

#include "CppUTest/TestHarness.h"
#include "CppUTest/MemoryLeakDetector.h"
#include "CppUTest/PlatformSpecificFunctions.h"

class MemoryLeakFailureForTest : public MemoryLeakFailure
{
public:
   virtual ~MemoryLeakFailureForTest(){};
   virtual void fail(char* fail_string) {*message = fail_string;}

   SimpleString *message;
};

TEST_GROUP(MemoryLeakDetectorTest)
{
   MemoryLeakDetector* detector;
   MemoryLeakFailureForTest *reporter;

   void setup()
   {
	  detector = new MemoryLeakDetector;
	  reporter = new MemoryLeakFailureForTest;
      detector->init(reporter);
      detector->enable();
      detector->startChecking();
      reporter->message = new SimpleString();
   }
   void teardown()
   {
	  delete reporter->message;
	  delete detector;
	  delete reporter;
   }
};

TEST(MemoryLeakDetectorTest, OneLeak)
{
   char* mem = detector->allocOperatorNew(1);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains(MEM_LEAK_HEADER));
   CHECK(output.contains("size: 1"));
   CHECK(output.contains("new"));
   CHECK(output.contains(MEM_LEAK_FOOTER));
   PlatformSpecificFree(mem);
}

TEST(MemoryLeakDetectorTest, OneLeakOutsideCheckingPeriod)
{
   detector->stopChecking();
   char* mem = detector->allocOperatorNew(4);
   SimpleString output = detector->report(mem_leak_period_all);
   CHECK(output.contains(MEM_LEAK_HEADER));
   CHECK(output.contains("size: 4"));
   CHECK(output.contains("new"));
   CHECK(output.contains(MEM_LEAK_FOOTER));
   PlatformSpecificFree(mem);
}

TEST(MemoryLeakDetectorTest, NoLeaksWhatsoever)
{
   detector->stopChecking();
   STRCMP_EQUAL(MEM_LEAK_NONE, detector->report(mem_leak_period_checking));
   STRCMP_EQUAL(MEM_LEAK_NONE, detector->report(mem_leak_period_all));
}

TEST(MemoryLeakDetectorTest, TwoLeaksUsingOperatorNew)
{
   char* mem = detector->allocOperatorNew(4);
   char* mem2 = detector->allocOperatorNew(8);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_checking));
   CHECK(output.contains("size: 8"));
   CHECK(output.contains("size: 4"));
   PlatformSpecificFree(mem);
   PlatformSpecificFree(mem2);
}

TEST(MemoryLeakDetectorTest, OneAllocButNoLeak)
{
   char* mem = detector->allocOperatorNew(4);
   detector->freeOperatorDelete(mem);
   detector->stopChecking();
   STRCMP_EQUAL(MEM_LEAK_NONE, detector->report(mem_leak_period_checking));
}

TEST(MemoryLeakDetectorTest, TwoAllocOneFreeOneLeak)
{
   char* mem = detector->allocOperatorNew(4);
   char* mem2 = detector->allocOperatorNew(12);
   detector->freeOperatorDelete(mem);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   CHECK(output.contains("size: 12"));
   CHECK(!output.contains("size: 4"));
   PlatformSpecificFree (mem2);
}

TEST(MemoryLeakDetectorTest, TwoAllocOneFreeOneLeakReverseOrder)
{
   char* mem = detector->allocOperatorNew(4);
   char* mem2 = detector->allocOperatorNew(12);
   detector->freeOperatorDelete(mem2);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   CHECK(!output.contains("size: 12"));
   CHECK(output.contains("size: 4"));
   PlatformSpecificFree (mem);
}

TEST(MemoryLeakDetectorTest, DeleteNonAlocatedMemory)
{
   char a;
   char* pa = &a;
   detector->freeFree(pa, "FREE.c", 100);
   detector->stopChecking();
   CHECK(reporter->message->contains(MEM_LEAK_DEALLOC_NON_ALLOCATED));
   CHECK(reporter->message->contains("   allocated at file: <unknown> line: 0 size: 0 type: unknown"));
   CHECK(reporter->message->contains("   dealllocated at file: FREE.c line: 100 type: malloc"));
}

TEST(MemoryLeakDetectorTest, IgnoreMemoryAllocatedOutsideCheckingPeriod)
{
   detector->stopChecking();
   char* mem = detector->allocOperatorNew(4);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->freeOperatorDelete(mem);
}

TEST(MemoryLeakDetectorTest, IgnoreMemoryAllocatedOutsideCheckingPeriodComplicatedCase)
{
   char* mem = detector->allocOperatorNew(4);
   detector->stopChecking();

   char* mem2 = detector->allocOperatorNew(8);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->clearAllAccounting(mem_leak_period_checking);
   PlatformSpecificFree(mem);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));

   detector->startChecking();
   char* mem3 = detector->allocOperatorNew(4);
   detector->stopChecking();
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));

   detector->clearAllAccounting(mem_leak_period_checking);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));

   detector->clearAllAccounting(mem_leak_period_all);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   PlatformSpecificFree(mem2);
   PlatformSpecificFree(mem3);
}

TEST(MemoryLeakDetectorTest, OneLeakUsingOperatorNewWithFileLine)
{
   char* mem = detector->allocOperatorNew(4, "file.cpp", 1234);
   detector->stopChecking();
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("file.cpp"));
   CHECK(output.contains("1234"));
   PlatformSpecificFree(mem);
}

TEST(MemoryLeakDetectorTest, OneAllocAndFreeUsingArrayNew)
{
   char* mem = detector->allocOperatorNewArray(10, "file.cpp", 1234);
   char* mem2 = detector->allocOperatorNewArray(12);
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("new []"));
   detector->freeOperatorDeleteArray(mem);
   detector->freeOperatorDeleteArray(mem2);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->stopChecking();
}

TEST(MemoryLeakDetectorTest, OneAllocAndFreeUsingMalloc)
{
   char* mem = detector->allocMalloc(10, "file.cpp", 1234);
   char* mem2 = detector->allocMalloc(12);
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_checking));
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("malloc"));
   detector->freeFree(mem);
   detector->freeFree(mem2, "file.c", 5678);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->stopChecking();
}

TEST(MemoryLeakDetectorTest, OneRealloc)
{
   char* mem1 = detector->allocMalloc(10, "file.cpp", 1234);

   char* mem2 = detector->allocRealloc(mem1, 1000, "other.cpp", 5678);

   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   SimpleString output = detector->report(mem_leak_period_checking);
   CHECK(output.contains("other.cpp"));

   detector->freeFree(mem2);
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->stopChecking();
}

TEST(MemoryLeakDetectorTest, AllocOneTypeFreeAnotherType)
{
   char* mem = detector->allocOperatorNewArray(100, "ALLOC.c", 10);
   detector->freeFree(mem, "FREE.c", 100);
   detector->stopChecking();
   CHECK(reporter->message->contains(MEM_LEAK_ALLOC_DEALLOC_MISMATCH));
   CHECK(reporter->message->contains("   allocated at file: ALLOC.c line: 10 size: 100 type: new []"));
   CHECK(reporter->message->contains("   dealllocated at file: FREE.c line: 100 type: malloc"));
}

TEST(MemoryLeakDetectorTest, AllocOneTypeFreeAnotherTypeWithCheckingDisabled)
{
   detector->disableAllocationTypeChecking ();
   char* mem = detector->allocOperatorNewArray(100, "ALLOC.c", 10);
   detector->freeFree(mem, "FREE.c", 100);
   detector->stopChecking();
   STRCMP_EQUAL("", reporter->message->asCharString());
   detector->enableAllocationTypeChecking ();
}

TEST(MemoryLeakDetectorTest, MarkCheckingPeriodLeaksAsNonCheckingPeriod)
{
   char* mem = detector->allocOperatorNewArray(100);
   char* mem2 = detector->allocOperatorNewArray(100);
   detector->stopChecking();
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));
   detector->markCheckingPeriodLeaksAsNonCheckingPeriod();
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   LONGS_EQUAL(2, detector->totalMemoryLeaks(mem_leak_period_all));
   PlatformSpecificFree(mem);
   PlatformSpecificFree(mem2);
}

TEST(MemoryLeakDetectorTest, memoryCorruption)
{
   char* mem = detector->allocMalloc(10, "ALLOC.c", 10);
   mem[10] = 'O';
   mem[11] = 'H';
   detector->freeFree(mem, "FREE.c", 100);
   detector->stopChecking();
   CHECK(reporter->message->contains(MEM_LEAK_MEMORY_CORRUPTION));
   CHECK(reporter->message->contains("   allocated at file: ALLOC.c line: 10 size: 10 type: malloc"));
   CHECK(reporter->message->contains("   dealllocated at file: FREE.c line: 100 type: malloc"));
}

TEST(MemoryLeakDetectorTest, safelyDeleteNULL)
{
   detector->freeOperatorDelete(0);
   STRCMP_EQUAL("", reporter->message->asCharString());
}

TEST(MemoryLeakDetectorTest, periodDisabled)
{
   detector->disable();
   char* mem = detector->allocMalloc(2);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_disabled));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_enabled));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->freeFree(mem);
}

TEST(MemoryLeakDetectorTest, periodEnabled)
{
   detector->enable();
   char* mem = detector->allocMalloc(2);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_disabled));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_enabled));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->freeFree(mem);
}

TEST(MemoryLeakDetectorTest, periodChecking)
{
   char* mem = detector->allocMalloc(2);
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_all));
   LONGS_EQUAL(0, detector->totalMemoryLeaks(mem_leak_period_disabled));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_enabled));
   LONGS_EQUAL(1, detector->totalMemoryLeaks(mem_leak_period_checking));
   detector->freeFree(mem);
}

<|endoftext|>


<commit_before>/*
 * Copyright 2007-2022 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS 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
 * FOUNDATION OR 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.
 */

#pragma once

#include "Session.hxx"
#include "pool/UniquePtr.hxx"
#include "event/net/BufferedSocket.hxx"
#include "net/SocketAddress.hxx"
#include "util/IntrusiveList.hxx"

struct pool;
class FilteredSocket;
class HttpServerConnectionHandler;
class HttpServerRequestHandler;

namespace NgHttp2 {

class ServerConnection final : BufferedSocketHandler {
	struct pool &pool;

	const UniquePoolPtr<FilteredSocket> socket;

	HttpServerConnectionHandler &handler;
	HttpServerRequestHandler &request_handler;

	const SocketAddress local_address, remote_address;

	const char *const local_host_and_port;
	const char *const remote_host;

	NgHttp2::Session session;

	class Request;
	using RequestList = IntrusiveList<Request>;

	RequestList requests;

#ifndef NDEBUG
	/**
	 * The total number of bytes passed to our
	 * data_chunk_recv_callback() which needs to be reported to
	 * nghttp2_session_consume().
	 */
	size_t unconsumed = 0;
#endif

public:
	ServerConnection(struct pool &_pool,
			 UniquePoolPtr<FilteredSocket> _socket,
			 SocketAddress remote_address,
			 HttpServerConnectionHandler &_handler,
			 HttpServerRequestHandler &request_handler);

	~ServerConnection() noexcept;

	void Consume(std::size_t nbytes) noexcept {
		nghttp2_session_consume_connection(session.get(),
						   nbytes);
		DeferWrite();
	}

private:
	void DeferWrite() noexcept;

	ssize_t SendCallback(std::span<const std::byte> src) noexcept;

	static ssize_t SendCallback(nghttp2_session *, const uint8_t *data,
				    size_t length, int,
				    void *user_data) noexcept {
		auto &c = *(ServerConnection *)user_data;
		return c.SendCallback({(const std::byte *)data, length});
	}

	int OnFrameRecvCallback(const nghttp2_frame *frame) noexcept;

	static int OnFrameRecvCallback(nghttp2_session *,
				       const nghttp2_frame *frame,
				       void *user_data) noexcept {
		auto &c = *(ServerConnection *)user_data;
		return c.OnFrameRecvCallback(frame);
	}

	int OnBeginHeaderCallback(const nghttp2_frame *frame) noexcept;

	static int OnBeginHeaderCallback(nghttp2_session *,
					 const nghttp2_frame *frame,
					 void *user_data) noexcept {
		auto &c = *(ServerConnection *)user_data;
		return c.OnBeginHeaderCallback(frame);
	}

	/* virtual methods from class BufferedSocketHandler */
	BufferedResult OnBufferedData() override;
	bool OnBufferedClosed() noexcept override;
	bool OnBufferedWrite() override;
	// TODO bool OnBufferedDrained() noexcept override;
	void OnBufferedError(std::exception_ptr e) noexcept override;
};

} // namespace NgHttp2
<commit_msg>nghttp2/Server: remove obsolete debug-only field "unconsumed"<commit_after>/*
 * Copyright 2007-2022 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS 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
 * FOUNDATION OR 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.
 */

#pragma once

#include "Session.hxx"
#include "pool/UniquePtr.hxx"
#include "event/net/BufferedSocket.hxx"
#include "net/SocketAddress.hxx"
#include "util/IntrusiveList.hxx"

struct pool;
class FilteredSocket;
class HttpServerConnectionHandler;
class HttpServerRequestHandler;

namespace NgHttp2 {

class ServerConnection final : BufferedSocketHandler {
	struct pool &pool;

	const UniquePoolPtr<FilteredSocket> socket;

	HttpServerConnectionHandler &handler;
	HttpServerRequestHandler &request_handler;

	const SocketAddress local_address, remote_address;

	const char *const local_host_and_port;
	const char *const remote_host;

	NgHttp2::Session session;

	class Request;
	using RequestList = IntrusiveList<Request>;

	RequestList requests;

public:
	ServerConnection(struct pool &_pool,
			 UniquePoolPtr<FilteredSocket> _socket,
			 SocketAddress remote_address,
			 HttpServerConnectionHandler &_handler,
			 HttpServerRequestHandler &request_handler);

	~ServerConnection() noexcept;

	void Consume(std::size_t nbytes) noexcept {
		nghttp2_session_consume_connection(session.get(),
						   nbytes);
		DeferWrite();
	}

private:
	void DeferWrite() noexcept;

	ssize_t SendCallback(std::span<const std::byte> src) noexcept;

	static ssize_t SendCallback(nghttp2_session *, const uint8_t *data,
				    size_t length, int,
				    void *user_data) noexcept {
		auto &c = *(ServerConnection *)user_data;
		return c.SendCallback({(const std::byte *)data, length});
	}

	int OnFrameRecvCallback(const nghttp2_frame *frame) noexcept;

	static int OnFrameRecvCallback(nghttp2_session *,
				       const nghttp2_frame *frame,
				       void *user_data) noexcept {
		auto &c = *(ServerConnection *)user_data;
		return c.OnFrameRecvCallback(frame);
	}

	int OnBeginHeaderCallback(const nghttp2_frame *frame) noexcept;

	static int OnBeginHeaderCallback(nghttp2_session *,
					 const nghttp2_frame *frame,
					 void *user_data) noexcept {
		auto &c = *(ServerConnection *)user_data;
		return c.OnBeginHeaderCallback(frame);
	}

	/* virtual methods from class BufferedSocketHandler */
	BufferedResult OnBufferedData() override;
	bool OnBufferedClosed() noexcept override;
	bool OnBufferedWrite() override;
	// TODO bool OnBufferedDrained() noexcept override;
	void OnBufferedError(std::exception_ptr e) noexcept override;
};

} // namespace NgHttp2
<|endoftext|>


<commit_before>/*
 * RandomNumber.cc
 *
 * Copyright (C) 2015 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the
 * exceptions at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/util/mt19937ar.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/NumberNode.h>

#include "RandomNumber.h"

using namespace opencog;

static MT19937RandGen randy(616432);

void RandomNumberLink::init()
{
	if (_outgoing.size() != 2)
		throw SyntaxException(TRACE_INFO,
			"Expecting a numerical min and max; got %s",
			toString().c_str());
}

RandomNumberLink::RandomNumberLink(const HandleSeq& oset,
                       TruthValuePtr tv, AttentionValuePtr av)
	: FunctionLink(RANDOM_NUMBER_LINK, oset, tv, av)
{
	init();
}

RandomNumberLink::RandomNumberLink(Link &l)
	: FunctionLink(l)
{
	// Type must be as expected
	Type tscope = l.getType();
	if (not classserver().isA(tscope, RANDOM_NUMBER_LINK))
	{
		const std::string& tname = classserver().getTypeName(tscope);
		throw InvalidParamException(TRACE_INFO,
			"Expecting an RandomNumberLink, got %s", tname.c_str());
	}
	init();
}

// ---------------------------------------------------------------

// Pattern matching hack. The pattern matcher returns sets of atoms;
// if that set contains numbers or something numeric, then unwrap it.
static NumberNodePtr unwrap_set(Handle h)
{
	if (SET_LINK == h->getType())
	{
		if (1 != h->getArity())
			throw SyntaxException(TRACE_INFO,
				"Don't know how to do arithmetic with this: %s",
				h->toString().c_str());
		h = h->getOutgoingAtom(0);
	}

	NumberNodePtr na(NumberNodeCast(h));
	if (nullptr == na)
		throw SyntaxException(TRACE_INFO,
			"Don't know how to do arithmetic with this: %s",
			h->toString().c_str());
	return na;
}

Handle RandomNumberLink::execute(AtomSpace * as) const
{
	NumberNodePtr nmin(unwrap_set(_outgoing[0]));
	NumberNodePtr nmax(unwrap_set(_outgoing[1]));

	double cept = nmin->get_value();
	double slope = nmax->get_value() - cept;

	double ary = slope * randy.randdouble() + cept;

	// XXX This is probably wrong ... if the as is null, we should
	// probably use the atomspace that this link is in, right?
	// We need to make a decision here and in many other places...
	// We should probably be doing "lazy-add-to-atomsapce" ...
	if (NULL == as)
		return Handle(createNumberNode(ary));

	return as->add_atom(createNumberNode(ary));
}

/* ===================== END OF FILE ===================== */
<commit_msg>Clarify the error message<commit_after>/*
 * RandomNumber.cc
 *
 * Copyright (C) 2015 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the
 * exceptions at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/util/mt19937ar.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/NumberNode.h>

#include "RandomNumber.h"

using namespace opencog;

static MT19937RandGen randy(616432);

void RandomNumberLink::init()
{
	if (_outgoing.size() != 2)
		throw SyntaxException(TRACE_INFO,
			"Expecting a numerical min and max; got %s",
			toString().c_str());
}

RandomNumberLink::RandomNumberLink(const HandleSeq& oset,
                       TruthValuePtr tv, AttentionValuePtr av)
	: FunctionLink(RANDOM_NUMBER_LINK, oset, tv, av)
{
	init();
}

RandomNumberLink::RandomNumberLink(Link &l)
	: FunctionLink(l)
{
	// Type must be as expected
	Type tscope = l.getType();
	if (not classserver().isA(tscope, RANDOM_NUMBER_LINK))
	{
		const std::string& tname = classserver().getTypeName(tscope);
		throw InvalidParamException(TRACE_INFO,
			"Expecting an RandomNumberLink, got %s", tname.c_str());
	}
	init();
}

// ---------------------------------------------------------------

// Pattern matching hack. The pattern matcher returns sets of atoms;
// if that set contains numbers or something numeric, then unwrap it.
static NumberNodePtr unwrap_set(Handle h)
{
	if (SET_LINK == h->getType())
	{
		if (0 == h->getArity())
			throw SyntaxException(TRACE_INFO,
				"Expecting a number, got the empty set!\n");
		if (1 != h->getArity())
			throw SyntaxException(TRACE_INFO,
				"Expecting only one number, got more than that: %s",
				h->toString().c_str());
		h = h->getOutgoingAtom(0);
	}

	NumberNodePtr na(NumberNodeCast(h));
	if (nullptr == na)
		throw SyntaxException(TRACE_INFO,
			"Expecting a number, got this: %s",
			h->toString().c_str());
	return na;
}

Handle RandomNumberLink::execute(AtomSpace * as) const
{
	NumberNodePtr nmin(unwrap_set(_outgoing[0]));
	NumberNodePtr nmax(unwrap_set(_outgoing[1]));

	double cept = nmin->get_value();
	double slope = nmax->get_value() - cept;

	double ary = slope * randy.randdouble() + cept;

	// XXX This is probably wrong ... if the as is null, we should
	// probably use the atomspace that this link is in, right?
	// We need to make a decision here and in many other places...
	// We should probably be doing "lazy-add-to-atomsapce" ...
	if (NULL == as)
		return Handle(createNumberNode(ary));

	return as->add_atom(createNumberNode(ary));
}

/* ===================== END OF FILE ===================== */
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Raine Makelainen <raine.makelainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "testobject.h"

#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickView>
#include <QQuickItem>
#include <QSignalSpy>
#include <QTime>
#include <QtTest>

TestObject::TestObject()
    : QObject()
{
    QTime time = QTime::currentTime();
    qsrand((uint)time.msec());
}

TestObject::TestObject(QByteArray qmlData)
    : QObject()
{
    QTime time = QTime::currentTime();
    qsrand((uint)time.msec());

    setTestData(qmlData);
    mView.showFullScreen();
    QTest::qWaitForWindowExposed(&mView);
}

void TestObject::init(const QUrl &url)
{
    setTestUrl(url);
    mView.showFullScreen();
    QTest::qWaitForWindowExposed(&mView);
}

/*!
    Wait signal of \a spy to be emitted \a expectedSignalCount.

    Note: this might cause indefinite loop, if not used cautiously. Check
    that \a spy is initialized before expected emits can happen.
*/
void TestObject::waitSignals(QSignalSpy &spy, int expectedSignalCount, int timeout) const
{
    int i = 0;
    int maxWaits = 10;
    while (spy.count() < expectedSignalCount && i < maxWaits) {
        spy.wait(timeout);
        ++i;
    }
}

void TestObject::setTestData(QByteArray qmlData)
{
    QQmlComponent component(mView.engine());
    component.setData(qmlData, QUrl());
    mRootObject = component.create(mView.engine()->rootContext());
    mView.setContent(QUrl(""), 0, mRootObject);
}

void TestObject::setTestUrl(const QUrl &url)
{
    mView.setSource(url);
    mRootObject = mView.rootObject();
}

void TestObject::setContextProperty(const QString &name, QObject *value)
{
    mView.rootContext()->setContextProperty(name, value);
}

int TestObject::random(int min, int max)
{
    return qrand() % ((max + 1) - min) + min;
}
<commit_msg>Persist scene graph and opengl context similarly as with browser app<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Jolla Ltd.
** Contact: Raine Makelainen <raine.makelainen@jolla.com>
**
****************************************************************************/

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "testobject.h"

#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickView>
#include <QQuickItem>
#include <QSignalSpy>
#include <QTime>
#include <QtTest>

TestObject::TestObject()
    : QObject()
{
    QTime time = QTime::currentTime();
    qsrand((uint)time.msec());
}

TestObject::TestObject(QByteArray qmlData)
    : QObject()
{
    QTime time = QTime::currentTime();
    qsrand((uint)time.msec());

    mView.setPersistentOpenGLContext(true);
    mView.setPersistentSceneGraph(true);

    setTestData(qmlData);
    mView.showFullScreen();
    QTest::qWaitForWindowExposed(&mView);
}

void TestObject::init(const QUrl &url)
{
    mView.setPersistentOpenGLContext(true);
    mView.setPersistentSceneGraph(true);

    setTestUrl(url);
    mView.showFullScreen();
    QTest::qWaitForWindowExposed(&mView);
}

/*!
    Wait signal of \a spy to be emitted \a expectedSignalCount.

    Note: this might cause indefinite loop, if not used cautiously. Check
    that \a spy is initialized before expected emits can happen.
*/
void TestObject::waitSignals(QSignalSpy &spy, int expectedSignalCount, int timeout) const
{
    int i = 0;
    int maxWaits = 10;
    while (spy.count() < expectedSignalCount && i < maxWaits) {
        spy.wait(timeout);
        ++i;
    }
}

void TestObject::setTestData(QByteArray qmlData)
{
    QQmlComponent component(mView.engine());
    component.setData(qmlData, QUrl());
    mRootObject = component.create(mView.engine()->rootContext());
    mView.setContent(QUrl(""), 0, mRootObject);
}

void TestObject::setTestUrl(const QUrl &url)
{
    mView.setSource(url);
    mRootObject = mView.rootObject();
}

void TestObject::setContextProperty(const QString &name, QObject *value)
{
    mView.rootContext()->setContextProperty(name, value);
}

int TestObject::random(int min, int max)
{
    return qrand() % ((max + 1) - min) + min;
}
<|endoftext|>


<commit_before>#include <apfMDS.h>
#include <apfMesh2.h>
#include <apfShape.h>
#include <fstream>
#include <gmi.h>

#define MAX_ELEM_NODES 10

namespace apf {

static void ansys2apf(int ansysType, int& apfType, apf::FieldShape*& shape)
{
  switch (ansysType) {
    case 702:
      apfType = apf::Mesh::TET;
      shape = apf::getLagrange(1);
      return;
    case 902:
      apfType = apf::Mesh::TET;
      shape = apf::getLagrange(2);
      return;
  };
}

static void parseElemInt(std::string const& line, int at, int& out)
{
  assert(line.length() >= ((size_t)at + 1) * 6);
  std::string field = line.substr(at * 6, 6);
  if (field != "      ") {
    std::stringstream ss(field);
    ss >> out;
  }
}

static bool parseElem(std::istream& f, int nodes[MAX_ELEM_NODES],
    int& apfType, int& id, apf::FieldShape*& shape)
{
  std::string line;
  std::getline(f, line);
  if (!line.length())
    return false;
  for (int i = 0; i < 8; ++i)
    parseElemInt(line, i, nodes[i]);
  int ansysType;
  parseElemInt(line,  9, ansysType);
  parseElemInt(line, 13, id);
  ansys2apf(ansysType, apfType, shape);
  int nnodes = shape->getEntityShape(apfType)->countNodes();
  if (nnodes <= 8)
    return true;
  std::getline(f, line);
  for (int i = 0; i < (nnodes - 8); ++i)
    parseElemInt(line, i, nodes[i + 8]);
  return true;
}

typedef std::map<int, apf::Vector3> Nodes;

static bool parseNode(std::istream& f, int& id, apf::Vector3& p)
{
  std::string line;
  std::getline(f, line);
  if (!line.length())
    return false;
  std::stringstream ss(line);
  ss >> id;
  int i = 0;
  while (ss >> p[i])
    ++i;
  for (; i < 3; ++i)
    p[i] = 0;
  return true;
}

static void parseNodes(const char* nodefile, Nodes& nodes)
{
  std::ifstream f(nodefile);
  if (!f.is_open()) {
    fprintf(stderr, "couldn't open ANSYS node file \"%s\"\n", nodefile);
    abort();
  }
  std::pair<int, apf::Vector3> entry;
  while (parseNode(f, entry.first, entry.second))
    nodes.insert(entry);
}

typedef std::map<int, MeshEntity*> Vertices;

static Mesh2* parseElems(const char* elemfile, Nodes& nodes)
{
  Mesh2* m = 0;
  std::ifstream f(elemfile);
  if (!f.is_open()) {
    fprintf(stderr, "couldn't open ANSYS elem file \"%s\"\n", elemfile);
    abort();
  }
  Vertices verts;
  int en[MAX_ELEM_NODES];
  int type;
  int id;
  apf::FieldShape* shape = 0;
  apf::FieldShape* prevShape = 0;
  while (parseElem(f, en, type, id, shape)) {
    if (!m) {
      m = makeEmptyMdsMesh(gmi_load(".null"), Mesh::typeDimension[type], false);
      if (shape != m->getShape())
        changeMeshShape(m, shape, false);
    }
    if (prevShape)
      assert(prevShape == shape);
    int nev = Mesh::adjacentCount[type][0];
    int nen = shape->getEntityShape(type)->countNodes();
    Downward ev;
    for (int i = 0; i < nen; ++i)
      if (!nodes.count(en[i])) {
        fprintf(stderr, "node %d in file \"%s\" not found in node file\n",
            en[i], elemfile);
        abort();
      }
    int i = 0;
    for (i = 0; i < nev; ++i) {
      if (!verts.count(en[i])) {
        MeshEntity* v = m->createVert(0);
        m->setPoint(v, 0, nodes[en[i]]);
        verts[en[i]] = v;
      }
      ev[i] = verts[en[i]];
    }
    MeshEntity* e = buildElement(m, 0, type, ev);
    for (int d = 1; d <= m->getDimension(); ++d) {
      if (!shape->hasNodesIn(d))
        continue;
      Downward ee;
      int nee = m->getDownward(e, d, ee);
      for (int j = 0; j < nee; ++j) {
        assert(shape->countNodesOn(m->getType(ee[j])) == 1);
        m->setPoint(ee[j], 0, nodes[en[i]]);
        ++i;
      }
    }
    assert(i == nen);
    prevShape = shape;
  }
  return m;
}

Mesh2* loadMdsFromANSYS(const char* nodefile, const char* elemfile)
{
  Nodes nodes;
  parseNodes(nodefile, nodes);
  Mesh2* m = parseElems(elemfile, nodes);
  m->acceptChanges();
  deriveMdsModel(m);
  return m;
}

}
<commit_msg>recording ANSYS element numbers<commit_after>#include "apfMDS.h"
#include <apfMesh2.h>
#include <apfShape.h>
#include <apfNumbering.h>
#include <fstream>
#include <gmi.h>

#define MAX_ELEM_NODES 10

namespace apf {

static void ansys2apf(int ansysType, int& apfType, apf::FieldShape*& shape)
{
  switch (ansysType) {
    case 702:
      apfType = apf::Mesh::TET;
      shape = apf::getLagrange(1);
      return;
    case 902:
      apfType = apf::Mesh::TET;
      shape = apf::getLagrange(2);
      return;
  };
}

static void parseElemInt(std::string const& line, int at, int& out)
{
  assert(line.length() >= ((size_t)at + 1) * 6);
  std::string field = line.substr(at * 6, 6);
  if (field != "      ") {
    std::stringstream ss(field);
    ss >> out;
  }
}

static bool parseElem(std::istream& f, int nodes[MAX_ELEM_NODES],
    int& apfType, int& id, apf::FieldShape*& shape)
{
  std::string line;
  std::getline(f, line);
  if (!line.length())
    return false;
  for (int i = 0; i < 8; ++i)
    parseElemInt(line, i, nodes[i]);
  int ansysType;
  parseElemInt(line,  9, ansysType);
  parseElemInt(line, 13, id);
  ansys2apf(ansysType, apfType, shape);
  int nnodes = shape->getEntityShape(apfType)->countNodes();
  if (nnodes <= 8)
    return true;
  std::getline(f, line);
  for (int i = 0; i < (nnodes - 8); ++i)
    parseElemInt(line, i, nodes[i + 8]);
  return true;
}

typedef std::map<int, apf::Vector3> Nodes;

static bool parseNode(std::istream& f, int& id, apf::Vector3& p)
{
  std::string line;
  std::getline(f, line);
  if (!line.length())
    return false;
  std::stringstream ss(line);
  ss >> id;
  int i = 0;
  while (ss >> p[i])
    ++i;
  for (; i < 3; ++i)
    p[i] = 0;
  return true;
}

static void parseNodes(const char* nodefile, Nodes& nodes)
{
  std::ifstream f(nodefile);
  if (!f.is_open()) {
    fprintf(stderr, "couldn't open ANSYS node file \"%s\"\n", nodefile);
    abort();
  }
  std::pair<int, apf::Vector3> entry;
  while (parseNode(f, entry.first, entry.second))
    nodes.insert(entry);
}

typedef std::map<int, MeshEntity*> Vertices;

static Mesh2* parseElems(const char* elemfile, Nodes& nodes)
{
  Mesh2* m = 0;
  std::ifstream f(elemfile);
  if (!f.is_open()) {
    fprintf(stderr, "couldn't open ANSYS elem file \"%s\"\n", elemfile);
    abort();
  }
  Vertices verts;
  int en[MAX_ELEM_NODES];
  int type;
  int id;
  apf::FieldShape* shape = 0;
  apf::FieldShape* prevShape = 0;
  apf::Numbering* enumbers = 0;
  while (parseElem(f, en, type, id, shape)) {
    if (!m) {
      m = makeEmptyMdsMesh(gmi_load(".null"), Mesh::typeDimension[type], false);
      if (shape != m->getShape())
        changeMeshShape(m, shape, false);
      enumbers = createNumbering(m, "ansys_element", getConstant(m->getDimension()), 1);
    }
    if (prevShape)
      assert(prevShape == shape);
    int nev = Mesh::adjacentCount[type][0];
    int nen = shape->getEntityShape(type)->countNodes();
    Downward ev;
    for (int i = 0; i < nen; ++i)
      if (!nodes.count(en[i])) {
        fprintf(stderr, "node %d in file \"%s\" not found in node file\n",
            en[i], elemfile);
        abort();
      }
    int i = 0;
    for (i = 0; i < nev; ++i) {
      if (!verts.count(en[i])) {
        MeshEntity* v = m->createVert(0);
        m->setPoint(v, 0, nodes[en[i]]);
        verts[en[i]] = v;
      }
      ev[i] = verts[en[i]];
    }
    MeshEntity* e = buildElement(m, 0, type, ev);
    for (int d = 1; d <= m->getDimension(); ++d) {
      if (!shape->hasNodesIn(d))
        continue;
      Downward ee;
      int nee = m->getDownward(e, d, ee);
      for (int j = 0; j < nee; ++j) {
        assert(shape->countNodesOn(m->getType(ee[j])) == 1);
        m->setPoint(ee[j], 0, nodes[en[i]]);
        ++i;
      }
    }
    number(enumbers, e, 0, 0, id);
    assert(i == nen);
    prevShape = shape;
  }
  return m;
}

Mesh2* loadMdsFromANSYS(const char* nodefile, const char* elemfile)
{
  Nodes nodes;
  parseNodes(nodefile, nodes);
  Mesh2* m = parseElems(elemfile, nodes);
  m->acceptChanges();
  deriveMdsModel(m);
  return m;
}

}
<|endoftext|>


<commit_before>/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2017 Advanced Micro Devices, Inc.
 *
 * 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 <miopen/kernel_cache.hpp>
#include <miopen/softmax.hpp>
#include <miopen/float_equal.hpp>
#include <miopen/check_numerics.hpp>

namespace miopen {

int nextPow2(int v)
{

    if(v == 1)
    {
        return (v << 1);
    }
    else
    {
        v--;
        v |= v >> 1;
        v |= v >> 2;
        v |= v >> 4;
        v |= v >> 8;
        v |= v >> 16;
        v++;
        return v;
    }
}

miopenStatus_t SoftmaxForward(
    Handle& handle, const void* alpha, const void* beta, const TensorDescriptor& yDesc, Data_t y)
{
    if(!float_equal(*(static_cast<const float*>(alpha)), 1.0) ||
       !float_equal(*(static_cast<const float*>(beta)), 0))
    {
        MIOPEN_THROW("Only alpha=1 and beta=0 is supported");
    }
    int n, c, h, w;
    std::tie(n, c, h, w) = tien<4>(yDesc.GetLengths());
    // using workgroup size of 256 by default
    int grid_size   = n * h * w;
    int spatial_dim = h * w;
    // num_spatial_dims or pixels each workgroup can compute
    int num_batch = c < 256 ? nextPow2(256 / c) : 1;

    const std::vector<size_t> vld{256, 1, 1};

    bool usefp16 = false;
    bool usefp32 = true;
    if(yDesc.GetType() == miopenHalf)
    {
        usefp16 = true;
        usefp32 = false;
    }

    // See Kernels/MIOpenSoftmax.cl for description
    if(num_batch == 1)
    { // CSR-Vector like approach

        // Control the max. number of workgroups launched so that we do not
        // start getting workgroup scheduling overheads
        size_t workgroups = std::min(grid_size, 64 * 40 * 8);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxForwardOneBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            //auto kernel = kernels.front();
            kernels.front()(y, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxForward";

            // compile parameters
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) +
                                " -DMIOPEN_USE_FP16=" + std::to_string(usefp16) +
                                " -DMIOPEN_USE_FP32=" + std::to_string(usefp32);
            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                y, c, grid_size, spatial_dim);
        }
    }
    else
    { // CSR-Stream like approach

        // num_threads iterating over channels for one spatial_dim
        int batch_size = 256 / num_batch;
        // num_channels each threads iterates over to cover all the channels
        int u_batch_size = (c > batch_size) ? nextPow2(c / batch_size) : 1;

        //size_t workgroups =
        //    grid_size % num_batch == 0 ? grid_size / num_batch : grid_size / num_batch + 1;
        size_t workgroups = (grid_size % num_batch == 0) ? (grid_size / num_batch) : (grid_size / num_batch + 1);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxForwardMultiBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c) +
            std::to_string(u_batch_size) + std::to_string(batch_size);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            kernels.front()(y, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxForward";
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) +
                                " -DBATCH_SIZE=" + std::to_string(batch_size) +
                                " -DU_BATCH_SIZE=" + std::to_string(u_batch_size) +
                                " -DMIOPEN_USE_FP16=" + std::to_string(usefp16) +
                                " -DMIOPEN_USE_FP32=" + std::to_string(usefp32);

            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                y, c, grid_size, spatial_dim);
        }
    }
    if(miopen::CheckNumericsEnabled())
    {
        miopen::checkNumericsOutput(handle, yDesc, y);
    }
    return miopenStatusSuccess;
}

miopenStatus_t SoftmaxBackward(Handle& handle,
                               const void* alpha,
                               const TensorDescriptor& yDesc,
                               ConstData_t y,
                               const void* beta,
                               const TensorDescriptor& dxDesc,
                               Data_t dx)
{
    if(yDesc != dxDesc)
    {
        MIOPEN_THROW(miopenStatusBadParm);
    }
    if(!float_equal(*(static_cast<const float*>(alpha)), 1.0) ||
       !float_equal(*(static_cast<const float*>(beta)), 0))
    {
        MIOPEN_THROW("Only alpha=1 and beta=0 is supported");
    }
    if(miopen::CheckNumericsEnabled())
    {
        miopen::checkNumericsInput(handle, yDesc, y);
    }

    int n, c, h, w;
    std::tie(n, c, h, w) = tien<4>(dxDesc.GetLengths());

    // using workgroup size of 256 by default
    int grid_size   = n * h * w;
    int spatial_dim = h * w;
    int num_batch   = (c < 256) ? nextPow2(256 / c) : 1;

    const std::vector<size_t> vld{256, 1, 1};

    bool usefp16 = false;
    bool usefp32 = true;
    if(yDesc.GetType() == miopenHalf)
    {
        usefp16 = true;
        usefp32 = false;
    }

    // See Kernels/MIOpenSoftmax.cl for description
    if(num_batch == 1)
    { // CSR-Vector like approach

        // Control the max. number of workgroups launched so that we do not
        // start getting workgroup scheduling overheads
        size_t workgroups = std::min(grid_size, 64 * 40 * 8);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxBackwardOneBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            kernels.front()(y, dx, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxBackward";
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) +
                                " -DMIOPEN_USE_FP16=" + std::to_string(usefp16) +
                                " -DMIOPEN_USE_FP32=" + std::to_string(usefp32);
            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                y, dx, c, grid_size, spatial_dim);
        }
    }
    else
    { // CSR-Stream like approach
        int batch_size   = 256 / num_batch;
        int u_batch_size = (c > batch_size) ? nextPow2(c / batch_size) : 1;
        size_t workgroups = (grid_size % num_batch == 0) ? (grid_size / num_batch) : (grid_size / num_batch + 1);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxBackwardMultiBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c) +
            std::to_string(u_batch_size) + std::to_string(batch_size);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            kernels.front()(y, dx, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxBackward";
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) +
                                " -DBATCH_SIZE=" + std::to_string(batch_size) +
                                " -DU_BATCH_SIZE=" + std::to_string(u_batch_size) +
                                " -DMIOPEN_USE_FP16=" + std::to_string(usefp16) +
                                " -DMIOPEN_USE_FP32=" + std::to_string(usefp32);
            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                 y, dx, c, grid_size, spatial_dim);
        }
    }
    if(miopen::CheckNumericsEnabled())
    {
        miopen::checkNumericsOutput(handle, dxDesc, dx);
    }

    return miopenStatusSuccess;
}

} // namespace miopen
<commit_msg>Formatting.<commit_after>/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2017 Advanced Micro Devices, Inc.
 *
 * 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 <miopen/kernel_cache.hpp>
#include <miopen/softmax.hpp>
#include <miopen/float_equal.hpp>
#include <miopen/check_numerics.hpp>

namespace miopen {

int nextPow2(int v)
{

    if(v == 1)
    {
        return (v << 1);
    }
    else
    {
        v--;
        v |= v >> 1;
        v |= v >> 2;
        v |= v >> 4;
        v |= v >> 8;
        v |= v >> 16;
        v++;
        return v;
    }
}

miopenStatus_t SoftmaxForward(
    Handle& handle, const void* alpha, const void* beta, const TensorDescriptor& yDesc, Data_t y)
{
    if(!float_equal(*(static_cast<const float*>(alpha)), 1.0) ||
       !float_equal(*(static_cast<const float*>(beta)), 0))
    {
        MIOPEN_THROW("Only alpha=1 and beta=0 is supported");
    }
    int n, c, h, w;
    std::tie(n, c, h, w) = tien<4>(yDesc.GetLengths());
    // using workgroup size of 256 by default
    int grid_size   = n * h * w;
    int spatial_dim = h * w;
    // num_spatial_dims or pixels each workgroup can compute
    int num_batch = c < 256 ? nextPow2(256 / c) : 1;

    const std::vector<size_t> vld{256, 1, 1};

    bool usefp16 = false;
    bool usefp32 = true;
    if(yDesc.GetType() == miopenHalf)
    {
        usefp16 = true;
        usefp32 = false;
    }

    // See Kernels/MIOpenSoftmax.cl for description
    if(num_batch == 1)
    { // CSR-Vector like approach

        // Control the max. number of workgroups launched so that we do not
        // start getting workgroup scheduling overheads
        size_t workgroups = std::min(grid_size, 64 * 40 * 8);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxForwardOneBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            // auto kernel = kernels.front();
            kernels.front()(y, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxForward";

            // compile parameters
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) + " -DMIOPEN_USE_FP16=" +
                                std::to_string(usefp16) + " -DMIOPEN_USE_FP32=" +
                                std::to_string(usefp32);
            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                y, c, grid_size, spatial_dim);
        }
    }
    else
    { // CSR-Stream like approach

        // num_threads iterating over channels for one spatial_dim
        int batch_size = 256 / num_batch;
        // num_channels each threads iterates over to cover all the channels
        int u_batch_size = (c > batch_size) ? nextPow2(c / batch_size) : 1;

        // size_t workgroups =
        //    grid_size % num_batch == 0 ? grid_size / num_batch : grid_size / num_batch + 1;
        size_t workgroups =
            (grid_size % num_batch == 0) ? (grid_size / num_batch) : (grid_size / num_batch + 1);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxForwardMultiBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c) +
            std::to_string(u_batch_size) + std::to_string(batch_size);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            kernels.front()(y, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxForward";
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) + " -DBATCH_SIZE=" +
                                std::to_string(batch_size) + " -DU_BATCH_SIZE=" +
                                std::to_string(u_batch_size) + " -DMIOPEN_USE_FP16=" +
                                std::to_string(usefp16) + " -DMIOPEN_USE_FP32=" +
                                std::to_string(usefp32);

            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                y, c, grid_size, spatial_dim);
        }
    }
    if(miopen::CheckNumericsEnabled())
    {
        miopen::checkNumericsOutput(handle, yDesc, y);
    }
    return miopenStatusSuccess;
}

miopenStatus_t SoftmaxBackward(Handle& handle,
                               const void* alpha,
                               const TensorDescriptor& yDesc,
                               ConstData_t y,
                               const void* beta,
                               const TensorDescriptor& dxDesc,
                               Data_t dx)
{
    if(yDesc != dxDesc)
    {
        MIOPEN_THROW(miopenStatusBadParm);
    }
    if(!float_equal(*(static_cast<const float*>(alpha)), 1.0) ||
       !float_equal(*(static_cast<const float*>(beta)), 0))
    {
        MIOPEN_THROW("Only alpha=1 and beta=0 is supported");
    }
    if(miopen::CheckNumericsEnabled())
    {
        miopen::checkNumericsInput(handle, yDesc, y);
    }

    int n, c, h, w;
    std::tie(n, c, h, w) = tien<4>(dxDesc.GetLengths());

    // using workgroup size of 256 by default
    int grid_size   = n * h * w;
    int spatial_dim = h * w;
    int num_batch   = (c < 256) ? nextPow2(256 / c) : 1;

    const std::vector<size_t> vld{256, 1, 1};

    bool usefp16 = false;
    bool usefp32 = true;
    if(yDesc.GetType() == miopenHalf)
    {
        usefp16 = true;
        usefp32 = false;
    }

    // See Kernels/MIOpenSoftmax.cl for description
    if(num_batch == 1)
    { // CSR-Vector like approach

        // Control the max. number of workgroups launched so that we do not
        // start getting workgroup scheduling overheads
        size_t workgroups = std::min(grid_size, 64 * 40 * 8);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxBackwardOneBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            kernels.front()(y, dx, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxBackward";
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) + " -DMIOPEN_USE_FP16=" +
                                std::to_string(usefp16) + " -DMIOPEN_USE_FP32=" +
                                std::to_string(usefp32);
            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                y, dx, c, grid_size, spatial_dim);
        }
    }
    else
    { // CSR-Stream like approach
        int batch_size   = 256 / num_batch;
        int u_batch_size = (c > batch_size) ? nextPow2(c / batch_size) : 1;
        size_t workgroups =
            (grid_size % num_batch == 0) ? (grid_size / num_batch) : (grid_size / num_batch + 1);
        const std::vector<size_t> vgd{workgroups * vld[0], 1, 1};

        std::string algo_name = "SoftmaxBackwardMultiBatch";
        std::string network_config =
            std::to_string(num_batch) + std::to_string(usefp16) + std::to_string(usefp32) +
            std::to_string(vgd[0]) + std::to_string(vld[0]) + std::to_string(spatial_dim) +
            std::to_string(grid_size) + std::to_string(workgroups) + std::to_string(c) +
            std::to_string(u_batch_size) + std::to_string(batch_size);

        auto&& kernels = handle.GetKernels(algo_name, network_config);

        if(!kernels.empty())
        {
            kernels.front()(y, dx, c, grid_size, spatial_dim);
        }
        else
        {
            std::string program_name = "MIOpenSoftmax.cl";
            std::string kernel_name  = "SoftmaxBackward";
            std::string parms = "-DNUM_BATCH=" + std::to_string(num_batch) + " -DBATCH_SIZE=" +
                                std::to_string(batch_size) + " -DU_BATCH_SIZE=" +
                                std::to_string(u_batch_size) + " -DMIOPEN_USE_FP16=" +
                                std::to_string(usefp16) + " -DMIOPEN_USE_FP32=" +
                                std::to_string(usefp32);
            handle.AddKernel(algo_name, network_config, program_name, kernel_name, vld, vgd, parms)(
                y, dx, c, grid_size, spatial_dim);
        }
    }
    if(miopen::CheckNumericsEnabled())
    {
        miopen::checkNumericsOutput(handle, dxDesc, dx);
    }

    return miopenStatusSuccess;
}

} // namespace miopen
<|endoftext|>