04
Jun 25

Calling Scala Objects from JRuby in Java21

I’ve just gone a bit doo-lally figuring out how to make this work, so in the interests of saving the next poor git, if you’re upgrading from JRE8 to JRE21, JRuby-Scala integration changed and broke a few things. There’s no documentation apart from a single poorly-written line in a wiki page on github about how the change works, so good luck figuring out how this translates to real life examples:

// Add scala aliases for apply/update to roughly equivalent Ruby names
if (name.equals("apply")) {
  addUnassignedAlias("[]", assignedNames, Priority.ALIAS); 

After 223 attempts to call the [] operator in various ways trying to get it to call apply() behind the scenes and about twenty other approaches to getting the reference needed, I had to dive into how Scala gets compiled for the JVM, find that when the Scala code is for a Scala Object with a companion class (so basically a class with an associated singleton object), Scala compiles that to two classes on the JVM, one with the original name from the Scala code and one with a postfix’d dollarsign symbol.

To get the singleton reference for a Scala Object when it has a companion class, you need to access the MODULE$ field in the class with the $ postfix, but you can’t just call for that in JRuby as $ is a special character for accessing global variables amongst other things, so you have to go round the houses a bit.

It goes from this in JRE8 (actual package and class names changed to protect the guilty):

Foo::Bar.apply.addListener(self)

to this mess in JRE21:

bob = 'Java::Foo::Bar$'.split('::').inject(Object) { |n, c| n.const_get c }
fred = bob.java_class.get_field('MODULE$').get(nil)
fred.apply.addListener(self)

But, it works, and fred.apply gets back to the Object so you can call addListener() and register your callback function (this is from some inhouse library code, which looks like this, but obviously changed to skirt NDAs):

object Bar {
  private val A = new BarImpl
  def apply(): Bar = A
}

private[kafka] class BarImpl extends Bar {
  //Lots of internal guff redacted including a list of listeners
  @PublicAPI
  def addListener(listener: ListenerThingy) {
    val exists = listeners.contains(listener)
    if (!exists) {
      listeners.add(listener)
      notifyOtherThings(listener)
    }
  }

Next time, I choose an easier language. Like, say, assembler.


02
Nov 17

fork()ing and fstat()ing in JRuby using FFI on linux

Sometimes, $DAYJOB can get kindof technical. For reasons I won’t go into here because NDA, the following axioms are true for this puzzle:

  • we have to work in JRuby
  • we are in a plugin within a larger framework providing a service
  • we have to restart the entire service
  • we don’t have a programmatic way to do so
  • we don’t want to rely on external artifacts and cron

Now, this isn’t the initial framing set of axioms you understand; this is what we’re facing into after a few weeks of trying everything else first.

So; obvious solution, system('/etc/init.d/ourService restart').
Except that JRuby doesn’t do system(). Or fork(), exec(), daemon(), or indeed any kind of process duplication I could find. Oh-kay, so we can write to a file, have a cronjob watch for the file and restart the service and delete the file if it finds it. Except that for Reasons (again, NDA), that’s not possible because we can’t rely on having access to cron on all platforms.

Okay. Can we cheat?

Well, yes… allegedly. We can use the Foreign Function Interface to bind to libc and access the functions behind JRuby’s back.

require 'ffi'

module Exec
   extend FFI::Library

   attach_function :my_exec, :execl, [:string, :string, :varargs], :int
   attach_function :fork, [], :int
end

vim1 = '/usr/bin/vim'
vim2 = 'vim'
if Exec.fork == 0
   Exec.my_exec vim1, vim2, :pointer, nil
end

Process.waitall

Of course, I’m intending to kill the thing that fires this off, so a little more care is needed. For a start, it’s not vim I’m playing with. So…

module LibC
   extend FFI::Library

   ffi_lib FFI::Library::LIBC

   # Timespec struct datatype
      class Timespec < FFI::Struct
      layout :tv_sec, :time_t,
      :tv_nsec, :long
   end

   # stat struct datatype
   # (see /usr/include/sys/stat.h and /usr/include/bits/stat.h)
   class Stat < FFI::Struct
      layout :st_dev, :dev_t,
             :st_ino, :ino_t,
             :st_nlink, :nlink_t,
             :st_mode, :mode_t,
             :st_uid, :uid_t,
             :st_gid, :gid_t,
             :__pad0, :int,
             :st_rdev, :dev_t,
             :st_size, :off_t,
             :st_blksize, :long,
             :st_blocks, :long,
             :st_atimespec, LibC::Timespec,
             :st_mtimespec, LibC::Timespec,
             :st_ctimespec, LibC::Timespec,
             :__unused0, :long,
             :__unused1, :long,
             :__unused2, :long,
             :__unused3, :long,
             :__unused4, :long
   end

   # Filetype mask
   S_IFMT = 0o170000

   # File types.
   S_IFIFO = 0o010000
   S_IFCHR = 0o020000
   S_IFDIR = 0o040000
   S_IFBLK = 0o060000
   S_IFREG = 0o100000
   S_IFLNK = 0o120000
   S_IFSOCK = 0o140000

   attach_function :getpid, [], :pid_t
   attach_function :setsid, [], :pid_t
   attach_function :fork, [], :int
   attach_function :execl, [:string, :string, :string, :varargs], :int
   attach_function :chdir, [:string], :int
   attach_function :close, [:int], :int
   attach_function :fstat, :__fxstat, [:int, :int, :pointer], :int
end

So that’s bound a bunch of libc functions for use in JRuby. But why __fxstat() instead of fstat()? Interesting detail; the stat() function family aren’t in libc, at least not on most modern linux platforms. They’re in a small static library (libc_unshared.a in centOS). There’s usually a linker directive that makes that transparent but here we’re acting behind the scenes so we don’t get that niceity so we directly access the underlying xstat() functions instead.

I need to close some network ports (or the restart goes badly because the child process inherits the ports’ file descriptors and someone didn’t set them to close on exec()). A small helper function is useful here:

# Helper function to check if a file descriptor is a socket or not
def socket?(fd)
   # data structure to hold the stat_t data
   stat = LibC::Stat.new

   # JRuby's IO object types can't seem get a grip on fd's inherited from
   # another process correctly in a forked child process so we have
   # to FFI out to libc.
   rc = LibC.fstat(0, fd, stat.pointer)
   if rc == -1
      errno = FFI::LastError.error
      false
   else
      # Now we do some bit twiddling. In Octal, no less.
      filetype = stat[:st_mode] & LibC::S_IFMT

      if filetype == LibC::S_IFSOCK
         true
      else
         false
      end
   end
rescue => e
   false
end

And now the actual restart function itself:

def restart
   pid = LibC.getpid
   rc = LibC.chdir('/')
   if rc == -1
      errno = FFI::LastError.error
      return errno
   end

   # close any open network sockets so the restart doesn't hang
   fds = Dir.entries("/proc/#{pid}/fd")
   fds.each do |fd|
      # skip . and .. which we pick up because of the /proc approach to
      # getting the list of file descriptors
      next if fd.to_i.zero?

      # skip any non-network socket file descriptors as they're not going to
      # cause us any issues and leaving them lets us log a little longer.
      next unless socket?(fd.to_i)

      # JRuby's IO objects can't get a handle on these fd's for some reason,
      # possibly because we're in a child process. So we use libc's close()
      rc = LibC.close(fd.to_i)
      next if rc.zero?
      errno = FFI::LastError.error
      return errno
   end

   # We're now ready to fork and restart the service
   rc = LibC.fork
   if rc == -1
      # If fork() failed we're probably in a world of hurt
      errno = FFI::LastError.error
      return errno
   elsif rc.zero?
      # We are now the daemon. We can't hang about (thanks to 
      # JRuby's un-thread-safe nature) so we immediately swap out our 
      # process image with that of the service restart script. 
      # This marks the end of execution of this thread and there is no return.
      LibC.execl '/etc/init.d/ourService', 'ourService', 'restart', :pointer, nil
   end
rescue => e
# Handle errors here (removed for clarity)
end

An interesting problem to solve, this one. And by “interesting” I mean “similar to learning how to pull teeth while only able to access the mouth via the nose”. But in case it’s of use to someone…


14
Jul 17

DerBlinkenLightzen!

8-pin microcontrollers are pretty neat things. I’d only used the larger PICs before, I didn’t see how these could be all that useful. And the 16C74s could do more… twenty years ago. These days, the 8-pin PICs have caught up with the older models in internal features, if not pincount. It’s really quite remarkable:

Same amount of program memory as the one from PICrat (actually, the modern version of the chip from PICrat, with the fancy flash memory), the CPU’s faster, it has more RAM, it has an internal EEPROM for persistent storage, same number of digital comms, one less CCP (but 32 fewer pins, so okay), half as many ADC channels (again, 32 fewer pins) but higher precision on those channels, same number of timers, a new comparator, a wider range of operating voltages and it takes less power (much less, it turns out).

Yes, it’s an 8-pin microcontroller; most of us forget that these shiny PCs are only a tiny, tiny, tiny fraction of the deployed computers in the world. These PICs probably outnumber them, and Moore’s law is in full swing on that side of the industry still.

So anyway, a fortnight or so back I’m watching a Big Clive video about making up a DMX cable tester with a PIC12F635…

…and I thought I’d like to play with a 12F so I bought a tube of them (they’re a lot less than a euro apiece) and a programmer. I splurged and got one of the higher-end 12Fs, the 12F1840, and then got to wrestling with resurrecting a 20-year-old toolchain.

GPASM, it turns out, has improved considerably, and there’s finally a CLI for the PICkit3 (I remember it coming out and the sheer daftness of the dropping of the PICkit2’s pk2cmd program that rendered the PICkit3 useless on launch to anyone running linux at the time). It only took a few hours of rereading manuals and poking things randomly before getting the flashing LED “hello world” program to work.

So, what’s next? Well, apart from the really neat WS2812 array in the vice there, which has a fun data protocol that’ll need a bit of programming work to hook up to (there is a C library for it, but I’ll have to rework that in assembly for my toolchain), and ignoring this idea I have for an interface between a sensor and an I2C link to a raspberry Pi that I have for another project, there’s also a little box dropped off by the postman to one side here. See, ebay’s one of those places where you can get fun toys for next to nothing if you look and are patient…

70 5×7 LED matrix displays for €17. 70! Mind you, I’ll need a few 74 series parts, some darlington arrays and a bit of wiring, but this could be fun…