9.11. Lock Routines#

This section is about the use of lock routines for synchronization.

9.11.1. omp_init_lock Routine#

The following example demonstrates how to initialize an array of locks in a parallel region by using omp_init_lock.

//%compiler: clang
//%cflags: -fopenmp

/*
* name: init_lock.1
* type: C++
*/
#include <omp.h>

omp_lock_t *new_locks() {
  int i;
  omp_lock_t *lock = new omp_lock_t[1000];

  #pragma omp parallel for private(i)
    for (i=0; i<1000; i++)
    { omp_init_lock(&lock[i]); }

    return lock;
}
!!%compiler: gfortran
!!%cflags: -fopenmp

! name: init_lock.1
! type: F-fixed
      FUNCTION NEW_LOCKS()
        USE OMP_LIB        ! or INCLUDE "omp_lib.h"
        INTEGER(OMP_LOCK_KIND), DIMENSION(1000) :: NEW_LOCKS
        INTEGER I

!$OMP   PARALLEL DO PRIVATE(I)
          DO I=1,1000
            CALL OMP_INIT_LOCK(NEW_LOCKS(I))
          END DO
!$OMP   END PARALLEL DO

      END FUNCTION NEW_LOCKS

9.11.2. omp_init_lock_with_hint Routine#

The following example demonstrates how to initialize an array of locks in a parallel region by using omp_init_lock_with_hint. Note, hints are combined with an | or + operator in C/C++ and a + operator in Fortran.

//%compiler: clang
//%cflags: -fopenmp

/*
* name: init_lock_with_hint.1
* type: C++
* version: omp_5.0
*/
#include <omp.h>

omp_lock_t *new_locks()
{
  int i;
  omp_lock_t *lock = new omp_lock_t[1000];

  #pragma omp parallel for private(i)
    for (i=0; i<1000; i++)
    {
      omp_init_lock_with_hint(&lock[i],
        static_cast<omp_lock_hint_t>(omp_sync_hint_contended |
                                     omp_sync_hint_speculative));
    }
    return lock;
}
!!%compiler: gfortran
!!%cflags: -fopenmp

! name: init_lock_with_hint.1
! type: F-fixed
! version: omp_5.0
      FUNCTION NEW_LOCKS()
        USE OMP_LIB        ! or INCLUDE "omp_lib.h"
        INTEGER(OMP_LOCK_KIND), DIMENSION(1000) :: NEW_LOCKS

        INTEGER I

!$OMP   PARALLEL DO PRIVATE(I)
          DO I=1,1000
            CALL OMP_INIT_LOCK_WITH_HINT(NEW_LOCKS(I),
     &             OMP_SYNC_HINT_CONTENDED + OMP_SYNC_HINT_SPECULATIVE)
          END DO
!$OMP   END PARALLEL DO

      END FUNCTION NEW_LOCKS

9.11.3. Ownership of Locks#

Ownership of locks has changed since OpenMP 2.5. In OpenMP 2.5, locks are owned by threads; so a lock released by the omp_unset_lock routine must be owned by the same thread executing the routine. Beginning with OpenMP 3.0, locks are owned by task regions; so a lock released by the omp_unset_lock routine in a task region must be owned by the same task region.

This change in ownership requires extra care when using locks. The following program is conforming in OpenMP 2.5 because the thread that releases the lock lck in the parallel region is the same thread that acquired the lock in the sequential part of the program (primary thread of parallel region and the initial thread are the same). However, it is not conforming beginning with OpenMP 3.0, because the task region that releases the lock lck is different from the task region that acquires the lock.

//%compiler: clang
//%cflags: -fopenmp

/*
* name: lock_owner.1
* type: C
* version:    omp_5.1
*/
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

int main()
{
  int x;
  omp_lock_t lck;

  omp_init_lock (&lck);
  omp_set_lock (&lck);
  x = 0;

#pragma omp parallel shared (x)
  {
    #pragma omp masked
      {
        x = x + 1;
        omp_unset_lock (&lck);
      }

    /* Some more stuff. */
  }
  omp_destroy_lock (&lck);
  return 0;
}
!!%compiler: gfortran
!!%cflags: -fopenmp

! name: lock_owner.1
! type: F-fixed
! version:    omp_5.1
        program lock
        use omp_lib
        integer :: x
        integer (kind=omp_lock_kind) :: lck

        call omp_init_lock (lck)
        call omp_set_lock(lck)
        x = 0

!$omp parallel shared (x)
!$omp masked
        x = x + 1
        call omp_unset_lock(lck)
!$omp end masked

!       Some more stuff.
!$omp end parallel

        call omp_destroy_lock(lck)

        end

9.11.4. Simple Lock Routines#

In the following example, the lock routines cause the threads to be idle while waiting for entry to the first critical section, but to do other work while waiting for entry to the second. The omp_set_lock function blocks, but the omp_test_lock function does not, allowing the work in skip to be done.

Note that the argument to the lock routines should have type omp_lock_t (or omp_lock_kind in Fortran), and that there is no need to flush the lock variable ( lck ).

//%compiler: clang
//%cflags: -fopenmp

/*
* name: simple_lock.1
* type: C
*/
#include <stdio.h>
#include <omp.h>
void skip(int i) {}
void work(int i) {}
int main()
{
  omp_lock_t lck;
  int id;
  omp_init_lock(&lck);

  #pragma omp parallel shared(lck) private(id)
  {
    id = omp_get_thread_num();

    omp_set_lock(&lck);
    /*  only one thread at a time can execute this printf */
    printf("My thread id is %d.\n", id);
    omp_unset_lock(&lck);

    while (! omp_test_lock(&lck)) {
      skip(id);   /* we do not yet have the lock,
                     so we must do something else */
    }

    work(id);      /* we now have the lock
                      and can do the work */

    omp_unset_lock(&lck);
  }
  omp_destroy_lock(&lck);

  return 0;
}
!!%compiler: gfortran
!!%cflags: -fopenmp

! name: simple_lock.1
! type: F-fixed
      SUBROUTINE SKIP(ID)
      END SUBROUTINE SKIP

      SUBROUTINE WORK(ID)
      END SUBROUTINE WORK

      PROGRAM SIMPLELOCK

        INCLUDE "omp_lib.h"     ! or USE OMP_LIB

        INTEGER(OMP_LOCK_KIND) LCK
        INTEGER ID

        CALL OMP_INIT_LOCK(LCK)

!$OMP   PARALLEL SHARED(LCK) PRIVATE(ID)
          ID = OMP_GET_THREAD_NUM()
          CALL OMP_SET_LOCK(LCK)
          PRINT *, 'My thread id is ', ID
          CALL OMP_UNSET_LOCK(LCK)

          DO WHILE (.NOT. OMP_TEST_LOCK(LCK))
            CALL SKIP(ID)     ! We do not yet have the lock
                              ! so we must do something else
          END DO

          CALL WORK(ID)       ! We now have the lock
                              ! and can do the work

          CALL OMP_UNSET_LOCK( LCK )

!$OMP   END PARALLEL

        CALL OMP_DESTROY_LOCK( LCK )

      END PROGRAM SIMPLELOCK

9.11.5. Nestable Lock Routines#

The following example demonstrates how a nestable lock can be used to synchronize updates both to a whole structure and to one of its members.

//%compiler: clang
//%cflags: -fopenmp

/*
* name: nestable_lock.1
* type: C
*/

#include <omp.h>

typedef struct {
   int a,b;
   omp_nest_lock_t lck;
} pair;

int work1();
int work2();
int work3();

void incr_a(pair *p, int a)
{

  /* Called only from incr_pair, no need to lock. */
  p->a += a;

}

void incr_b(pair *p, int b)
{

  /* Called both from incr_pair and elsewhere, */
  /* so need a nestable lock. */

  omp_set_nest_lock(&p->lck);
  p->b += b;
  omp_unset_nest_lock(&p->lck);

}

void incr_pair(pair *p, int a, int b)
{

  omp_set_nest_lock(&p->lck);
  incr_a(p, a);
  incr_b(p, b);
  omp_unset_nest_lock(&p->lck);

}

void nestlock(pair *p)
{

  #pragma omp parallel sections
  {
    #pragma omp section
      incr_pair(p, work1(), work2());
    #pragma omp section
      incr_b(p, work3());
  }

}
!!%compiler: gfortran
!!%cflags: -fopenmp

! name: nestable_lock.1
! type: F-fixed
      MODULE DATA
        USE OMP_LIB, ONLY: OMP_NEST_LOCK_KIND
        TYPE LOCKED_PAIR
          INTEGER A
          INTEGER B
          INTEGER (OMP_NEST_LOCK_KIND) LCK
       END TYPE
      END MODULE DATA

      SUBROUTINE INCR_A(P, A)
        ! called only from INCR_PAIR, no need to lock
        USE DATA
        TYPE(LOCKED_PAIR) :: P
        INTEGER A
        P%A = P%A + A
      END SUBROUTINE INCR_A

      SUBROUTINE INCR_B(P, B)
        ! called from both INCR_PAIR and elsewhere,
        ! so we need a nestable lock
        USE OMP_LIB       ! or INCLUDE "omp_lib.h"
        USE DATA
        TYPE(LOCKED_PAIR) :: P
        INTEGER B
        CALL OMP_SET_NEST_LOCK(P%LCK)
        P%B = P%B + B
        CALL OMP_UNSET_NEST_LOCK(P%LCK)
      END SUBROUTINE INCR_B

      SUBROUTINE INCR_PAIR(P, A, B)
        USE OMP_LIB        ! or INCLUDE "omp_lib.h"
        USE DATA
        TYPE(LOCKED_PAIR) :: P
        INTEGER A
        INTEGER B

        CALL OMP_SET_NEST_LOCK(P%LCK)
        CALL INCR_A(P, A)
        CALL INCR_B(P, B)
        CALL OMP_UNSET_NEST_LOCK(P%LCK)
      END SUBROUTINE INCR_PAIR

      SUBROUTINE NESTLOCK(P)
        USE OMP_LIB        ! or INCLUDE "omp_lib.h"
        USE DATA
        TYPE(LOCKED_PAIR) :: P
        INTEGER WORK1, WORK2, WORK3
        EXTERNAL WORK1, WORK2, WORK3

!$OMP   PARALLEL SECTIONS

!$OMP   SECTION
          CALL INCR_PAIR(P, WORK1(), WORK2())
!$OMP   SECTION
          CALL INCR_B(P, WORK3())
!$OMP   END PARALLEL SECTIONS

      END SUBROUTINE NESTLOCK