Use `std::ofstream` with opentelemetry `OStreamSpanExporter` - c++

I want to create an opentelemetry tracing configuration that writes the exporter logs to a file. For that, I used the OStreamSpanExporter class that takes a ref to a std::ostream object (by default, the ctor argument is std::cout). So here is what I did:
#include <fstream>
namespace trace_sdk = opentelemetry::sdk::trace;
std::ofstream file_handle(log_trace_output_file_.c_str());
auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter(file_handle));
auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
new trace_sdk::TracerProvider(std::move(processor)));
// Set the global trace provider
opentelemetry::trace::Provider::SetTracerProvider(provider);
This compiles nicely. Before you ask, we checked that log_trace_output_file_.c_str() is not empty. However I encounter segmentation fault as soon as I start creating spans... Do you know what I might have been doing wrong here ? Thank you.

Ok, I realised that because of the std::move when declaring the processor, we were giving away the ownership thus we were trying to access a stream that was nullptr...
Here is what I ended up doing:
main.cpp
auto trace_provider = new TraceProvider(vm["trace-provider"].as<std::string>(), vm["trace-output-log-file"].as<std::string>());
trace_provider->InitTracer();
TraceProvider.hpp
class TraceProvider {
public:
TraceProvider(std::string exporter_backend_str, std::string log_trace_output_file = std::string());
~TraceProvider();
void InitTracer();
private:
std::string exporter_backend_str_;
std::string log_trace_output_file_;
std::shared_ptr<std::ofstream> log_trace_output_file_handle_ = nullptr;
void initSimpleTracer();
};
TraceProvider.cpp
TraceProvider::TraceProvider(std::string exporter_backend_str, std::string log_trace_output_file) {
exporter_backend_str_ = exporter_backend_str;
exporter_backend_ = string_to_trace_exporter(exporter_backend_str);
log_trace_output_file_ = log_trace_output_file;
if (exporter_backend_ == Exporter::SIMPLE) {
try {
if (log_trace_output_file_.compare("") != 0) {
log_trace_output_file_handle_ = std::make_shared<std::ofstream>(std::ofstream(log_trace_output_file.c_str()));
} else {
throw std::runtime_error("You chose the Simple trace exporter but you specified an empty log file.");
}
} catch(std::exception const& e) {
std::cout << "Exception: " << e.what() << "\n";
}
}
}
TraceProvider::~TraceProvider() {
// If it exists, close the file stream and delete the ptr
if (log_trace_output_file_handle_ != nullptr) {
std::cout << "Closing tracing log file at: " << log_trace_output_file_ << std::endl;
log_trace_output_file_handle_.get()->close();
log_trace_output_file_handle_.reset();
log_trace_output_file_handle_ = nullptr;
}
}
void TraceProvider::InitTracer() {
switch (exporter_backend_) {
case Exporter::SIMPLE:
initSimpleTracer();
break;
case Exporter::JAEGER:
initJaegerTracer();
break;
default:
std::stringstream err_msg_stream;
err_msg_stream << "Invalid tracing backend: " << exporter_backend_str_
<< "\n";
throw po::validation_error(po::validation_error::invalid_option_value,
err_msg_stream.str());
}
}
void TraceProvider::initSimpleTracer() {
std::unique_ptr<trace_sdk::SpanExporter> exporter;
if (log_trace_output_file_.compare("") != 0)
exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter(*log_trace_output_file_handle_.get()));
} else {
exporter = std::unique_ptr<trace_sdk::SpanExporter>(new opentelemetry::exporter::trace::OStreamSpanExporter);
}
auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
new trace_sdk::TracerProvider(std::move(processor), resources));
// Set the global trace provider
opentelemetry::trace::Provider::SetTracerProvider(provider);
// Set global propagator
context::propagation::GlobalTextMapPropagator::SetGlobalPropagator(
nostd::shared_ptr<context::propagation::TextMapPropagator>(
new opentelemetry::trace::propagation::HttpTraceContext()));
std::cout << "Simple (log stream) exporter successfully initialized!"
<< std::endl;
}

Related

boost::shared_ptr seems to disappear when using Linux

I am using a boost::shared_ptr to point to a plugin class. Plugin is a map <string, shared_ptr>. The first time I find a certain plugin in the map, it works fine. However, any subsequent time I try to find a particular plugin, I get a SIGSEGV error. When stepping through my code, I get to foundPlugin = a->second->onCommand(command);and find that a->second is not accessible anymore. This error only happens when I am running in Linux, however. I have no issues while running in Windows. Is there some sort of issue with boost::shared_ptr and linux? I have tried using std::shared_ptr, but I have to use a boost::dll::import function that returns a boost::shared_ptr, and I haven't found an alternative for that yet. Any insight is greatly appreciated!
I load plugins like this:
bool PluginManager::loadPlugin(std::string pluginPath, std::string
pluginName, std::string pluginType)
{
bool couldLoad = false;
boost::filesystem::path libPath = boost::filesystem::current_path();
boost::shared_ptr<my_plugin_api> plugin;
std::cout << "Loading the plugin " << pluginName << std::endl;
if (pluginName == "")
{
pluginName = "plugName";
}
try
{
plugin = boost::dll::import<my_plugin_api>(
libPath / pluginName,
pluginType,
dll::load_mode::append_decorations
);
Plugin.insert(std::pair<std::string,boost::shared_ptr<my_plugin_api>>
(pluginName, plugin));
std::cout << "Loading the plugin " << pluginName << " (SUCCESS)" <<
std::endl;
couldLoad = true;
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return couldLoad;
}
After much more testing, I feel like my problems are in the above section of code. the boost::dll::import function acts as if it finds a .so, but does not return anything in the boost::shared_ptr, which in turn causes the second snippet of code to fail. Any ideas of why this boost::dll::import function might be acting weirdly in Linux?
bool PluginManager::onCommand(const char* command, const char* pluginName)
{
bool foundPlugin = false;
auto a = Plugin.find(pluginName);
if (a == Plugin.end())
{
std::cerr << "plugin " << pluginName << " not found" << std::endl;
}
else
{
foundPlugin = a->second->onCommand(command);
}
return foundPlugin;
}

How to translate C file pointers into generic C++ streams?

Consider the following C code:
void openFile(const char *mode, char *filename, FILE **fileptr)
{
...
*fileptr = fopen(filename, mode);
...
}
FILE *logstream;
if (LOG_FILE_ENABLED)
{
openFile("w", "mylogfile.txt", logstream);
}
else
{
logstream = stderr;
}
fprintf(logstream, "[DEBUG] Some debug message...\n");
fclose(logstream);
I am attempting to translate this to idiomatic C++. How can I overload openFile() such that it takes a std::ofstream, but keep logstream stream-agnostic? I was assuming it would be something like this:
void openFile(const char *mode, char *filename, std::ofstream &ofs)
{
...
ofs.open(filename);
...
}
std::ostream logstream;
if (LOG_FILE_ENABLED)
{
logstream = std::ofstream();
openFile("w", "mylogfile.txt", logstream);
}
else
{
logstream = std::cerr;
}
logstream << "[DEBUG] Some debug message..." << std::endl;
logstream.close();
However this is apparently wildly incorrect - you can't even initialize a plain std::ostream like that. How should I handle this - preferably while avoiding the use of raw pointers?
I would move the actual work to a separate function or lambda that takes a std::ostream as input. The caller can then decide which type of std::ostream to pass in, eg:
void doRealWork(std::ostream &log)
{
...
log << "[DEBUG] Some debug message..." << std::endl;
...
}
if (LOG_FILE_ENABLED)
{
std::ofstream log("mylogfile.txt");
doRealWork(log);
}
else
{
doRealWork(std::cerr);
}
Or:
auto theRealWork = [&](std::ostream &&log)
{
...
log << "[DEBUG] Some debug message..." << std::endl;
...
}
if (LOG_FILE_ENABLED) {
theRealWork(std::ofstream{"mylogfile.txt"});
} else {
theRealWork(static_cast<std::ostream&&>(std::cerr));
}
UPDATE: Otherwise, you can do something more like this instead:
using unique_ostream_ptr = std::unique_ptr<std::ostream, void(*)(std::ostream*)>;
unique_ostream_ptr logstream;
if (LOG_FILE_ENABLED) {
logstream = unique_ostream_ptr(new std::ofstream("mylogfile.txt"), [](std::ostream *strm){ delete strm; });
} else {
logstream = unique_ostream_ptr(&std::cerr, [](std::ostream *){});
}
*logstream << "[DEBUG] Some debug message...\n";
Or:
using shared_ostream_ptr = std::shared_ptr<std::ostream>;
shared_ostream_ptr logstream;
if (LOG_FILE_ENABLED) {
logstream = std::make_shared<std::ofstream>("mylogfile.txt");
} else {
logstream = shared_ostream_ptr(&std::cerr, [](std::ostream*){});
}
*logstream << "[DEBUG] Some debug message...\n";
C++ stream library has pretty ancient design.
Nevertheless - its basic idea is that ostream or istream are just wrapper objects over stream-buffers.
So you might try something like in this code:
std::ostream get_log(bool str) {
if (str) return std::ostream(new std::stringbuf());
// else
std::filebuf* f = new std::filebuf();
f->open("log", std::ios_base::out);
return std::ostream(f);
}
But, as I mentioned, this is very ancient design - so no RAII - this buffer is not owned by stream - you would need to delete it by yourself:
int main() {
std::ostream log = get_log(true);
log << "aaa";
std::cout << static_cast<std::stringbuf&>(*log.rdbuf()).str();
delete log.rdbuf(); // (!)
}
So this is not very usable.
So my final advice - use smart pointer over ostream - like this:
std::unique_ptr<std::ostream> get_log(bool str) {
if (str) return new std::ostringstream();
std::ofstream* f = new std::ofstream();
f->open("log", std::ios_base::out);
return f;
}
int main() {
auto log = get_log(true);
*log << "aaa";
}
You were nearly there; you just need to be mindful of scoping rules, and of the fact that there is no such thing as an std::ostream other than as an abstract base class.
So:
std::ostream* logstreamPtr = nullptr;
std::ofstream ofs;
if (LOG_FILE_ENABLED)
{
logstreamPtr = &ofs;
openFile("w", "mylogfile.txt", ofs);
}
else
{
logstreamPtr = &std::cerr;
}
std::ostream& logstream = *logstreamPtr;
logstream << "[DEBUG] Some debug message..." << std::endl;
logstream.close();
You don't need the reference logstream, but it saves you from having to repeatedly reference logstreamPtr later, which would be boring.
Don't be afraid of this raw pointer. This is the purest application of pointers there is. You can go down the smart pointer route if you like, but you gain nothing and lose readability (and, in some cases, performance).
By the way, if you're worried about performance, don't open and close the log file for every single message; that's extremely wasteful.

Capnproto - Make client request in server callback

I need to do some client-requests inside a server-callback and are not sure where to store the created capnp::EzRpcClient and CompareNetwork::Client comparer objects. That's because the clients go out of scope (I think - I just get a SEGFAULT, but that seems to be the reason). It's basically a master who forwards a loading request to it's slaves (slaves can register via a REG-request and their address is stored).
So - where/how should I store the client objects? Is there any "best-practice" with this? I think temporarily storing them in some class member variable is a little dirty, isn't it?
GroupMaster2.cpp:
kj::Promise<void> GroupMaster2::CompareMasterImpl::load(LoadContext context) {
auto loadData = context.getParams().getIds();
slaveListLock.lock();
auto promList = kj::Vector<kj::Promise<std::pair<std::string, CompareNetwork::Status>>>();
for(auto& slave : slaveList) {
try {
capnp::EzRpcClient client(slave.second->address);
CompareNetwork::Client comparer = client.getMain<CompareNetwork>();
auto request = comparer.loadRequest();
std::string addrCopy(slave.first);
request.setIds(loadData);
auto loadPromise = request.send();
promList.add(loadPromise.then([addrCopy](auto response) {
return std::make_pair(addrCopy, response.getStatus());
},
[&, addrCopy](kj::Exception && exception) {
slaveListLock.lock();
slaveList.erase(addrCopy);//something failed, remove this slave!
slaveListLock.unlock();
std::cout << "ErrLoad0: " << std::string(exception.getDescription()) << std::endl;
return std::make_pair(addrCopy, CompareNetwork::Status::ERROR);
}));
}
catch(...) {
std::cout << "Error sending load to: " << slave.first << std::endl;
}
}
slaveListLock.unlock();
auto retProm = kj::joinPromises(promList.releaseAsArray()).then([&, KJ_CPCAP(context)](kj::Array<std::pair<std::string, CompareNetwork::Status>> res) mutable {
bool error = false;
for(auto& loadRes : res) {
switch(loadRes.second) {
case CompareNetwork::Status::OK: {
std::cout << "LOAD OK: " << loadRes.first << std::endl;
break;
}
case CompareNetwork::Status::ERROR: {
std::cout << "LOAD ERROR: " << loadRes.first << std::endl;
error = true;
break;
}
}
}
if(!error)
context.getResults().setStatus(CompareNetwork::Status::OK);
else
context.getResults().setStatus(CompareNetwork::Status::ERROR);
}, [](kj::Exception && exception) {
std::cout << __FILE__ << ":" << __LINE__ << std::string(exception.getDescription()) << std::endl;
}).eagerlyEvaluate([](kj::Exception && exception) {
std::cout << __FILE__ << ":" << __LINE__ << std::string(exception.getDescription()) << std::endl;
});
std::cout << "ReturnedLoad" << std::endl;
return retProm;
}
GroupNetworkData.capnp:
interface CompareNetwork {
compare #0 (jobs :JobPartList) -> (res :JobResList);
load #1 (ids :WIDList) -> (status :Status);
struct JobPartList {
jobParts #0 :List(JobPart);
struct JobPart {
jobs #0 :List(Job);
startID #1 :UInt32;
struct Job {
wid1 #0 :UInt32;
wid2 #1 :UInt32;
}
}
}
struct JobResList {
jobResults #0 :List(JobRes);
struct JobRes {
jobIndex #0 :UInt32;
result #1 :Float64;
}
}
struct WIDList {
ids #0 :List(WID);
struct WID {
id #0 :UInt32;
}
}
enum Status {
ok #0;
error #1;
}
}
interface CompareMaster extends(CompareNetwork) {
reg #0 (data :SlaveData) -> (status :CompareNetwork.Status);
struct SlaveData {
perfInd #0 :Float64;
maxMem #1 :UInt32;
address #2 :Text;
}
}
Thanks in advance!
dvs23
C++14 seems to have the answer:
How to capture a unique_ptr into a lambda expression?
Allocate both objects on heap with kj::heap and move the kj::Own to the callback lambda:
for(auto& slave : slaveList) {
try {
auto client = kj::heap<capnp::EzRpcClient>(slave.second->address);
auto comparer = kj::heap<CompareNetwork::Client>(client->getMain<CompareNetwork>());
auto request = comparer->loadRequest();
std::string addrCopy(slave.first);
request.setIds(loadData);
auto loadPromise = request.send();
promList.add(loadPromise.then([addrCopy, client = std::move(client), comparer = std::move(comparer)](auto response) {
return std::make_pair(addrCopy, response.getStatus());
},
[&, addrCopy](kj::Exception && exception) {
slaveListLock.lock();
slaveList.erase(addrCopy);//something failed, remove this slave!
slaveListLock.unlock();
std::cout << "ErrLoad0: " << std::string(exception.getDescription()) << std::endl;
return std::make_pair(addrCopy, CompareNetwork::Status::ERROR);
}));
}
catch(...) {
std::cout << "Error sending load to: " << slave.first << std::endl;
}
}
Better ideas are welcome, but at the moment it seems to work pretty well :)

CORBA omniORB unable to get remote object after resolving name context

I have an IDL as below
module IClientServer {
interface IClient
{
void serverResponse(in string name, in string data);
void start();
void stop();
void shutdown();
};
interface IServer
{
// Server calls back to client just once in a
// recursive call before returning.
// void one_time(in CallBack cb, in string mesg);
void DataFromX(in string name,in string data,in long lbytes,in short usg);
void Authenticate(in IClient client, in string dataToNegotiate);
// Shuts down the server.
void shutdown();
};
};
for which I generated proxy & skeleton using idl2cpp utility (onmiORB) and linked generated files to server & client app as suggested in the document
Then I started name service (omniNames) and added registry key omniORB\InitRef as suggested in documentation for the server & client apps to connect without using commandline arguments
Below is the server code
int _tmain(int argc, _TCHAR* argv[])
{
try
{
int argc = 0;
_BRDTRACE("Initializing....\n");
CORBA::ORB_var orb = CORBA::ORB_init(argc, NULL);
// cerr << "Initialized." << endl;
CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
_BRDTRACE("Resolved.\n");
PortableServer::POA_var poa = PortableServer::POA::_narrow(obj);
_BRDTRACE("Narrowed..\n");
// Obtain a reference to the object, and register it in
// the naming service.
server_i* myserver = new server_i();
// cerr << "Constructed." << endl;
obj = myserver->_this();
_BRDTRACE("obj retrieved.\n");
CORBA::String_var x;
x = orb->object_to_string(obj);
_BRDTRACE("obj to string.\n");
if( !bindObjectToName(orb, obj) )
{
// cerr << "Failed to bind obj to name." << endl;
throw;
}
_BRDTRACE("binded\n");
myserver->_remove_ref();
// cerr << "removed ref." << endl;
PortableServer::POAManager_var pman = poa->the_POAManager();
pman->activate();
_BRDTRACE("activated.\n");
// cerr << "Executing..." << endl;
orb->run();
_BRDTRACE("Terminated.\n");
myserver->shutdown();
// cerr << "Shutdown." << endl;
}
catch(CORBA::SystemException&)
{
_BRDTRACE("Caught CORBA::SystemException.\n");
}
catch(CORBA::Exception&) {
_BRDTRACE("Caught CORBA::Exception.\n");
}
catch(omniORB::fatalException&)
{
_BRDTRACE("Caught omniORB::fatalException:\n");
// cerr << " file: " << fe.file() << endl;
// cerr << " line: " << fe.line() << endl;
// cerr << " mesg: " << fe.errmsg() << endl;
}
catch(...) {
_BRDTRACE("Caught unknown exception.\n");
}
}
static CORBA::Boolean
bindObjectToName(CORBA::ORB_ptr orb, CORBA::Object_ptr objref)
{
CosNaming::NamingContext_var rootContext;
try {
// Obtain a reference to the root context of the Name service:
CORBA::Object_var obj;
obj = orb->resolve_initial_references("NameService");
// Narrow the reference returned.
rootContext = CosNaming::NamingContext::_narrow(obj);
if( CORBA::is_nil(rootContext) ) {
_BRDTRACE("Failed to narrow the root naming context.\n");
return 0;
}
}
catch(CORBA::ORB::InvalidName& ex) {
// This should not happen!
_BRDTRACE("Service required is invalid [does not exist].\n");
return 0;
}
try {
// Bind a context called "test" to the root context:
CosNaming::Name contextName;
contextName.length(1);
contextName[0].id = (const char*) "birdseye"; // string copied
contextName[0].kind = (const char*) "collections_context"; // string copied
// Note on kind: The kind field is used to indicate the type
// of the object. This is to avoid conventions such as that used
// by files (name.type -- e.g. test.ps = postscript etc.)
//CosNaming::NamingContext_var testContext;
try {
// Bind the context to root.
rootContext->bind(contextName, objref);
}
catch(CosNaming::NamingContext::AlreadyBound& ex) {
// If the context already exists, this exception will be raised.
// In this case, just resolve the name and assign testContext
// to the object returned:
CORBA::Object_var obj;
obj = rootContext->resolve(contextName);
CosNaming::NamingContext_var testContext = CosNaming::NamingContext::_narrow(obj);
if( CORBA::is_nil(testContext) ) {
_BRDTRACE("Failed to narrow naming context.\n");
return 0;
}
}
} catch(CORBA::COMM_FAILURE& ex) {
_BRDTRACE("Caught system exception COMM_FAILURE -- unable to contact the naming service.\n");
return 0;
}
catch(CORBA::SystemException&) {
_BRDTRACE("Caught a CORBA::SystemException while using the naming service.\n");
return 0;
}
return 1;
}
But the below code in the client side returns nil object after name
context resolution. Not able to figure out the issue. Please help!
int _tmain(int argc, _TCHAR* argv[])
{
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
CORBA::Object_var objRef = orb->resolve_initial_references("NameService");
CORBA::Object_var obj = getObjectReference(orb);
IClientServer::IServer_var svr = IClientServer::IServer::_narrow(obj.in());
if( CORBA::is_nil(svr) ) { **//THIS IS WHERE THE ISSUE IS**
// _BRDTRACE("cb_client: The server reference is nil!\n");
return 0;
}
return 0;
}
static CORBA::Object_ptr
getObjectReference(CORBA::ORB_ptr orb)
{
CosNaming::NamingContext_var rootContext;
try {
// Obtain a reference to the root context of the Name service:
CORBA::Object_var obj;
obj = orb->resolve_initial_references("NameService");
// Narrow the reference returned.
rootContext = CosNaming::NamingContext::_narrow(obj);
if( CORBA::is_nil(rootContext) ) {
// cerr << "Failed to narrow the root naming context." << endl;
return CORBA::Object::_nil();
}
}
catch(CORBA::ORB::InvalidName& ) {
// This should not happen!
return CORBA::Object::_nil();
}
// Create a name object, containing the name test/context:
CosNaming::Name name;
name.length(1);
name[0].id = (const char*) "birdseye"; // string copied
name[0].kind = (const char*) "collections_context"; // string copied
// Note on kind: The kind field is used to indicate the type
// of the object. This is to avoid conventions such as that used
// by files (name.type -- e.g. test.ps = postscript etc.)
try {
// Resolve the name to an object reference.
return rootContext->resolve(name);
}
catch(CosNaming::NamingContext::NotFound& nf) {
}
catch(CORBA::COMM_FAILURE& ) {
}
catch(CORBA::SystemException&) {
}
return CORBA::Object::_nil();
}
UPDATE-5PM:
Infact the server side code also has same issue server->authenticate is never called due to nil reference.
Guess : Can there be an issue with proxy & stubs generated with idl2cpp tool?
UPDATE-7:30PM
The ambiguity on stubs not ok is also gone, the issue still persists after regenerating the stubs & rebuilding both client & server apps again
UPDATE 3-31|11AM
I am using omniORB 4.0.3 which is over 10 year old. This has worked great in earlier windows OS versions compiled with VC6, I doubt there is issue when recompiled on VS 2008. Just thinking of upgrade to ommiORB 4.2 released last year. Just clueless whatsoever...
UPDATE 3-31|5:30PM
Currently building omniORB4.2.1 source code. While I do this I still want to know if there is any issue linking the .lib files that are generated in older systems. In this case the omniORB .lib files I am using in Windows 7 is built on Windows XP, would that be an issue? Even this post could not answer, I have a old .lib that compiled & linked well without any issues & even the runtime it did not crash as well
UPDATE 4-01|4:30PM
Actually I noticed there is no server running, the server code I posted earlier is also client, I now updated real server code(the code which binds name to server obj). But the issue remains same even after this correction
Finally I fixed the issue. There are 2 issues with the code
issue 1 I already updated in question (missing server code)
issue 2 is with name context binding..
below is modified version of bindObjectToName in server
static CORBA::Boolean
bindObjectToName(CORBA::ORB_ptr orb, CORBA::Object_ptr objref)
{
CosNaming::NamingContext_var rootContext;
try {
// Obtain a reference to the root context of the Name service:
CORBA::Object_var obj;
obj = orb->resolve_initial_references("NameService");
// Narrow the reference returned.
rootContext = CosNaming::NamingContext::_narrow(obj);
if( CORBA::is_nil(rootContext) ) {
// cerr << "Failed to narrow the root naming context." << endl;
return 0;
}
}
catch(CORBA::ORB::InvalidName& ex) {
// This should not happen!
// cerr << "Service required is invalid [does not exist]." << endl;
return 0;
}
try {
// Bind a context called "test" to the root context:
CosNaming::Name contextName;
contextName.length(1);
contextName[0].id = (const char*) "birdsEYE"; // string copied
contextName[0].kind = (const char*) "DataCollectionS"; // string copied
// Note on kind: The kind field is used to indicate the type
// of the object. This is to avoid conventions such as that used
// by files (name.type -- e.g. test.ps = postscript etc.)
CosNaming::NamingContext_var testContext;
try {
// Bind the context to root.
testContext = rootContext->bind_new_context(contextName);
}
catch(CosNaming::NamingContext::AlreadyBound& ex) {
// If the context already exists, this exception will be raised.
// In this case, just resolve the name and assign testContext
// to the object returned:
CORBA::Object_var obj;
obj = rootContext->resolve(contextName);
testContext = CosNaming::NamingContext::_narrow(obj);
if( CORBA::is_nil(testContext) ) {
// cerr << "Failed to narrow naming context." << endl;
return 0;
}
}
// Bind sender :
CosNaming::Name objectName;
objectName.length(1);
objectName[0].id = (const char*) "clienT"; // string copied
objectName[0].kind = (const char*) "sendeR"; // string copied
try {
testContext->bind(objectName, objref);
}
catch(CosNaming::NamingContext::AlreadyBound& ex) {
testContext->rebind(objectName, objref);
}
// Note: Using rebind() will overwrite any Object previously bound
// to /test/Echo with obj.
// Alternatively, bind() can be used, which will raise a
// CosNaming::NamingContext::AlreadyBound exception if the name
// supplied is already bound to an object.
// Bind receiver :
CosNaming::Name objectName2;
objectName2.length(1);
objectName2[0].id = (const char*) "clienT"; // string copied
objectName2[0].kind = (const char*) "receiveR"; // string copied
try {
testContext->bind(objectName2, objref);
}
catch(CosNaming::NamingContext::AlreadyBound& ex) {
testContext->rebind(objectName2, objref);
}
// Amendment: When using OrbixNames, it is necessary to first try bind
// and then rebind, as rebind on it's own will throw a NotFoundexception if
// the Name has not already been bound. [This is incorrect behaviour -
// it should just bind].
}
catch(CORBA::COMM_FAILURE& ex) {
//cerr << "Caught system exception COMM_FAILURE -- unable to contact the "
// << "naming service." << endl;
return 0;
}
catch(CORBA::SystemException&) {
//cerr << "Caught a CORBA::SystemException while using the naming service."
// << endl;
return 0;
}
return 1;
}
And below the modified getObjectReference in client code
static CORBA::Object_ptr
getObjectReference(CORBA::ORB_ptr orb)
{
CosNaming::NamingContext_var rootContext;
try {
// Obtain a reference to the root context of the Name service:
CORBA::Object_var obj;
obj = orb->resolve_initial_references("NameService");
// Narrow the reference returned.
rootContext = CosNaming::NamingContext::_narrow(obj);
if( CORBA::is_nil(rootContext) ) {
// cerr << "Failed to narrow the root naming context." << endl;
return CORBA::Object::_nil();
}
}
catch(CORBA::ORB::InvalidName& ex) {
// This should not happen!
// cerr << "Service required is invalid [does not exist]." << endl;
_BRDTRACE("getObjRef: service required is invalid.\n");
exit(0);
return CORBA::Object::_nil();
}
// Create a name object, containing the name test/context:
CosNaming::Name name;
name.length(2);
name[0].id = (const char*) "birdsEYE"; // string copied
name[0].kind = (const char*) "DataCollectionS"; // string copied
name[1].id = (const char*) "clienT";
name[1].kind = (const char*) "sendeR";
// Note on kind: The kind field is used to indicate the type
// of the object. This is to avoid conventions such as that used
// by files (name.type -- e.g. test.ps = postscript etc.)
try {
// Resolve the name to an object reference.
return rootContext->resolve(name);
}
catch(CosNaming::NamingContext::NotFound& ex) {
// This exception is thrown if any of the components of the
// path [contexts or the object] aren't found:
//cerr << "Context not found." << endl;
_BRDTRACE("getObjRef: context not found.\n");
exit(0);
}
catch(CORBA::COMM_FAILURE& ex) {
//cerr << "Caught system exception COMM_FAILURE -- unable to contact the "
// << "naming service." << endl;
_BRDTRACE("getObjRef: caught system exception COMM_FAILURE.\n");
exit(0);
}
catch(CORBA::SystemException&) {
//cerr << "Caught a CORBA::SystemException while using the naming service."
//<< endl;
_BRDTRACE("getObjRef: caught system exception while using the name service.\n");
exit(0);
}
return CORBA::Object::_nil();
}

C++ boost threadgroup.interrupt_all() causing main thread to exit too

I'm using the below code to create threads and add them to a thread pool. The threads load fine and each perform a simple looping routine until the main thread calls ResetWorkerThreads a second time and kills off the sub threads. The sub threads are interrupted however the main thread exits also. There are no errors written to console. I can't wrap my head around it as it doesn't appear to have any exception and the main thread has not been added to the vecThreads thread pool. Also the second time this function is all the "All Threads Killed" is not outputted as if it never reaches that point.
std::string strPreviousSettings = "0";
std::string strPreviousAgentSettings = "0";
bool boolResetWorkers;
std::string strIP;
std::string strMACAddress;
boost::thread_group vecThreads;
std::string GetIP()
{
std::string strIP;
try
{
using namespace boost::network;
std::string strRequest;
http::client client;
http::client::request request("http://test.com/ip.php");
http::client::response response = client.get(request);
strIP = body(response);
}
catch(...)
{
cout << "GetLocalIP - Error: " << endl;
}
return strIP;
}
std::string getMacAddress()
{
std::string strMACAddress = GetFileContents("/sys/class/net/eth0/address");
boost::replace_all(strMACAddress, ":", "");
boost::replace_all(strMACAddress, "\n", "");
return strMACAddress;
}
void ThreadSettingsWorker()
{
int x = 1;
strIP = GetIP();
strMACAddress = getMacAddress();
do {
CheckEventSettings();
CheckAgentSettings();
if(boolResetWorkers==true)
{
ResetWorkerThreads();
} else {
boost::this_thread::sleep(boost::posix_time::milliseconds(3000));
}
} while ( x != 0 );
}
void ResetWorkerThreads()
{
cout << "Resetting Workers Threads\n";
boolResetWorkers = false;
int intWorkerCount = 10; //Spawn 10 workers
int X = 0;
int intI = 1;
cout << "Kill All Threads\n";
try
{
vecThreads.interrupt_all();
}
catch(...)
{
//std::cerr << "Kill All Threads: " << std::endl;
}
cout << "All Threads Killed\n";
for (int i = 0; i < intWorkerCount; ++i)
{
cout << "Starting Worker: " << (i + 1) << "\n";
boost::thread tWorker(&ThreadWorker, (i + 1));
vecThreads.add_thread(&tWorker);
}
}
void TestRequest()
{
try
{
using namespace boost::network;
std::stringstream ss;
http::client client;
ss << "http://test.com/sadasdasd.html";
http::client::request request(ss.str());
http::client::response response = client.get(request);
std::string strOutput = body(response);
cout << "Test Request Out: " << strOutput << "\n";
}
catch(...)
{
cout << "TestRequest - Error: " << endl;
return;
}
}
void ThreadWorker(int intThread)
{
try
{
int X = 0;
do {
cout << "Thread " << intThread << "\n";
TestRequest();
} while ( X != 55 );
}
catch(...)
{
}
}
void CheckEventSettings()
{
try
{
using namespace boost::network;
std::string strRequest;
http::client client;
http::client::request request("http://test.com/events.php");
http::client::response response = client.get(request);
std::string strOutput = body(response);
if(strPreviousSettings==strOutput)
{
cout << "No Event Settings Changes\n";
} else {
cout << "Applying New Event Settings\n";
strPreviousSettings = strOutput;
std::string strDividerLine = "<br>";
std::string strDividerField = "<field>";
std::vector<std::string> vEvents;
vEvents = EasySplit(strOutput, strDividerLine);
for(std::vector<std::string>::const_iterator iEvent = vEvents.begin(); iEvent != vEvents.end() - 1; ++iEvent) {
}
}
}
catch(...)
{
cout << "CheckEventSettings - Error: " << endl;
return;
}
}
void CheckAgentSettings()
{
try
{
using namespace boost::network;
std::stringstream ss;
http::client client;
ss << "http://test.com/checksettings.php";
http::client::request request(ss.str());
http::client::response response = client.get(request);
std::string strOutput = body(response);
if(strPreviousAgentSettings==strOutput)
{
cout << "No Agent Settings Changes\n";
} else {
cout << "Applying New Agent Settings\n";
strPreviousAgentSettings = strOutput;
boolResetWorkers = true;
}
}
catch(...)
{
cout << "CheckAgentSettings - Error: " << endl;
return;
}
}
int main()
{
// Start thread
boost::thread tCheckSettings(&ThreadSettingsWorker);
// Ask thread to stop
//tCheckSettings.interrupt();
// Join - wait when thread actually exits
tCheckSettings.join();
return 0;
}
You have an error here:
boost::thread tWorker(&ThreadWorker, (i + 1));
vecThreads.add_thread(&tWorker);
You create a local object tWorker that is deleted just after call to add_thread(). So vecThreads contains the dangling pointers to threads. When you call vecThreads.interrupt_all() you get undefined behavior because vecThreads tries to access the deleted thread objects and I suppose your program just terminates because of access violation or something.
You have to change your code to something like this:
boost::thread* ptWorker = new boost::thread(&ThreadWorker, (i + 1));
vecThreads.add_thread(ptWorker);
Please note that you don't need to delete those thread objects yourself. thread_group will delete them itself.
ADDITION:
The problem with terminate() may be caused by destructor of http::client throwing an exception. Please try this to possibly eliminate that problem in TestRequest():
try{
http::client client;
try{
// other code
}
catch (){}
}
catch(){}
Also I'd suggest resetting vecThreads after interrupt_all(). For example you can define it as boost::scoped_ptr and then do pvecThreads.reset(new boost::thread_group()) after the call to interrupt_all().
At present the interrupted threads still remain in the thread_group after the interruption and then you try to interrupt them again along with the new threads added to the thread_group later in ResetWorkerThreads().
Inside of ResetWorkerThreads You have:
for (int i = 0; i < intWorkerCount; ++i)
{
cout << "Starting Worker: " << (i + 1) << "\n";
// One issue is here, don't create a thread on the stack
// and pass it to the thread group use new instead!
boost::thread tWorker(&ThreadWorker, (i + 1));
vecThreads.add_thread(&tWorker);
}
You are adding a thread created on the stack to the thread group. As soon as you iterate over the loop that threads memory is invalidated. You will need to new the thread and pass that pointer to add_thread.