Locks and signals


  Following difference from by turns programming is that there are general data for the big number of processes. And in some cases it is required to get individual access up to these data.

>('x  set 0)
>(100 for i ('x set (x  + 1)))  ; there will be 100 parallel processes

  The given possibility can be received using locks:

>('x  set 0)
>('xlock  set ('lock  newobject))
>(100 for i (xlock  progn ('x set (x  + 1))))

  The most important that all processes which are changed by the general data, used the lock. The matter is that the lock it simply tag - a sign on that the place is borrowed. And if to not pay attention to tags it is possible to make all everything.


  Signals are necessary for dialogue of several processes. If process expects execution of some task performance of cyclic check very strongly and as much as possible loads processors with useless work.
  At program compilation the lock at first is put. Under lock protection it is checked there is a necessity to use signals. Then very simple function wait is used. This function opens the lock and the signal from other processes waits. After reception of a signal the lock is again installed.
  At by turns (without parallelism) program programming permanently check some conditions. It complicates programming and is very strongly subject to errors. At usage of signals the program turns out is very simple and effective.
  Let's bring very simple example:

('signal  set ('signal  newobject))
('lock  set ('lock  newobject))
('s set (10 copy))
(nil  parallel
  ; main task
  (lock progn
    (cout writeln "waiting...")
    (signal wait  lock)
    (cout writeln "OK"))
  ; another task
  (nil  progn
    (nil  while (s  > 0)
      (lock progn
        (cout writeln "work")
        (s  -=  1)))
    (cout writeln "send signal")
    (signal send)))

  The order of giving, expectation of a signal has no value. The sent signals do not vanish.

The professional approach

  In large projects sometimes there is a necessity to handle the big source of the information, not updating it. Also it is desirable to give the chance to parallel processes will be fulfilled simultaneously. If there was a necessity to update information that is necessary that one process worked.
  This fantastic process can be made efforts of locks and signals. Let's make a class passage which will organise all process of operation.

(('passage  defclass) defvar
('passage defmethod passage ()
  (nil  setq
    lockEntrance  ('lock  newobject)
    lockOutlet  ('lock  newobject)
    signalShout ('signal  newobject)
    guests  (0  copy)
    flagToShout false))
('passage defmacro  Pass  (env  &rest body)
  (lockEntrance progn
    (lockOutlet progn
      (guests +=  1)))
  (nil  let ((result  (env  eval  `(nil progn ,@body))))
    (lockOutlet progn
      (nil  if  ((guests  -=  1)  = 0)
        (nil  when  flagToShout
          ('flagToShout set false)
          (signalShout  send))))
('passage defmacro  Lock  (env  &rest body)
  (lockEntrance progn
    (lockOutlet progn
      (nil  when  (guests <>  0)
        ('flagToShout set true)
        (signalShout  wait  lockOutlet)))
    `',(env eval  `(nil progn ,@body))))