3

I was writing a piece of multithreaded code when I found that a for loop was not terminating. The starting code was approximately like this:

for(int i = V-1-tid; i >= 0; i-=NTHREADS){
  */ stuff */
}

V and NTHREADS are constants and tid is the thread id passed using pthread_create.

I then removed everything from the loop and wrote something like this to make sure nothing was interfering with i:

for(int i = 0; i<100; i++){
  std::cout<<i<<"<100? "<<(i<100)<<std::endl;
}

This still does not stop.

I spawn the threads using a simple:

for(int i = 0; i < NTHREADS; i++){
  pthread_create(&(threads[i]), NULL, foo, &(parameters[i]));
}

I tried declaring i as volatile, but this changed nothing. If I compile with -O0 then the loop stops correctly, but everything above -O0 has the same problem.

I am using gcc 9.4.0, more specifically g++-9 (Homebrew GCC 9.4.0) 9.4.0, and the flags I am using are:

-O3 -mavx -mavx2 -mfma -std=c++11 -march=native -fno-rtti -lquadmath -lpthread -g

I am currently looking through the assembly output from gcc to see what's happening, but understanding optimized x86 is a bit of a pain.

Am I missing something obvious? Is there anything I can try?

Edit: Added example.

EXAMPLE CODE:

#include <iostream>
#include <pthread.h>
#define NTHREADS 1

void *foo(void *args){
  for(int i = 0; i < 100; i++){
    std::cout<<i<<std::endl;
  }
}

int main(){
  pthread_t threads[NTHREADS];

  for(int i = 0; i < NTHREADS; i++){
    pthread_create(&(threads[i]), NULL, foo, NULL);
  }

  for(int i = 0; i < NTHREADS; i++){
    pthread_join(threads[i], NULL);
  }
}

The output I am getting can be seen here: godbolt.org/z/Mfjrj6Khr

6
  • you only wrote about the symptoms but there is not enough information to know why you get what you get. Please try to provide a minimal reproducible example Commented Jul 2, 2021 at 8:39
  • Thanks for letting me know. I've added an example with the same behaviour. Commented Jul 2, 2021 at 8:55
  • 1
    what output do you get from that example? I get the expected here: godbolt.org/z/4YTqYPdcn. Though technically the code has undefined behavior, because foo does not return a void*, ie the output could be anything Commented Jul 2, 2021 at 8:56
  • If I add -O3 on godbolt then it doesn't stop : godbolt.org/z/Mfjrj6Khr Commented Jul 2, 2021 at 8:59
  • Add return NULL. I can just reproduce with int main() { foo(0); } Commented Jul 2, 2021 at 9:01

1 Answer 1

3

The code has undefined behavior - a function has return type void * but does not return anything. The compiler gets confused and generates an endless loop.

Make sure to enable and listen to compiler warnings, they tell you that:

1.cpp: In function ‘void* foo(void*)’:
1.cpp:7:1: warning: no return statement in function returning non-void [-Wreturn-type]
    7 | }
      | ^

The shortest MCVE I was able to come up with that illustrates the point:

#include <cstdio>
void* foo() {
  for(int i = 0; i < 100; i++){
    putchar(i);
  }
}
int main() {
    foo();
}

that when compiled with -O3 on godbolt with g++11.1 does:

foo():
        push    rbx
        xor     ebx, ebx
.L2:
        mov     rsi, QWORD PTR stdout[rip]
        mov     edi, ebx
        add     ebx, 1
        call    putc
        jmp     .L2            # Just an endless loop.
main:
        sub     rsp, 8
        call    foo()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.