.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Event::Distributor 3pm" .TH Event::Distributor 3pm "2019-08-12" "perl v5.28.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" "Event::Distributor" \- a simple in\-process pub/sub mechanism .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use Event::Distributor; \& \& my $dist = Event::Distributor\->new; \& \& $dist\->declare_signal( "announce" ); \& \& \& $dist\->subscribe_sync( announce => sub { \& my ( $message ) = @_; \& say $message; \& }); \& \& $dist\->subscribe_async( announce => sub { \& my ( $message ) = @_; \& return $async_http\->POST( "http://server/message", $message ); \& }); \& \& \& $dist\->fire_sync( announce => "Hello, world!" ); .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Instances of this class provide a simple publish/subscribe mechanism within a single process, for either synchronous or Future\-based asynchronous use. .PP A given instance has a set of named events. Subscribers are \f(CW\*(C`CODE\*(C'\fR references attached to a named event. Publishers can declare the existence of a named event, and then later invoke it by passing in arguments, which are distributed to all of the subscribers of that named event. .PP It is specifically \fInot\fR an error to request to subscribe an event that has not yet been declared, in order to allow multiple modules of code to be loaded and subscribe events the others publish, without introducing loading order dependencies. An event only needs to be declared by the time it is fired. .PP Natively all of the events provided by the distributor are fully-asynchronous in nature. Each subscriber is expected to return a Future instance which will indicate its completion; the results of these are merged into a single future returned by the fire method itself. However, to support synchronous or semi-synchronous programs using it, both the observe and invoke methods also have a synchronous variant. Note however, that this module does not provide any kind of asynchronous detachment of synchronous functions; using the \&\*(L"subscribe_sync\*(R" method to subscribe a long-running blocking function will cause the \f(CW\*(C`fire_*\*(C'\fR methods to block until that method returns. To achieve a truely-asynchronous experience the attached code will need to use some kind of asynchronous event system. .PP This module is very-much a work-in-progress, and many ideas may still be added or changed about it. It is the start of a concrete implementaion of some of the ideas in my \*(L"Event-Reflexive Programming\*(R" series of blog posts. See the \&\*(L"\s-1TODO\*(R"\s0 and \*(L"\s-1SEE ALSO\*(R"\s0 sections for more detail. .SH "EVENTS" .IX Header "EVENTS" Each of the events known by a distributor has a name. Conceptually each also has a type. Currently there are three types of event, a \*(L"signal\*(R", an \*(L"action\*(R", and a \*(L"query\*(R". .IP "\(bu" 2 A signal event simply informs subscribers that some event or condition has occurred. Additional arguments can be passed from the invoker to the subscribers, but subscriptions are not expected to return a meaningful value, nor does firing this event return a value. All subscriber functions are invoked sequentually and synchronously by a \f(CW\*(C`fire_*\*(C'\fR method (though, of course, asynchronous subscribers synchronously return a future instance, which allows them to continue working asynchronously). .IP "\(bu" 2 An action event requires a single subscriber, and represents a request from the invoker to the subscriber to perform some activity. This behaves much like a regular (Future\-returning) method call, except that the indirection mechanism of the distributor allows a more flexible method of connection between the two sides. .IP "\(bu" 2 A query event invokes subscriber code expecting a successful return, returning the first result that is successful. If a synchronous subscriber returns a result, or if an asynchronous one returns a successful immediate Future, then no further subscribers are invoked, and that result is taken immediately. Any other pending Futures are then cancelled. .SH "METHODS" .IX Header "METHODS" .SS "declare_signal" .IX Subsection "declare_signal" .Vb 1 \& $distributor\->declare_signal( $name ) .Ve .PP Declares a new \*(L"signal\*(R" event of the given name. .SS "declare_action" .IX Subsection "declare_action" .Vb 1 \& $distributor\->declare_action( $name ) .Ve .PP \&\fISince version 0.04.\fR .PP Declares a new \*(L"action\*(R" event of the given name. .SS "declare_query" .IX Subsection "declare_query" .Vb 1 \& $distributor\->declare_query( $name ) .Ve .PP \&\fISince version 0.02.\fR .PP Declares a new \*(L"query\*(R" event of the given name. .SS "subscribe_async" .IX Subsection "subscribe_async" .Vb 1 \& $distributor\->subscribe_async( $name, $code ) .Ve .PP Adds a new \f(CW\*(C`CODE\*(C'\fR reference to the list of subscribers for the named event. This subscriber is expected to return a Future that will eventually yield its result. .PP When invoked the code will be passed the distributor object itself and the list of arguments, and is expected to return a Future. .PP .Vb 1 \& $f = $code\->( $distributor, @args ) .Ve .SS "subscribe_sync" .IX Subsection "subscribe_sync" .Vb 1 \& $distributor\->subscribe_sync( $name, $code ) .Ve .PP Adds a new \f(CW\*(C`CODE\*(C'\fR reference to the list of subscribers for the named event. This subscriber is expected to perform its work synchronously and return its result immediately. .PP In non-blocking or asynchronous applications, this method should only be used for simple subscribers which can immediately return having completed their work. If the work is likely to take some time by blocking on external factors, consider instead using the \*(L"subscribe_async\*(R" method. .PP When invoked the code will be passed the distributor object itself and the list of arguments. .PP .Vb 1 \& $code\->( $distributor, @args ) .Ve .SS "fire_async" .IX Subsection "fire_async" .Vb 1 \& $f = $distributor\->fire_async( $name, @args ) .Ve .PP Invokes the named event, passing the arguments to the subscriber functions. This function returns as soon as all the subscriber functions have been invoked, returning a Future that will eventually complete when all the futures returned by the subscriber functions have completed. .SS "fire_sync" .IX Subsection "fire_sync" .Vb 1 \& $distributor\->fire_sync( $name, @args ) .Ve .PP Invokes the named event, passing the arguments to the subscriber functions. This function synchronously waits until all the subscriber futures have completed, and will return once they have all done so. .PP Note that since this method calls the \f(CW\*(C`get\*(C'\fR method on the Future instance returned by \*(L"fire_async\*(R", it is required that this either be an immediate, or be some subclass that can actually perform the await operation. This should be the case if it is provided by an event framework or similar, or custom application logic. .SH "TODO" .IX Header "TODO" Some of these ideas appear in the \*(L"Event-Reflexive Progamming\*(R" series of blog posts, and may be suitable for implementation here. All of these ideas are simply for consideration; there is no explicit promise that any of these will actually be implemented. .IP "\(bu" 4 Unsubscription from events. .IP "\(bu" 4 Define (or document the lack of) ordering between subscriptions of a given event. .IP "\(bu" 4 Refine the failure-handling semantics of signals. .IP "\(bu" 4 Ability to invoke signals after the current one is finished, by deferring the \&\f(CW\*(C`fire\*(C'\fR method. Should this be a new \f(CW\*(C`fire_*\*(C'\fR method, or a property of the signal itself? .IP "\(bu" 4 More control over the semantics of value-returning events \- scatter/map/gather pattern. .IP "\(bu" 4 Sub-heirarchies of events. .IP "\(bu" 4 Subclasses for specific event frameworks (IO::Async). .IP "\(bu" 4 Subclasses (or other behaviours) for out-of-process event serialisation and subscribers. .IP "\(bu" 4 Event parameter filtering mechanics \- allows parametric heirarchies, instrumentation logging, efficient out-of-process subscribers. .SH "SEE ALSO" .IX Header "SEE ALSO" .IP "Event-Reflexive Programming " 4 .IX Item "Event-Reflexive Programming " .SH "AUTHOR" .IX Header "AUTHOR" Paul Evans