Events: Difference between revisions
(Events en Yabasic) |
(added Easylang) |
||
(6 intermediate revisions by 6 users not shown) | |||
Line 28: | Line 28: | ||
The event interface: |
The event interface: |
||
< |
<syntaxhighlight lang="ada">protected type Event is |
||
procedure Signal; |
procedure Signal; |
||
procedure Reset; |
procedure Reset; |
||
Line 34: | Line 34: | ||
private |
private |
||
Fired : Boolean := False; |
Fired : Boolean := False; |
||
end Event;</ |
end Event;</syntaxhighlight> |
||
The event implementation: |
The event implementation: |
||
< |
<syntaxhighlight lang="ada">protected body Event is |
||
procedure Signal is |
procedure Signal is |
||
begin |
begin |
||
Line 49: | Line 49: | ||
null; |
null; |
||
end Wait; |
end Wait; |
||
end Event;</ |
end Event;</syntaxhighlight> |
||
With the event defined above: |
With the event defined above: |
||
< |
<syntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO; |
||
procedure Test_Events is |
procedure Test_Events is |
||
Line 68: | Line 68: | ||
Put_Line ("Signal X"); |
Put_Line ("Signal X"); |
||
X.Signal; |
X.Signal; |
||
end Test_Events;</ |
end Test_Events;</syntaxhighlight> |
||
Sample output: |
Sample output: |
||
<pre> |
<pre> |
||
Line 77: | Line 77: | ||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
< |
<syntaxhighlight lang="autohotkey">SetTimer, internal, 1000 |
||
Return |
Return |
||
Line 87: | Line 87: | ||
F2:: ; external event: fire on F2 key press |
F2:: ; external event: fire on F2 key press |
||
TrayTip, external, f2 key pressed |
TrayTip, external, f2 key pressed |
||
Return</ |
Return</syntaxhighlight> |
||
=={{header|BASIC256}}== |
=={{header|BASIC256}}== |
||
{{trans|Gambas}} |
{{trans|Gambas}} |
||
<syntaxhighlight lang="basic256"> |
|||
<lang BASIC256> |
|||
subroutine Timer1_Timer() |
subroutine Timer1_Timer() |
||
print hour; ":"; minute; ":"; second |
print hour; ":"; minute; ":"; second |
||
end subroutine |
end subroutine |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 107: | Line 107: | ||
===API=== |
===API=== |
||
This uses a Windows event object: |
This uses a Windows event object: |
||
< |
<syntaxhighlight lang="bbcbasic"> INSTALL @lib$+"TIMERLIB" |
||
WAIT_TIMEOUT = 258 |
WAIT_TIMEOUT = 258 |
||
Line 123: | Line 123: | ||
DEF PROCelapsed |
DEF PROCelapsed |
||
SYS "SetEvent", hEvent% |
SYS "SetEvent", hEvent% |
||
ENDPROC</ |
ENDPROC</syntaxhighlight> |
||
===Native=== |
===Native=== |
||
This uses a simple variable as a semaphore: |
This uses a simple variable as a semaphore: |
||
< |
<syntaxhighlight lang="bbcbasic"> INSTALL @lib$+"TIMERLIB" |
||
Event% = FALSE |
Event% = FALSE |
||
Line 141: | Line 141: | ||
DEF PROCelapsed |
DEF PROCelapsed |
||
Event% = TRUE |
Event% = TRUE |
||
ENDPROC</ |
ENDPROC</syntaxhighlight> |
||
=={{header|C}}== |
=={{header|C}}== |
||
Using pipe to communicate to <code>fork</code>ed child. Since child will be blocking trying to read the other end of the pipe, this can be used for synchronization. |
Using pipe to communicate to <code>fork</code>ed child. Since child will be blocking trying to read the other end of the pipe, this can be used for synchronization. |
||
< |
<syntaxhighlight lang="c">#include <stdio.h> |
||
#include <unistd.h> |
#include <unistd.h> |
||
Line 163: | Line 163: | ||
} |
} |
||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
=={{header|C sharp|C#}}== |
=={{header|C sharp|C#}}== |
||
< |
<syntaxhighlight lang="csharp">using System; |
||
using System.Timers; |
using System.Timers; |
||
Line 185: | Line 185: | ||
((Timer)sender).Stop(); |
((Timer)sender).Stop(); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Sample output: |
Sample output: |
||
<pre>10-11-2010 18:35:11 |
<pre>10-11-2010 18:35:11 |
||
Line 192: | Line 192: | ||
=={{header|Clojure}}== |
=={{header|Clojure}}== |
||
{{trans|Go}} |
{{trans|Go}} |
||
< |
<syntaxhighlight lang="lisp">(ns async-example.core |
||
(:require [clojure.core.async :refer [>! <! >!! <!! go chan]]) |
(:require [clojure.core.async :refer [>! <! >!! <!! go chan]]) |
||
(:require [clj-time.core :as time]) |
(:require [clj-time.core :as time]) |
||
Line 229: | Line 229: | ||
; Invoke -main function |
; Invoke -main function |
||
(-main) |
(-main) |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{Output}} |
{{Output}} |
||
<pre> |
<pre> |
||
Line 240: | Line 240: | ||
=={{header|Delphi}}== |
=={{header|Delphi}}== |
||
< |
<syntaxhighlight lang="delphi">program Events; |
||
{$APPTYPE CONSOLE} |
{$APPTYPE CONSOLE} |
||
Line 293: | Line 293: | ||
end; |
end; |
||
Readln; |
Readln; |
||
end.</ |
end.</syntaxhighlight> |
||
Sample output: |
Sample output: |
||
<pre> |
<pre> |
||
Line 301: | Line 301: | ||
=={{header|E}}== |
=={{header|E}}== |
||
< |
<syntaxhighlight lang="e">def makeEvent() { |
||
def [var fired, var firer] := Ref.promise() |
def [var fired, var firer] := Ref.promise() |
||
Line 324: | Line 324: | ||
return event |
return event |
||
}</ |
}</syntaxhighlight> |
||
The event object has this behavior: the return value of <code>.wait()</code> will be resolved after the time of the earliest <code>.signal()</code> for which there is no intervening <code>.reset()</code>. |
The event object has this behavior: the return value of <code>.wait()</code> will be resolved after the time of the earliest <code>.signal()</code> for which there is no intervening <code>.reset()</code>. |
||
< |
<syntaxhighlight lang="e">def e := makeEvent() |
||
{ |
{ |
||
Line 341: | Line 341: | ||
}) |
}) |
||
println("[1] Waiting 1 second...") |
println("[1] Waiting 1 second...") |
||
}</ |
}</syntaxhighlight> |
||
=={{header|EasyLang}}== |
|||
[https://easylang.dev/show/#cod=y89TKMnMTS3iUlBQyM0vS1UoSsxLyc9NU9BSMDQwQOWB1CRnFiXnpCoYgdhgjQqGXHpc+XlAzaXFqfEp+eV5cKMgQhVQuhJFux4XRLcBFwA= Run it] |
|||
<syntaxhighlight> |
|||
on timer |
|||
move randomf * 100 randomf * 100 |
|||
circle 2 |
|||
timer 1 |
|||
. |
|||
on mouse_down |
|||
move mouse_x mouse_y |
|||
circle 2 |
|||
. |
|||
timer 0 |
|||
</syntaxhighlight> |
|||
=={{header|Elixir}}== |
=={{header|Elixir}}== |
||
{{trans|Erlang}} |
{{trans|Erlang}} |
||
< |
<syntaxhighlight lang="elixir">defmodule Events do |
||
def log(msg) do |
def log(msg) do |
||
time = Time.utc_now |> to_string |> String.slice(0..7) |
time = Time.utc_now |> to_string |> String.slice(0..7) |
||
Line 372: | Line 389: | ||
end |
end |
||
Events.main</ |
Events.main</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 385: | Line 402: | ||
=={{header|Erlang}}== |
=={{header|Erlang}}== |
||
Events can be implemented by using the selective receive expression and erlang's built in message passing. Here task waits for the message 'go' before it will continue. |
Events can be implemented by using the selective receive expression and erlang's built in message passing. Here task waits for the message 'go' before it will continue. |
||
< |
<syntaxhighlight lang="erlang"> |
||
-module(events). |
-module(events). |
||
-compile(export_all). |
-compile(export_all). |
||
Line 408: | Line 425: | ||
P ! go, |
P ! go, |
||
timer:sleep(100). |
timer:sleep(100). |
||
</syntaxhighlight> |
|||
</lang> |
|||
'''Output:''' |
'''Output:''' |
||
< |
<syntaxhighlight lang="erlang"> |
||
66> events:main(). |
66> events:main(). |
||
0: 0:57 => Program start |
0: 0:57 => Program start |
||
Line 418: | Line 435: | ||
0: 0:58 => Task resumed |
0: 0:58 => Task resumed |
||
ok |
ok |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|F_Sharp|F#}}== |
=={{header|F_Sharp|F#}}== |
||
{{trans|C#}} |
{{trans|C#}} |
||
< |
<syntaxhighlight lang="fsharp">open System |
||
open System.Timers |
open System.Timers |
||
Line 436: | Line 453: | ||
timer.Start() |
timer.Start() |
||
ignore <| Console.ReadLine() |
ignore <| Console.ReadLine() |
||
0</ |
0</syntaxhighlight> |
||
=={{header|FreeBASIC}}== |
=={{header|FreeBASIC}}== |
||
{{trans|Gambas}} |
{{trans|Gambas}} |
||
< |
<syntaxhighlight lang="freebasic"> |
||
Sub Timer1_Timer() |
Sub Timer1_Timer() |
||
Print Time |
Print Time |
||
End Sub |
End Sub |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 451: | Line 468: | ||
</pre> |
</pre> |
||
=={{header|FutureBasic}}== |
|||
Event timer fires every tenth of a second |
|||
<syntaxhighlight lang="futurebasic"> |
|||
timerbegin, 0.1, YES |
|||
cls : printf @"%@", time(@"h:mm:ss a zzz") |
|||
timerend |
|||
HandleEvents |
|||
</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
4:44:36 PM EDT |
|||
</pre> |
|||
=={{header|Gambas}}== |
=={{header|Gambas}}== |
||
< |
<syntaxhighlight lang="gambas">Public Sub Timer1_Timer() |
||
Print Str(Time(Now)) |
Print Str(Time(Now)) |
||
End</ |
End</syntaxhighlight> |
||
Output: |
Output: |
||
<pre> |
<pre> |
||
Line 472: | Line 503: | ||
=={{header|Go}}== |
=={{header|Go}}== |
||
A Go channel can represent an manual-reset event, as described by the task. The two states of signaled and reset correspond to the presence or absence of a value on the channel. The program signals by sending a value on the channel. The event is reset when the waiting task explicitly executes the channel receive operation, <-event. |
A Go channel can represent an manual-reset event, as described by the task. The two states of signaled and reset correspond to the presence or absence of a value on the channel. The program signals by sending a value on the channel. The event is reset when the waiting task explicitly executes the channel receive operation, <-event. |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 494: | Line 525: | ||
event <- 0 |
event <- 0 |
||
time.Sleep(100 * time.Millisecond) |
time.Sleep(100 * time.Millisecond) |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 505: | Line 536: | ||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import Control.Concurrent (threadDelay, forkIO) |
||
import Control.Concurrent.SampleVar |
import Control.Concurrent.SampleVar |
||
Line 515: | Line 546: | ||
signalEvent (Event sv) = writeSampleVar sv () |
signalEvent (Event sv) = writeSampleVar sv () |
||
resetEvent (Event sv) = emptySampleVar sv |
resetEvent (Event sv) = emptySampleVar sv |
||
waitEvent (Event sv) = readSampleVar sv</ |
waitEvent (Event sv) = readSampleVar sv</syntaxhighlight> |
||
< |
<syntaxhighlight lang="haskell">main = do e <- newEvent |
||
forkIO (waitTask e) |
forkIO (waitTask e) |
||
putStrLn "[1] Waiting 1 second..." |
putStrLn "[1] Waiting 1 second..." |
||
Line 526: | Line 557: | ||
waitTask e = do putStrLn "[2] Waiting for event..." |
waitTask e = do putStrLn "[2] Waiting for event..." |
||
waitEvent e |
waitEvent e |
||
putStrLn "[2] Received event."</ |
putStrLn "[2] Received event."</syntaxhighlight> |
||
Note: Because there is no serialization of the text output, there is a chance that it will appear interleaved. |
Note: Because there is no serialization of the text output, there is a chance that it will appear interleaved. |
||
Line 533: | Line 564: | ||
The following only works in Unicon. The example illustrates the multiple tasks can |
The following only works in Unicon. The example illustrates the multiple tasks can |
||
receive the same event: |
receive the same event: |
||
< |
<syntaxhighlight lang="unicon">record Event(cond, value) |
||
procedure main() |
procedure main() |
||
Line 552: | Line 583: | ||
signal(event.cond,0) |
signal(event.cond,0) |
||
every wait(t1|t2) |
every wait(t1|t2) |
||
end</ |
end</syntaxhighlight> |
||
Sample run: |
Sample run: |
||
Line 567: | Line 598: | ||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
An example using the [[wp:Yahoo!_UI_Library|YUI]] library: |
An example using the [[wp:Yahoo!_UI_Library|YUI]] library: |
||
< |
<syntaxhighlight lang="javascript">YUI().use('event-custom', function(Y) { |
||
// add a custom event: |
// add a custom event: |
||
Y.on('my:event', function () { |
Y.on('my:event', function () { |
||
Line 576: | Line 607: | ||
Y.fire('my:event'); |
Y.fire('my:event'); |
||
}, 1000); |
}, 1000); |
||
});</ |
});</syntaxhighlight> |
||
An example simulating [[wp:Document_Object_Model|DOM]] events: |
An example simulating [[wp:Document_Object_Model|DOM]] events: |
||
< |
<syntaxhighlight lang="javascript">YUI().use('node-event-simulate', function(Y) { |
||
// add a click event handler to a DOM node with id "button": |
// add a click event handler to a DOM node with id "button": |
||
Y.one("#button").on("click", function (e) { |
Y.one("#button").on("click", function (e) { |
||
Line 587: | Line 618: | ||
Y.one("#button").simulate("click"); |
Y.one("#button").simulate("click"); |
||
}, 1000); |
}, 1000); |
||
});</ |
});</syntaxhighlight> |
||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
Line 594: | Line 625: | ||
between two child threads. |
between two child threads. |
||
< |
<syntaxhighlight lang="julia"> |
||
function dolongcomputation(cond) |
function dolongcomputation(cond) |
||
det(rand(4000, 4000)) |
det(rand(4000, 4000)) |
||
Line 615: | Line 646: | ||
sleep(5) |
sleep(5) |
||
println("Done sleeping.") |
println("Done sleeping.") |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{output}}<pre> |
{{output}}<pre> |
||
Starting task, sleeping... |
Starting task, sleeping... |
||
Line 628: | Line 659: | ||
Paste in the REPL: |
Paste in the REPL: |
||
< |
<syntaxhighlight lang="lisp"> |
||
(defun log (msg) |
(defun log (msg) |
||
(let ((`#(,h ,m ,s) (erlang:time))) |
(let ((`#(,h ,m ,s) (erlang:time))) |
||
Line 648: | Line 679: | ||
(! pid 'go) |
(! pid 'go) |
||
(timer:sleep 100)))) |
(timer:sleep 100)))) |
||
</syntaxhighlight> |
|||
</lang> |
|||
Usage: |
Usage: |
||
Line 667: | Line 698: | ||
To catch an event, a corresponding event handler - a function with a predefined name - has to be definined in the code. Examples for such event handlers are: |
To catch an event, a corresponding event handler - a function with a predefined name - has to be definined in the code. Examples for such event handlers are: |
||
< |
<syntaxhighlight lang="lingo">-- the current window was closed |
||
on closeWindow |
on closeWindow |
||
... |
... |
||
Line 675: | Line 706: | ||
on mouseDown |
on mouseDown |
||
... |
... |
||
end</ |
end</syntaxhighlight> |
||
Also "Sprites" (visual elements) receive events by setting up such event handlers in scripts attached to them. Both predefined and custom events can be sent programmatically to sprites, e.g. using: |
Also "Sprites" (visual elements) receive events by setting up such event handlers in scripts attached to them. Both predefined and custom events can be sent programmatically to sprites, e.g. using: |
||
< |
<syntaxhighlight lang="lingo">-- send event #mouseDown programmatically to sprite 1 |
||
sendSprite(1, #mouseDown) |
sendSprite(1, #mouseDown) |
||
Line 684: | Line 715: | ||
-- send custom event #fooBar to all existing sprites |
-- send custom event #fooBar to all existing sprites |
||
sendAllSprites(#fooBar)</ |
sendAllSprites(#fooBar)</syntaxhighlight> |
||
Using a binary plugin ("Xtra"), in Windows also lower level window messages can be both sent and received: |
Using a binary plugin ("Xtra"), in Windows also lower level window messages can be both sent and received: |
||
{{libheader|Msg Xtra}} |
{{libheader|Msg Xtra}} |
||
< |
<syntaxhighlight lang="lingo">mx = xtra("Msg").new() |
||
-- send message WM_LBUTTONDOWN to a specific window identified by HWND hwnd |
-- send message WM_LBUTTONDOWN to a specific window identified by HWND hwnd |
||
Line 702: | Line 733: | ||
WM_COPYDATA = 74 |
WM_COPYDATA = 74 |
||
WM_MOUSEWHEEL = 522 |
WM_MOUSEWHEEL = 522 |
||
mx.msg_listen([WM_COPYDATA, WM_MOUSEWHEEL], VOID, #msgReceived)</ |
mx.msg_listen([WM_COPYDATA, WM_MOUSEWHEEL], VOID, #msgReceived)</syntaxhighlight> |
||
=={{header|Mathematica}}== |
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
||
Mathematica supports events from timers (via Pause[]), task schedule descriptors. This will print a message after 4 seconds, then terminate the program. |
Mathematica supports events from timers (via Pause[]), task schedule descriptors. This will print a message after 4 seconds, then terminate the program. |
||
< |
<syntaxhighlight lang="mathematica">Print["Will exit in 4 seconds"]; Pause[4]; Quit[] |
||
->Will exit in 4 seconds</ |
->Will exit in 4 seconds</syntaxhighlight> |
||
=={{header|Nim}}== |
=={{header|Nim}}== |
||
{{trans|C}} |
{{trans|C}} |
||
< |
<syntaxhighlight lang="nim">import posix |
||
var p: array[2, cint] |
var p: array[2, cint] |
||
Line 724: | Line 755: | ||
discard close p[1] |
discard close p[1] |
||
discard p[0].read(addr p[1], 1) |
discard p[0].read(addr p[1], 1) |
||
echo "received signal from pipe"</ |
echo "received signal from pipe"</syntaxhighlight> |
||
===Stdlib Semaphore=== |
===Stdlib Semaphore=== |
||
This version using locks module for signaling the condition. |
This version using locks module for signaling the condition. |
||
< |
<syntaxhighlight lang="nim">import locks |
||
from os import sleep |
from os import sleep |
||
import times |
import times |
||
Line 758: | Line 789: | ||
deinitLock lock |
deinitLock lock |
||
main()</ |
main()</syntaxhighlight> |
||
Compile and run: <pre>nim c -r --threads:on events_cond.nim</pre> |
Compile and run: <pre>nim c -r --threads:on events_cond.nim</pre> |
||
Line 771: | Line 802: | ||
An event is often implemented with a control channel. A task is waiting for an object on the channel. When the event occurs, another task sends an object on this channel. |
An event is often implemented with a control channel. A task is waiting for an object on the channel. When the event occurs, another task sends an object on this channel. |
||
< |
<syntaxhighlight lang="oforth">: anEvent |
||
| ch | |
| ch | |
||
Channel new ->ch |
Channel new ->ch |
||
#[ ch receive "Ok, event is signaled !" println ] & |
#[ ch receive "Ok, event is signaled !" println ] & |
||
System sleep(1000) |
System sleep(1000) |
||
ch send($myEvent) ;</ |
ch send($myEvent) ;</syntaxhighlight> |
||
An emitter is a general implementation for handling events : an emitter waits for events emitted and launches listeners that are waiting for those events. |
An emitter is a general implementation for handling events : an emitter waits for events emitted and launches listeners that are waiting for those events. |
||
< |
<syntaxhighlight lang="oforth">import: emitter |
||
: anEvent2 |
: anEvent2 |
||
Line 789: | Line 820: | ||
$myEvent e emit |
$myEvent e emit |
||
] |
] |
||
e close ;</ |
e close ;</syntaxhighlight> |
||
=={{header|Oz}}== |
=={{header|Oz}}== |
||
{{trans|Haskell}} |
{{trans|Haskell}} |
||
Events can be implemented as mutable references to dataflow variables: |
Events can be implemented as mutable references to dataflow variables: |
||
< |
<syntaxhighlight lang="oz">declare |
||
fun {NewEvent} |
fun {NewEvent} |
||
{NewCell _} |
{NewCell _} |
||
Line 822: | Line 853: | ||
{Delay 1000} |
{Delay 1000} |
||
{System.showInfo "[1] Signaling event."} |
{System.showInfo "[1] Signaling event."} |
||
{SignalEvent E}</ |
{SignalEvent E}</syntaxhighlight> |
||
However, this code is quite unidiomatic. If we need to wait for an event just once (like in this example), we can simply use a dataflow variable, i.e. an event that cannot be reset: |
However, this code is quite unidiomatic. If we need to wait for an event just once (like in this example), we can simply use a dataflow variable, i.e. an event that cannot be reset: |
||
< |
<syntaxhighlight lang="oz">declare |
||
E |
E |
||
in |
in |
||
Line 836: | Line 867: | ||
{Delay 1000} |
{Delay 1000} |
||
{System.showInfo "[1] Signaling event."} |
{System.showInfo "[1] Signaling event."} |
||
E = unit</ |
E = unit</syntaxhighlight> |
||
If we want to synchronize two threads repeatedly and exchange data, it is natural to use ports and streams. Streams are just lists with an unbound tail. A port is basically a pointer to the tail of a list, i.e. it keeps track of where the next event can be written to: |
If we want to synchronize two threads repeatedly and exchange data, it is natural to use ports and streams. Streams are just lists with an unbound tail. A port is basically a pointer to the tail of a list, i.e. it keeps track of where the next event can be written to: |
||
< |
<syntaxhighlight lang="oz">declare |
||
MyPort |
MyPort |
||
in |
in |
||
Line 857: | Line 888: | ||
{System.showInfo "[1] Signaling event."} |
{System.showInfo "[1] Signaling event."} |
||
{Port.send MyPort unit} |
{Port.send MyPort unit} |
||
end</ |
end</syntaxhighlight> |
||
It is important to limit the scope of a stream as much as possible to ensure that the already read part of the stream is garbage-collected. |
It is important to limit the scope of a stream as much as possible to ensure that the already read part of the stream is garbage-collected. |
||
Line 863: | Line 894: | ||
This is an example of using the [http://search.cpan.org/perldoc?AnyEvent AnyEvent] module. |
This is an example of using the [http://search.cpan.org/perldoc?AnyEvent AnyEvent] module. |
||
The result is this: it prints "Hello world!" after one second, then after another second prints "Hi!" four times every quarter of a second and then immediately prints "Bye!" and quits: |
The result is this: it prints "Hello world!" after one second, then after another second prints "Hi!" four times every quarter of a second and then immediately prints "Bye!" and quits: |
||
< |
<syntaxhighlight lang="perl">use AnyEvent; |
||
# a new condition with a callback: |
# a new condition with a callback: |
||
Line 893: | Line 924: | ||
# wait for the $quit condition to be ready: |
# wait for the $quit condition to be ready: |
||
$quit->recv();</ |
$quit->recv();</syntaxhighlight> |
||
This is the same using AnyEvent [http://search.cpan.org/perldoc?AE simplified API]: |
This is the same using AnyEvent [http://search.cpan.org/perldoc?AE simplified API]: |
||
< |
<syntaxhighlight lang="perl">use AnyEvent; |
||
my $quit = AE::cv sub { warn "Bye!\n" }; |
my $quit = AE::cv sub { warn "Bye!\n" }; |
||
Line 909: | Line 940: | ||
}; |
}; |
||
$quit->recv;</ |
$quit->recv;</syntaxhighlight> |
||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
Line 915: | Line 946: | ||
in main() acts as signalling an event, and the one in echo() from whichever goes first acts to signal |
in main() acts as signalling an event, and the one in echo() from whichever goes first acts to signal |
||
that the other can/should resume. |
that the other can/should resume. |
||
<!--<syntaxhighlight lang="phix">(notonline)--> |
|||
<lang Phix>constant lock = init_cs() |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">lock</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">init_cs</span><span style="color: #0000FF;">()</span> |
|||
include timedate.e |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">timedate</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
procedure showtime() |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">showtime</span><span style="color: #0000FF;">()</span> |
|||
puts(1,format_timedate(date()," h:m:s\n")) |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">format_timedate</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">date</span><span style="color: #0000FF;">(),</span><span style="color: #008000;">" h:m:s\n"</span><span style="color: #0000FF;">))</span> |
|||
end procedure |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
procedure echo(string s) |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">echo</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
sleep(rnd()/10) -- see note |
|||
<span style="color: #7060A8;">sleep</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">rnd</span><span style="color: #0000FF;">()/</span><span style="color: #000000;">10</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- see note</span> |
|||
enter_cs(lock) |
|||
<span style="color: #7060A8;">enter_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lock</span><span style="color: #0000FF;">)</span> |
|||
puts(1,s) |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
sleep(1) |
|||
<span style="color: #7060A8;">sleep</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
showtime() |
|||
<span style="color: #000000;">showtime</span><span style="color: #0000FF;">()</span> |
|||
leave_cs(lock) |
|||
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lock</span><span style="color: #0000FF;">)</span> |
|||
end procedure |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
procedure main() |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
enter_cs(lock) |
|||
<span style="color: #7060A8;">enter_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lock</span><span style="color: #0000FF;">)</span> |
|||
sequence threads = {create_thread(routine_id("echo"),{"job1"}), |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">threads</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">create_thread</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">routine_id</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"echo"</span><span style="color: #0000FF;">),{</span><span style="color: #008000;">"job1"</span><span style="color: #0000FF;">}),</span> |
|||
create_thread(routine_id("echo"),{"job2"})} |
|||
<span style="color: #000000;">create_thread</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">routine_id</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"echo"</span><span style="color: #0000FF;">),{</span><span style="color: #008000;">"job2"</span><span style="color: #0000FF;">})}</span> |
|||
puts(1,"main") |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"main"</span><span style="color: #0000FF;">)</span> |
|||
showtime() |
|||
<span style="color: #000000;">showtime</span><span style="color: #0000FF;">()</span> |
|||
sleep(1) |
|||
<span style="color: #7060A8;">sleep</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
puts(1,"free") |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"free"</span><span style="color: #0000FF;">)</span> |
|||
showtime() |
|||
<span style="color: #000000;">showtime</span><span style="color: #0000FF;">()</span> |
|||
leave_cs(lock) |
|||
<span style="color: #7060A8;">leave_cs</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lock</span><span style="color: #0000FF;">)</span> |
|||
wait_thread(threads) |
|||
<span style="color: #000000;">wait_thread</span><span style="color: #0000FF;">(</span><span style="color: #000000;">threads</span><span style="color: #0000FF;">)</span> |
|||
puts(1,"done\n") |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"done\n"</span><span style="color: #0000FF;">)</span> |
|||
end procedure |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
main()</lang> |
|||
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
{{out}} |
||
Typically the first thread to attempt enter_cs() is released first, but there is |
Typically the first thread to attempt enter_cs() is released first, but there is |
||
Line 957: | Line 990: | ||
</pre> |
</pre> |
||
External events such as timers and user input are handled in pGUI, eg |
External events such as timers and user input are handled in pGUI, eg |
||
<!--<syntaxhighlight lang="phix">--> |
|||
<lang Phix>function timer_cb(Ihandle /*ih*/) |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">timer_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span> |
|||
IupUpdate(canvas) |
|||
<span style="color: #7060A8;">IupUpdate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">)</span> |
|||
return IUP_IGNORE |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_IGNORE</span> |
|||
end function |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
Ihandle timer = IupTimer(Icallback("timer_cb"), 1000) |
|||
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">timer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupTimer</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"timer_cb"</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">1000</span><span style="color: #0000FF;">)</span> |
|||
function key_cb(Ihandle /*ih*/, atom c) |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">key_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">)</span> |
|||
if c=K_ESC then return IUP_CLOSE end if |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_ESC</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CLOSE</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
if c=K_F5 then |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_F5</span> <span style="color: #008080;">then</span> |
|||
iteration = 0 |
|||
<span style="color: #000000;">iteration</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> |
|||
IupSetInt(timer,"RUN",1) -- (restart timer) |
|||
<span style="color: #7060A8;">IupSetInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">timer</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"RUN"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- (restart timer)</span> |
|||
end if |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
return IUP_CONTINUE |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CONTINUE</span> |
|||
end function |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
IupSetCallback(dlg, "K_ANY", Icallback("key_cb"))</lang> |
|||
<span style="color: #7060A8;">IupSetCallback</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"K_ANY"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"key_cb"</span><span style="color: #0000FF;">))</span> |
|||
<!--</syntaxhighlight>--> |
|||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
Line 983: | Line 1,018: | ||
This will print a message after one second, then terminate the program after |
This will print a message after one second, then terminate the program after |
||
another four seconds: |
another four seconds: |
||
< |
<syntaxhighlight lang="picolisp">(alarm 1 |
||
(prinl "Exit in 4 seconds") |
(prinl "Exit in 4 seconds") |
||
(alarm 4 (bye)) )</ |
(alarm 4 (bye)) )</syntaxhighlight> |
||
=={{header|PowerShell}}== |
=={{header|PowerShell}}== |
||
<syntaxhighlight lang="powershell"> |
|||
<lang PowerShell> |
|||
$timer = New-Object -TypeName System.Timers.Timer -Property @{Enabled=$true; Interval=1000; AutoReset=$true} |
$timer = New-Object -TypeName System.Timers.Timer -Property @{Enabled=$true; Interval=1000; AutoReset=$true} |
||
Line 1,005: | Line 1,040: | ||
$global:counter = 0 |
$global:counter = 0 |
||
& $job.Module {$global:counter} |
& $job.Module {$global:counter} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{Out}} |
{{Out}} |
||
<pre> |
<pre> |
||
Line 1,017: | Line 1,052: | ||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
< |
<syntaxhighlight lang="purebasic">OpenWindow (0, 10, 10, 150, 40, "Event Demo") |
||
ButtonGadget (1, 10, 10, 35, 20, "Quit") |
ButtonGadget (1, 10, 10, 35, 20, "Quit") |
||
Line 1,028: | Line 1,063: | ||
EndIf |
EndIf |
||
ForEver</ |
ForEver</syntaxhighlight> |
||
=={{header|Python}}== |
=={{header|Python}}== |
||
<syntaxhighlight lang="python"> |
|||
<lang Python> |
|||
import threading |
import threading |
||
import time |
import time |
||
Line 1,053: | Line 1,088: | ||
print("Main: Done") |
print("Main: Done") |
||
t.join() |
t.join() |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
Line 1,064: | Line 1,099: | ||
the task (since it's a useless value): |
the task (since it's a useless value): |
||
< |
<syntaxhighlight lang="racket"> |
||
#lang racket |
#lang racket |
||
Line 1,073: | Line 1,108: | ||
(void (sync task)) ; wait for the task to be done before exiting |
(void (sync task)) ; wait for the task to be done before exiting |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
(formerly Perl 6) |
(formerly Perl 6) |
||
{{trans|Go}} |
{{trans|Go}} |
||
<lang |
<syntaxhighlight lang="raku" line>note now, " program start"; |
||
my $event = Channel.new; |
my $event = Channel.new; |
||
Line 1,091: | Line 1,126: | ||
note now, " program signaling event"; |
note now, " program signaling event"; |
||
$event.send(0); |
$event.send(0); |
||
await $todo;</ |
await $todo;</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>Instant:1403880984.089974 program start |
<pre>Instant:1403880984.089974 program start |
||
Line 1,104: | Line 1,139: | ||
Although REXX can be event driven, most events would probably have to be actively checked to see if the event occurs. |
Although REXX can be event driven, most events would probably have to be actively checked to see if the event occurs. |
||
<br>Here is a ''time-driven'' example of events happening, based on specific timer ticks. |
<br>Here is a ''time-driven'' example of events happening, based on specific timer ticks. |
||
< |
<syntaxhighlight lang="rexx">/*REXX program demonstrates a method of handling events (this is a time─driven pgm).*/ |
||
signal on halt /*allow |
signal on halt /*allow user to HALT (Break) the pgm.*/ |
||
parse arg timeEvent /*allow the "event" to be specified. */ |
parse arg timeEvent /*allow the "event" to be specified. */ |
||
if timeEvent='' then timeEvent= |
if timeEvent='' then timeEvent= 5 /*Not specified? Then use the default.*/ |
||
event?: do forever /*determine if an event has occurred. */ |
event?: do forever /*determine if an event has occurred. */ |
||
theEvent=right(time(),1) |
theEvent= right(time(), 1) /*maybe it's an event, ─or─ maybe not.*/ |
||
if pos(theEvent,timeEvent) |
if pos(theEvent, timeEvent)>0 then signal happening |
||
end /*forever*/ |
end /*forever*/ |
||
say 'Control should never get here!' /*This is a logic can─never─happen ! */ |
say 'Control should never get here!' /*This is a logic can─never─happen ! */ |
||
halt: say '════════════ program halted.'; |
halt: say '════════════ program halted.'; exit 0 /*stick a fork in it, we're all done. */ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
happening: say 'an event occurred at' time()", the event is:" theEvent |
happening: say 'an event occurred at' time()", the event is:" theEvent |
||
do |
do while theEvent==right(time(), 1) /*spin until a tic (a second) changes. */ |
||
nop /*replace NOP with the "process" code.*/ |
nop /*replace NOP with the "process" code.*/ |
||
end /*while*/ /*NOP |
end /*while*/ /*NOP is a REXX statement, does nothing*/ |
||
signal event? /*see if another event has happened. */</syntaxhighlight> |
|||
{{out|output|text= when using the input of: <tt> 1 3 5 0 7 9 </tt>}} |
|||
<pre> |
<pre> |
||
an event occurred at 16:13:29, the event is: 9 |
an event occurred at 16:13:29, the event is: 9 |
||
Line 1,147: | Line 1,182: | ||
Rust ensures memory safety at compile-time without needing a garbage collector or runtime. There are several concurrency primitives in it's standard library. |
Rust ensures memory safety at compile-time without needing a garbage collector or runtime. There are several concurrency primitives in it's standard library. |
||
<syntaxhighlight lang="rust"> |
|||
<lang Rust> |
|||
use std::{sync::mpsc, thread, time::Duration}; |
use std::{sync::mpsc, thread, time::Duration}; |
||
Line 1,165: | Line 1,200: | ||
Ok(()) |
Ok(()) |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
Tcl has been event-driven since 7.5, but only supported channel and timer events (plus variable traces, which can be used to create event-like entitites). With the addition of coroutines, it becomes much simpler to create general events: |
Tcl has been event-driven since 7.5, but only supported channel and timer events (plus variable traces, which can be used to create event-like entitites). With the addition of coroutines, it becomes much simpler to create general events: |
||
{{works with|Tcl|8.6}} |
{{works with|Tcl|8.6}} |
||
< |
<syntaxhighlight lang="tcl"># Simple task framework built from coroutines |
||
proc pause ms { |
proc pause ms { |
||
after $ms [info coroutine];yield |
after $ms [info coroutine];yield |
||
Line 1,223: | Line 1,258: | ||
X signal |
X signal |
||
} |
} |
||
waitForTasksToFinish</ |
waitForTasksToFinish</syntaxhighlight> |
||
Output: |
Output: |
||
<pre>waiting for event |
<pre>waiting for event |
||
Line 1,229: | Line 1,264: | ||
received event</pre> |
received event</pre> |
||
Of course, the classic way of writing this is much shorter, but intermingles the tasks: |
Of course, the classic way of writing this is much shorter, but intermingles the tasks: |
||
< |
<syntaxhighlight lang="tcl">after 1000 set X signalled |
||
puts "waiting for event" |
puts "waiting for event" |
||
vwait X |
vwait X |
||
puts "received event"</ |
puts "received event"</syntaxhighlight> |
||
=={{header|Wren}}== |
=={{header|Wren}}== |
||
Line 1,238: | Line 1,273: | ||
The tasks to be executed are added to a list by the Scheduler class. The Timer.sleep method suspends the current fiber and signals the scheduler (by calling a private method) to execute the tasks one by one in their own fibers - in Wren only one fiber can execute at a time. The task results are then available to the main fiber on its resumption after Timer.sleep has completed. |
The tasks to be executed are added to a list by the Scheduler class. The Timer.sleep method suspends the current fiber and signals the scheduler (by calling a private method) to execute the tasks one by one in their own fibers - in Wren only one fiber can execute at a time. The task results are then available to the main fiber on its resumption after Timer.sleep has completed. |
||
< |
<syntaxhighlight lang="wren">import "scheduler" for Scheduler |
||
import "timer" for Timer |
import "timer" for Timer |
||
Line 1,254: | Line 1,289: | ||
System.print(a) // still 3 |
System.print(a) // still 3 |
||
Timer.sleep(3000) // wait 3 seconds |
Timer.sleep(3000) // wait 3 seconds |
||
System.print(a) // now 3 * 3 + 1 = 10</ |
System.print(a) // now 3 * 3 + 1 = 10</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,261: | Line 1,296: | ||
10 |
10 |
||
</pre> |
</pre> |
||
=={{header|Yabasic}}== |
=={{header|Yabasic}}== |
||
{{trans|Gambas}} |
{{trans|Gambas}} |
||
<syntaxhighlight lang="yabasic"> |
|||
<lang Yabasic> |
|||
sub Timer1_Timer() |
sub Timer1_Timer() |
||
print Time$ |
print Time$ |
||
end sub |
end sub |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
zkl provides an Atomics library for things like this. Events are async, waiting for an event doesn't poll. |
zkl provides an Atomics library for things like this. Events are async, waiting for an event doesn't poll. |
||
< |
<syntaxhighlight lang="zkl">var event=Atomic.Bool(); // False |
||
// create thread waiting for event |
// create thread waiting for event |
||
fcn(event){event.wait(); println(vm," ping!")}.launch(event); |
fcn(event){event.wait(); println(vm," ping!")}.launch(event); |
||
Atomic.sleep(1); |
Atomic.sleep(1); |
||
event.set(); |
event.set(); |
||
println("done")</ |
println("done")</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
Latest revision as of 19:43, 1 December 2023
You are encouraged to solve this task according to the task description, using any language you may know.
Event is a synchronization object. An event has two states signaled and reset. A task may await for the event to enter the desired state, usually the signaled state. It is released once the state is entered. Releasing waiting tasks is called event notification. Programmatically controlled events can be set by a task into one of its states.
In concurrent programming event also refers to a notification that some state has been reached through an asynchronous activity. The source of the event can be:
- internal, from another task, programmatically;
- external, from the hardware, such as user input, timer, etc. Signaling an event from the hardware is accomplished by means of hardware interrupts.
Event is a low-level synchronization mechanism. It neither identify the state that caused it signaled, nor the source of, nor who is the subject of notification. Events augmented by data and/or publisher-subscriber schemes are often referred as messages, signals etc.
In the context of general programming event-driven architecture refers to a design that deploy events in order to synchronize tasks with the asynchronous activities they must be aware of. The opposite approach is polling sometimes called busy waiting, when the synchronization is achieved by an explicit periodic querying the state of the activity. As the name suggests busy waiting consumes system resources even when the external activity does not change its state.
Event-driven architectures are widely used in GUI design and SCADA systems. They are flexible and have relatively short response times. At the same time event-driven architectures suffer to the problems related to their unpredictability. They face race condition, deadlocking, live locks and priority inversion. For this reason real-time systems tend to polling schemes, trading performance for predictability in the worst case scenario.
Variants of events
Manual-reset event
This event changes its state by an explicit request of a task. I.e. once signaled it remains in this state until it will be explicitly reset.
Pulse event
A pulse event when signaled releases all tasks awaiting it and then is automatically reset.
Sample implementations / APIs
Show how a manual-reset event can be implemented in the language or else use an API to a library that provides events. Write a program that waits 1s and then signals the event to a task waiting for the event.
Ada
Ada provides higher-level concurrency primitives, which are complete in the sense that they also allow implementations of the lower-level ones, like event. Here is an implementation of the manual-reset event.
The event interface:
protected type Event is
procedure Signal;
procedure Reset;
entry Wait;
private
Fired : Boolean := False;
end Event;
The event implementation:
protected body Event is
procedure Signal is
begin
Fired := True;
end Signal;
procedure Reset is
begin
Fired := False;
end Reset;
entry Wait when Fired is
begin
null;
end Wait;
end Event;
With the event defined above:
with Ada.Text_IO; use Ada.Text_IO;
procedure Test_Events is
-- Place the event implementation here
X : Event;
task A;
task body A is
begin
Put_Line ("A is waiting for X");
X.Wait;
Put_Line ("A received X");
end A;
begin
delay 1.0;
Put_Line ("Signal X");
X.Signal;
end Test_Events;
Sample output:
A is waiting for X Signal X A received X
AutoHotkey
SetTimer, internal, 1000
Return
internal: ; fire on a timer
TrayTip, internal, internal event!`npress F2 for external event
SetTimer, internal, off
Return
F2:: ; external event: fire on F2 key press
TrayTip, external, f2 key pressed
Return
BASIC256
subroutine Timer1_Timer()
print hour; ":"; minute; ":"; second
end subroutine
- Output:
Igual que la entrada de Gambas.
BBC BASIC
API
This uses a Windows event object:
INSTALL @lib$+"TIMERLIB"
WAIT_TIMEOUT = 258
SYS "CreateEvent", 0, 1, 0, 0 TO hEvent%
timerID% = FN_ontimer(1000, PROCelapsed, 0)
PRINT "Waiting for event..."
REPEAT
SYS "WaitForSingleObject", hEvent%, 1 TO res%
UNTIL res% <> WAIT_TIMEOUT
PRINT "Event signalled"
END
DEF PROCelapsed
SYS "SetEvent", hEvent%
ENDPROC
Native
This uses a simple variable as a semaphore:
INSTALL @lib$+"TIMERLIB"
Event% = FALSE
timerID% = FN_ontimer(1000, PROCelapsed, 0)
PRINT "Waiting for event..."
REPEAT
WAIT 0
UNTIL Event%
PRINT "Event signalled"
END
DEF PROCelapsed
Event% = TRUE
ENDPROC
C
Using pipe to communicate to fork
ed child. Since child will be blocking trying to read the other end of the pipe, this can be used for synchronization.
#include <stdio.h>
#include <unistd.h>
int main()
{
int p[2];
pipe(p);
if (fork()) {
close(p[0]);
sleep(1);
write(p[1], p, 1);
wait(0);
} else {
close(p[1]);
read(p[0], p + 1, 1);
puts("received signal from pipe");
}
return 0;
}
C#
using System;
using System.Timers;
class Program
{
static void Main()
{
var timer = new Timer(1000);
timer.Elapsed += new ElapsedEventHandler(OnElapsed);
Console.WriteLine(DateTime.Now);
timer.Start();
Console.ReadLine();
}
static void OnElapsed(object sender, ElapsedEventArgs eventArgs)
{
Console.WriteLine(eventArgs.SignalTime);
((Timer)sender).Stop();
}
}
Sample output:
10-11-2010 18:35:11 10-11-2010 18:35:12
Clojure
(ns async-example.core
(:require [clojure.core.async :refer [>! <! >!! <!! go chan]])
(:require [clj-time.core :as time])
(:require [clj-time.format :as time-format])
(:gen-class))
;; Helper functions (logging & time stamp)
; Time stamp format
(def custom-formatter (time-format/formatter "yyyy:MM:dd:ss.SS"))
(defn safe-println [& more]
" This function avoids interleaving of text output when using println due to race condition for multi-processes printing
as discussed http://yellerapp.com/posts/2014-12-11-14-race-condition-in-clojure-println.html "
(.write *out* (str (clojure.string/join " " more) "\n")))
(defn log [s]
" Outputs mesage with time stamp "
(safe-println (time-format/unparse custom-formatter (time/now)) ":" s))
;; Main code
(defn -main [& args]
(let [c (chan)]
(log "Program start")
(go
(log "Task start")
(log (str "Event received by task: "(<! c))))
(<!!
(go
(log "program sleeping")
(Thread/sleep 1000) ; Wait 1 second
(log "Program signaling event")
(>! c "reset") ; Send message to task
))))
; Invoke -main function
(-main)
- Output:
2016:10:18:06.93 : Program start 2016:10:18:06.94 : task start 2016:10:18:06.94 : program sleeping 2016:10:18:07.94 : Program signaling event 2016:10:18:07.94 : Event received by task: reset
Delphi
program Events;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes, Windows;
type
TWaitThread = class(TThread)
private
FEvent: THandle;
public
procedure Sync;
procedure Execute; override;
constructor Create(const aEvent: THandle); reintroduce;
end;
{ TWaitThread }
constructor TWaitThread.Create(const aEvent: THandle);
begin
inherited Create(False);
FEvent := aEvent;
end;
procedure TWaitThread.Execute;
var
res: Cardinal;
begin
res := WaitForSingleObject(FEvent, INFINITE);
if res = 0 then
Synchronize(Sync);
end;
procedure TWaitThread.Sync;
begin
Writeln(DateTimeToStr(Now));
end;
var
event: THandle;
begin
Writeln(DateTimeToStr(Now));
event := CreateEvent(nil, False, False, 'Event');
with TWaitThread.Create(event) do
try
Sleep(1000);
SetEvent(event)
finally
Free;
end;
Readln;
end.
Sample output:
09.08.2011 0:27:43 09.08.2011 0:27:44
E
def makeEvent() {
def [var fired, var firer] := Ref.promise()
def event {
to signal() {
firer.resolveRace(null) # all current and future wait()s will resolve
}
to reset() {
if (firer.isDone()) { # ignore multiple resets. If we didn't, then
# reset() wait() reset() signal() would never
# resolve that wait().
# create all fresh state
def [p, r] := Ref.promise()
fired := p
firer := r
}
}
to wait() {
return fired
}
}
return event
}
The event object has this behavior: the return value of .wait()
will be resolved after the time of the earliest .signal()
for which there is no intervening .reset()
.
def e := makeEvent()
{
when (e.wait()) -> {
println("[2] Received event.")
}
println("[2] Waiting for event...")
}
{
timer.whenPast(timer.now() + 1000, def _() {
println("[1] Signaling event.")
e.signal()
})
println("[1] Waiting 1 second...")
}
EasyLang
on timer
move randomf * 100 randomf * 100
circle 2
timer 1
.
on mouse_down
move mouse_x mouse_y
circle 2
.
timer 0
Elixir
defmodule Events do
def log(msg) do
time = Time.utc_now |> to_string |> String.slice(0..7)
IO.puts "#{time} => #{msg}"
end
def task do
log("Task start")
receive do
:go -> :ok
end
log("Task resumed")
end
def main do
log("Program start")
{pid,ref} = spawn_monitor(__MODULE__,:task,[])
log("Program sleeping")
Process.sleep(1000)
log("Program signalling event")
send(pid, :go)
receive do
{:DOWN,^ref,_,_,_} -> :task_is_down
end
end
end
Events.main
- Output:
06:27:05 => Program start 06:27:05 => Program sleeping 06:27:05 => Task start 06:27:06 => Program signalling event 06:27:06 => Task resumed
Erlang
Events can be implemented by using the selective receive expression and erlang's built in message passing. Here task waits for the message 'go' before it will continue.
-module(events).
-compile(export_all).
log(Msg) ->
{H,M,S} = erlang:time(),
io:fwrite("~2.B:~2.B:~2.B => ~s~n",[H,M,S,Msg]).
task() ->
log("Task start"),
receive
go -> ok
end,
log("Task resumed").
main() ->
log("Program start"),
P = spawn(?MODULE,task,[]),
log("Program sleeping"),
timer:sleep(1000),
log("Program signalling event"),
P ! go,
timer:sleep(100).
Output:
66> events:main().
0: 0:57 => Program start
0: 0:57 => Program sleeping
0: 0:57 => Task start
0: 0:58 => Program signalling event
0: 0:58 => Task resumed
ok
F#
open System
open System.Timers
let onElapsed (sender : obj) (eventArgs : ElapsedEventArgs) =
printfn "%A" eventArgs.SignalTime
(sender :?> Timer).Stop()
[<EntryPoint>]
let main argv =
let timer = new Timer(1000.)
timer.Elapsed.AddHandler(new ElapsedEventHandler(onElapsed))
printfn "%A" DateTime.Now
timer.Start()
ignore <| Console.ReadLine()
0
FreeBASIC
Sub Timer1_Timer()
Print Time
End Sub
- Output:
Igual que la entrada de Gambas.
FutureBasic
Event timer fires every tenth of a second
timerbegin, 0.1, YES
cls : printf @"%@", time(@"h:mm:ss a zzz")
timerend
HandleEvents
- Output:
4:44:36 PM EDT
Gambas
Public Sub Timer1_Timer()
Print Str(Time(Now))
End
Output:
16:14:18 16:14:19 16:14:20 16:14:21 16:14:22 16:14:23 16:14:24 16:14:25
Go
A Go channel can represent an manual-reset event, as described by the task. The two states of signaled and reset correspond to the presence or absence of a value on the channel. The program signals by sending a value on the channel. The event is reset when the waiting task explicitly executes the channel receive operation, <-event.
package main
import (
"log"
"os"
"time"
)
func main() {
l := log.New(os.Stdout, "", log.Ltime | log.Lmicroseconds)
l.Println("program start")
event := make(chan int)
go func() {
l.Println("task start")
<-event
l.Println("event reset by task")
}()
l.Println("program sleeping")
time.Sleep(1 * time.Second)
l.Println("program signaling event")
event <- 0
time.Sleep(100 * time.Millisecond)
}
- Output:
01:27:21.862000 program start 01:27:21.862245 program sleeping 01:27:21.867269 task start 01:27:22.868294 program signaling event 01:27:22.868346 event reset by task
Haskell
import Control.Concurrent (threadDelay, forkIO)
import Control.Concurrent.SampleVar
-- An Event is defined as a SampleVar with no data.
-- http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent-SampleVar.html
newtype Event = Event (SampleVar ())
newEvent = fmap Event (newEmptySampleVar)
signalEvent (Event sv) = writeSampleVar sv ()
resetEvent (Event sv) = emptySampleVar sv
waitEvent (Event sv) = readSampleVar sv
main = do e <- newEvent
forkIO (waitTask e)
putStrLn "[1] Waiting 1 second..."
threadDelay 1000000 {- µs -}
putStrLn "[1] Signaling event."
signalEvent e
threadDelay 1000000 {- µs -} -- defer program exit for reception
waitTask e = do putStrLn "[2] Waiting for event..."
waitEvent e
putStrLn "[2] Received event."
Note: Because there is no serialization of the text output, there is a chance that it will appear interleaved.
Icon and Unicon
The following only works in Unicon. The example illustrates the multiple tasks can receive the same event:
record Event(cond, value)
procedure main()
event := Event(condvar())
t1 := thread {
write("Task one waiting for event....")
critical event.cond: while /(event.value) do wait(event.cond)
write("Task one received event.")
}
t2 := thread {
write("Task two waiting for event....")
critical event.cond: while /(event.value) do wait(event.cond)
write("Task two received event.")
}
delay(1000) # Let main thread post the event.
event.value := "yes"
write("Signalling event.")
signal(event.cond,0)
every wait(t1|t2)
end
Sample run:
->event Task two waiting for event.... Task one waiting for event.... Signalling event. Task two received event. Task one received event. ->
JavaScript
An example using the YUI library:
YUI().use('event-custom', function(Y) {
// add a custom event:
Y.on('my:event', function () {
alert("Event fired");
});
// fire the event after one second:
setTimeout(function () {
Y.fire('my:event');
}, 1000);
});
An example simulating DOM events:
YUI().use('node-event-simulate', function(Y) {
// add a click event handler to a DOM node with id "button":
Y.one("#button").on("click", function (e) {
alert("Button clicked");
});
// simulate the click after one second:
setTimeout(function () {
Y.one("#button").simulate("click");
}, 1000);
});
Julia
Julia provides a variety of high and low level functions and macros for multitasking and events. The code below uses a Condition() event semaphore created in the base thread for communication between two child threads.
function dolongcomputation(cond)
det(rand(4000, 4000))
Base.notify(cond)
end
function printnotice(cond)
Base.wait(cond)
println("They are finished.")
end
function delegate()
println("Starting task, sleeping...")
condition = Base.Condition()
Base.@async(printnotice(condition))
Base.@async(dolongcomputation(condition))
end
delegate()
sleep(5)
println("Done sleeping.")
- Output:
Starting task, sleeping... They are finished. Done sleeping.
LFE
Paste in the REPL:
(defun log (msg)
(let ((`#(,h ,m ,s) (erlang:time)))
(lfe_io:format "~2.B:~2.B:~2.B => ~s~n" `(,h ,m ,s ,msg))))
(defun task ()
(log "Task start")
(receive
('go 'ok))
(log "Task resumed"))
(defun run ()
(log "Program start")
(let ((pid (spawn (lambda () (task)))))
(progn
(log "Program sleeping")
(timer:sleep 1000)
(log "Program signalling event")
(! pid 'go)
(timer:sleep 100))))
Usage:
> (run) 18:34:53 => Program start 18:34:53 => Program sleeping 18:34:53 => Task start 18:34:54 => Program signalling event 18:34:54 => Task resumed ok
OTP comes with a gen_even
t behavior that is more robust and resilient than this version. That is what should be used for any non-toy example or project.
Lingo
Lingo/Director uses (stateless) events for system/application state change notifications, user action notifications and inter-sprite communication.
To catch an event, a corresponding event handler - a function with a predefined name - has to be definined in the code. Examples for such event handlers are:
-- the current window was closed
on closeWindow
...
end
-- the left mouse button was pressed by the user
on mouseDown
...
end
Also "Sprites" (visual elements) receive events by setting up such event handlers in scripts attached to them. Both predefined and custom events can be sent programmatically to sprites, e.g. using:
-- send event #mouseDown programmatically to sprite 1
sendSprite(1, #mouseDown)
-- send custom event #foo to named sprite "bar"
sendSprite("bar", #foo)
-- send custom event #fooBar to all existing sprites
sendAllSprites(#fooBar)
Using a binary plugin ("Xtra"), in Windows also lower level window messages can be both sent and received:
mx = xtra("Msg").new()
-- send message WM_LBUTTONDOWN to a specific window identified by HWND hwnd
WM_LBUTTONDOWN = 513
MK_LBUTTON = 1
lParam = 65536*y + x
mx.send_msg (hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lParam)
-- listen for WM_COPYDATA and WM_MOUSEWHEEL messages sent to current application
-- window, notify Lingo callback function 'msgReceived' when such messages occur.
-- This callback function will receive hwnd, message, wParam and lParam as arguments
-- (and for WM_COPYDATA messages also the data that was sent as ByteArray).
WM_COPYDATA = 74
WM_MOUSEWHEEL = 522
mx.msg_listen([WM_COPYDATA, WM_MOUSEWHEEL], VOID, #msgReceived)
Mathematica/Wolfram Language
Mathematica supports events from timers (via Pause[]), task schedule descriptors. This will print a message after 4 seconds, then terminate the program.
Print["Will exit in 4 seconds"]; Pause[4]; Quit[]
->Will exit in 4 seconds
Nim
import posix
var p: array[2, cint]
discard pipe p
if fork() > 0:
discard close p[0]
discard sleep 1
discard p[1].write(addr p[0], 1)
var x: cint = 0
discard wait (addr x)
else:
discard close p[1]
discard p[0].read(addr p[1], 1)
echo "received signal from pipe"
Stdlib Semaphore
This version using locks module for signaling the condition.
import locks
from os import sleep
import times
from strformat import fmt
var
# condition variable which shared across threads
cond: Cond
lock: Lock
threadproc: Thread[void]
proc waiting {.thread.} =
echo "spawned waiting proc"
let start = getTime()
cond.wait lock
echo fmt"thread ended after waiting {getTime() - start}."
proc main =
initCond cond
initLock lock
threadproc.createThread waiting
echo "in main proc"
os.sleep 1000
echo "send signal/event notification"
signal cond
joinThread threadproc
deinitCond cond
deinitLock lock
main()
Compile and run:
nim c -r --threads:on events_cond.nim
- Output:
in main proc spawned waiting proc send signal/event notification thread ended after waiting 1 second, 61 microseconds, and 311 nanoseconds.
Oforth
An event is often implemented with a control channel. A task is waiting for an object on the channel. When the event occurs, another task sends an object on this channel.
: anEvent
| ch |
Channel new ->ch
#[ ch receive "Ok, event is signaled !" println ] &
System sleep(1000)
ch send($myEvent) ;
An emitter is a general implementation for handling events : an emitter waits for events emitted and launches listeners that are waiting for those events.
import: emitter
: anEvent2
| e i |
Emitter new(null) ->e
e onEvent($myEvent, #[ "Event is signaled !" println ])
10 loop: i [
1000 System sleep
$myEvent e emit
]
e close ;
Oz
Events can be implemented as mutable references to dataflow variables:
declare
fun {NewEvent}
{NewCell _}
end
proc {SignalEvent Event}
@Event = unit
end
proc {ResetEvent Event}
Event := _
end
proc {WaitEvent Event}
{Wait @Event}
end
E = {NewEvent}
in
thread
{System.showInfo "[2] Waiting for event..."}
{WaitEvent E}
{System.showInfo "[2] Received event."}
end
{System.showInfo "[1] Waiting 1 second..."}
{Delay 1000}
{System.showInfo "[1] Signaling event."}
{SignalEvent E}
However, this code is quite unidiomatic. If we need to wait for an event just once (like in this example), we can simply use a dataflow variable, i.e. an event that cannot be reset:
declare
E
in
thread
{System.showInfo "[2] Waiting for event..."}
{Wait E}
{System.showInfo "[2] Received event."}
end
{System.showInfo "[1] Waiting 1 second..."}
{Delay 1000}
{System.showInfo "[1] Signaling event."}
E = unit
If we want to synchronize two threads repeatedly and exchange data, it is natural to use ports and streams. Streams are just lists with an unbound tail. A port is basically a pointer to the tail of a list, i.e. it keeps track of where the next event can be written to:
declare
MyPort
in
thread
MyStream
in
{NewPort ?MyStream ?MyPort}
{System.showInfo "[2] Waiting for event..."}
for Event in MyStream do
{System.showInfo "[2] Received event."}
{System.showInfo "[2] Waiting for event again..."}
end
end
for do
{System.showInfo "[1] Waiting 1 second..."}
{Delay 1000}
{System.showInfo "[1] Signaling event."}
{Port.send MyPort unit}
end
It is important to limit the scope of a stream as much as possible to ensure that the already read part of the stream is garbage-collected.
Perl
This is an example of using the AnyEvent module. The result is this: it prints "Hello world!" after one second, then after another second prints "Hi!" four times every quarter of a second and then immediately prints "Bye!" and quits:
use AnyEvent;
# a new condition with a callback:
my $quit = AnyEvent->condvar(
cb => sub {
warn "Bye!\n";
}
);
# a new timer, starts after 2s and repeats every 0.25s:
my $counter = 1;
my $hi = AnyEvent->timer(
after => 2,
interval => 0.25,
cb => sub {
warn "Hi!\n";
# flag the condition as ready after 4 times:
$quit->send if ++$counter > 4;
}
);
# another timer, runs the callback once after 1s:
my $hello = AnyEvent->timer(
after => 1,
cb => sub {
warn "Hello world!\n";
}
);
# wait for the $quit condition to be ready:
$quit->recv();
This is the same using AnyEvent simplified API:
use AnyEvent;
my $quit = AE::cv sub { warn "Bye!\n" };
my $counter = 1;
my $hi = AE::timer 2, 0.25, sub {
warn "Hi!\n";
$quit->send if ++$counter > 4;
};
my $hello = AE::timer 1, 0, sub {
warn "Hello world!\n";
};
$quit->recv;
Phix
The primary synchronisation primitive in phix is the critical section, in the following the leave_cs() in main() acts as signalling an event, and the one in echo() from whichever goes first acts to signal that the other can/should resume.
constant lock = init_cs() include timedate.e procedure showtime() puts(1,format_timedate(date()," h:m:s\n")) end procedure procedure echo(string s) sleep(rnd()/10) -- see note enter_cs(lock) puts(1,s) sleep(1) showtime() leave_cs(lock) end procedure procedure main() enter_cs(lock) sequence threads = {create_thread(routine_id("echo"),{"job1"}), create_thread(routine_id("echo"),{"job2"})} puts(1,"main") showtime() sleep(1) puts(1,"free") showtime() leave_cs(lock) wait_thread(threads) puts(1,"done\n") end procedure main()
- Output:
Typically the first thread to attempt enter_cs() is released first, but there is no guarantee of that. The sleep(rnd()/10) above evens out the likelihood, by pausing for up to 0.1s, but otherwise isn't necessary.
main 10:00:57 free 10:00:58 job2 10:00:59 job1 10:01:00 done
External events such as timers and user input are handled in pGUI, eg
function timer_cb(Ihandle /*ih*/) IupUpdate(canvas) return IUP_IGNORE end function Ihandle timer = IupTimer(Icallback("timer_cb"), 1000) function key_cb(Ihandle /*ih*/, atom c) if c=K_ESC then return IUP_CLOSE end if if c=K_F5 then iteration = 0 IupSetInt(timer,"RUN",1) -- (restart timer) end if return IUP_CONTINUE end function IupSetCallback(dlg, "K_ANY", Icallback("key_cb"))
PicoLisp
PicoLisp supports events from timers (via 'task' and 'alarm'), file descriptors (also 'task') and various 'signals'. This will print a message after one second, then terminate the program after another four seconds:
(alarm 1
(prinl "Exit in 4 seconds")
(alarm 4 (bye)) )
PowerShell
$timer = New-Object -TypeName System.Timers.Timer -Property @{Enabled=$true; Interval=1000; AutoReset=$true}
$action = {
$global:counter += 1
Write-Host “Event counter is ${counter}: $((Get-Date).ToString("hh:mm:ss"))”
if ($counter -ge $event.MessageData)
{
Write-Host “Timer stopped”
$timer.Stop()
}
}
$job = Register-ObjectEvent -InputObject $timer -MessageData 5 -SourceIdentifier Count -EventName Elapsed -Action $action
$global:counter = 0
& $job.Module {$global:counter}
- Output:
Event counter is 1: 04:58:04 Event counter is 2: 04:58:05 Event counter is 3: 04:58:06 Event counter is 4: 04:58:07 Event counter is 5: 04:58:08 Timer stopped
PureBasic
OpenWindow (0, 10, 10, 150, 40, "Event Demo")
ButtonGadget (1, 10, 10, 35, 20, "Quit")
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget And EventGadget() = 1
End
EndIf
ForEver
Python
import threading
import time
def wait_for_event(event):
event.wait()
print("Thread: Got event")
e = threading.Event()
t = threading.Thread(target=wait_for_event, args=(e,))
t.start()
print("Main: Waiting one second")
time.sleep(1.0)
print("Main: Setting event")
e.set()
time.sleep(1.0)
print("Main: Done")
t.join()
Racket
Racket comes with events as part of its implementation; various types of events are used for different purposes: there are events that become ready when some input is available in a port, when a TCP connection is made, when a thread is dead, etc etc. Here we use a simple alarm event as requested, even though it's a odd to send the actual event result to the task (since it's a useless value):
#lang racket
(define task (thread (lambda () (printf "Got: ~s\n" (thread-receive)))))
(thread-send task ; wait for it, then send it
(sync (alarm-evt (+ 1000 (current-inexact-milliseconds)))))
(void (sync task)) ; wait for the task to be done before exiting
Raku
(formerly Perl 6)
note now, " program start";
my $event = Channel.new;
my $todo = start {
note now, " task start";
$event.receive;
note now, " event reset by task";
}
note now, " program sleeping";
sleep 1;
note now, " program signaling event";
$event.send(0);
await $todo;
- Output:
Instant:1403880984.089974 program start Instant:1403880984.095400 program sleeping Instant:1403880984.095491 task start Instant:1403880985.099381 program signaling event Instant:1403880985.109395 event reset by task
See also Handle_a_signal#Raku for an example of using Supplies to do reactive programming based on events (Unix signals in this case).
REXX
Although REXX can be event driven, most events would probably have to be actively checked to see if the event occurs.
Here is a time-driven example of events happening, based on specific timer ticks.
/*REXX program demonstrates a method of handling events (this is a time─driven pgm).*/
signal on halt /*allow user to HALT (Break) the pgm.*/
parse arg timeEvent /*allow the "event" to be specified. */
if timeEvent='' then timeEvent= 5 /*Not specified? Then use the default.*/
event?: do forever /*determine if an event has occurred. */
theEvent= right(time(), 1) /*maybe it's an event, ─or─ maybe not.*/
if pos(theEvent, timeEvent)>0 then signal happening
end /*forever*/
say 'Control should never get here!' /*This is a logic can─never─happen ! */
halt: say '════════════ program halted.'; exit 0 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
happening: say 'an event occurred at' time()", the event is:" theEvent
do while theEvent==right(time(), 1) /*spin until a tic (a second) changes. */
nop /*replace NOP with the "process" code.*/
end /*while*/ /*NOP is a REXX statement, does nothing*/
signal event? /*see if another event has happened. */
- output when using the input of: 1 3 5 0 7 9
an event occurred at 16:13:29, the event is: 9 an event occurred at 16:13:30, the event is: 0 an event occurred at 16:13:31, the event is: 1 an event occurred at 16:13:33, the event is: 3 an event occurred at 16:13:35, the event is: 5 an event occurred at 16:13:37, the event is: 7 an event occurred at 16:13:39, the event is: 9 an event occurred at 16:13:40, the event is: 0 an event occurred at 16:13:41, the event is: 1 an event occurred at 16:13:43, the event is: 3 an event occurred at 16:13:45, the event is: 5 an event occurred at 16:13:47, the event is: 7 an event occurred at 16:13:49, the event is: 9 an event occurred at 16:13:50, the event is: 0 an event occurred at 16:13:51, the event is: 1 an event occurred at 16:13:53, the event is: 3 ════════════ program halted.
Rust
Rust ensures memory safety at compile-time without needing a garbage collector or runtime. There are several concurrency primitives in it's standard library.
use std::{sync::mpsc, thread, time::Duration};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("[1] Starting");
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
println!("[2] Waiting for event");
rx.recv();
println!("[2] Received event");
});
thread::sleep(Duration::from_secs(1));
println!("[1] Sending event");
tx.send(())?;
thread::sleep(Duration::from_secs(1));
Ok(())
}
Tcl
Tcl has been event-driven since 7.5, but only supported channel and timer events (plus variable traces, which can be used to create event-like entitites). With the addition of coroutines, it becomes much simpler to create general events:
# Simple task framework built from coroutines
proc pause ms {
after $ms [info coroutine];yield
}
proc task {name script} {
coroutine $name apply [list {} \
"set ::tasks(\[info coro]) 1;$script;unset ::tasks(\[info coro])"]
}
proc waitForTasksToFinish {} {
global tasks
while {[array size tasks]} {
vwait tasks
}
}
# Make an Ada-like event class
oo::class create Event {
variable waiting fired
constructor {} {
set waiting {}
set fired 0
}
method wait {} {
while {!$fired} {
lappend waiting [info coroutine]
yield
}
}
method signal {} {
set wake $waiting
set waiting {}
set fired 1
foreach task $wake {
$task
}
}
method reset {} {
set fired 0
}
}
# Execute the example
Event create X
task A {
puts "waiting for event"
X wait
puts "received event"
}
task B {
pause 1000
puts "signalling X"
X signal
}
waitForTasksToFinish
Output:
waiting for event signalling X received event
Of course, the classic way of writing this is much shorter, but intermingles the tasks:
after 1000 set X signalled
puts "waiting for event"
vwait X
puts "received event"
Wren
Wren-cli supports the scheduling of tasks using a timer.
The tasks to be executed are added to a list by the Scheduler class. The Timer.sleep method suspends the current fiber and signals the scheduler (by calling a private method) to execute the tasks one by one in their own fibers - in Wren only one fiber can execute at a time. The task results are then available to the main fiber on its resumption after Timer.sleep has completed.
import "scheduler" for Scheduler
import "timer" for Timer
var a = 3
// add a task
Scheduler.add {
a = a * a
}
// add another task
Scheduler.add {
a = a + 1
}
System.print(a) // still 3
Timer.sleep(3000) // wait 3 seconds
System.print(a) // now 3 * 3 + 1 = 10
- Output:
3 10
Yabasic
sub Timer1_Timer()
print Time$
end sub
zkl
zkl provides an Atomics library for things like this. Events are async, waiting for an event doesn't poll.
var event=Atomic.Bool(); // False
// create thread waiting for event
fcn(event){event.wait(); println(vm," ping!")}.launch(event);
Atomic.sleep(1);
event.set();
println("done")
- Output:
// snooze done // setting is fast, receiving maybe not so VM#4 ping! // and thread 4 exits
I ran this from the REPL so I didn't have to worry about the main thread exiting and nuking the child thread.
- Programming Tasks
- Concurrency
- Encyclopedia
- Ada
- AutoHotkey
- BASIC256
- BBC BASIC
- C
- C sharp
- Clojure
- Delphi
- E
- EasyLang
- Elixir
- Erlang
- F Sharp
- FreeBASIC
- FutureBasic
- Gambas
- Go
- Haskell
- Unicon
- JavaScript
- Julia
- LFE
- Lingo
- Msg Xtra
- Mathematica
- Wolfram Language
- Nim
- Oforth
- Oz
- Perl
- Phix
- PicoLisp
- PowerShell
- PureBasic
- Python
- Racket
- Raku
- REXX
- Rust
- Tcl
- Wren
- Yabasic
- Zkl
- GUISS/Omit
- Maxima/Omit
- PARI/GP/Omit
- TI-83 BASIC/Omit
- TI-89 BASIC/Omit