Skip to content

Fix GUI thread deadlock caused by orphaned OpenMP pragmas - #14593

Open
magnesj wants to merge 9 commits into
OPM:devfrom
magnesj:vizfwk-remove-orphaned-omp-pragmas
Open

Fix GUI thread deadlock caused by orphaned OpenMP pragmas#14593
magnesj wants to merge 9 commits into
OPM:devfrom
magnesj:vizfwk-remove-orphaned-omp-pragmas

Conversation

@magnesj

@magnesj magnesj commented Aug 24, 2026

Copy link
Copy Markdown
Member

Fixes #14596

An orphaned #pragma omp for (no enclosing omp parallel) is not compiled away by MSVC. It emits a vcomp_barrier sized for the calling thread's OpenMP team, so the thread blocks forever waiting for pool workers that never reach it. In cvfqt::Utils::toTextureImageRegion, reached from caf::Viewer::paintGL, this freezes the GUI.

The fix removes the orphaned directives, and the rest of the branch is the follow up from reviewing all OpenMP use in the code base.

Commit Change
Remove stray #pragma omp for The deadlock itself, in cvfqt::Utils::toTextureImageRegion
Use #pragma omp parallel for Same orphaned construct in RifRoffFileTools and RimCornerPointCase, given the parallelism it was meant to have
Remove dead reserve and barrier RigCellFaceGeometryTools: the reduction variable was never incremented, so the reserve was a no-op called unsynchronized by every thread
Remove dead ordered clause RivNNCGeometryGenerator: ordered with no ordered region, plus a lock per triangle, replaced by per thread buffers
Remove unnecessary critical RiaCurveMerger: each iteration writes to its own element
Collect cells per thread RimPolygonFilter: eight critical sections removed from per cell loops
Document OpenMP conventions docs/agents/coding-style.md
Name the remaining critical sections All unnamed regions share one process wide lock

Each commit is independent and can be dropped on its own. The parallelism change to the two import loops is a performance change, not a correctness fix, and has not been benchmarked.

@kriben
kriben self-requested a review August 24, 2026 09:25
Comment thread ApplicationLibCode/FileInterface/RifRoffFileTools.cpp Outdated
Comment thread Fwk/VizFwk/LibGuiQt/cvfqtUtils.cpp Outdated

@kriben kriben left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, but comment is verbose and adds lots of context which will not be important when looking at the code in the future.

…eRegion


The pragma had no enclosing parallel region, so it did not parallelize anything and only added confusion.

This code caused a deadlock on Windows for the following workflow:
1. import a grid model
2. Import a large summary ensemble with no ESMRY that triggers display of a progress dialog. Make sure the progress dialog is displayed on top of the 3D models -> deadlock
The loops were annotated with #pragma omp for, which is a work-sharing construct that does nothing outside an enclosing parallel region. Replace it with #pragma omp parallel for so the cell loops actually run in parallel.
totalNumberOfConnections was declared, listed in the reduction clause and used to size otherConnections, but it was never incremented. The counting was lost when the loop body was moved into extractConnectionsForFace, so the call has always been reserve( size() + 0 ), which RigConnectionContainer::reserve turns into a no-op.

The reserve was also called by every thread on the shared container without synchronization. Removing it eliminates that data race. The explicit barrier is redundant as well, since the omp for construct has no nowait clause and therefore already synchronizes before the merge.
@magnesj
magnesj force-pushed the vizfwk-remove-orphaned-omp-pragmas branch from 7b29c97 to e66c69b Compare August 24, 2026 11:39
…metry generation


The loop was declared with an ordered clause, but the body contains no ordered region, so the clause only enabled the ordered scheduling machinery without providing any ordering. The intended deterministic vertex order was not achieved either, because the actual synchronization was a critical section, which does not preserve iteration order.

Collect vertices in per thread buffers and merge them in thread order after the parallel region, following the pattern already used in RivFaultGeometryGenerator. This removes a lock acquisition per triangle in the innermost polygon loop, and makes the generated geometry identical from run to run.
Each iteration writes to its own element of accumulatedValidValues, which is sized before the loop, so no synchronization is required. The write to curveValues a few lines below uses the same pattern without a critical section.

All unnamed critical regions share a single process wide lock, so this also removes contention against unrelated parallel loops.
… critical sections


All four cell filter loops guarded a push_back into a shared container with an unnamed critical section, taking a process wide lock for every cell matching the polygon. On large grids with a permissive polygon this can be slower than running single threaded, and it made the resulting cell order vary between runs.

Collect the cells in per thread buffers and append them in thread order after the parallel region, following the pattern used elsewhere in the code base. This removes eight critical sections.
Describe the OpenMP 2.0 constraints that follow from building with MSVC /openmp, and why an orphaned work sharing construct can deadlock the application when the master thread is also the Qt GUI thread.

Also document the pattern of collecting results in per thread buffers and merging after the parallel region, the convention of naming critical sections, and the rule that exceptions must not escape a structured block.
All unnamed critical regions map to the same implicit name, so a single process wide lock was shared by summary import, geometry generation, grid bounding box computation and NNC merging. Unrelated parallel loops therefore serialized against each other.

Give each region a critical_section_ name describing what it protects. The two regions guarding RifOpmCommonEclipseSummary::sm_createdEsmryFileCount deliberately share one name, as they protect the same counter.
@magnesj
magnesj marked this pull request as ready for review August 24, 2026 12:47
@magnesj
magnesj requested a review from kriben August 24, 2026 12:48
Comment thread ApplicationLibCode/ProjectDataModel/CellFilters/RimPolygonFilter.cpp Outdated
… K filter


Move the call to cellCornerVertices below the K filter early out, so the corner coordinates are only computed for the cells that are actually tested against the polygon.
@magnesj
magnesj force-pushed the vizfwk-remove-orphaned-omp-pragmas branch from 3f23cc0 to fa6e257 Compare August 24, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GUI freeze: orphaned OpenMP pragmas deadlock the paint thread

2 participants