3

I am developing a server application which based on clients requests executes certain image processing action using OpenCV library. The nature of application dictates to use multiple threads. Lately I have been dealing with a very stubborn bug which is causing segfault. I was able to zero-in to the code part where the segmentation fault occurs.

Here is the minimal implementation.

#include <iostream>
#include <thread>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

int main() {
  cv::Mat img_input = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);
  cv::Mat img_output = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);

  int i = 1;
  while (i < 100) {
    std::cout << "- - - - - - - - - - - - - - " << i++ << std::endl;
    std::thread([&]() {
        std::cout << "Thread started." << std::endl;
        cv::Canny(img_input, img_output, 10, 20);
        std::cout << "Thread finished." << std::endl;
    }).join();

    std::cout << "Thread joined." << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(150));
  }

  return 0;
}

And the program fails with 2 different outputs. With this output ...

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Segmentation fault (core dumped)

.. or with this one.

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Thread finished.
Segmentation fault (core dumped)

Let me share my additional findings. The segfault occurs only on the embedded linux device (Toradex Colibri iMX6 - Computer on Module) where I have installed OpenCV version 3.3.0-dev. The segfault is caused only when I am using the Canny() function within a new thread. I have tried calling also other OpenCV functions, but non of them generated any faults.

When I run the program on my PC (Ubuntu 16.04, OpenCV version 3.3.0) no segfault occurs.

Any ideas?

****** Update 1 ******

I tried to follow some suggestions from the comments. However the problem still persist.

I move the Mat variables inside the scope of each thread to make them local to the thread. I also added some extra sleep time to wait the threads to finish.

Here is my new implementation.

#include <iostream>
#include <thread>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>


void run() {
  cv::Mat img_input = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);
  cv::Mat img_output = cv::Mat::zeros(cv::Size(240, 320), CV_8UC1);

  std::cout << "Thread started." << std::endl;
  cv::Canny(img_input, img_output, 10, 20);
  std::cout << "Thread finished." << std::endl;
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

int main() {
  int i = 1;
  while (i < 100) {
    std::cout << "- - - - - - - - - - - - - - " << i++ << std::endl;
    std::thread t1(run);

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    std::cout << "Waiting to join thread." << std::endl;
    t1.join();
    std::cout << "Thread joined." << std::endl;

    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  return 0;
}

The output again varies between ...

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Waiting to join thread.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Thread finished.
Segmentation fault (core dumped)

... and ...

- - - - - - - - - - - - - - 1
Thread started.
Thread finished.
Waiting to join thread.
Thread joined.
- - - - - - - - - - - - - - 2
Thread started.
Segmentation fault (core dumped)

****** Update 2 ******

I have found a post with similar issue here. A suggestion is made that adding the following line to the code solves the issue.

cv::setNumThreads(0);

I have added this line on the beginning of my main() function and is seems that it solves the problem of segmentation faults. Does that help to explain what is happening with the program?

For now this seems as good quick fix, but before I accept it as an appropriate solution I would like to understand the background of it. Can anyone explain why the segfault does not occur any more?

Is there a better solution?

Matej Jeglič
  • 403
  • 3
  • 11
  • The mat variables may need mutex lock so that one thread do not over write when it is in use. But to ensure parallelism, better use vector ( each thread handle cv::Mat independently) and the thread should not be blocking ( don't join immediately). – seccpur Aug 29 '18 at 07:43
  • Thank you for the suggestions. I tried some of your suggestions (see the updated question). I moved the Mat variables inside the scope of the thread because I do not need any sharing of this variables between the threads. I did not use any mutex lock, because I'm assuming that I don't need them with this setup. I also added some sleep to force that threads don't join immediately. Unfortunately this changes do not help. – Matej Jeglič Aug 29 '18 at 10:00
  • Why not `gdb` to see what happened? – xiaobing Aug 29 '18 at 10:39
  • @xiaobing, I used gdb debugger to find the portion of the code where it fails. when the program fails the program stops at the following disassembler line. 0x767d5770 04 30 12 e5 ldr r3, [r2, #-4] I don't know how to help myself with this information. Can you point me in the right direction? – Matej Jeglič Aug 29 '18 at 11:17
  • "OpenCV version 3.3.0-dev" -- that smells a lot like a unstable build made from the master branch sometime after 3.3.0 was released. Can you try with a stable version? Can you try with some other version (on the same hardware)? – Dan Mašek Aug 29 '18 at 14:02
  • You need setup environment to step into `cv::Canny`, typically by compiling OpenCV from source on your system, but, if you can't (because limited resource on embedded device), you may need `gdb` remote debugging – xiaobing Aug 30 '18 at 07:50

2 Answers2

1

It turns out that setting the number of threads to 0 or 1 solves the problem (segfaults do not occur anymore). This is done by one of the following two lines of code.

cv::setNumThreads(0); // Setting the number of thread to 0.
cv::setNumThreads(1); // Setting the number of thread to 1.

I don't know OpenCV library well enough to understand why segfaults occur and why this line is fixes the problem, but at this point I am satisfied with the result and will mark this question as answered. Hopefully this will be helpful for others.

Matej Jeglič
  • 403
  • 3
  • 11
  • 1
    for completeness there is the [documentation link for setNumThreads](https://docs.opencv.org/3.3.1/db/de0/group__core__utils.html#gae78625c3c2aa9e0b83ed31b73c6549c0), it appears that, with this solution, Canny will not use any parallelization features it might have implemented – slawekwin Sep 06 '18 at 06:55
0

This is rather a comment, but I'm missing the corresponding rights. Sorry for that.

It is quite probable that the error is not caused by the call to OpenCV but by the threading facilities in use. One possibility is your use of a temporary std::thread object in the while loop. Try giving it a name, e.g.

std::thread worker([]...);
worker.join()

As the change above didn't help, the issue may also be on a lower level (e.g., library conflicts).

Marc
  • 96
  • 7
  • Thank you for the comment. I rearranged the code to follow your suggestion (see the updated question), but unfortunately I still get the segfault. – Matej Jeglič Aug 29 '18 at 10:03
  • Too bad. As there is no more data sharing in the new version, your issue should be something different. I'd try looking into similar issues here, e.g., https://stackoverflow.com/questions/9002264/starting-a-stdthread-with-static-linking-causes-segmentation-fault?rq=1 – Marc Aug 29 '18 at 10:06
  • I found out that explicitly telling the OpenCV library to disable threading optimizations (calling function cv::setNumThreads(0); ) prevents the segfault. Do you maybe know why this helps? I updated the original post with this additional information. – Matej Jeglič Aug 29 '18 at 11:49
  • That is really interesting! Could you check which parallel framework is used by your embedded and desktop builds? cv::getBuildInformation() should provide the information – Marc Aug 29 '18 at 12:00
  • Parallel framework (desktop): TBB (ver 4.4 interface 9002) Parallel framework (embedded): TBB (ver 2017.0 interface 9106) whole output: (desktop): https://text-share.com/view/d7c75635#7KK2WC6wbZsF833dFAF4zXxZb0pzInJG (embedded): https://text-share.com/view/86fcec3a#25brz6iGZLIBsWjKXCMWe26qWVjEekKM – Matej Jeglič Aug 29 '18 at 12:53
  • Looks like TBB is the problem, then. You may want to update your question title. I don't know enough about the library to be of help in that regards. Good luck! – Marc Aug 29 '18 at 13:18