As an example of the use signals, imagine we want to have two processes start off performing some computation each time the user clicks on a label. Using a Signal as the synchronisation mechanism:
ticker :: Signal Int -> Label -> IO ()
ticker signal lab =
waitSignal signal >>= \ v ->
setLabel lab (show v) >>
ticker signal lab
main =
mkDC ["*name: Signal"] >>= \ dc ->
label "Hit me!" dc >>= \ (lab, dh) ->
catchMouseEv dh >>= \ (mouse,dh1) ->
label "0" dc >>= \ (label1, dh2) ->
label "0" dc >>= \ (label2, dh3) ->
realiseDH dc
(hbox [dh2,dh1,dh3]) >>
newSignal >>= \ signal ->
forkIO (ticker signal label1) >>
forkIO (ticker signal label2) >>
let
loop n =
getMouseDown mouse >>
fireSignal signal n >>
loop (n+1)
in
loop 1

A Signal handle is created with newSignal. A Signal can be used to synchronise on values of any type, in this example loop will broadcast an Int each time the mouse btn goes down over the middle label.
A process running the ticker loop is forked for each label initially displaying 0. The ticker function first blocks waiting for a signal, and each time the process running loop fires an Int using fireSignal, the two ticker processes will be woken up and their labels will be updated before blocking again on the shared Signal.
Note that a Signal can be shared, and that a value signalled one the Signal handle will be seen by all processes vurrently blocked waiting via waitSignal. waitSignal always blocks a process.