2

I am making a client server application, with the server having a GUI. I am using Qt. For communication I am using pipes.

I have divided the server application into a backend, and a GUI. The backend has a PipeServer class, and in the GUI, I have overriden functions like onReceiveMessage etc.

Everything worked fine until I decided to add a std::queue as a base class member.

At the start of the application, I get an exception, and upon inspection it seems that my queue does not start with 0 elements. In fact it seems like the queue is not initialized at all. There are 2 possibilites: it could be because I the GUI class inherits 2 classes, and somehow the second base class, which is my PipeServer does not properly initialize its members, or it could be because the pipeServerGUI object is moved to a different thread by QT.

Any ideas on how I could solve this?

Relevant code:

class HookServer
{
    PIPEINST Pipe[INSTANCES]; 
    HANDLE hEvents[INSTANCES]; 

    VOID DisconnectAndReconnect(DWORD); 
    BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED); 
    VOID GetAnswerToRequest(LPPIPEINST); 

public:
    std::queue<std::string> messages;

    int init(std::string pipename);
    int run();
    virtual void onNewConnection() {};
    virtual void onReceiveMessage(std::string message) {};
};

class HookServerGUI : public QObject, public HookServer
{
    Q_OBJECT
    void onReceiveMessage(std::string message);
    void onNewConnection();

    public slots:

    void doWork() {
        init("\\\\.\\pipe\\hookpipe");
        run();
    }

    signals:
    void signalGUI(QString message);
};

    //GUIServerCreation

    QThread *thread = new QThread;
    HookServerGUI* worker = new HookServerGUI;
    QObject::connect(worker,SIGNAL(signalGUI(const QString&)),this,SLOT(processMessage(const QString&)));

    worker->moveToThread(thread);
    thread->start();



    QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);

EDIT:

The exception is a access violation exception. It happens in this part of code:

VOID HookServer::GetAnswerToRequest(LPPIPEINST pipe)
{
    onReceiveMessage(pipe->chRequest);
    if(!messages.empty())
    {
        std::string s = messages.front();
        messages.pop();
        strcpy(pipe->chReply,s.c_str());
        pipe->cbToWrite = strlen(s.c_str()+1);
    }
}

Since messages.empty() return some huge number, it tries to read the first object and somehow fails.

There is also no PipeServerGUI constructor.

EDIT2:

I solved part of this problem by placing parenthesis after new HookServerGUI();

The problem is that still the function does not work, and throws a access violation exception. It happens on the front() line. When checked in a debugger, the function does have 1 element, so it is not because it is empty. Any ideas?

EDIT3:

With the second run, unfortunately the queue.size() is still incorrect. Seems like a data race to me.

Bartlomiej Lewandowski
  • 10,771
  • 14
  • 44
  • 75
  • I don't think either of your two options are likely candidates. HookServer doesn't implement a constructor, so the default constructor should invoke the default constructor of its members (i.e. the std::queue). I don't see how threading could be part of the problem since HookServerGUI either gets constructed or it doesn't. Where is the error? Is it during the constructor for HookServerGUI? What is the exception type? – sbaker Jan 15 '14 at 21:44
  • it seems I partially solved the problem, I haven't placed the parenthesis after `new HookServerGUI`. – Bartlomiej Lewandowski Jan 15 '14 at 21:57
  • GetAnswerToRequest() is a private function. Who calls it? Maybe you should try creating a constructor and destructor in HookServer and log something or put breakpoints in there to make sure that your object is being created and not deleted before this function is called. – sbaker Jan 15 '14 at 22:00
  • @sbaker it is called in the run() function. For now I changed the queue to a string, and explicitly set it to an empty string. – Bartlomiej Lewandowski Jan 15 '14 at 22:26
  • Stylistic nitpicks: The `Qt::QueuedConnection` in `invokeMethod` is unnecessary, automatic connection will work correctly. `QObject::connect` can be passed normalized signatures, so `SIGNAL(signalGUI(QString))` and `SLOT(processMessage(QString))` is sufficient - it's less writing that way. The signature of the slot method should be `void signalGUI(const QString &)` - saves a `detach()` on the shared string data that way. – Kuba hasn't forgotten Monica Jan 15 '14 at 23:03
  • A data race implies a multithreaded application. Is your application multithreaded? If it is, you're probably accessing non-thread-safe data from multiple threads at the same time. That's a bug. – Kuba hasn't forgotten Monica Jan 22 '14 at 14:18

1 Answers1

0

The problems are in the code that you don't show, and it's a classic case of a memory bug, it looks like. Some code somewhere is writing on memory it doesn't own. Probably you have a bug in the way you use winapi. You need to create a minimal, self-contained test case.

I think you might be shooting yourself in the foot by not using QLocalSocket: on Windows, it's a named pipe - exactly what you want.

Besides, this is C++ code. There is no reason at all to put either PIPEINST or HANDLE into a raw C array. Use QVector or std::vector. Probably the rest of the code is full of C-isms like that, and something somewhere goes wrong.

I wouldn't discount a buffer overrun, since obviously you are ignoring the size of the buffer in PIPEINST from the - the strcpy can overrun the buffer. I'm also not sure that PIPEINST from the example code is using the same character type as what std::string::c_str() is returning.

Even if you wanted to implement your code using explicit pipes without QLocalSocket, you should still use C++, QString etc. and understand what's going on with your data.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I wanted to separate the Qt GUI from the backend, so that later I will be able to have a console application for example. – Bartlomiej Lewandowski Jan 15 '14 at 23:15
  • @BartlomiejLewandowski: You do understand that Qt core has nothing to do with the gui, and is perfect for use in server code, right? The backend, the gui fronted, and the console frontend can all be written using Qt. – Kuba hasn't forgotten Monica Jan 15 '14 at 23:18
  • @BartlomiejLewandowski: For example, [Qt interoperates nicely](http://stackoverflow.com/a/20642135/1329652) with nurses to create a console application on Unix/Linux. – Kuba hasn't forgotten Monica Jan 15 '14 at 23:19
  • But it seems that this is a big jump. It's like using boost for one little function. I don't want to force people that might want to use the code to have to have Qt. – Bartlomiej Lewandowski Jan 15 '14 at 23:21
  • I think the problem lies not in the pipe code itself. Since before even running it, I do not have them initialized. I will try to make a self contained example tommorow. – Bartlomiej Lewandowski Jan 15 '14 at 23:22
  • @BartlomiejLewandowski: You've just shown that using bare C APIs is harder, since you've shot yourself in the foot. If you are providing a backend using Qt and are exposing the interface as "stuff sent over the pipe" then people can use that pipe from any language, using any framework they care for. The pipe is the interface, the fact that the backend binary uses Qt is irrelevant unless your users will recompile the backend. – Kuba hasn't forgotten Monica Jan 15 '14 at 23:22
  • @BartlomiejLewandowski: There is no way to have an "uninitialized" `std::queue` as a member of a class unless you're overwriting its memory. It's a standard C++ container class and it simply doesn't exist in a state that's not ready for use. What you describe **is a memory bug in your code, no doubt about it**. – Kuba hasn't forgotten Monica Jan 15 '14 at 23:25
  • @BartlomiejLewandowski: It's of course possible that there is another bug that actually forces "a lot" of strings into that queue, very quickly. In that case `messages.size()` will return "a lot" and it's not uninitialized at all. That case may be less likely, though - hard to tell without seeing the code. At the moment I would discount this case. – Kuba hasn't forgotten Monica Jan 15 '14 at 23:26