Created
October 19, 2010 18:03
-
-
Save Rolias/634688 to your computer and use it in GitHub Desktop.
Example of using a service provider in C++ for the NetBurner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//--------------------------------------------------------------------------- | |
//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