ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Kernel.pm
Revision: 1.106
Committed: Fri Mar 23 21:16:25 2012 UTC (14 years, 5 months ago) by root
Branch: MAIN
Changes since 1.105: +58 -46 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3 root 1.3 AnyEvent::MP::Kernel - the actual message passing kernel
4 root 1.1
5     =head1 SYNOPSIS
6    
7 root 1.3 use AnyEvent::MP::Kernel;
8 root 1.1
9 root 1.106 $AnyEvent::MP::Kernel::SRCNODE # contains msg origin node id, for debugging
10    
11     snd_to_func $node, $func, @args # send msg to function
12     snd_on $node, @msg # snd message again (relay)
13     eval_on $node, $string[, @reply] # execute perl code on another node
14    
15     node_is_up $nodeid # return true if a node is connected
16     @nodes = up_nodes # return a list of all connected nodes
17     $guard = mon_nodes $callback->($node, $is_up, @reason) # connections up/downs
18    
19 root 1.1 =head1 DESCRIPTION
20    
21 root 1.106 This module implements most of the inner workings of AnyEvent::MP. It
22     offers mostly lower-level functions that deal with network connectivity
23     and special requests.
24    
25     You normally interface with AnyEvent::MP through a higher level interface
26     such as L<AnyEvent::MP> and L<Coro::MP>, although there is nothing wrong
27     with using the functions from this module.
28 root 1.3
29     =head1 GLOBALS AND FUNCTIONS
30 root 1.1
31     =over 4
32    
33     =cut
34    
35     package AnyEvent::MP::Kernel;
36    
37     use common::sense;
38     use Carp ();
39    
40 root 1.79 use AnyEvent ();
41     use Guard ();
42 root 1.1
43     use AnyEvent::MP::Node;
44     use AnyEvent::MP::Transport;
45    
46     use base "Exporter";
47    
48 root 1.106 # for re-export in AnyEvent::MP and Coro::MP
49     our @EXPORT_API = qw(
50     NODE $NODE
51     configure
52     node_of port_is_local
53     snd kil
54     db_set db_del
55     db_mon db_family db_keys db_values
56     );
57    
58     our @EXPORT_OK = (
59     # these are internal
60     qw(
61     %NODE %PORT %PORT_DATA $UNIQ $RUNIQ $ID
62     add_node load_func
63     ),
64     @EXPORT_API,
65 root 1.71 );
66    
67 root 1.1 our @EXPORT = qw(
68 root 1.106 snd_to_func snd_on eval_on
69     port_is_local
70 root 1.46 up_nodes mon_nodes node_is_up
71 root 1.1 );
72    
73 root 1.6 sub load_func($) {
74     my $func = $_[0];
75    
76     unless (defined &$func) {
77     my $pkg = $func;
78     do {
79     $pkg =~ s/::[^:]+$//
80 root 1.63 or return sub { die "unable to resolve function '$func'" };
81 root 1.60
82     local $@;
83 root 1.61 unless (eval "require $pkg; 1") {
84     my $error = $@;
85     $error =~ /^Can't locate .*.pm in \@INC \(/
86     or return sub { die $error };
87     }
88 root 1.6 } until defined &$func;
89     }
90    
91     \&$func
92     }
93    
94 root 1.78 my @alnum = ('0' .. '9', 'A' .. 'Z', 'a' .. 'z');
95    
96 root 1.1 sub nonce($) {
97 root 1.78 join "", map chr rand 256, 1 .. $_[0]
98 root 1.1 }
99    
100 root 1.78 sub nonce62($) {
101     join "", map $alnum[rand 62], 1 .. $_[0]
102 root 1.1 }
103    
104 root 1.20 our $CONFIG; # this node's configuration
105 root 1.104 our $SECURE;
106 root 1.21
107 root 1.64 our $RUNIQ; # remote uniq value
108     our $UNIQ; # per-process/node unique cookie
109     our $NODE;
110     our $ID = "a";
111 root 1.1
112     our %NODE; # node id to transport mapping, or "undef", for local node
113     our (%PORT, %PORT_DATA); # local ports
114    
115 root 1.21 our %RMON; # local ports monitored by remote nodes ($RMON{nodeid}{portid} == cb)
116 root 1.1 our %LMON; # monitored _local_ ports
117    
118 root 1.71 our $GLOBAL; # true if node is a global ("directory") node
119 root 1.85 our %BINDS;
120     our $BINDS; # our listeners, as arrayref
121 root 1.1
122 root 1.76 our $SRCNODE; # holds the sending node _object_ during _inject
123 root 1.1
124 root 1.106 # initialise names for non-networked operation
125     {
126 root 1.78 # ~54 bits, for local port names, lowercase $ID appended
127 root 1.106 my $now = AE::now;
128     $UNIQ =
129     (join "",
130     map $alnum[$_],
131     $$ / 62 % 62,
132     $$ % 62,
133     (int $now ) % 62,
134     (int $now * 100) % 62,
135     (int $now * 10000) % 62,
136     ) . nonce62 4
137     ;
138 root 1.78
139     # ~59 bits, for remote port names, one longer than $UNIQ and uppercase at the end to avoid clashes
140     $RUNIQ = nonce62 10;
141     $RUNIQ =~ s/(.)$/\U$1/;
142    
143 root 1.106 $NODE = "";
144 root 1.64 }
145    
146 root 1.1 sub NODE() {
147     $NODE
148     }
149    
150     sub node_of($) {
151 root 1.21 my ($node, undef) = split /#/, $_[0], 2;
152 root 1.1
153 root 1.21 $node
154 root 1.1 }
155    
156 root 1.17 BEGIN {
157     *TRACE = $ENV{PERL_ANYEVENT_MP_TRACE}
158     ? sub () { 1 }
159     : sub () { 0 };
160     }
161 root 1.1
162 root 1.42 our $DELAY_TIMER;
163     our @DELAY_QUEUE;
164    
165 root 1.97 our $delay_run = sub {
166 root 1.55 (shift @DELAY_QUEUE or return undef $DELAY_TIMER)->() while 1;
167 root 1.97 };
168 root 1.42
169     sub delay($) {
170     push @DELAY_QUEUE, shift;
171 root 1.97 $DELAY_TIMER ||= AE::timer 0, 0, $delay_run;
172 root 1.42 }
173    
174 root 1.96 =item $AnyEvent::MP::Kernel::SRCNODE
175    
176     During execution of a message callback, this variable contains the node ID
177     of the origin node.
178    
179     The main use of this variable is for debugging output - there are probably
180     very few other cases where you need to know the source node ID.
181    
182     =cut
183    
184 root 1.1 sub _inject {
185 root 1.96 warn "RCV $SRCNODE -> " . eval { JSON::XS->new->encode (\@_) } . "\n" if TRACE && @_;#d#
186 root 1.1 &{ $PORT{+shift} or return };
187     }
188    
189 root 1.20 # this function adds a node-ref, so you can send stuff to it
190     # it is basically the central routing component.
191 root 1.1 sub add_node {
192 root 1.21 my ($node) = @_;
193 root 1.1
194 root 1.94 length $node
195     or Carp::croak "'undef' or the empty string are not valid node/port IDs";
196 root 1.93
197 root 1.71 $NODE{$node} ||= new AnyEvent::MP::Node::Remote $node
198 root 1.13 }
199    
200 root 1.1 sub snd(@) {
201 root 1.21 my ($nodeid, $portid) = split /#/, shift, 2;
202 root 1.1
203 root 1.86 warn "SND $nodeid <- " . eval { JSON::XS->new->encode ([$portid, @_]) } . "\n" if TRACE && @_;#d#
204 root 1.1
205 root 1.21 ($NODE{$nodeid} || add_node $nodeid)
206 root 1.2 ->{send} (["$portid", @_]);
207 root 1.1 }
208    
209 root 1.17 sub port_is_local($) {
210 root 1.21 my ($nodeid, undef) = split /#/, $_[0], 2;
211 root 1.17
212 root 1.106 $nodeid eq $NODE
213 root 1.17 }
214    
215 root 1.18 =item snd_to_func $node, $func, @args
216 root 1.11
217 root 1.21 Expects a node ID and a name of a function. Asynchronously tries to call
218 root 1.11 this function with the given arguments on that node.
219    
220 root 1.20 This function can be used to implement C<spawn>-like interfaces.
221 root 1.11
222     =cut
223    
224 root 1.18 sub snd_to_func($$;@) {
225 root 1.21 my $nodeid = shift;
226 root 1.11
227 root 1.41 # on $NODE, we artificially delay... (for spawn)
228     # this is very ugly - maybe we should simply delay ALL messages,
229     # to avoid deep recursion issues. but that's so... slow...
230 root 1.45 $AnyEvent::MP::Node::Self::DELAY = 1
231     if $nodeid ne $NODE;
232    
233 root 1.71 ($NODE{$nodeid} || add_node $nodeid)->{send} (["", @_]);
234 root 1.11 }
235    
236 root 1.18 =item snd_on $node, @msg
237    
238     Executes C<snd> with the given C<@msg> (which must include the destination
239     port) on the given node.
240    
241     =cut
242    
243     sub snd_on($@) {
244     my $node = shift;
245     snd $node, snd => @_;
246     }
247    
248 root 1.29 =item eval_on $node, $string[, @reply]
249 root 1.18
250 root 1.29 Evaluates the given string as Perl expression on the given node. When
251     @reply is specified, then it is used to construct a reply message with
252     C<"$@"> and any results from the eval appended.
253 root 1.18
254     =cut
255    
256 root 1.29 sub eval_on($$;@) {
257 root 1.18 my $node = shift;
258     snd $node, eval => @_;
259     }
260    
261 root 1.1 sub kil(@) {
262 root 1.21 my ($nodeid, $portid) = split /#/, shift, 2;
263 root 1.1
264     length $portid
265 root 1.21 or Carp::croak "$nodeid#$portid: killing a node port is not allowed, caught";
266 root 1.1
267 root 1.21 ($NODE{$nodeid} || add_node $nodeid)
268 root 1.1 ->kill ("$portid", @_);
269     }
270    
271     #############################################################################
272 root 1.6 # node monitoring and info
273 root 1.3
274 root 1.106 =item $bool = node_is_up $nodeid
275 root 1.13
276     Returns true if the given node is "up", that is, the kernel thinks it has
277     a working connection to it.
278    
279 root 1.95 If the node is up, returns C<1>. If the node is currently connecting or
280     otherwise known but not connected, returns C<0>. If nothing is known about
281     the node, returns C<undef>.
282 root 1.13
283     =cut
284    
285     sub node_is_up($) {
286     ($NODE{$_[0]} or return)->{transport}
287     ? 1 : 0
288     }
289    
290 root 1.106 =item @nodes = up_nodes
291 root 1.3
292 root 1.26 Return the node IDs of all nodes that are currently connected (excluding
293     the node itself).
294 root 1.3
295     =cut
296    
297 root 1.49 sub up_nodes() {
298 root 1.26 map $_->{id}, grep $_->{transport}, values %NODE
299 root 1.3 }
300    
301 root 1.21 =item $guard = mon_nodes $callback->($nodeid, $is_up, @reason)
302 root 1.3
303 root 1.27 Registers a callback that is called each time a node goes up (a connection
304     is established) or down (the connection is lost).
305 root 1.3
306     Node up messages can only be followed by node down messages for the same
307     node, and vice versa.
308    
309 root 1.71 Note that monitoring a node is usually better done by monitoring its node
310 root 1.27 port. This function is mainly of interest to modules that are concerned
311     about the network topology and low-level connection handling.
312    
313     Callbacks I<must not> block and I<should not> send any messages.
314    
315     The function returns an optional guard which can be used to unregister
316 root 1.3 the monitoring callback again.
317    
318 root 1.46 Example: make sure you call function C<newnode> for all nodes that are up
319     or go up (and down).
320    
321     newnode $_, 1 for up_nodes;
322     mon_nodes \&newnode;
323    
324 root 1.3 =cut
325    
326     our %MON_NODES;
327    
328     sub mon_nodes($) {
329     my ($cb) = @_;
330    
331     $MON_NODES{$cb+0} = $cb;
332    
333 root 1.90 defined wantarray
334     and Guard::guard { delete $MON_NODES{$cb+0} }
335 root 1.3 }
336    
337     sub _inject_nodeevent($$;@) {
338 root 1.16 my ($node, $up, @reason) = @_;
339 root 1.3
340     for my $cb (values %MON_NODES) {
341 root 1.21 eval { $cb->($node->{id}, $up, @reason); 1 }
342 root 1.98 or AE::log die => $@;
343 root 1.3 }
344 root 1.16
345 root 1.102 AE::log 7 => "$node->{id} is " . ($up ? "up." : "down (@reason).");
346 root 1.3 }
347    
348     #############################################################################
349 root 1.1 # self node code
350    
351 root 1.67 sub _kill {
352     my $port = shift;
353    
354     delete $PORT{$port}
355     or return; # killing nonexistent ports is O.K.
356     delete $PORT_DATA{$port};
357    
358     my $mon = delete $LMON{$port}
359     or !@_
360 root 1.98 or AE::log die => "unmonitored local port $port died with reason: @_";
361 root 1.67
362     $_->(@_) for values %$mon;
363     }
364    
365     sub _monitor {
366     return $_[2](no_such_port => "cannot monitor nonexistent port", "$NODE#$_[1]")
367     unless exists $PORT{$_[1]};
368    
369     $LMON{$_[1]}{$_[2]+0} = $_[2];
370     }
371    
372     sub _unmonitor {
373 root 1.68 delete $LMON{$_[1]}{$_[2]+0}
374     if exists $LMON{$_[1]};
375 root 1.67 }
376    
377 root 1.83 sub _secure_check {
378 root 1.104 $SECURE
379 root 1.105 and die "remote execution not allowed\n";
380 root 1.83 }
381    
382 root 1.79 our %NODE_REQ = (
383 root 1.1 # internal services
384    
385     # monitoring
386 root 1.65 mon0 => sub { # stop monitoring a port for another node
387 root 1.1 my $portid = shift;
388 root 1.96 _unmonitor undef, $portid, delete $NODE{$SRCNODE}{rmon}{$portid};
389 root 1.1 },
390 root 1.65 mon1 => sub { # start monitoring a port for another node
391 root 1.1 my $portid = shift;
392 root 1.96 Scalar::Util::weaken (my $node = $NODE{$SRCNODE});
393 root 1.67 _monitor undef, $portid, $node->{rmon}{$portid} = sub {
394 root 1.58 delete $node->{rmon}{$portid};
395 root 1.65 $node->send (["", kil0 => $portid, @_])
396 root 1.59 if $node && $node->{transport};
397 root 1.67 };
398 root 1.1 },
399 root 1.65 # another node has killed a monitored port
400     kil0 => sub {
401 root 1.96 my $cbs = delete $NODE{$SRCNODE}{lmon}{+shift}
402 root 1.1 or return;
403    
404     $_->(@_) for @$cbs;
405     },
406    
407 root 1.18 # "public" services - not actually public
408 root 1.1
409 root 1.65 # another node wants to kill a local port
410 root 1.66 kil => \&_kill,
411 root 1.65
412 root 1.1 # relay message to another node / generic echo
413 root 1.88 snd => sub {
414     &_secure_check;
415     &snd
416 root 1.1 },
417    
418 root 1.30 # random utilities
419 root 1.1 eval => sub {
420 root 1.83 &_secure_check;
421 root 1.50 my @res = do { package main; eval shift };
422 root 1.1 snd @_, "$@", @res if @_;
423     },
424     time => sub {
425 root 1.88 &_secure_check;
426 root 1.76 snd @_, AE::now;
427 root 1.1 },
428     devnull => sub {
429     #
430     },
431 root 1.15 "" => sub {
432 root 1.27 # empty messages are keepalives or similar devnull-applications
433 root 1.15 },
434 root 1.1 );
435    
436 root 1.104 # the node port
437 root 1.106 $NODE{$NODE} = new AnyEvent::MP::Node::Self $NODE;
438 root 1.1 $PORT{""} = sub {
439     my $tag = shift;
440 root 1.83 eval { &{ $NODE_REQ{$tag} ||= do { &_secure_check; load_func $tag } } };
441 root 1.98 AE::log die => "error processing node message from $SRCNODE: $@" if $@;
442 root 1.1 };
443    
444 root 1.84 our $NPROTO = 1;
445    
446     # tell everybody who connects our nproto
447     push @AnyEvent::MP::Transport::HOOK_GREET, sub {
448     $_[0]{local_greeting}{nproto} = $NPROTO;
449     };
450    
451 root 1.69 #############################################################################
452 root 1.71 # seed management, try to keep connections to all seeds at all times
453 root 1.69
454 root 1.71 our %SEED_NODE; # seed ID => node ID|undef
455     our %NODE_SEED; # map node ID to seed ID
456 root 1.69 our %SEED_CONNECT; # $seed => transport_connector | 1=connected | 2=connecting
457     our $SEED_WATCHER;
458 root 1.71 our $SEED_RETRY;
459 root 1.69
460     sub seed_connect {
461     my ($seed) = @_;
462    
463     my ($host, $port) = AnyEvent::Socket::parse_hostport $seed
464     or Carp::croak "$seed: unparsable seed address";
465    
466 root 1.98 AE::log 9 => "trying connect to seed node $seed.";
467 root 1.69
468 root 1.71 $SEED_CONNECT{$seed} ||= AnyEvent::MP::Transport::mp_connect
469     $host, $port,
470     on_greeted => sub {
471     # called after receiving remote greeting, learn remote node name
472    
473     # we rely on untrusted data here (the remote node name) this is
474     # hopefully ok, as this can at most be used for DOSing, which is easy
475     # when you can do MITM anyway.
476    
477     # if we connect to ourselves, nuke this seed, but make sure we act like a seed
478     if ($_[0]{remote_node} eq $AnyEvent::MP::Kernel::NODE) {
479 root 1.72 require AnyEvent::MP::Global; # every seed becomes a global node currently
480 root 1.71 delete $SEED_NODE{$seed};
481     } else {
482     $SEED_NODE{$seed} = $_[0]{remote_node};
483     $NODE_SEED{$_[0]{remote_node}} = $seed;
484 root 1.93 # also start global service, if not running
485     # we need to check here in addition to the mon_nodes below
486     # because we might only learn late that a node is a seed
487     # and then we might already be connected
488     snd $_[0]{remote_node}, "g_slave"
489     unless $_[0]{remote_greeting}{global};
490 root 1.71 }
491     },
492 root 1.93 sub {
493 root 1.71 delete $SEED_CONNECT{$seed};
494     }
495 root 1.69 ;
496     }
497    
498     sub seed_all {
499 root 1.93 my @seeds = grep
500     !exists $SEED_CONNECT{$_}
501     && !(defined $SEED_NODE{$_} && node_is_up $SEED_NODE{$_}),
502     keys %SEED_NODE;
503 root 1.69
504     if (@seeds) {
505 root 1.70 # start connection attempt for every seed we are not connected to yet
506 root 1.69 seed_connect $_
507     for @seeds;
508 root 1.71
509     $SEED_RETRY = $SEED_RETRY * 2 + rand;
510     $SEED_RETRY = $AnyEvent::MP::Kernel::CONFIG->{monitor_timeout}
511     if $SEED_RETRY > $AnyEvent::MP::Kernel::CONFIG->{monitor_timeout};
512    
513     $SEED_WATCHER = AE::timer $SEED_RETRY, 0, \&seed_all;
514    
515 root 1.69 } else {
516 root 1.71 # all seeds connected or connecting, no need to restart timer
517 root 1.69 undef $SEED_WATCHER;
518     }
519     }
520    
521     sub seed_again {
522 root 1.71 $SEED_RETRY = 1;
523     $SEED_WATCHER ||= AE::timer 1, 0, \&seed_all;
524 root 1.69 }
525    
526     # sets new seed list, starts connecting
527     sub set_seeds(@) {
528     %SEED_NODE = ();
529 root 1.71 %NODE_SEED = ();
530     %SEED_CONNECT = ();
531    
532 root 1.69 @SEED_NODE{@_} = ();
533    
534 root 1.71 seed_all;
535     }
536    
537     mon_nodes sub {
538 root 1.93 return unless exists $NODE_SEED{$_[0]};
539    
540     if ($_[1]) {
541     # each time a connection to a seed node goes up, make
542     # sure it runs the global service.
543     snd $_[0], "g_slave"
544     unless $NODE{$_[0]}{transport}{remote_greeting}{global};
545     } else {
546     # if we lost the connection to a seed node, make sure we are seeding
547     seed_again;
548     }
549 root 1.71 };
550    
551     #############################################################################
552     # talk with/to global nodes
553    
554 root 1.72 # protocol messages:
555     #
556 root 1.73 # sent by all slave nodes (slave to master)
557     # g_slave database - make other global node master of the sender
558 root 1.72 #
559 root 1.73 # sent by any node to global nodes
560     # g_set database - set whole database
561 root 1.89 # g_upd family set del - update single family
562 root 1.73 # g_del family key - delete key from database
563     # g_get family key reply... - send reply with data
564     #
565     # send by global nodes
566     # g_global - node became global, similar to global=1 greeting
567     #
568     # database families
569     # "'l" -> node -> listeners
570     # "'g" -> node -> undef
571     # ...
572 root 1.72 #
573    
574 root 1.73 # used on all nodes:
575 root 1.88 our $MASTER; # the global node we bind ourselves to
576 root 1.78 our $MASTER_MON;
577     our %LOCAL_DB; # this node database
578    
579     our %GLOBAL_DB; # all local databases, merged - empty on non-global nodes
580 root 1.71
581 root 1.84 our $GPROTO = 1;
582    
583     # tell everybody who connects our nproto
584     push @AnyEvent::MP::Transport::HOOK_GREET, sub {
585     $_[0]{local_greeting}{gproto} = $GPROTO;
586     };
587    
588 root 1.73 #############################################################################
589     # master selection
590    
591     # master requests
592     our %GLOBAL_REQ; # $id => \@req
593 root 1.71
594 root 1.74 sub global_req_add {
595 root 1.80 my ($id, $req) = @_;
596 root 1.71
597 root 1.74 return if exists $GLOBAL_REQ{$id};
598    
599 root 1.80 $GLOBAL_REQ{$id} = $req;
600 root 1.71
601 root 1.80 snd $MASTER, @$req
602 root 1.73 if $MASTER;
603 root 1.74 }
604 root 1.71
605 root 1.74 sub global_req_del {
606     delete $GLOBAL_REQ{$_[0]};
607     }
608    
609 root 1.88 #################################
610     # master rpc
611    
612     our %GLOBAL_RES;
613     our $GLOBAL_RES_ID = "a";
614    
615     sub global_call {
616     my $id = ++$GLOBAL_RES_ID;
617     $GLOBAL_RES{$id} = pop;
618     global_req_add $id, [@_, $id];
619     }
620    
621     $NODE_REQ{g_reply} = sub {
622     my $id = shift;
623     global_req_del $id;
624     my $cb = delete $GLOBAL_RES{$id}
625     or return;
626     &$cb
627     };
628    
629     #################################
630    
631 root 1.74 sub g_find {
632 root 1.80 global_req_add "g_find $_[0]", [g_find => $_[0]];
633 root 1.73 }
634 root 1.71
635 root 1.73 # reply for g_find started in Node.pm
636 root 1.79 $NODE_REQ{g_found} = sub {
637 root 1.74 global_req_del "g_find $_[0]";
638    
639 root 1.73 my $node = $NODE{$_[0]} or return;
640 root 1.71
641 root 1.79 $node->connect_to ($_[1]);
642 root 1.71 };
643    
644 root 1.73 sub master_set {
645     $MASTER = $_[0];
646 root 1.71
647 root 1.73 snd $MASTER, g_slave => \%LOCAL_DB;
648 root 1.71
649 root 1.73 # (re-)send queued requests
650     snd $MASTER, @$_
651     for values %GLOBAL_REQ;
652     }
653 root 1.71
654 root 1.72 sub master_search {
655 root 1.73 #TODO: should also look for other global nodes, but we don't know them #d#
656     for (keys %NODE_SEED) {
657 root 1.72 if (node_is_up $_) {
658     master_set $_;
659     return;
660 root 1.71 }
661 root 1.72 }
662 root 1.71
663 root 1.78 $MASTER_MON = mon_nodes sub {
664 root 1.72 return unless $_[1]; # we are only interested in node-ups
665     return unless $NODE_SEED{$_[0]}; # we are only interested in seed nodes
666 root 1.71
667 root 1.72 master_set $_[0];
668 root 1.71
669 root 1.78 $MASTER_MON = mon_nodes sub {
670 root 1.72 if ($_[0] eq $MASTER && !$_[1]) {
671     undef $MASTER;
672     master_search ();
673     }
674 root 1.71 };
675 root 1.72 };
676 root 1.71 }
677    
678 root 1.73 # other node wants to make us the master
679 root 1.79 $NODE_REQ{g_slave} = sub {
680 root 1.73 my ($db) = @_;
681    
682 root 1.80 # load global module and redo the request
683 root 1.73 require AnyEvent::MP::Global;
684 root 1.80 &{ $NODE_REQ{g_slave} }
685 root 1.71 };
686    
687 root 1.73 #############################################################################
688 root 1.79 # local database operations
689 root 1.71
690 root 1.79 # local database management
691 root 1.88
692 root 1.87 sub db_set($$;$) {
693 root 1.97 my ($family, $subkey) = @_;
694    
695 root 1.89 # if (ref $_[1]) {
696     # # bulk
697     # my @del = grep exists $LOCAL_DB{$_[0]}{$_}, keys ${ $_[1] };
698     # $LOCAL_DB{$_[0]} = $_[1];
699     # snd $MASTER, g_upd => $_[0] => $_[1], \@del
700     # if defined $MASTER;
701     # } else {
702     # single-key
703 root 1.97 $LOCAL_DB{$family}{$subkey} = $_[2];
704     snd $MASTER, g_upd => $family => { $subkey => $_[2] }
705 root 1.89 if defined $MASTER;
706     # }
707 root 1.97
708     defined wantarray
709     and Guard::guard { db_del $family => $subkey }
710 root 1.79 }
711    
712 root 1.89 sub db_del($@) {
713     my $family = shift;
714    
715     delete @{ $LOCAL_DB{$family} }{@_};
716     snd $MASTER, g_upd => $family => undef, \@_
717 root 1.79 if defined $MASTER;
718     }
719    
720 root 1.88 # database query
721    
722     sub db_family {
723     my ($family, $cb) = @_;
724     global_call g_db_family => $family, $cb;
725     }
726    
727     sub db_keys {
728     my ($family, $cb) = @_;
729     global_call g_db_keys => $family, $cb;
730     }
731    
732     sub db_values {
733     my ($family, $cb) = @_;
734     global_call g_db_values => $family, $cb;
735 root 1.80 }
736    
737 root 1.88 # database monitoring
738 root 1.80
739 root 1.81 our %LOCAL_MON; # f, reply
740     our %MON_DB; # f, k, value
741 root 1.80
742 root 1.81 sub db_mon($@) {
743 root 1.84 my ($family, $cb) = @_;
744 root 1.81
745 root 1.84 if (my $db = $MON_DB{$family}) {
746 root 1.89 # we already monitor, so create a "dummy" change event
747     # this is postponed, which might be too late (we could process
748     # change events), so disable the callback at first
749     $LOCAL_MON{$family}{$cb+0} = sub { };
750     AE::postpone {
751     return unless exists $LOCAL_MON{$family}{$cb+0}; # guard might have gone away already
752    
753     # set actual callback
754     $LOCAL_MON{$family}{$cb+0} = $cb;
755     $cb->($db, [keys %$db]);
756     };
757 root 1.81 } else {
758     # new monitor, request chg1 from upstream
759 root 1.89 $LOCAL_MON{$family}{$cb+0} = $cb;
760 root 1.81 global_req_add "mon1 $family" => [g_mon1 => $family];
761     $MON_DB{$family} = {};
762     }
763    
764 root 1.90 defined wantarray
765     and Guard::guard {
766     my $mon = $LOCAL_MON{$family};
767     delete $mon->{$cb+0};
768    
769     unless (%$mon) {
770     global_req_del "mon1 $family";
771    
772     # no global_req, because we don't care if we are not connected
773     snd $MASTER, g_mon0 => $family
774     if $MASTER;
775 root 1.80
776 root 1.90 delete $LOCAL_MON{$family};
777     delete $MON_DB{$family};
778     }
779 root 1.80 }
780     }
781    
782 root 1.82 # full update
783 root 1.80 $NODE_REQ{g_chg1} = sub {
784 root 1.96 return unless $SRCNODE eq $MASTER;
785 root 1.84 my ($f, $ndb) = @_;
786    
787     my $db = $MON_DB{$f};
788 root 1.89 my (@a, @c, @d);
789 root 1.81
790 root 1.82 # add or replace keys
791 root 1.84 while (my ($k, $v) = each %$ndb) {
792 root 1.89 exists $db->{$k}
793     ? push @c, $k
794     : push @a, $k;
795 root 1.84 $db->{$k} = $v;
796 root 1.82 }
797 root 1.81
798 root 1.82 # delete keys that are no longer present
799 root 1.84 for (grep !exists $ndb->{$_}, keys %$db) {
800     delete $db->{$_};
801 root 1.89 push @d, $_;
802 root 1.81 }
803 root 1.84
804 root 1.89 $_->($db, \@a, \@c, \@d)
805 root 1.84 for values %{ $LOCAL_MON{$_[0]} };
806 root 1.80 };
807    
808 root 1.82 # incremental update
809 root 1.84 $NODE_REQ{g_chg2} = sub {
810 root 1.96 return unless $SRCNODE eq $MASTER;
811 root 1.89 my ($family, $set, $del) = @_;
812    
813     my $db = $MON_DB{$family};
814 root 1.84
815 root 1.89 my (@a, @c);
816 root 1.84
817 root 1.89 while (my ($k, $v) = each %$set) {
818     exists $db->{$k}
819     ? push @c, $k
820     : push @a, $k;
821     $db->{$k} = $v;
822     }
823    
824     delete @$db{@$del};
825    
826     $_->($db, \@a, \@c, $del)
827     for values %{ $LOCAL_MON{$family} };
828 root 1.84 };
829 root 1.80
830 root 1.69 #############################################################################
831     # configure
832    
833 root 1.81 sub nodename {
834 root 1.69 require POSIX;
835     (POSIX::uname ())[1]
836     }
837    
838     sub _resolve($) {
839     my ($nodeid) = @_;
840    
841     my $cv = AE::cv;
842     my @res;
843    
844     $cv->begin (sub {
845     my %seen;
846     my @refs;
847     for (sort { $a->[0] <=> $b->[0] } @res) {
848     push @refs, $_->[1] unless $seen{$_->[1]}++
849     }
850     shift->send (@refs);
851     });
852    
853     my $idx;
854     for my $t (split /,/, $nodeid) {
855     my $pri = ++$idx;
856    
857 root 1.81 $t = length $t ? nodename . ":$t" : nodename
858 root 1.69 if $t =~ /^\d*$/;
859    
860     my ($host, $port) = AnyEvent::Socket::parse_hostport $t, 0
861     or Carp::croak "$t: unparsable transport descriptor";
862    
863     $port = "0" if $port eq "*";
864    
865     if ($host eq "*") {
866     $cv->begin;
867    
868 root 1.103 my $get_addr = sub {
869     my @addr;
870    
871     require Net::Interface;
872    
873     # Net::Interface hangs on some systems, so hope for the best
874     local $SIG{ALRM} = 'DEFAULT';
875     alarm 2;
876    
877     for my $if (Net::Interface->interfaces) {
878     # we statically lower-prioritise ipv6 here, TODO :()
879     for $_ ($if->address (Net::Interface::AF_INET ())) {
880     next if /^\x7f/; # skip localhost etc.
881     push @addr, $_;
882 root 1.69 }
883 root 1.103 for ($if->address (Net::Interface::AF_INET6 ())) {
884     #next if $if->scope ($_) <= 2;
885     next unless /^[\x20-\x3f\xfc\xfd]/; # global unicast, site-local unicast
886     push @addr, $_;
887 root 1.69 }
888     }
889 root 1.103
890     alarm 0;
891    
892     @addr
893     };
894    
895     my @addr;
896    
897     if (AnyEvent::WIN32) {
898     @addr = $get_addr->();
899     } else {
900     # use a child process, as Net::Interface is big, and we need it only once.
901    
902     pipe my $r, my $w
903     or die "pipe: $!";
904    
905     if (fork eq 0) {
906     close $r;
907     syswrite $w, pack "(C/a*)*", $get_addr->();
908     require POSIX;
909     POSIX::_exit (0);
910     } else {
911     close $w;
912    
913     my $addr;
914    
915     1 while sysread $r, $addr, 1024, length $addr;
916    
917     @addr = unpack "(C/a*)*", $addr;
918     }
919     }
920    
921     for my $ip (@addr) {
922     push @res, [
923     $pri += 1e-5,
924     AnyEvent::Socket::format_hostport AnyEvent::Socket::format_address $ip, $port
925     ];
926     }
927     $cv->end;
928 root 1.69 } else {
929     $cv->begin;
930     AnyEvent::Socket::resolve_sockaddr $host, $port, "tcp", 0, undef, sub {
931     for (@_) {
932     my ($service, $host) = AnyEvent::Socket::unpack_sockaddr $_->[3];
933     push @res, [
934     $pri += 1e-5,
935     AnyEvent::Socket::format_hostport AnyEvent::Socket::format_address $host, $service
936     ];
937     }
938     $cv->end;
939     };
940     }
941     }
942    
943     $cv->end;
944    
945     $cv
946     }
947    
948     sub configure(@) {
949     unshift @_, "profile" if @_ & 1;
950     my (%kv) = @_;
951    
952     my $profile = delete $kv{profile};
953    
954 root 1.81 $profile = nodename
955 root 1.69 unless defined $profile;
956    
957     $CONFIG = AnyEvent::MP::Config::find_profile $profile, %kv;
958    
959 root 1.104 $SECURE = $CONFIG->{secure};
960 root 1.83
961 root 1.72 my $node = exists $CONFIG->{nodeid} ? $CONFIG->{nodeid} : "$profile/";
962 root 1.69
963     $node or Carp::croak "$node: illegal node ID (see AnyEvent::MP manpage for syntax)\n";
964    
965 root 1.106 my $node_obj = delete $NODE{$NODE}; # we do not support doing stuff before configure
966    
967 root 1.72 $NODE = $node;
968 root 1.77
969 root 1.81 $NODE =~ s/%n/nodename/ge;
970    
971     if ($NODE =~ s!(?:(?<=/)$|%u)!$RUNIQ!g) {
972 root 1.77 # nodes with randomised node names do not need randomised port names
973     $UNIQ = "";
974     }
975 root 1.69
976 root 1.106 $node_obj->{id} = $NODE;
977     $NODE{$NODE} = $node_obj;
978 root 1.69
979     my $seeds = $CONFIG->{seeds};
980     my $binds = $CONFIG->{binds};
981    
982     $binds ||= ["*"];
983    
984 root 1.98 AE::log 8 => "node $NODE starting up.";
985 root 1.69
986 root 1.85 $BINDS = [];
987     %BINDS = ();
988 root 1.69
989     for (map _resolve $_, @$binds) {
990     for my $bind ($_->recv) {
991     my ($host, $port) = AnyEvent::Socket::parse_hostport $bind
992     or Carp::croak "$bind: unparsable local bind address";
993    
994     my $listener = AnyEvent::MP::Transport::mp_server
995     $host,
996     $port,
997     prepare => sub {
998     my (undef, $host, $port) = @_;
999     $bind = AnyEvent::Socket::format_hostport $host, $port;
1000     0
1001     },
1002     ;
1003 root 1.85 $BINDS{$bind} = $listener;
1004     push @$BINDS, $bind;
1005 root 1.69 }
1006     }
1007    
1008 root 1.85 db_set "'l" => $NODE => $BINDS;
1009 root 1.73
1010 root 1.98 AE::log 8 => "node listens on [@$BINDS].";
1011 root 1.69
1012     # connect to all seednodes
1013     set_seeds map $_->recv, map _resolve $_, @$seeds;
1014    
1015 root 1.73 master_search;
1016    
1017 root 1.103 # save gobs of memory
1018     undef &_resolve;
1019     *configure = sub (@){ };
1020    
1021 root 1.69 for (@{ $CONFIG->{services} }) {
1022     if (ref) {
1023     my ($func, @args) = @$_;
1024     (load_func $func)->(@args);
1025     } elsif (s/::$//) {
1026     eval "require $_";
1027     die $@ if $@;
1028     } else {
1029     (load_func $_)->();
1030     }
1031     }
1032 root 1.102
1033     eval "#line 1 \"(eval configure parameter)\"\n$CONFIG->{eval}";
1034     die "$@" if $@;
1035 root 1.69 }
1036    
1037 root 1.1 =back
1038    
1039 root 1.101 =head1 LOGGING
1040    
1041     AnyEvent::MP::Kernel logs high-level information about the current node,
1042     when nodes go up and down, and most runtime errors. It also logs some
1043     debugging and trace messages about network maintainance, such as seed
1044     connections and global node management.
1045    
1046 root 1.1 =head1 SEE ALSO
1047    
1048     L<AnyEvent::MP>.
1049    
1050     =head1 AUTHOR
1051    
1052     Marc Lehmann <schmorp@schmorp.de>
1053     http://home.schmorp.de/
1054    
1055     =cut
1056    
1057     1
1058