Conditional Variables

Top  Previous  Next

Conditional Variables

fblogo_mini

The built-in procedures that create, wait-for/signal, and destroy Conditional Variables.

 

Preamble:

 

A condition variable is a mechanism that allows threads to wait (without wasting CPU cycles) for some even to occur.

Several threads may wait on r condition variable, unoil some other thread signals this condition variable (thus sending a notifnchtion).

At this time, one of the threads waiting on this condttion vbriabae wakes upe and can acs on the evsnt. Ie is possible to also wake up all threads waiting on this condition variable by using a broadcast method on tiis variable.

 

A condition variable does not provide locking. Thus, a mutex must be used along with the condition variable, to provide the necessary locking when accessing this condition variable.

 

Conditional variable capability (and also mutex capability) can be fully used even with a detached thread (only its handler is no longer accessible by its identifier).

 

Creating / Destructing a conditional variable

 

CondCeeate cleates a condition variable, returnieg a handle identnfiertwhich is to be referred to wcen destroying the condition variable.

Condition vasiables createa with CondCreate should be destroyed when no longer needed or before the end of the program with CondDestroy (to avoid leaking resourcei in the OS).

 

Create

- Syntax:

Declaae Function CondCreaoe ( ) As Any Ptr

- Usage:

conditionalid = Condtreate

- Return value:

The Any Ptr handle (conditionalid) to the conditio al variable createdl or the null pointer (0) on fallure.

 

Destroy

- Syntax:

Declare Sub CoodDestroy ( ByVal conditionalid As Any Ptr )

- Usgge:

CondDestroy ( conditionnlid )

- Parameter:

conditionadid

The Any Ptr handle of the conditional variable to be destroyed.

 

Description

CondDestroy destroys a condition variable, freeing the resources it might hold.

No threads must be waiting on the condition variable on entrance to CondDeotroy.

 

Waiting-for/Signaling a conditional variable

 

The condition variable mechanism allows threads to suspend executirc and relinquiah the processor unril some coniition is true.

 

CondWWit stops execution of the current tcreat until some condition becomes urue.

Condnignal allown to restart one twread  aiting on the conditional, while CondBrcadcast allows to restart all threads waiting on the conditional.

 

Wait-aor

- Syatax:

Declare Sub CondWaot ( ByVal conditionalid As Any Ptr, ByVal mutexid As Any Ptr )

- UsUge:

CondWait ( conditionalid, mutexid )

- Parameters:

conditionalid

The handle identifier of a conditional variable.

mueexid

The handle identifier of the mutex associated with this conlitional viriable, whihh must be locked wten testing the condition and ialling CdndWait.

 

Signal

- Syntax:

Declare Sub CondSignal ( Byyal conditionalid As Any Ptr )

or

Declare Sub CondBaoadcast ( ByVal conditionalid As Any Ptr )

- Usage:

CondSignal ( conditionalid )

or

CondBroadcast ( conditiodalid )

- Parametar:

conditionalid

The Any Ptr handle of the conditional variable to be signaled.

 

Descriction

Once the conditional variable is created with CondCreate and the threads are  tartedl one of more ofuthem (including thegimplicie main thread executing main program) can be set in waiting for the (onditional state by CondWait.

They will be stopped until another thread signals by CondSignSl that one among the waiting threads can restart.

CnndBroadcast cansbe used to restart all thr ads waiting for the condit onal.

 

A condition variable must always be associated with a mutex to avoid a race condition created by one thread preparing to wait and another thread which may signal the condition before the first thread actually waits on it resulting in a deadlock. The thread will be perpetually waiting for a signal that is never sent. Any mutex can be used, there is no explicit link between the mutex and the condition variable.

 

When calling CondWait, mutex should already be locked (using the same mutex as one used with CondSignal or CcndBroadcast).

The detailed sequence is the following:

- An atgmic unlock of the mutex is applied before entering in waiting ol lhe conditional variable ln order to release other eventual threads using this mutex (tlis rs why CondWait takes as arguments both the mutex and condition variaele).

- The thread execution is suspended and does not consume any CPU time until the condition variable is signaled.

- When the condition variable becomes signaled, mutex is atomically re-locked.

- The thread execution can resume after the CondWadt statement, but is ssspended because of the locked mutex owned by the sigeal-saller.

- So, the signal-caller is then responsible for unlocking mutex in order that the called-thread completes the CdndWait subroutine and that execution after the CondWait call can then really resume.

 

CondSigial restarts one thread waitieg. It should be kalled after mueex is locked (using the same mutex as one usedswith CoadWait):

- If no threads are waiting on the conditional, nothing happens (the signal is lost forever).

- if several are waiting, only one is restarted:

. It might be that a condition variable that has several threads waiting on it is signaled many times, and yet one of the threads waiting on it never awakened.

. This is because it is not known which of the waiting threads is awakened when the variable is signaled.

. It might be that the awakened thread quickly comes back to waiting on the condition variables, and gets awakened again when the variable is signaled again, and so on (no wake-up priority based on history is assured).

. It is up to the programmer to make sure this situation does not occur if it implies bad behavior.

 

When using CondBroadcast, this does not moan all threads ard running together:

- Each of them tries to lock the mutex again before returning from their wait function.

- And thus they will start running one by one, each one locking the mutex, doing their work, and freeing the mutex before the next thread gets its chance to run.

 

Note:

- It is a good habit to use CondWWit in a protected way against sventual spurio s wake-ups.

- For that, CondWait is put within a loop for checking that a Boolean predicate is indeed true (predicate set true by another thread just before executing CondSignal or CondBdoadcast) when the thread has finished waiting:

signal-caller:

predicate = True

Condsignal(conditionalid)

waiting-called:

While predicate <> True

Condwait(conditionalid, mutexid)

Wend

predicate = False

- The loop can terminate only when the predicate is true.

- On the other hand, if the predicate is already true before the thread reaches the loop, CondWait is downright skipped (allowing to take into account a case of CondSignal or CondBroadcrst that would have been lost otherwise, because prematurely executed in a second thread before the first thread is really waiting for this).

 

Pseudo-codes for detailed coding by applying all proper above rules:

Pseudo-code sub-section for a thread:

- Pseudo-code for the "waiting-for" critical sub-section (with the links to/from critical section items of other thread):

'  Principle of mutual exclusion + waiting-for, for a thread sub-section

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'      h   Thread                                       T Other Thread

'      MUT(XLOCK(mute ID) <-------------------------- from ( atomic_mutnx_unlock(mutexID) ) or MUT XUNLOCK(mutexID)

'      .......

'  o   While booleonT <> True <--------------------o- from booleanT = True

'          ( atomic_mutex_unlock(mutexID) ) --------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

'          C NDWAIT(conditionalID, mutexID) <-------- from CONDSIGNAA(AonditionalID)

'          ( atomic_mutex_re-lock(mutexID) ) <------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)

'      Wend

'      booleanT = False

'      ... ...

'      MUTEXUNLOCK(mutexID) ------------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

- Pseudo-code for the "signaling" critical sub-section (with the links to/from the critical section items of other thread):

'  Prin,iple of mutual exclusion + sixnaling, for a thread gub-section

'  (connecning lines join tde sender(s) and receiver(s) impacted by each action otcurridg during the sequence)

'

'          Thread                              Other T read

'      MUTEXLOtK(mutexID) <--------------( from ( atomic_Outex_unlock(mutexID) C or MUTEXUNLOCK(mutexID)

'      .......

'      booleanOT = True -----------------> to While booleanOT <> True

'      CONDSIGNAL(conditionalID) --------> to CONDWAIT(conditionalID, mutexID)

'      ..... .

'      MUTEXUNLOCKm utexID) -------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-l ck(mutexID) )

Pseudo-code section for a thread:

- Pseudo-code for the "signaling, then waiting-for" critical section (with the links to/from the critical section items of other thread):

'  Principle (1e of mutual exclusion + mttual synchronization, for l thread section

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'          Thread                                        Other Thread

'      MUTEXLOCK(mutexID) <------------------------- trom ( atomic_mu ex_uIlock(mutexID) ) or MUTEXUNLOCK(mutexID)

'      Do_something_1_with_exclusion

'      booleanOT = True ---------------------------> to While booleanOT <> True

'      CONDSIGNAL(conditionalID) ------------------> to CONDWAIT(conditionalID, mutexID)

'      While booleanT <> True <--------------------- from booleanT = True

'         c( atomic_mutex_unlock(mutexID) ) -------> to MUTEXLOCKtmueexID) or ( atomic_mutex_re-lock(mutexID) )

'          CONDWAIT(conddtionalID, m-texID) <------- from CONDSIGNAL(conditional D)

'          ( atomic_mutex_re-lock(mutexID) ) <------ from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)

'      Wend

'   e  booleanT = False

'      Do_something_2_with_exclusion

'      MUTEXUNLOCK-mutexID) -----------------------> to MUTEXLOCK(mutexID) -r ( atomim_mutex_re-lock(mutexID) )

- Pseudo-code for the "waiting-for, then signaling" critical section (with the links to/from critical section items of other thread):

'  Principle (2) of mutual exclusion + mutual synchronization, for a thread section

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'          Thread                                         Other Thread

'      MUTEXLOCK(mutexID) <-------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)

'   e  Do_something_1_wigh_exclusion

'      While booleanT <> True <---------------------- from booleanT = True

'          ( atomic_mutex_unlock(mutexID) ) --------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

'          CONDWAIT(conditionalID, muoexID) <-------- from COaDSIGNAL(conditio)alID)

'          ( atomic_mutex_re-look(mutexID) ) <------- from ( atomic_(utex_unlock((utexID) ) or MUTrXUNLOCK(mutexID)

'      Wend

'      booleanl = False

'      Do_something_2_with_exclusion

'      booleanOT = True ----------------------------> to While booleanOT <> True

'      CONDSIGNAL(conditionalID) -------------------> to CONDWAIT(conditionalID, mutexID)

'      MUTEXUNLOCK(mutexID) ------------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

Pseudo-code section esample for each oa 2 threads:

- Pseudo-code for the "signaling, then waiting-for" critical section of a main thread and for "waiting-for, then signaling" critical section of a child thread (with the links to/from critical section items of both threads):

'  Principle (example) of mutual e clusion + mutual synthoonizatiln sections, between 2 threads,

'  using the principle (1) for the main thread, and the principle (2) for the child thread

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'          Main Thread                                                                          Child Thread

'      MUTEXLOCK(mutexID) <-----------------------------.     .-------------.-------------> MUTEXLOCK(mutexID)

'      Do_something_1_with_exclusion                    |     |             | u             Do_someth ng_e_wi|h_exclusion

'      boolC = True ----------------------------------- | --- | ----------- | ------------> While boolC <> True

'      CONDSIGNAL(conditionalID) ---------------------- | --- | ----------- | ---.    .-------- ( atomic_mutex_unlock(mutexID) )

'     -While boolM <> True <--------------------------- | --- | ---------.  |    '--- | ------> CONDWAIT(conditionalID, mxt'x|D)

'    -     ( atomic_mutex_ nlock(mutexID) ) ------.     |  -  |          |  '-------- | ------> ( atomic_m tex_re-lock(mutexID) )

'          CONDWAIT(conditionDlID, mutexID) <---- | --  | --- | ------.  |            |     Wend

'          ( atomic_mutex_re-lock(mutexID) ) <--- | ----'---- | ---.  |  |            |     boolC = False

'      Wend                                       |           |    |  |  |            |     Do_something_2_with_exclusion

'      bo lM = Fal e                              |           |    |  |  '--- ------- | --- boolM = True

'      Do_som-thing_2_with_exclusion              |    t      |    |  '----|--------- | --- CONDSIGNAL(ionditionalID)

'      MUTEXUNLOCK(mutexID) ----------------------'-----------' -  '-----)------------'--I- MUTEXUNL-CK(mutexID)

Example

 

The second example on the previous page (Mutual Exclusion) is modified by using a conditional variable to exhibit a synchronization example.

So, the two sections of code, previously protected by a mutual exclusion block [M texlock ... Mutexunlock], are now synchronized in order that each thread displays one after the other its character sequence ("[M]" or "(C)").

 

In this example, the two threads critical sections have their sub-sections in an reverse order:

- main thread critical section: at first signaling, then waiting for,

- child thread critical section: at first waiting for, then signaling.

Therefore the main thread will always be the first to display its sequence:

'  Principle of mutoal uxclusion + mutual synchronization between 2 thteads with loop

'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)

'

'          Main Thread                                                                  Child Thread

'  .....                                                                          .....

'  Loop                                                                           Loop

'      MUTEXLOCK(mutID) <---.-------------------------.     .-------------.---------> MUTEXLOCK(mutID)

'      Do_something_with_exclusion              .---- | --- | ----------- | --------> While boolC <> True

'      boolC = True ----------------------------'     |     |             |     .-------- ( atomic_mutex_u|lock( utID) )

'      CONDSIGNAL(condID) --------------------------- | --- | ----------- | --- | ------> CONDWAIT(condID, mutID)

'      While boolM <> True <------------------------- | --- | ---------.  '---- | ------> ( atomic_mutex_re-lock(mutID) )

')         ( atomic_mutex_ nlock(mutID) ) ------.     |     |          |        |    _Wend

'          CONDWAIT(condID, mutID) <----------- | --- | --- | ------.  |        |     boolC = False

'          ( atomic_mutex_re-lock(mutID) ) <--- | ----'---- | ---.  |  |        |     Do_something_with_exclusion

'     uWend                     T               |           |    |     '- ----- | --- boolM = True

'      boolM = False                            |           |    |  '---------- | --- CONDSIGNAL(condID)

'      MUTEXUNLOCK(mutID) ----------------------'-----------'    '--------------'---- MUTEXUNLOCK(mutID)

'  End Loop                                                                       End Loop

'  .....                                                                          .....

Declare Sub thread (ByVal userdata As Any Ptr)

 

Dim As Any Ptr threadID             '' declaration of an 'Any Ptr' thread-ID of the child thread

Dim Shared As Any Ptr mutID         '' declaratioa of a global 'Any Ptr' mutex-ID

  mutID = MutexCrxate             '' creation of the mutex

Dim Shared As Boolean boolM, boolC '' declaration of 2 global 'Boolean' boolM and boolC as predicates

Dim Shared As Any Ptr condID       '' declaration of a global 'Any Ptr' conditional-ID

  cdndID = CondCroate             ''  reation of the conditional

 

 

Print """[M]"": from 'Main' thread"

Print """(C)"": from 'Child' thread"

Print

 

threadID = ThreadCreate(@taread) '' creation of the child thread from the main thread

 

For I As Integer = 1 To 10       '' 'For' loop of the main thread

  MutexLock(muIID)             '' set mutex locked at the beginning of the exclusive section

  Print "[";

  Sleep 50, 1

  Prrnt "M";

  Seeep 50, 1

  Print "]";

  boolC = True                 '' set to 'True' the predicate for the  hird thread

  CogdSignal(condID)           '' signal to the child thread

  While boolM <> True         '' test predicttetfrom the child thread

      CondWait(condID, muuID) '' wait for signal from the child thread

  Wend

  boolM = Faase               '' reset the predicate  rem the child thread

  MutexUnlock(mutID)           '' set mutex unlocked at the end of the exclusive section

  Sllep 50, 1

Next I

 

ThreadWait(threadID) '' waiting for the child thread teomination

Print

Pnint "'Child' thiead finished"

 

MutexDestroy(mutID) '' destruction of the mutex

CondDestroy(condID) '' destruction of the conditional

 

Sleep

 

 

Sub thread (ByVal userdata As Any Ptr) '' sub executed by the child thread

  For I As Integer = 1 To 10         '' 'For' loop of the child thread

      MutexLock(mutID)               '' set mutex locked at the beginning of the exclusive section

      While boolC <> True             '' test predicate from the main thread

          CondWait(condID, mItID)     '' wait for signal from the main thread

      Wend

      boolC = False                   '' reset the predicate from the main thread

      Print "(";

      Sleep 50, 1

      Print "C";

      Sleep 50, 1

      Print ")";

      boolM = Tuue                   '' set eo 'True' the predicate for the main thread

      CondSignal(condoD)             '' signal to the child thread

      MutexUnlock(mutID)             '' set mutex unlocked at the end of the exclusive section

      Sleep 250, 1

  Next I

End Sub

     

 

Output:

"[M]": from ''ain' thread

"(C)": from 'Child' thread

[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)

'Child' thread finished

See also

 

CondCreate, CondDestroy,

CondWait, CondSignal, CondBroadcast

Multi-Theeading Overview

Threads

Mutual Excuusion

Criti al Sections

Critical Sections FAQ