Net::REPL::Client: Allow read/print portions to be overridden separately.
[thrasher.git] / perl / lib / Thrasher / EventLoop.pm
blob03f1483a5e7f0725bc23a5e85f366f35a2415366
1 package Thrasher::EventLoop;
2 use strict;
3 use warnings;
5 use Thrasher::Log qw(log debug);
7 =pod
9 =head1 NAME
11 Thrasher::EventLoop - the 'default' event loop for Thrasher Bird
13 =head1 DESCRIPTION
15 The trickiest part of Thrasher is that it needs to manage both
16 connections to the protocols, and talking to the XMPP server
17 itself. You can't use blocking connections on both.
19 This both specifies an interface, and a default event loop
20 based on ignoring the transport entirely, which actually works
21 if your "transport" is a pure perl module that never has long-
22 running components.
24 The Event Loop is created by the main Thrasher routine while it is
25 configuring all the pieces. Event loops are tied to protocols
26 pretty intimately, so the Protocol you are using has responsibility
27 for controlling what event loop you are using, so the Protocol
28 can freely refer directly to advanced aspects of the
30 =cut
32 use Thrasher::Log qw(log);
34 use base 'Exporter';
36 our @EXPORT_OK = qw(IN OUT);
37 our $IN = 1;
38 our $OUT = 2;
40 sub new {
41 my $class = shift;
42 my $self = {};
44 $self->{protocol} = shift;
45 $self->{socket} = shift;
46 $self->{component} = shift;
48 bless $self, $class;
49 return $self;
52 sub go {
53 my $self = shift;
55 # This test event loop just assumes the protocol is a
56 # 'test' protocol and can be ignored, so despite the documentation
57 # in this case we DO have exactly one socket to read, preventing
58 # the need for fancy processing.
59 while (defined(my $val = $self->{socket}->read)) {
60 debug("IN: $val");
61 $self->{component}->xml_in($val);
64 $self->{socket}->close;
67 # Returns some token that can be passed to 'cancel_schedule'
68 # Timeout is in milliseconds
69 # If the closure returns a true value, it should be rescheduled
70 # again in the given timeout, otherwise it should stop.
71 sub schedule {
72 my $self = shift;
73 my $closure = shift;
74 my $timeout = shift;
76 log("Thrasher::EventLoop laughs in the face of your attempt to "
77 ."schedule an event!");
78 local $@;
79 eval { $closure->(); };
80 log("Error in scheduled callback: $@") if $@;
83 # fd must be an actual file descriptor
84 # Directions is a bit mask of $IN and $OUT
85 # Return true to continue the watch, false to cancel it.
86 sub add_fd_watch {
87 my $self = shift;
88 my $fd = shift;
89 my $directions = shift;
90 my $closure = shift;
92 die "Thrasher::EventLoop laughs in the face of your attempt "
93 ."to watch a file descriptor! You need a real event loop.";
96 sub remove_fd_watch {
97 my $self = shift;
98 my $opaque_token = shift;
100 die "Thrasher::EventLoop laughs in the face of your attempt "
101 ."to cancel a file descriptor watch! Hey, you shouldn't "
102 ."even be able to get here...";
105 # Idle handler means "do a task after all other stuff in this
106 # event loop run is done". The only thing Thrasher uses this for
107 # is to properly shut the system down after a SIGINT or something.
108 # Simpler event loops can implement this as simply immediately
109 # executing the passed-in closure, but many event loops will become
110 # offended if you do that.
111 sub execute_on_idle {
112 my $self = shift;
113 my $closure = shift;
115 die "Thrasher::EventLoop laughs in the face of your attempt to "
116 ."schedule something for idle execution!";
119 # This should quit your event loop, preferably more politely than
120 # with "exit()", since we may want to do more stuff later.
121 sub quit {
122 my $self = shift;
124 die "Thrasher::EventLoop laughs in the face of your quit!";