Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

#pragma omp cancel for compilation error

AndrewC
New Contributor III
2,906 Views

The following code fails to compile with

error : cancel for must be closely nested in a for region.

I am at a loss here, as this code seems to follow the example in OpenMP 4.0 examples PDF

void causes_an_exception()
{
	throw std::logic_error("exception");
}

void example()
{
	std::exception *ex = NULL;
	bool cancelled = false, failed = false;
	int N = 100000;
#pragma omp parallel for
	for (int i = 0; i < N; i++) {
		// no 'if' that prevents compiler optimizations
		try {
			causes_an_exception();
		}
		catch (std::exception *e) {
			// still must remember exception for later handling
#pragma omp atomic write
			ex = e;
			// cancel worksharing construct
#pragma omp cancel for
		}
	}
}

 

0 Kudos
1 Solution
Andrey_C_Intel1
Employee
2,906 Views

"It seems to cancel the thread running when the exception is thrown, but the other threads keep running to completion."

This is required by the OpenMP specification.  A thread can only be cancelled at cancellation points (e.g. "#pragma omp cancellation point for").  Once other threads do not encounter any cancellation point they are bound to run to completion. If you want other threads to early react on cancellation you need to insert cancellation point(s) where threads can check for cancellation.

 

View solution in original post

0 Kudos
11 Replies
Olga_M_Intel
Employee
2,906 Views

According to OpenMP specification "the cancel construct must be closely nested inside an OpenMP construct that matches the type specified in construct-type-clause of the cancel construct". So, the error seems to be correct but the example probably not.
 

0 Kudos
AndrewC
New Contributor III
2,906 Views

The OpenMP construct is a parallel for and the omp cancel statement is "cancel for" which matches the "for" construct

So as far as I am concerned this is a compiler bug unless an OpenMP guru can convince me otherwise.

If I make a small change to the code, adding a parallel region block I can get it to compile

void example()
{
	std::exception *ex = NULL;
	bool cancelled = false, failed = false;
	int N = 100000;
#pragma omp parallel
	{
#pragma omp for
		for (int i = 0; i < N; i++) {
			// no 'if' that prevents compiler optimizations
			try {
				causes_another_exception();
			}
			catch (std::exception *e) {
				// still must remember exception for later handling
#pragma omp atomic write
				ex = e;
				// cancel worksharing construct
#pragma omp cancel for
			}
		}
	}
}

 

0 Kudos
AndrewC
New Contributor III
2,906 Views

When I implement the above pattern in my code (not the trivial example above)

I get ( ICC 18.0 Update 2)

1>": : error : ** The compiler has encountered an unexpected problem.
1>** Segmentation violation signal raised. **
1>Access violation or stack overflow. Please contact Intel Support for assistance.

0 Kudos
AndrewC
New Contributor III
2,906 Views

Using GCC 6.3.1 gives the following message on my first example

warning: ‘#pragma omp cancel for’ inside ‘nowait’ for construct

0 Kudos
AndrewC
New Contributor III
2,906 Views

The following code causes an error in the Intel Compiller 18.0 Update 2

1>": : error : ** The compiler has encountered an unexpected problem.
1>** Segmentation violation signal raised. **
1>Access violation or stack overflow. Please contact Intel Support for assistance.

Windows 64/Release build

#include <stdexcept>
#include <list>
#include <vector>
#include <iostream>
#include <omp.h>


void causes_another_exception(int i)
{
	if(i==10){
		throw std::logic_error("exception");
	}else{
#pragma omp critical	
		std::cout << "thread:" << omp_get_thread_num() << " i:" << i << std::endl;
	}
}

void example()
{
	std::exception ex ;
	bool cancelled = false, failed = false;
	int N = 500;
#pragma omp parallel
	{
#pragma omp for
		for (int i = 0; i < N; i++) {
			try {
				causes_another_exception(i);
			}
			catch (std::exception &e) {
#pragma omp critical	
				std::cout << "thread " << omp_get_thread_num() << " caught exception:" << i << std::endl;
		
				// still must remember exception for later handling

				ex = e;
				// cancel worksharing construct
#pragma omp cancel for
			}
		}
	}
}

int main()
{
	char *str=getenv("OMP_CANCELLATION");
    if(str){
		std::cout << "OMP_CANCELLATION:"<<str << std::endl;
	}
	example();
}

 

0 Kudos
AndrewC
New Contributor III
2,906 Views

I have created a support ticket 03380632

0 Kudos
Andrey_C_Intel1
Employee
2,906 Views

Thank you for reporting the bug (actually two bugs, - one is the mistake in "illegal" constructs nesting, another one is the internal compiler error).

Regards, 
Andrey
 

0 Kudos
AndrewC
New Contributor III
2,906 Views

I did some tests using GCC 6.3.1

It complains about my first example with

// GCC test.cpp:38:23: warning: ‘#pragma omp cancel for’ inside ‘nowait’ for construct

There is some argument on stack exchange about this.

Interestingly, using GCC 6.3.1, although it can compile and build the final example, I cannot get "omp cancel for" to work as expected even with OMP_CANCELLATION=true. It seems to cancel the thread running when the exception is thrown, but the other threads keep running to completion.

 

0 Kudos
Andrey_C_Intel1
Employee
2,907 Views

"It seems to cancel the thread running when the exception is thrown, but the other threads keep running to completion."

This is required by the OpenMP specification.  A thread can only be cancelled at cancellation points (e.g. "#pragma omp cancellation point for").  Once other threads do not encounter any cancellation point they are bound to run to completion. If you want other threads to early react on cancellation you need to insert cancellation point(s) where threads can check for cancellation.

 

0 Kudos
AndrewC
New Contributor III
2,906 Views

Ok, thinking about that, it makes sense.

0 Kudos
AndrewC
New Contributor III
2,906 Views

Something like below works as expected.

I think the requirement for OMP_CANCELLATION to be set via as an env variable makes the whole idea a bit useless in the real world.

 

void example()
{
	std::exception ex ;
	bool cancelled = false;
	int N = 500;
#pragma omp parallel
	{
#pragma omp for
		for (int i = 0; i < N; i++) {
			if(cancelled){
				#pragma omp cancel for
			}
			try {
				causes_another_exception(i);
			}
			catch (std::exception &e) {
#pragma omp critical	
				std::cout << "thread " << omp_get_thread_num() << " caught exception:" << i << std::endl;
		
				// still must remember exception for later handling

				ex = e;
				cancelled=true;

			}
		}
	}
}

 

0 Kudos
Reply