Skip to content

Instantly share code, notes, and snippets.

@Rolias
Created October 19, 2010 18:03
Show Gist options
  • Save Rolias/634688 to your computer and use it in GitHub Desktop.
Save Rolias/634688 to your computer and use it in GitHub Desktop.
Example of using a service provider in C++ for the NetBurner
//---------------------------------------------------------------------------
//Here's StartupServiceProvider.h - only the include codeguards are missing
//---------------------------------------------------------------------------
namespace SyncorLibrary
{
//Here area a copule of external classes that I want to treat as a singletons
//They have default constructors that make no OS calls.
class TcpServer;
class TcpServerReset;
class StartupServiceProvider
{
//NOTE How in this class everything is static
public:
static TcpServer& GetTcpServerAsSingleton();
static TcpServerReset& GetTcpServerResetAsSingleton();
private:
static TcpServerReset _instanceTcpServerReset;
static TcpServer _instanceTcpServer;
StartupServiceProvider(); //everything is static don't allow constructor call.
};
}
//---------------------------------------------------------------------------
//Here's StartupSErviceProvider.cpp
//---------------------------------------------------------------------------
#include "StartupServiceProvider.h"
#include "TcpServer.h"
namespace SyncorLibrary
{
//Since they are static we have to create an instance of them
TcpServerReset StartupServiceProvider::_instanceTcpServerReset ;
TcpServer StartupServiceProvider::_instanceTcpServer(&_instanceTcpServerReset);
TcpServer& StartupServiceProvider::GetTcpServerAsSingleton()
{
return _instanceTcpServer;
}
TcpServerReset& StartupServiceProvider::GetTcpServerResetAsSingleton()
{
return _instanceTcpServerReset;
}
}
//---------------------------------------------------------------------------
//Now once you are in UserMain you can get the singleton version of this
//class. You can store that singleton in a local or member variable. If some
//other class needs this singleton they get it the same way. If your class
//needs to make some OS calls to be fully setup then do it only one time
//and guarantee that the initialization happens before any other class uses it
//---------------------------------------------------------------------------
TcpServer& my_tcp_server = StartupServiceProvider::GetTcpServerAsSingleton();
//If you need initialization - write it outside the constructor in an Initialization method
//so you can do this
my_tcp_server.Initialization();
//Now anywhere else in your code that you need TcpServer you just call
//StartupServiceProvider::GetTcpServerAsSingleton(); and you get the initialized
//fully set up (invariant at this point) version.
//---------------------------------------------------------------------------
//In reality TcpServer doesn't need any initialization. Below is the full
//unit test that uses the actual class. I didn't include all the other classes
//and unit test support code but hopefully you'll get the idea. Really it's
//just the constructor for TcpServerFixture that is of interest. One thing to
//know is that TcpServer derives from TcpServerBase. The TcpConnectionHelper
//expects a TcpServerBase. You can see how in the constructor I get a singleton
// TcpServer and then inject that into the constructor for TcpConnectionHelper.
//That way calls on _myTcpServer and on _connectionHelper are using the same
//underlying TcpServerBase instance.
//---------------------------------------------------------------------------
SUITE(TcpServer)
{
struct TcpServerFixture
{
TcpServerFixture() :
_myTcpServer(StartupServiceProvider::GetTcpServerAsSingleton()),
_connectionHelper(
TcpConnectionHelper(&_myTcpServer))
{
_myTcpServer.SetConnectionWaitTicks(3);
_myTcpServer.SetReadTimeoutTicks(2);//Default is 100 but for testing we can make it
//much shorter, this makes it faster to shutdown the process.
Debug::Write("\n\n\n---TcpServerFixture Constructor ---\n");
}
TcpServer& _myTcpServer;
TcpConnectionHelper _connectionHelper;
void WaitForShutdown() const
{
_connectionHelper.WaitForShutdown(110); //wait x ticks before giving up.
}
int MakeConnection()
{
return _connectionHelper.MakeConnection(_myTcpServer.GetTcpListenPort());
}
void CloseConnection()
{
_connectionHelper.CloseConnection();
}
void SendTestMessage(int fd, string msg)
{
int n = write(fd, msg.c_str(), msg.length());
Debug::WriteVar("Wrote bytes: ", n);
}
void Setup()
{
_myTcpServer.SetTaskPriority(HTTP_PRIO - 1);
_myTcpServer.Startup();
}
void KeepAliveSetup()
{
//Debug::SetLevel(dl_TACITURN);
_myTcpServer.KeepAliveEnabled(true);
Setup();
}
};
TEST_FIXTURE(TcpServerFixture, TaskPriority_IsSetCorrectly)
{
Debug::Write("TaskPriority_IsSetCorrectly");
int target = 51;
_myTcpServer.SetTaskPriority(target);
CHECK_EQUAL(target, _myTcpServer.GetTaskPriority());
}
TEST_FIXTURE(TcpServerFixture, TaskPriority_CannotBeSetBelowMin)
{
Debug::Write("TaskPriority_CannotBeSetBelowMin");
int target = _myTcpServer.GetMinTaskPriority() - 1;
_myTcpServer.SetTaskPriority(target);
CHECK(target != _myTcpServer.GetTaskPriority());
}
TEST_FIXTURE(TcpServerFixture, TaskPriority_CannotBeSetAboveMax)
{
Debug::Write("TaskPriority_CannotBeSetAboveMax");
;
int target = _myTcpServer.GetMaxTaskPriority() + 1;
_myTcpServer.SetTaskPriority(target);
CHECK(target != _myTcpServer.GetTaskPriority());
}
TEST_FIXTURE(TcpServerFixture, CanStartupShutdown)
{
Debug::Write("CanStartupShutdown");
Setup();
OSTimeDly(2);
CHECK (!_myTcpServer.IsShutdownComplete());
CHECK (_myTcpServer.ListenSocketIsValid());
_myTcpServer.Shutdown();
WaitForShutdown();
CHECK (_myTcpServer.IsShutdownComplete());
}
TEST_FIXTURE(TcpServerFixture, CanMakeConnection)
{
Debug::Write("CanMakeConnection");
_myTcpServer.SetTaskPriority(HTTP_PRIO - 1);
_myTcpServer.SetTcpListenPort(23);//just showing how to do it, 23 is the default anyway
_myTcpServer.SetConnectionWaitTicks(20); //default is 0 or wait forever, which would also work
_myTcpServer.Startup();
OSTimeDly(1);
int my_connection = MakeConnection();
CHECK(my_connection > 0);
CloseConnection();
_myTcpServer.Shutdown();
WaitForShutdown();
}
TEST_FIXTURE(TcpServerFixture, CanMakeConnection_When_PendingNotAllowed)
{
// Debug::SetLevel(dl_TACITURN);
Debug::Write("CanMakeConnection_When_PendingNotAllowed");
_myTcpServer.SetPendingSocketsAllowed(false);
_myTcpServer.Startup();
OSTimeDly(2);
int my_connection = MakeConnection();
CHECK(my_connection > 0);
//Listen socket should now be closed.
CHECK(!_myTcpServer.ListenSocketIsValid());
CHECK(_myTcpServer.ConnectionIsOpen());
CloseConnection();
_myTcpServer.Shutdown();
WaitForShutdown();
}
TEST_FIXTURE(TcpServerFixture, TestMode_SendsToMailbox_RespondsToSemaphore)
{
Debug::Write("TestMode_SendsToMailbox_RespondsToSemaphore");
int MAILBOX_WAIT_TICKS = 20;
Setup();
OSTimeDly(1);
CHECK (!_myTcpServer.IsShutdownComplete());
OS_MBOX& tcp_mailbox = _myTcpServer.GetMailbox();
OS_SEM& msg_processed_sem = const_cast<OS_SEM&> (_myTcpServer.GetSemaphore());
BYTE err;
int fd = MakeConnection();
SendTestMessage(fd, "TcpServer TestMessage");
void * pdata_from_tcp = OSMboxPend(&tcp_mailbox, MAILBOX_WAIT_TICKS, &err);
string mbox_response;
if (err == OS_TIMEOUT)
{
CHECK_EQUAL("","Mailbox timeout on initial message");
}
else
{
mbox_response = (char*) pdata_from_tcp;
//Let the tcp server know we've processed the message
OSSemPost(&msg_processed_sem);
OSTimeDly(2);
}
CloseConnection();
_myTcpServer.Shutdown();
OSTimeDly(1);
WaitForShutdown();
CHECK_EQUAL("TcpServer TestMessage",mbox_response.c_str());
CHECK (_myTcpServer.IsShutdownComplete());
}
TEST_FIXTURE(TcpServerFixture, KeepAlive_DoesntClose_GoodConnection)
{
Debug::Write("KeepAlive_DoesntClose_GoodConnection");
KeepAliveSetup();
OSTimeDly(2);
CHECK (!_myTcpServer.IsShutdownComplete());
const bool listen_sock_valid = _myTcpServer.ListenSocketIsValid();
CHECK (listen_sock_valid);
MakeConnection();
OSTimeDly(5); //make sure enough time for a couple of keep alive checks.
const bool connection_is_open = _myTcpServer.ConnectionIsOpen();
CHECK(connection_is_open);
_myTcpServer.Shutdown();
OSTimeDly(1);
WaitForShutdown();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment