Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - JohnWS

Pages: [1]
1
Hi!

Waiting for threads using join() on 32-bit Windows Vista platforms doesn't appear to wait for the thread to finish (it just appears to return immediately), whereas on 32-bit Windows XP platforms join() seems to work as expected.
Has anyone come across this problem before?
Here is a simple piece of code that (when compiled using Microsoft Visual C++ 2008 (Express Edition) at least) exhibits this problem:


// JoinTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include <sstream>

#include <thread>
#include <mutex>

#include "windows.h"


void log(const char * const message) {
   static std::mutex logMutex;

   // Lock the log mutex to prevent overlapped log I/O.
   std::lock_guard<std::mutex> log_lock(logMutex);

   std::cout << message;
}

void log(std::string & message) {
   log(message.c_str());
}

void threadCode(unsigned threadId) {
   std::ostringstream message;

   message << __FUNCTION__ << ": thread ID: " << threadId << " started.\n";
   log(message.str());

   // Wait for a while, so we can check what join() does.
   Sleep(threadId * 5000L);

   message << __FUNCTION__ << ": thread ID: " << threadId << " finished.\n";
   log(message.str());
}

int _tmain(int argc, _TCHAR* argv[])
{
   log("Main thread started.\n");

   std::thread myThread1(threadCode, 1);
   std::thread myThread2(threadCode, 2);

   log("Main thread waiting for child thread 1...\n");
   myThread1.join();
   log("Main thread waiting for child thread 2...\n");
   myThread2.join();

   log("Main thread finished.\n");

   // Wait for user input before closing-down the window.
   char ch;
   std::cin >> ch;

   return 0;
}


On XP the child threads complete before the main thread. However, on Vista it looks like the main thread gets to the end (waiting for user input) before the child threads have even started, but if you wait for a while (i.e. up to 30 seconds) before pressing a key, the child threads do actually start and finish (it's just that the main thread isn't waiting for them).

Am I doing something wrong or is a problem/limitation of the thread library code.

2
To answer my own question - I was missing the join the synchronized the main thread with the child one.
Once I had added that, the second temporary got deleted (but only at the point of the join).

So, here's the amended program:

// ThreadTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include <sstream>

#include <thread>
#include <mutex>

void log(std::string & message) {
   static std::mutex logMutex;

   // Lock the log mutex to prevent overlapped log I/O.
   std::lock_guard<std::mutex> log_lock(logMutex);

   std::cout << message;
}

class MyObject {
   public:
      MyObject(void) : data(0) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }
      ~MyObject(void) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject & operator=(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }

      int data;
};

void threadTest(MyObject myObject) {
   std::cout << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
}

int _tmain(int argc, _TCHAR* argv[])
{
   std::ostringstream message;
   MyObject myObject;
   myObject.data = 1;

   message << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
   log(message.str());

   std::thread myThread(threadTest, myObject);

   myThread.join();

   return 0;
}

and the new results:

MyObject::MyObject called. this=0012FECC.
wmain: myObject is at 0012FECC.
MyObject::MyObject called. this=0012FDA0, source=0012FECC.
MyObject::MyObject called. this=0052FFF4, source=0012FDA0.
MyObject::~MyObject called. this=0012FDA0.
MyObject::MyObject called. this=0052FC80, source=0052FFF4.
threadTest: myObject is at 0052FC80.
MyObject::~MyObject called. this=0052FC80.
MyObject::~MyObject called. this=0052FFF4.
MyObject::~MyObject called. this=0012FECC.

Sometimes, all it takes is to simplify the problem and to try to explain it...  :)

3
Further to my earlier post - I get the same symptoms if I just construct a thread using a simple function and object as a parameter.
Here is a minimal program that demonstrates the point:

// ThreadTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include <sstream>

#include <thread>
#include <mutex>

void log(std::string & message) {
   static std::mutex logMutex;

   // Lock the log mutex to prevent overlapped log I/O.
   std::lock_guard<std::mutex> log_lock(logMutex);

   std::cout << message;
}

class MyObject {
   public:
      MyObject(void) : data(0) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }
      ~MyObject(void) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ".\n";
         log(message.str());
      }
      MyObject & operator=(const MyObject & source) {
         std::ostringstream message;
         message << __FUNCTION__ << " called. this=" << this << ", source=" << &source << ".\n";
         log(message.str());
         this->data = source.data;
      }

      int data;
};

void threadTest(MyObject myObject) {
   std::cout << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
}

int _tmain(int argc, _TCHAR* argv[])
{
   std::ostringstream message;
   MyObject myObject;
   myObject.data = 1;

   message << __FUNCTION__ << ": myObject is at " << &myObject << ".\n";
   log(message.str());

   std::thread myThread(threadTest, myObject);

   return 0;
}


Built as a Win32 console application using Microsoft Visual C++ 2008 (Express Edition) on my machine, it produces the following output:

MyObject::MyObject called. this=0012FECC.
wmain: myObject is at 0012FECC.
MyObject::MyObject called. this=0012FDA0, source=0012FECC.
MyObject::MyObject called. this=0052FFF4, source=0012FDA0.
MyObject::~MyObject called. this=0012FDA0.
MyObject::MyObject called. this=0052FC80, source=0052FFF4.
threadTest: myObject is at 0052FC80.
MyObject::~MyObject called. this=0052FC80.

which appears to show the constructor being called twice for temporaries but the destructor only being called for one of them.

Am I missing something here?

4
Hi!

I'm firing-off threads using a class with an operator() method containing the thread code and passing it an object as a parameter.
Looking at the output of the instrumentation (logging) that I have included in the object's constructor, copy constructor, assignment operator and destructor methods, it looks like the thread library is creating two temporaries before creating the actual instance that is passed to the new thread but only destroying one (the first) of those temporaries.

Has anyone come across this problem before?
I'm wondering if I'm doing something wrong or if there's a subtle bug in the library code.

5
Okay. That's good to know.

Thanks Anthony.

6
Hi

I'm using Microsoft Visual C++ 2008 Express Edition SP1, with just::thread C++ Thread Library v1.0 to compile as the basic sample program below:

// ThreadTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <thread>
#include <mutex>
#include <iostream>

std::mutex io_mutex;

void greeting(const char* message) {
    std::lock_guard<std::mutex> lk(io_mutex);
    std::cout << message << std::endl;
}

int _tmain(int argc, _TCHAR* argv[]) {
    std::thread t(greeting,"Hello from another thread");
    greeting("Hello from the main thread");
    t.join();
    return 0;
}

The compilation succeeds but I get the following warnings:

1>------ Rebuild All started: Project: ThreadTest, Configuration: Debug Win32 ------
1>Deleting intermediate and output files for project 'ThreadTest', configuration 'Debug|Win32'
1>Compiling...
1>stdafx.cpp
1>Compiling...
1>ThreadTest.cpp
1>c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(109) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
1>        c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(108) : while compiling class template member function 'unsigned int std::__atomic_integral<__itype>::fetch_sub(__itype,std::memory_order) volatile'
1>        with
1>        [
1>            __itype=unsigned int
1>        ]
1>        c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(446) : see reference to class template instantiation 'std::__atomic_integral<__itype>' being compiled
1>        with
1>        [
1>            __itype=unsigned int
1>        ]
1>c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(109) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
1>        c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(108) : while compiling class template member function 'unsigned long std::__atomic_integral<__itype>::fetch_sub(__itype,std::memory_order) volatile'
1>        with
1>        [
1>            __itype=unsigned long
1>        ]
1>        c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(486) : see reference to class template instantiation 'std::__atomic_integral<__itype>' being compiled
1>        with
1>        [
1>            __itype=unsigned long
1>        ]
1>c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(109) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
1>        c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(108) : while compiling class template member function '__jss::uintmax_t std::__atomic_integral<__itype>::fetch_sub(__itype,std::memory_order) volatile'
1>        with
1>        [
1>            __itype=__jss::uintmax_t
1>        ]
1>        c:\program files\justsoftwaresolutions\justthread\include\cstdatomic(506) : see reference to class template instantiation 'std::__atomic_integral<__itype>' being compiled
1>        with
1>        [
1>            __itype=__jss::uintmax_t
1>        ]
1>Compiling manifest to resources...
1>Microsoft (R) Windows (R) Resource Compiler Version 6.1.6723.1
1>Copyright (C) Microsoft Corporation.  All rights reserved.
1>Linking...
1>LINK : C:\ThreadTest\Debug\ThreadTest.exe not found or not built by the last incremental link; performing full link
1>Embedding manifest...
1>Microsoft (R) Windows (R) Resource Compiler Version 6.1.6723.1
1>Copyright (C) Microsoft Corporation.  All rights reserved.
1>Build log was saved at "file://c:\ThreadTest\ThreadTest\Debug\BuildLog.htm"
1>ThreadTest - 0 error(s), 3 warning(s)
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========


I think Kenneth reported these warnings (amongst other errors) in v0.3 of the library but I've not seen anything else since, which probably means that it's a school-boy error on my part. It doesn't appear to break anything (at least as far as I can tell from simple applications), but does anyone know how to get rid of these warnings?

John

Pages: [1]