don't use IPC from timer callback as this results in reentrancies in socket code, postpone it until the next idle handler call instead

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57808 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2009-01-03 02:21:12 +00:00
parent 0ed00ab5be
commit 05b9332789

View File

@@ -37,22 +37,25 @@
#include "wx/timer.h" #include "wx/timer.h"
#include "wx/datetime.h" #include "wx/datetime.h"
#include "wx/vector.h"
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
// Define a new application
class MyClient; class MyClient;
class MyApp: public wxApp // ----------------------------------------------------------------------------
// classes
// ----------------------------------------------------------------------------
class MyApp : public wxApp
{ {
public: public:
MyApp() { Connect(wxEVT_IDLE, wxIdleEventHandler(MyApp::OnIdle)); }
virtual bool OnInit(); virtual bool OnInit();
virtual int OnExit(); virtual int OnExit();
protected: private:
void OnIdle(wxIdleEvent& event);
MyClient *m_client; MyClient *m_client;
}; };
@@ -66,19 +69,38 @@ public:
virtual bool OnDisconnect(); virtual bool OnDisconnect();
}; };
class MyClient: public wxClient, public wxTimer class MyClient : public wxClient,
private wxTimer
{ {
public: public:
MyClient(); MyClient();
virtual ~MyClient(); virtual ~MyClient();
bool Connect(const wxString& sHost, const wxString& sService, const wxString& sTopic); bool Connect(const wxString& sHost, const wxString& sService, const wxString& sTopic);
void Disconnect(); void Disconnect();
wxConnectionBase *OnMakeConnection(); wxConnectionBase *OnMakeConnection();
bool IsConnected() { return m_connection != NULL; }; bool IsConnected() { return m_connection != NULL; };
virtual void Notify(); virtual void Notify();
protected: void StartNextTestIfNecessary();
private:
void TestRequest();
void TestPoke();
void TestExecute();
void TestStartAdvise();
void TestStopAdvise();
void TestDisconnect();
MyConnection *m_connection; MyConnection *m_connection;
// the test functions to be executed by StartNextTestIfNecessary()
typedef void (MyClient::*MyClientTestFunc)();
wxVector<MyClientTestFunc> m_tests;
// number of seconds since the start of the test
int m_step; int m_step;
}; };
@@ -99,14 +121,12 @@ bool MyApp::OnInit()
if ( !wxApp::OnInit() ) if ( !wxApp::OnInit() )
return false; return false;
delete wxLog::SetActiveTarget(new wxLogStderr);
// Create a new client // Create a new client
m_client = new MyClient; m_client = new MyClient;
bool retval = m_client->Connect("localhost", "4242", "IPC TEST"); bool retval = m_client->Connect("localhost", "4242", "IPC TEST");
wxLogMessage(_T("Client host=\"localhost\" port=\"4242\" topic=\"IPC TEST\" %s"), wxLogMessage("Client host=\"localhost\" port=\"4242\" topic=\"IPC TEST\" %s",
retval ? _T("connected") : _T("failed to connect")); retval ? "connected" : "failed to connect");
return retval; return retval;
} }
@@ -118,25 +138,40 @@ int MyApp::OnExit()
return 0; return 0;
} }
void MyApp::OnIdle(wxIdleEvent& event)
{
if ( m_client )
m_client->StartNextTestIfNecessary();
event.Skip();
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// MyClient // MyClient
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
MyClient::MyClient() : wxClient() MyClient::MyClient()
: wxClient()
{ {
m_connection = NULL; m_connection = NULL;
m_step = 0; m_step = 0;
} }
bool MyClient::Connect(const wxString& sHost, const wxString& sService, const wxString& sTopic) bool
MyClient::Connect(const wxString& sHost,
const wxString& sService,
const wxString& sTopic)
{ {
// suppress the log messages from MakeConnection() // suppress the log messages from MakeConnection()
wxLogNull nolog; wxLogNull nolog;
m_connection = (MyConnection *)MakeConnection(sHost, sService, sTopic); m_connection = (MyConnection *)MakeConnection(sHost, sService, sTopic);
if (m_connection) if ( !m_connection )
Start(1000, false); return false;
return m_connection != NULL;
Start(1000);
return true;
} }
wxConnectionBase *MyClient::OnMakeConnection() wxConnectionBase *MyClient::OnMakeConnection()
@@ -151,7 +186,7 @@ void MyClient::Disconnect()
m_connection->Disconnect(); m_connection->Disconnect();
delete m_connection; delete m_connection;
m_connection = NULL; m_connection = NULL;
wxLogMessage(_T("Client disconnected from server")); wxLogMessage("Client disconnected from server");
} }
wxGetApp().ExitMainLoop(); wxGetApp().ExitMainLoop();
} }
@@ -163,30 +198,78 @@ MyClient::~MyClient()
void MyClient::Notify() void MyClient::Notify()
{ {
switch (m_step++) // we shouldn't call wxIPC methods from here directly as we may be called
// from inside an IPC call when using TCP/IP as the sockets are used in
// non-blocking code and so can dispatch events, including the timer ones,
// while waiting for IO and so starting another IPC call would result in
// fatal reentrancies -- instead, just set a flag and perform the test
// indicated by it later from our idle event handler
MyClientTestFunc testfunc = NULL;
switch ( m_step++ )
{ {
case 0: case 0:
{ testfunc = &MyClient::TestRequest;
size_t size; break;
m_connection->Request(_T("Date"));
m_connection->Request(_T("Date+len"), &size); case 1:
m_connection->Request(_T("bytes[3]"), &size, wxIPC_PRIVATE); testfunc = &MyClient::TestPoke;
break;
case 2:
testfunc = &MyClient::TestExecute;
break;
case 3:
testfunc = &MyClient::TestStartAdvise;
break;
case 10:
testfunc = &MyClient::TestStopAdvise;
break;
case 15:
testfunc = &MyClient::TestDisconnect;
break; break;
} }
case 1:
if ( testfunc )
m_tests.push_back(testfunc);
wxWakeUpIdle();
}
void MyClient::StartNextTestIfNecessary()
{
if ( !m_tests.empty() )
{ {
MyClientTestFunc testfunc = m_tests.front();
m_tests.erase(m_tests.begin());
(this->*testfunc)();
}
}
void MyClient::TestRequest()
{
size_t size;
m_connection->Request("Date");
m_connection->Request("Date+len", &size);
m_connection->Request("bytes[3]", &size, wxIPC_PRIVATE);
}
void MyClient::TestPoke()
{
wxString s = wxDateTime::Now().Format(); wxString s = wxDateTime::Now().Format();
m_connection->Poke(_T("Date"), s); m_connection->Poke("Date", s);
s = wxDateTime::Now().FormatTime() + _T(" ") + wxDateTime::Now().FormatDate(); s = wxDateTime::Now().FormatTime() + " " + wxDateTime::Now().FormatDate();
m_connection->Poke(_T("Date"), (const char *)s.c_str(), s.length() + 1); m_connection->Poke("Date", (const char *)s.c_str(), s.length() + 1);
char bytes[3]; char bytes[3];
bytes[0] = '1'; bytes[1] = '2'; bytes[2] = '3'; bytes[0] = '1'; bytes[1] = '2'; bytes[2] = '3';
m_connection->Poke(_T("bytes[3]"), bytes, 3, wxIPC_PRIVATE); m_connection->Poke("bytes[3]", bytes, 3, wxIPC_PRIVATE);
break; }
}
case 2: void MyClient::TestExecute()
{ {
wxString s = _T("Date"); wxString s = "Date";
m_connection->Execute(s); m_connection->Execute(s);
m_connection->Execute((const char *)s.c_str(), s.length() + 1); m_connection->Execute((const char *)s.c_str(), s.length() + 1);
char bytes[3]; char bytes[3];
@@ -194,20 +277,23 @@ void MyClient::Notify()
bytes[1] = '2'; bytes[1] = '2';
bytes[2] = '3'; bytes[2] = '3';
m_connection->Execute(bytes, WXSIZEOF(bytes)); m_connection->Execute(bytes, WXSIZEOF(bytes));
break; }
}
case 3: void MyClient::TestStartAdvise()
wxLogMessage(_T("StartAdvise(\"something\")")); {
m_connection->StartAdvise(_T("something")); wxLogMessage("StartAdvise(\"something\")");
break; m_connection->StartAdvise("something");
case 10: }
wxLogMessage(_T("StopAdvise(\"something\")"));
m_connection->StopAdvise(_T("something")); void MyClient::TestStopAdvise()
break; {
case 15: wxLogMessage("StopAdvise(\"something\")");
m_connection->StopAdvise("something");
}
void MyClient::TestDisconnect()
{
Disconnect(); Disconnect();
break;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -217,35 +303,35 @@ void MyClient::Notify()
bool MyConnection::OnAdvise(const wxString& topic, const wxString& item, const void *data, bool MyConnection::OnAdvise(const wxString& topic, const wxString& item, const void *data,
size_t size, wxIPCFormat format) size_t size, wxIPCFormat format)
{ {
Log(_T("OnAdvise"), topic, item, data, size, format); Log("OnAdvise", topic, item, data, size, format);
return true; return true;
} }
bool MyConnection::OnDisconnect() bool MyConnection::OnDisconnect()
{ {
wxLogMessage(_T("OnDisconnect()")); wxLogMessage("OnDisconnect()");
wxGetApp().ExitMainLoop(); wxGetApp().ExitMainLoop();
return true; return true;
} }
bool MyConnection::DoExecute(const void *data, size_t size, wxIPCFormat format) bool MyConnection::DoExecute(const void *data, size_t size, wxIPCFormat format)
{ {
Log(_T("Execute"), wxEmptyString, wxEmptyString, data, size, format); Log("Execute", wxEmptyString, wxEmptyString, data, size, format);
bool retval = wxConnection::DoExecute(data, size, format); bool retval = wxConnection::DoExecute(data, size, format);
if (!retval) if (!retval)
wxLogMessage(_T("Execute failed!")); wxLogMessage("Execute failed!");
return retval; return retval;
} }
const void *MyConnection::Request(const wxString& item, size_t *size, wxIPCFormat format) const void *MyConnection::Request(const wxString& item, size_t *size, wxIPCFormat format)
{ {
const void *data = wxConnection::Request(item, size, format); const void *data = wxConnection::Request(item, size, format);
Log(_T("Request"), wxEmptyString, item, data, size ? *size : wxNO_LEN, format); Log("Request", wxEmptyString, item, data, size ? *size : wxNO_LEN, format);
return data; return data;
} }
bool MyConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format) bool MyConnection::DoPoke(const wxString& item, const void *data, size_t size, wxIPCFormat format)
{ {
Log(_T("Poke"), wxEmptyString, item, data, size, format); Log("Poke", wxEmptyString, item, data, size, format);
return wxConnection::DoPoke(item, data, size, format); return wxConnection::DoPoke(item, data, size, format);
} }