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

zommi
  • 33
  • 4
  • 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 [mcve] – 463035818_is_not_an_ai Jul 02 '21 at 08:39
  • Thanks for letting me know. I've added an example with the same behaviour. – zommi Jul 02 '21 at 08:55
  • 1
    what output do you get from that example? I get the expected here: https://godbolt.org/z/4YTqYPdcn. Though technically the code has undefined behavior, because `foo` does not return a `void*`, ie the output could be anything – 463035818_is_not_an_ai Jul 02 '21 at 08:56
  • If I add -O3 on godbolt then it doesn't stop : https://godbolt.org/z/Mfjrj6Khr – zommi Jul 02 '21 at 08:59
  • Add `return NULL`. I can just reproduce with `int main() { foo(0); }` – KamilCuk Jul 02 '21 at 09:01
  • please don't add "Solution" to the question. Thats what answers are for. Consider that questions and answers are supposed to be useful also for future readers, and when the answer is included in the question then it is rather confusing. Instead you could add what output you get from that example, because that information is currently missing – 463035818_is_not_an_ai Jul 02 '21 at 09:10

1 Answers1

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()
KamilCuk
  • 120,984
  • 8
  • 59
  • 111