From: Eric Wong <e@80x24.org>
To: Jean Boussier <jean.boussier@shopify.com>
Cc: unicorn-public@yhbt.net
Subject: Re: [PATCH] Master promotion with SIGURG (CoW optimization)
Date: Fri, 8 Jul 2022 00:13:56 +0000 [thread overview]
Message-ID: <20220708001356.M55364@dcvr> (raw)
In-Reply-To: <CANPRWbGArasDtbAej4LsCOGeYZSrNz87p5kLjG+x__jHAn-5ng@mail.gmail.com>
Jean Boussier <jean.boussier@shopify.com> wrote:
> >> It seems socketpair(..., SOCK_SEQPACKET) can be used for
> >> packetized bidirectional IPC, perhaps with send_io + recv_io iff
> >> necessary. There shouldn't be any need for new, fragile FS
> >> interactions.
>
> > Hum. I'm not familiar with socketpair, I'll have to read on it.
Please keep unicorn-public@yhbt.net Cc-ed for archival purposes
in case I'm arrested/kidnapped/killed. I'll quote and reply to
your other message separately, no need to resend.
> Could you clarify what you had in mind? Because I've read on socketpair, and
> from my understanding it's similar to anonymous pipes, as they have to
> be created prior
> to forking.
It's bidirectional, and you can use send_io/recv_io to send
pipes or any other IO objects across. SOCK_SEQPACKET
(sequenced-packet) also frees you from having to deal with
splitting on message boundaries or interleaving.
Ruby's send_io/recv_io does have a weakness in that it can't
send a useful string buffer along with the IO object, but a little
C can be used with sendmsg/recvmsg to make it happen (but
sendmsg + send_io is probably fine)
Ruby's socket ext just sends a 1 byte dummy padding buffer, but
it can allow whatever the socket buffers can handle (over 200k,
IIRC).
This should allow the a socketpair created from a master to
communicate bidirectionally with any of its kids.
If you don't feel like writing C, it might be easier to have
two socketpairs:
1) for string buffer messages
2) another exclusively for send_io/recv_io
> So if we start forking from a worker, I don't see how the new worker and
> the master can open a line of communication without exchanging on the
> filesystem.
If you're sending one end of a pipe from the worker to master:
m1, w1 = socketpair ...
m2, w2 = socketpair ...
w1.sendmsg("#$$-#{io.stat.ino}")
w2.send_io(io)
And the master will:
m1.recvmsg => "#{worker_pid}-#{inode}"
m2.recv_io => map inode of received IO to worker
One major weakness of the above is the non-atomicity if a worker
dies in between w1.sendmsg and w2.send_io; the master would
be hanging on m2.recv_io.
This is why having a bit of C to bundle the IO object with
sendmsg/recvmsg in a single syscall would be nice (along with
saving 2 FDs).
Fwiw, here's the equivalent send/recv wrappers I wrote for Perl
Inline::C a while back, but it should be easy to translate into
Ruby's C API.
In case you can't infer what the Sv* functions do by name,
pretty much all the Perl C API is documented in the perlapi(1)
manpage. Perl's API was easy enough for a doof like me to learn
after doing Ruby C for a bit...
/* allow send/recv of up to 10 IO objects at once */
#define SEND_FD_CAPA 10
#define SEND_FD_SPACE (SEND_FD_CAPA * sizeof(int))
union my_cmsg {
struct cmsghdr hdr;
char pad[sizeof(struct cmsghdr) + 16 + SEND_FD_SPACE];
};
static int sleep_wait(unsigned *tries, int err)
{
const struct timespec req = { 0, 100000000 }; /* 100ms */
switch (err) {
case ENOBUFS: case ENOMEM: case ETOOMANYREFS:
if (++*tries < 50) {
fprintf(stderr, "sleeping on sendmsg: %s (#%u)\n",
strerror(err), *tries);
nanosleep(&req, NULL);
return 1;
}
default:
return 0;
}
}
SV *send_foo(PerlIO *s, SV *svfds, SV *data, int flags)
{
struct msghdr msg = { 0 };
union my_cmsg cmsg = { 0 };
STRLEN dlen = 0;
struct iovec iov;
ssize_t sent;
AV *fds = (AV *)SvRV(svfds);
I32 i, nfds = av_len(fds) + 1;
int *fdp;
unsigned tries = 0;
if (SvOK(data)) {
iov.iov_base = SvPV(data, dlen);
iov.iov_len = dlen;
}
if (!dlen) { /* must be non-zero */
iov.iov_base = &msg.msg_namelen; /* whatever */
iov.iov_len = 1;
}
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (nfds) {
if (nfds > SEND_FD_CAPA) {
fprintf(stderr, "FIXME: bump SEND_FD_CAPA=%d\n", nfds);
nfds = SEND_FD_CAPA;
}
msg.msg_control = &cmsg.hdr;
msg.msg_controllen = CMSG_SPACE(nfds * sizeof(int));
cmsg.hdr.cmsg_level = SOL_SOCKET;
cmsg.hdr.cmsg_type = SCM_RIGHTS;
cmsg.hdr.cmsg_len = CMSG_LEN(nfds * sizeof(int));
fdp = (int *)CMSG_DATA(&cmsg.hdr);
for (i = 0; i < nfds; i++) {
SV **fd = av_fetch(fds, i, 0);
*fdp++ = SvIV(*fd);
}
}
do {
sent = sendmsg(PerlIO_fileno(s), &msg, flags);
} while (sent < 0 && sleep_wait(&tries, errno));
return sent >= 0 ? newSViv(sent) : &PL_sv_undef;
}
void recv_foo(PerlIO *s, SV *buf, STRLEN n)
{
union my_cmsg cmsg = { 0 };
struct msghdr msg = { 0 };
struct iovec iov;
ssize_t i;
Inline_Stack_Vars;
Inline_Stack_Reset;
if (!SvOK(buf))
sv_setpvn(buf, "", 0);
iov.iov_base = SvGROW(buf, n + 1);
iov.iov_len = n;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsg.hdr;
msg.msg_controllen = CMSG_SPACE(SEND_FD_SPACE);
i = recvmsg(PerlIO_fileno(s), &msg, 0);
if (i < 0)
Inline_Stack_Push(&PL_sv_undef);
else
SvCUR_set(buf, i);
if (i > 0 && cmsg.hdr.cmsg_level == SOL_SOCKET &&
cmsg.hdr.cmsg_type == SCM_RIGHTS) {
size_t len = cmsg.hdr.cmsg_len;
int *fdp = (int *)CMSG_DATA(&cmsg.hdr);
for (i = 0; CMSG_LEN((i + 1) * sizeof(int)) <= len; i++)
Inline_Stack_Push(sv_2mortal(newSViv(*fdp++)));
}
Inline_Stack_Done;
}
next prev parent reply other threads:[~2022-07-08 0:13 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-05 20:05 [PATCH] Master promotion with SIGURG (CoW optimization) Jean Boussier
2022-07-06 2:33 ` Eric Wong
2022-07-06 7:40 ` Jean Boussier
2022-07-07 10:23 ` Eric Wong
[not found] ` <CANPRWbHTNiEcYq5qhN6Kio8Wg9a+2gXmc2bAcB2oVw4LZv8rcw@mail.gmail.com>
[not found] ` <CANPRWbGArasDtbAej4LsCOGeYZSrNz87p5kLjG+x__jHAn-5ng@mail.gmail.com>
2022-07-08 0:13 ` Eric Wong [this message]
2022-07-08 0:30 ` Eric Wong
2022-07-08 6:22 ` Jean Boussier
2022-09-21 22:16 ` Eric Wong
2022-09-22 6:43 ` Jean Boussier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://yhbt.net/unicorn/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220708001356.M55364@dcvr \
--to=e@80x24.org \
--cc=jean.boussier@shopify.com \
--cc=unicorn-public@yhbt.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://yhbt.net/unicorn.git/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).