+ }
+ while ($full_stream && $n < $len) {
+ my $r = sysread($s2, $buf, $len - $n, $n) // croak "read: $!";
+ croak "read EOF after $n/$len bytes" if $r == 0;
+ $n = length($buf);
+ }
+ # Sereal dies on truncated data, Storable returns undef
+ my $args = ipc_thaw($buf) // die "thaw error on buffer of size: $n";
+ undef $buf;
+ my $sub = shift @$args;
+ eval { $self->$sub(@$args) };
+ warn "$$ $0 wq_worker: $@" if $@;
+ delete @$self{0..($nfd-1)};
+ $n;
+}
+
+sub wq_worker_loop ($$) {
+ my ($self, $bcast2) = @_;
+ my $wqw = PublicInbox::WQWorker->new($self, $self->{-wq_s2});
+ PublicInbox::WQWorker->new($self, $bcast2) if $bcast2;
+ PublicInbox::DS->SetPostLoopCallback(sub { $wqw->{sock} });
+ PublicInbox::DS->EventLoop;
+ PublicInbox::DS->Reset;
+}
+
+sub do_sock_stream { # via wq_io_do, for big requests
+ my ($self, $len) = @_;
+ recv_and_run($self, my $s2 = delete $self->{0}, $len, 1);
+}
+
+sub wq_broadcast {
+ my ($self, $sub, @args) = @_;
+ if (my $wkr = $self->{-wq_workers}) {
+ my $buf = ipc_freeze([$sub, @args]);
+ for my $bcast1 (values %$wkr) {
+ my $sock = $bcast1 // $self->{-wq_s1} // next;
+ send($sock, $buf, MSG_EOR) // croak "send: $!";
+ # XXX shouldn't have to deal with EMSGSIZE here...
+ }
+ } else {