0
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <iostream>

FILE*            fp;
pthread_mutex_t demoMutex         = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  conditionVariable = PTHREAD_COND_INITIALIZER;
unsigned int    condition         = 0;

struct serverInfo
{
    unsigned int serverId;
    pthread_t    threadId;
    std::vector<std::string> queue;
};
std::vector<serverInfo> serverInfoVector;

void* printHello(void* threadId)
{
    pthread_t* my_tid = (pthread_t*)threadId;

    pthread_mutex_lock(&demoMutex);
    while (condition == 0)
        pthread_cond_wait(&conditionVariable, &demoMutex);

    unsigned int i = 0;
    char found = false;

    if (serverInfoVector.size () > 0) {
       while ((i <= serverInfoVector.size()) && (found == false)) {
          if (*my_tid == serverInfoVector[i].threadId) {
             found = true;
             break;
          }
          else
             i++;
       }
    }

    while (!serverInfoVector[i].queue.empty()) {
       std::cout << "\nThread: " << pthread_self() << ", poped from queue: " << serverInfoVector[i].queue.front();
       serverInfoVector[i].queue.pop_back();
    }

    pthread_mutex_unlock(&demoMutex);
    pthread_exit(NULL);
}

void checkServerExists(unsigned int serverNumber, std::string message)
{
    unsigned int i = 0;
    char found = false;

    pthread_mutex_lock(&demoMutex);

    if (serverInfoVector.size () > 0) {
       while ((i <= serverInfoVector.size()) && (found == false)) {
          if (serverNumber == serverInfoVector[i].serverId) {
             found = true;
             break;
          }
          else
             i++;
       }
    }

    if (found == false) {
       // This server doesn't exist, so create a thread for it, create a queue for it, push the message in the corresponding queue.
       // Push the server number in the serverNumberArray.

       // Create a thread for it.
       pthread_t newThread;
       int returnValue;
       if ((returnValue = pthread_create (&newThread, NULL, printHello, (void*) &newThread)) != 0) {
          printf("\nerror: pthread_create failed with error number %d", returnValue);
       }
       printf("\nIn checkServerExists()`: thread id %ld\n", newThread);

       // Push the message in its queue.
       serverInfo obj;
       obj.serverId = serverNumber;
       obj.threadId = newThread;
       obj.queue.push_back(message);
       serverInfoVector.push_back(obj);

       condition++;
       pthread_cond_signal(&conditionVariable);
       pthread_mutex_unlock(&demoMutex);

       for (unsigned int i = 0; i < serverInfoVector.size(); i++)
          pthread_join(serverInfoVector[i].threadId, NULL);
    }
    else {
       // This server exists, so lookup its thread and queue, push the message in the corresponding queue.
       printf("\nIn else ()`: thread id %ld\n", serverInfoVector[i].threadId);
       serverInfoVector[i].queue.push_back(message);

       condition++;
       pthread_cond_signal(&conditionVariable);
       pthread_mutex_unlock(&demoMutex);

       for (unsigned int i = 0; i < serverInfoVector.size(); i++)
          pthread_join(serverInfoVector[i].threadId, NULL);
    }
}

int main()
{
   fp = fopen("xyz", "w");

   checkServerExists(1, "anisha");
   checkServerExists(2, "kaul");
   checkServerExists(1, "sanjeev");
   checkServerExists(2, "sharma");
}

Output:

In checkServerExists ()`: thread id 140233482061584

Thread: 140233482061584, poped from queue: anisha
In checkServerExists ()`: thread id 140233482061584

In else ()`: thread id 140233482061584

In else ()`: thread id 140233482061584

The problem is that it seems that only one thread is getting created! I have called the function checkServerExists 4 times in main() and 2 times with different serverID, so two threads should be created?

What am I missing?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411
  • 1
    Use C++ headers, not C headers. `cstdio`, not `stdio.h`. – Lightness Races in Orbit Mar 05 '12 at 09:59
  • Also, you might want to take a look at `boost::thread` or even `std::thread` if your toolchain provides it. – ereOn Mar 05 '12 at 10:06
  • @ereOn Currently I am learning only about pthreads, so would like to concentrate only them, thanks though. – Aquarius_Girl Mar 05 '12 at 10:07
  • @LightnessRacesinOrbit If you want C, use C headers. At least until C++11, what you got from the C++ headers was never what the standard required, and varied from one implementation to the next. – James Kanze Mar 05 '12 at 10:19
  • @James: Really? I always found the inherited headers to be fully compliant on any toolchain that I've used. Of course I'm sure I didn't try _all_ of the symbols. – Lightness Races in Orbit Mar 05 '12 at 10:23
  • @LightnessRacesinOrbit They weren't compliant (nor identical) in VC++, g++ or Sun CC. Those are the only compilers where I've actually verified them, but discussions with one of the authors of these libraries suggests very strongly that compliance is impossible without completely rewriting the C librarys. (There's a reason why C++11 changed the requirements.) – James Kanze Mar 05 '12 at 10:31
  • @JamesKanze: Can you give an example of a feature or symbol that is different between those toolchains, and/or non-compliant? – Lightness Races in Orbit Mar 05 '12 at 10:46
  • @LightnessRacesinOrbit For starters, they introduce additional names into `::`, which C++03 didn't allow. And they didn't always introduce the same names. Practically speaking, the `` headers, at least as defined in C++03, can be considered an experiment which didn't work; most professionals I know avoided them. – James Kanze Mar 05 '12 at 11:23
  • @JamesKanze: OK, well, at least now that we have C++11, don't use the C headers. Their use is deprecated (`C.3.1/1`) – Lightness Races in Orbit Mar 05 '12 at 13:10
  • @LightnessRacesinOrbit We don't all have C++11 yet. And even when we do get it, there's always the question of coherence with an older code base. I've yet to see any real advantage in the new headers---I use them when portability isn't a concern, and I know what they provide, but not otherwise. – James Kanze Mar 05 '12 at 13:11

4 Answers4

2

EDITED: the real problem is that the threads terminate and are joined as soon as they are created, as pointed out by hmjd. I'm leaving this, rather than deleting it, because the following are also problems.

I see two creations of a new thread in the output you post: "In checkServerExists" is only output if you create a new thread. I also see undefined behavior in the printf: newThread has type pthread_t, which can be anything the system wants it to be, and is likely something other than a long, which is what is required by the format you are passing to printf. There is, as far as I know, no way of (portably) outputting a pthread_t (other than a hex-dump of its bytes); the values you display as thread id's don't mean anything. Also, you can't compare pthread_t using ==, you need to use pthread_equal. (On at least one platform I've used, pthread_t was a struct.)

There are a number of other strange things with your code. Why declare found with type char, rather than type bool, for example. And why found == false, rather than !found. And why the break; in the loop, since you have the condition in the loop control variable. A much more idiomatic form of the start of checkServerExists would be:

for ( std::vector<ServerInfo>::iterator current = serverInfoVector.begin();
        current != serverInfoVector.end() && current->serverId != serverNumber;
        ++ current ) {
}
if ( current == serverInfoVector.end() ) {
    //  not found...
} else {
    //  found...
}

Assuming you didn't create a predicte object for the lookup, and just use std::find.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • +1 Hadn't noticed the misuse of `printf()` with `pthread_t`, which may invalidate my explanation. – hmjd Mar 05 '12 at 10:20
  • `why the break; in the loop,` to preserve the value of `i` to be used as an index value. – Aquarius_Girl Mar 06 '12 at 06:11
  • 1
    @AnishaKaul There is no way to print a `pthread_t`, except by printing a hex dump of the bytes in it. And that's not likely to be significant, since the type may contain padding, which is insignificant. – James Kanze Mar 06 '12 at 09:29
  • 1
    @AnishaKaul You don't need a `break` to preserve `i`. If the total condition is false, you stop looping, and `i` has whatever value it has at that moment. (Of course, the logical way to do your lookup is to use `std::find`.) – James Kanze Mar 06 '12 at 09:30
  • @JamesKanze ` If the total condition is false, you stop looping` I understand that now. – Aquarius_Girl Mar 06 '12 at 09:42
1

I am unsure if this is contributing to the behaviour, but the following is an error:

while ((i <= serverInfoVector.size ()) && (found == false))
{
    if (serverNumber == serverInfoVector [i].serverId)
    {
        found = true;
        break;
    }
    else
        i++;
}

serverInfoVector[i] will be accessing one too many due to the <= condition in the if. Change to:

while ((i < serverInfoVector.size ()) && (found == false))

EDIT:

I think this is the problem: when checkServerExists() is called, it seems to wait for the thread that it started to complete:

for (unsigned int i = 0; i < serverInfoVector.size(); i++)
    pthread_join(serverInfoVector[i].threadId, NULL);

This means that thread id 140233482061584 is no longer in use and is available again to be associated with a new thread. When the next call to checkServerExists() is made the thread id is reused, giving the impression that only one thread was started.

EDIT 2:

As pointed out by Schwarz this is incorrect:

if (*my_tid == serverInfoVector[i].threadId) {

you need to use pthread_equal() to compare two pthread_t. Change to:

if (pthread_equal(*my_tid, serverInfoVector[i].threadId)) {

or alternatively pass the serverId as the argument to the thread.

hmjd
  • 120,187
  • 20
  • 207
  • 252
  • I tried it, doesn't solve the problem. But still - good point. – Aquarius_Girl Mar 05 '12 at 10:06
  • 1
    The point in the edit is almost certainly the critical point. And it's even worse that @hmjd indicates; further attempts to use the thread will be no-ops, because it will have terminated. – James Kanze Mar 05 '12 at 10:20
  • hmjd, and @JamesKanze How to join then safely, in your opinion? – Aquarius_Girl Mar 05 '12 at 10:20
  • Join after all calls to `checkServerExists()` in `main()`. – hmjd Mar 05 '12 at 10:21
  • Also, you need to ensure the started threads live for as long as necessary to enable them to be reused. – hmjd Mar 05 '12 at 10:25
  • From the main thread, when you're ready to shut down. You send a signal (`pthread_cancel`) to each thread, then join it. – James Kanze Mar 05 '12 at 10:27
  • The issue is that the second thread isn't printing anything. That is because it picks up the first vector item, not the second where the string to print was put. – CashCow Mar 05 '12 at 10:32
  • @JamesKanze Why do I have to cancel them? If I simply put pthread_join in the main(), will that not be enough? – Aquarius_Girl Mar 06 '12 at 06:05
  • @AnishaKaul: The `pthread_join` function waits for the threads to terminate. If they're coded to terminate, that's fine. if not, you're going to be waiting an awfully long time. – David Schwartz Mar 06 '12 at 07:45
  • @DavidSchwartz `coded to terminate` means? I thought when threads finished there tasks they will terminate. How to code to terminate? – Aquarius_Girl Mar 06 '12 at 07:48
  • @AnishaKaul: You can call `pthread_exit` or you can return from the thread's starting function. What a thread does when it finishes its tasks is up to you, since you are writing its code. You could, for example, code it to wait for new tasks. – David Schwartz Mar 06 '12 at 08:19
  • @AnishaKaul As you've written the code, the threads will terminate immediately. And not be there for further requests. If you're trying to send messages to a thread, it shouldn't terminate. Except at some point when you want it to, so you can `join`. – James Kanze Mar 06 '12 at 09:28
1
      if (*my_tid == serverInfoVector[i].threadId) {

You cannot compare pthread_ts that way. This is a C interface, not a C++ interface. So there's no operator overloading to make this comparison work sensibly. It's wrong for the same reason this is wrong:

    const char *foo="foo";
    if(foo == "foo") ...

You have to use a sensible comparison function. In my example, strcmp. In your code, pthread_equal.

Also, after you pthread_join a thread, its pthread_t is no longer valid. You must not pass it to any pthread_* function again. That's as bad as dereferencing a pointer after passing it to free.

(You may want to fix some all of the bugs reported in this thread and post a new question with your updated code and a description of any issues you still have with it.)

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • The problem is not how he is comparing the thread ids. (If he couldn't compare them that way it would be a compiler error). The problem is that he shouldn't be comparing them at all. – CashCow Mar 05 '12 at 10:34
  • @CashCow the word should be "she" not "he". – Aquarius_Girl Mar 05 '12 at 10:35
  • It's not a compiler error, just like the string comparison is not a compiler error. It just has no semantic meaning because there is no guarantee that a given thread has one and only one valid thread ID representation. – David Schwartz Mar 05 '12 at 10:52
  • @DavidSchwartz In the cases I've seen where `pthread_t` was not an integral type, it was a `struct`, and comparison would have been a compile-time error. – James Kanze Mar 05 '12 at 11:57
  • @DavidSchwartz OK. I'll add that to my list:-). As far as Posix is concerned, it could even by a `double` (but I can't quite imagine that happening in practice). – James Kanze Mar 05 '12 at 13:11
  • David, the thread Id comparison was wrong anyway. I had to compare the server id, not the thread id. The function. pthread_equal is quite helpful. thanks. Can I join the threads at the end of main? – Aquarius_Girl Mar 06 '12 at 06:08
  • You can join any threads that you created joinable and have not yet joined or detached. – David Schwartz Mar 06 '12 at 06:11
0

I can see why it is failing to work the way you expect. Your server side is using the unique identifier passed in from main to determine what its thread id should be, but the thread function themselves is using the thread id.

The first worker thread you created terminated, and the second one is being created with the same id as the first, as this id is now available.

The main thread is putting the string onto the queue of the second element in the vector but your thread is picking up the first element of the vector because it has a matching thread id.

All the things they previous posters have said should also be considered, by the way. Just that it is not those that produce your behaviour.

void* printHello(void* serverptr) 
{ 
    serverInfo * info = static_cast< serverInfo * >(serverptr);

  // look through the queue
}

Change the collection type to std::list or std::deque so it will not invalidate the pointer if you subsequently do a push_back whilst the thread is processing it.

In checkServerExists, pass the address of serverInfo into the thread function rather than the address of the thread_id.

You might also "index" your collection with a map from int to serverInfo* or to the list iterator if you use a list. You should not use std::map<int, serverInfo> though because it might invalidate your pointers if you add new entries to the map.

Incidentally, your worker thread appears to terminate too early, because when you send the later info to the old ids, the threads have already gone.

The queue being empty should not be the condition on which to terminate the thread and you should use some other means.

Incidentally, whilst it is thread-safe, your mutex is locked for so long that you are not really going to achieve any decent performance by using multiple threads here.

CashCow
  • 30,981
  • 5
  • 61
  • 92