Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 12 Nov 2010 18:40:00 +0000
From:      Paul LeoNerd Evans <leonerd@leonerd.org.uk>
To:        freebsd-hackers@freebsd.org
Subject:   Managing userland data pointers in kqueue/kevent
Message-ID:  <20101112184000.GS11110@cel.leo>

next in thread | raw e-mail | index | archive | help

--IGH6DwWgOn9XAAl6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

I'm trying to build a high-level language wrapper around kqueue/kevent,
specifically, a Perl wrapper.

(In fact I am trying to fix this bug:
  http://rt.cpan.org/Public/Bug/Display.html?id=3D61481
)

My plan is to use the  void *udata  field of a kevent watcher to store a
pointer to some user-provided Perl data structure (an SV*), to associate
with the event. Typically this could be a code reference for an event
callback or similar, but the exact nature doesn't matter. It's a pointer
to a reference-counted data structure. SvREFCNT_dec(sv) is the function
used to decrement the reference counter.

To account for the fact that the kernel stores a pointer here, I'm
artificially increasing the reference count on the object, so that it
still remains alive even if the rest of the Perl code drops it, to rely
on getting it back out of the kernel in an individual kevent. At some
point when the kernel has finished looking after the event, this count
needs to be decreased again, so the structure can be freed.

I am having trouble trying to work out how to do this, or rather, when.
I have the following problems:

 * If the event was registered using EV_ONESHOT, when it gets fired the
   flags that come back in the event stucture do not include EV_ONESHOT.

 * Some events can only happen once, such as watching for EVFILT_PROC
   NOTE_EXIT events.

 * The kernel can silently drop watches, such as when the process calls
   close() on a filehandl with an EVFILT_READ or EVFILT_WRITE watch.

 * There doesn't seem to be a way to query that pointer back out of the
   kernel, in case the user code wants to EV_DELETE the watch.

These problems all mean that I never quite know when I ought to call
SvREFCNT_dec() on that pointer.

My current best-attack plan looks like the following:

 a) Store a structure in the  void *udata  that contains the actual SV*
    pointer and a flag to remember if the event had been installed as
    EV_ONESHOT (or remember if it was one of the event types that is
    oneshot anyway)

 b) Store an entire mapping in userland from filter+identity to pointer,
    so that if userland wants to EV_DELETE the watch early, it has the
    pointer to be able to drop it.

I can't think of a solution to the close() problem at all, though.

Part a of my solution seems OK (though I'd wonder why the flags back
=66rom the kernel don't contain EV_ONESHOT), but part b confuses me. I had
thought the point of kqueue/kevent is the O(1) nature of it, which is
among why the kernel is storing that  void *udata  pointer in the first
place. If I have to store a mapping from every filter+identity back to
my data pointer, why does the kernel store one at all? I could just
ignore the udata field and use my mapping for my own purposes.

Have I missed something here, then? I was hoping there'd be a nice way
for kernel to give me back those pointers so I can just decrement a
refcount on it, and have it reclaimed.=20

-----

I have an idea on a small addition to the kernel API that would make
this issue much simpler to manage, if there is nothing else.

By the addition of a new event flag, called something like
EV_FREEWATCH, the kernel can be told "tell userland whenever I am about
to drop this event watcher". So now, after a EV_ONESHOT or any of the
single events are fired, or when it gets EV_DELETEed, or when the kernel
itself drops because of a close() on a filehandle, it can fire an event
back up to userland with this flag, passing up the pointer.

Now, all userland has to do to correctly manage the memory is to always
set that flag on EV_ADD, and if the flag ever comes back in an event out
of the kernel, it can  SvREFCNT_dec(ev->udata);

--=20
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk
ICQ# 4135350       |  Registered Linux# 179460
http://www.leonerd.org.uk/

--IGH6DwWgOn9XAAl6
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)

iD8DBQFM3YoAvLS2TC8cBo0RAm+DAJ9oNPFVkflWdC9klRPPCbiZpLFojQCdGI47
mORHURNKRpQp/6fGEtXrFPM=
=uQQJ
-----END PGP SIGNATURE-----

--IGH6DwWgOn9XAAl6--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20101112184000.GS11110>