Trying to define struct epoll_event

553 views
Skip to first unread message

David Hoyt

unread,
Jun 3, 2013, 7:36:44 PM6/3/13
to jna-...@googlegroups.com
Hello,

I'm trying to define the epoll_event structure for use with epoll_ctl() and epoll_wait(). Here's what I have:

/*
    typedef union epoll_data
    {
      void *ptr;
      int fd;
      uint32_t u32;
      uint64_t u64;
    } epoll_data_t;
   */
  public static class epoll_data_t extends Union {
    public Pointer ptr;
    public int fd;
    public int u32;
    public long u64;
    public epoll_data_t() {
      super();
    }
    public epoll_data_t(int fd_or_u32) {
      super();
      this.u32 = this.fd = fd_or_u32;
      setType(Integer.TYPE);
    }
    public epoll_data_t(long u64) {
      super();
      this.u64 = u64;
      setType(Long.TYPE);
    }
    public epoll_data_t(Pointer ptr) {
      super();
      this.ptr = ptr;
      setType(Pointer.class);
    }
    public static class ByReference extends epoll_data_t implements com.sun.jna.Structure.ByReference {

    };
    public static class ByValue extends epoll_data_t implements com.sun.jna.Structure.ByValue {

    };
  }

  /*
    struct epoll_event
    {
      uint32_t events; //Epoll events
      epoll_data_t data; //User data variable
    } __EPOLL_PACKED;
   */
  public static class epoll_event extends Structure {
    /// Epoll events
    public int events;
    /// User data variable
    public epoll_data_t data;
    public epoll_event() {
      super();
    }
    protected List getFieldOrder() {
      return Arrays.asList("events", "data");
    }
    public epoll_event(Pointer p) {
      super(p);
    }
    public epoll_event(int events, epoll_data_t data) {
      super();
      this.events = events;
      this.data = data;
    }
    public static class ByReference extends epoll_event implements Structure.ByReference {

    };
    public static class ByValue extends epoll_event implements Structure.ByValue {

    };
  }

Calling epoll_ctl() seems to be working fine, but when I call epoll_wait(), it crashes. Here's how the relevant functions are defined:

  //extern int epoll_create (int __size) __THROW;
  public static native int epoll_create(int size);

  //extern int epoll_create1 (int __flags) __THROW;
  public static native int epoll_create1(int flags);

  //extern int epoll_ctl (int __epfd, int __op, int __fd, struct epoll_event *__event) __THROW;
  public static native int epoll_ctl(int epfd, int op, int fd, epoll_event.ByReference event);
  public static native int epoll_ctl(int epfd, int op, int fd, Pointer event);

  //extern int epoll_wait (int __epfd, struct epoll_event *__events, int __maxevents, int __timeout);
  public static native int epoll_wait(int epfd, Pointer events, int maxevents, int timeout);

  //extern int epoll_pwait (int __epfd, struct epoll_event *__events, int __maxevents, int __timeout, const __sigset_t *__ss);
  public static native int epoll_pwait(int epfd, Pointer events, int maxevents, int timeout, Pointer ss);

epoll_wait() is requesting an array of epoll_event structs. So I create an array like this (please ignore the PinnableMemory class for now -- it just keeps a strong reference to an instance of Memory):

  public static Pointer createPointerToStructureArray(Class<? extends Structure> cls, int size) {
    final int size_of_struct = Native.getNativeSize(cls);
    final PinnableMemory m = PinnableMemory.pin(size * Pointer.SIZE);
    for(int i = 0; i < size; ++i) {
      m.setPointer(i * Pointer.SIZE, PinnableMemory.pin(size_of_struct));
    }
    return m;
  }

  public static Pointer itemInStructureArrayAtIndex(Pointer array, int index) {
    return array.getPointer(index * Pointer.SIZE);
  }

  public static void disposeStructureArray(Pointer ptr) {
    final PinnableMemory orig = PinnableMemory.unpin(ptr);
    final int size = (int)(orig.size() / (long)Pointer.SIZE);

    PinnableMemory entry;
    for(int i = 0; i < size; ++i) {
      if ((entry = PinnableMemory.unpin(orig.getPointer(i * Pointer.SIZE))) != null) {
        entry.dispose();
      }
    }
    orig.dispose();
  }

To put it all together, I use it like:

final int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
final epoll_event.ByReference event = new epoll_event.ByReference();
event.data.fd = <some other file descriptor>;
event.events = EPOLLOUT | EPOLLET | EPOLLHUP | EPOLLONESHOT;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, <some other file descriptor>, event);

...

final int MAX_EVENTS = 2;
final Pointer events = createPointerToStructureArray(epoll_event.class, MAX_EVENTS);
epoll_event event;
int ready_count = 0;
int i = 0;

try {
  while((ready_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1)) > 0) {
    System.out.println("Got " + ready_count + " events");

    for(i = 0; i < ready_count; ++i) {
      event = new epoll_event(itemInStructureArrayAtIndex(events, i));
      System.out.println("  FD: " + event.data.fd);
    }
  }
} finally {
  disposeStructureArray(events);
}
 
It crashes on "event = new epoll_event(itemInStructureArrayAtIndex(events, i));". The logs say:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f52503269a0, pid=6021, tid=139991306516224
#
# JRE version: 6.0_45-b06
# Java VM: Java HotSpot(TM) 64-Bit Server VM (20.45-b01 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [libc.so.6+0x14d9a0]  __nss_hosts_lookup+0x152a0
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

---------------  T H R E A D  ---------------

Current thread (0x00007f52481bf800):  JavaThread "pool-1-thread-1" [_thread_in_vm, id=6043, stack(0x00007f524408b000,0x00007f524418c000)]

siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x000000000000000c

Registers:
RAX=0x00000000ebc158f8, RBX=0x00007f525006f300, RCX=0x0000000000000000, RDX=0x0000000000000010
RSP=0x00007f5244189a98, RBP=0x00007f5244189b20, RSI=0x000000000000001c, RDI=0x00000000ebc15908
R8 =0x0000000000000010, R9 =0x00007f52503269a0, R10=0x00007f525006b200, R11=0x00007f5250365b80
R12=0x0000000000000010, R13=0x0000000000000000, R14=0x0000000000000010, R15=0x00007f5244189ab0
RIP=0x00007f52503269a0, EFLAGS=0x0000000000010207, CSGSFS=0x0000000000000033, ERR=0x0000000000000004
  TRAPNO=0x000000000000000e

Top of Stack: (sp=0x00007f5244189a98)
0x00007f5244189a98:   00007f524fae1457 00007f5248006dc0
0x00007f5244189aa8:   000000000000000c 00007f52481bf800
0x00007f5244189ab8:   0000000000000000 00007f52481bf800
0x00007f5244189ac8:   00000000bd441440 00007f52481bf800
0x00007f5244189ad8:   00007f52481bf800 00007f52481bf800
0x00007f5244189ae8:   00007f524faa61ce 00000000bd55396b
0x00007f5244189af8:   00000000bd441440 0000000000000000
0x00007f5244189b08:   00000000bd441440 00007f5244189c08
0x00007f5244189b18:   00007f52481bf800 00007f5244189b50
0x00007f5244189b28:   00007f51ff5b67df 0000000000000010
0x00007f5244189b38:   00007f5244189bf8 000000000000000c
0x00007f5244189b48:   00007f52481bf9d0 00007f5244189bc8
0x00007f5244189b58:   00007f5245016cd5 00007f5244189c50
0x00007f5244189b68:   00007f5244189be8 00007f5208004300
0x00007f5244189b78:   00007f5208004320 00007f52481bf800
0x00007f5244189b88:   00007f5244189b88 00000000bd441440
0x00007f5244189b98:   00007f5244189c08 00000000bd447b08
0x00007f5244189ba8:   0000000000000000 00000000bd441440
0x00007f5244189bb8:   0000000000000000 00007f5244189be8
0x00007f5244189bc8:   00007f5244189c50 00007f5245005b02
0x00007f5244189bd8:   00000000bd444128 00007f5245012298
0x00007f5244189be8:   0000000000000010 0000000000000000
0x00007f5244189bf8:   00000000ebc158e8 000000000000000c
0x00007f5244189c08:   00000000ebbf6670 00007f5244189c10
0x00007f5244189c18:   00000000bd55396b 00007f5244189c88
0x00007f5244189c28:   00000000bd5692c0 0000000000000000
0x00007f5244189c38:   00000000bd5539b8 00007f5244189be8
0x00007f5244189c48:   00007f5244189c60 00007f5244189cd0
0x00007f5244189c58:   00007f5245005b02 0000000000000010
0x00007f5244189c68:   0000000000000000 00000000ebc158e8
0x00007f5244189c78:   0000000000000000 00007f52481bf800
0x00007f5244189c88:   00000000ebbf6670 00007f5244189c90 

Instructions: (pc=0x00007f52503269a0)
0x00007f5250326980:   4c 8b 56 e8 4c 8b 5e f0 48 8b 56 f8 4c 89 57 e8
0x00007f5250326990:   4c 89 5f f0 48 89 57 f8 c3 0f 1f 80 00 00 00 00
0x00007f52503269a0:   4c 8b 5e f0 48 8b 56 f8 4c 89 5f f0 48 89 57 f8
0x00007f52503269b0:   c3 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 

Register to memory mapping:

RAX=0x00000000ebc158f8 is an unknown value
RBX=0x00007f525006f300: <offset 0xaa6300> in /opt/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so at 0x00007f524f5c9000
RCX=0x0000000000000000 is an unknown value
RDX=0x0000000000000010 is an unknown value
RSP=0x00007f5244189a98 is pointing into the stack for thread: 0x00007f52481bf800
RBP=0x00007f5244189b20 is pointing into the stack for thread: 0x00007f52481bf800
RSI=0x000000000000001c is an unknown value
RDI=0x00000000ebc15908 is an oop
java.lang.String 
 - klass: 'java/lang/String'
R8 =0x0000000000000010 is an unknown value
R9 =0x00007f52503269a0: <offset 0x14d9a0> in /lib/x86_64-linux-gnu/libc.so.6 at 0x00007f52501d9000
R10=0x00007f525006b200: <offset 0xaa2200> in /opt/java/jdk1.6.0_45/jre/lib/amd64/server/libjvm.so at 0x00007f524f5c9000
R11=0x00007f5250365b80: <offset 0x18cb80> in /lib/x86_64-linux-gnu/libc.so.6 at 0x00007f52501d9000
R12=0x0000000000000010 is an unknown value
R13=0x0000000000000000 is an unknown value
R14=0x0000000000000010 is an unknown value
R15=0x00007f5244189ab0 is pointing into the stack for thread: 0x00007f52481bf800


Stack: [0x00007f524408b000,0x00007f524418c000],  sp=0x00007f5244189a98,  free space=1018k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libc.so.6+0x14d9a0]  __nss_hosts_lookup+0x152a0
C  [jna117475868801589856.tmp+0x67df]  Java_com_sun_jna_Native_read__J_3BII+0xbf
j  com.sun.jna.Native.read(J[BII)V+0
j  com.sun.jna.Pointer.read(J[BII)V+11
j  com.sun.jna.Pointer.getByteArray(JI)[B+11
j  com.sun.jna.Structure.toString(IZZ)Ljava/lang/String;+739
j  com.sun.jna.Structure.toString(Z)Ljava/lang/String;+4
j  com.sun.jna.Structure.toString()Ljava/lang/String;+7
j  com.intellij.rt.debugger.BatchEvaluatorServer.evaluate([Ljava/lang/Object;)[Ljava/lang/Object;+24
v  ~StubRoutines::call_stub
V  [libjvm.so+0x4e14a0]  JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x1e0
V  [libjvm.so+0x711a39]  os::os_exception_wrapper(void (*)(JavaValue*, methodHandle*, JavaCallArguments*, Thread*), JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x19
V  [libjvm.so+0x4e12b5]  JavaCalls::call(JavaValue*, methodHandle, JavaCallArguments*, Thread*)+0x25
V  [libjvm.so+0x51d0ef]  jni_invoke_nonstatic(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*)+0x1af
V  [libjvm.so+0x4f91f1]  unsigned+0x171

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  com.sun.jna.Native.read(J[BII)V+0
j  com.sun.jna.Pointer.read(J[BII)V+11
j  com.sun.jna.Pointer.getByteArray(JI)[B+11
j  com.sun.jna.Structure.toString(IZZ)Ljava/lang/String;+739
j  com.sun.jna.Structure.toString(Z)Ljava/lang/String;+4
j  com.sun.jna.Structure.toString()Ljava/lang/String;+7
j  com.intellij.rt.debugger.BatchEvaluatorServer.evaluate([Ljava/lang/Object;)[Ljava/lang/Object;+24
v  ~StubRoutines::call_stub
j  jcommon.process.platform.unix.UnixProcessLauncherEPoll$1$1.doWork()V+90
j  jcommon.core.concurrent.BoundedAutoGrowThreadPool$1.run()V+14
j  java.lang.Thread.run()V+11
v  ~StubRoutines::call_stub

---------------  P R O C E S S  ---------------

Java Threads: ( => current thread )
  0x00007f52481e5000 JavaThread "pool-1-thread-3" [_thread_in_native, id=6045, stack(0x00007f51ff3ae000,0x00007f51ff4af000)]
  0x00007f52481ff000 JavaThread "pool-1-thread-2" [_thread_in_native, id=6044, stack(0x00007f51ff4af000,0x00007f51ff5b0000)]
=>0x00007f52481bf800 JavaThread "pool-1-thread-1" [_thread_in_vm, id=6043, stack(0x00007f524408b000,0x00007f524418c000)]
  0x00007f52480a7000 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=6040, stack(0x00007f5244cfd000,0x00007f5244dfe000)]
  0x00007f52480a4800 JavaThread "C2 CompilerThread1" daemon [_thread_blocked, id=6039, stack(0x00007f5244dfe000,0x00007f5244eff000)]
  0x00007f52480a2000 JavaThread "C2 CompilerThread0" daemon [_thread_blocked, id=6038, stack(0x00007f5244eff000,0x00007f5245000000)]
  0x00007f521c001800 JavaThread "JDWP Command Reader" daemon [_thread_in_native, id=6037, stack(0x00007f524c071000,0x00007f524c172000)]
  0x00007f524809f800 JavaThread "JDWP Event Helper Thread" daemon [_thread_blocked, id=6036, stack(0x00007f524c172000,0x00007f524c273000)]
  0x00007f524809c000 JavaThread "JDWP Transport Listener: dt_socket" daemon [_thread_blocked, id=6033, stack(0x00007f524c273000,0x00007f524c374000)]
  0x00007f5248090000 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=6032, stack(0x00007f524c478000,0x00007f524c579000)]
  0x00007f5248074800 JavaThread "Finalizer" daemon [_thread_blocked, id=6031, stack(0x00007f524c579000,0x00007f524c67a000)]
  0x00007f5248072800 JavaThread "Reference Handler" daemon [_thread_blocked, id=6030, stack(0x00007f524c67a000,0x00007f524c77b000)]
  0x00007f524800a800 JavaThread "main" [_thread_blocked, id=6022, stack(0x00007f524f1c3000,0x00007f524f2c4000)]

Other Threads:
  0x00007f524806c000 VMThread [stack: 0x00007f524c77b000,0x00007f524c87c000] [id=6029]
  0x00007f52480b1800 WatcherThread [stack: 0x00007f5244bfc000,0x00007f5244cfd000] [id=6041]

VM state:not at safepoint (normal execution)


I'm unsure if I've defined the structs correctly or if there's something else afoot. I'm running this on Ubuntu 13.04 x64 w/ Oracle java 1.6u45 and JNA 3.5.1. I'd appreciate any help  you can provide!

David Hoyt

unread,
Jun 3, 2013, 9:29:25 PM6/3/13
to jna-...@googlegroups.com
Okay, nevermind -- I figured it out. For one, the array wasn't being allocated contiguously. It was an easy fix from there. I ended up with:

final int MAX_EVENTS = 1;

//Create a region of memory analogous to a native array where the elements are
//contiguously placed.
final int size_of_struct = new epoll_event().size();
final PinnableMemory ptr = new PinnableMemory(MAX_EVENTS * size_of_struct);
final epoll_event event = new epoll_event();

int ready_count = 0;
int i = 0;

try {
  while((ready_count = epoll_wait(epoll_fd, ptr, MAX_EVENTS, -1)) > 0) {
    int err = Native.getLastError();
    System.out.println("GOT " + ready_count + " events");
    System.out.println("ERR " + err);

    for(i = 0; i < ready_count; ++i) {
      event.reuse(ptr, size_of_struct * i);

      System.out.println("  FD: " + event.data.fd);
      System.out.println("  Events: " + event.events);
    }
  }
} finally {
  ptr.dispose();
}

Timothy Wall

unread,
Jun 4, 2013, 1:17:21 AM6/4/13
to jna-...@googlegroups.com
I don't understand why you'd need "PinnableMemory" in this context; if you did "new epoll_event().toArray(MAX_EVENTS)" the memory will be strongly referenced as long as you hold a reference to the first array element.

If for some reason you don't want to keep a reference to the structure itself, you should at least initialize the structure with your PinnableMemory instance, and then call toArray() on it.

Your epoll_wait() signature could just as well accept an array of epoll_event as its first argument; JNA would then be able to automatically synch the Java data structures before and after the native function call. If the events are effectively read-only, you can optimize the call by turning off auto-write, and you'd still get the benefit of auto-read, and not have to write your own synching functions (i.e. "epoll_event.reuse(Pointer)").
> --
> You received this message because you are subscribed to the Google Groups "Java Native Access" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

David Hoyt

unread,
Jun 4, 2013, 1:36:01 PM6/4/13
to jna-...@googlegroups.com
I used PinnableMemory (which is my own class) b/c it exposes the normally protected .dispose() method allowing me to free out the native memory ASAP (w/o waiting for garbage collection). Not a huge win in this particular case, but there you go.

I actually tried the new epoll_event().toArray(...) approach and that did not work. I forget why. I'm using direct-mapped functions so I couldn't use structure arrays which is why I was going around it. I tried it every which way I could but it just didn't work correctly until I did it the way I specified in my last post.

Timothy Wall

unread,
Jun 4, 2013, 5:39:06 PM6/4/13
to jna-...@googlegroups.com
Ah, right, direct mapping doesn't support structure arrays, although I suppose it could without too much additional trouble.

The alternative is to still call .toArray(), but then just pass the first element to the method (declaring it to take a Structure argument).

JNA remembers the fact that you turned the original into an array, even if you only explicitly pass the first element to your native method, and ensures all the members get properly synched.

David Hoyt

unread,
Jun 5, 2013, 7:41:46 PM6/5/13
to jna-...@googlegroups.com
So I'm seeing another issue where the size of the struct is coming back wrong. When I run:

#include <stdio.h>
#include <sys/epoll.h>

int main(int argc, char** argv) {
  printf("size of epoll_event: %d\n", sizeof(struct epoll_event));
}

I get:

size of epoll_event: 12

But running:

System.out.println("A - size of epoll_event: " + new epoll_event().size());
System.out.println("B - size of epoll_event: " + Native.getNativeSize(epoll_event.class));

I then get:

A - size of epoll_event: 16
B - size of epoll_event: 8

Odd that they're both wrong. Am I missing some add'l info. that JNA needs to correctly calculate the size of the struct? This became noticeable when I tried to use an array and the values were right for the first element, but not for the second. Makes sense if the size of the struct is supposed to be 12, but the memory's being read at the wrong offset (at 16 instead of at 12).

David Hoyt

unread,
Jun 5, 2013, 8:15:00 PM6/5/13
to jna-...@googlegroups.com
Minor update...

Tried this out w/ the 4.0 snapshot from today and it's still calculating the size incorrectly. The struct effectively looks like this:

public class epoll_event extends Structure {
  public int events;
  public epoll_data_t data;
}
public class epoll_data_t extends Union {

  public Pointer ptr;
  public int fd;
  public int u32;
  public long u64;
}

Makes sense to me that it should be 12. 4-byte int (epoll_event.events) + 8-byte pointer (epoll_data_t.ptr) on 64-bit linux = 12 bytes.

Timothy Wall

unread,
Jun 5, 2013, 10:12:07 PM6/5/13
to jna-...@googlegroups.com
Offhand I'd say it's going to be 12 bytes on 32-bit systems and 16 on 64-bit (at least with gcc).

The union requires 8-byte alignment for the pointer and long fields.

David Hoyt

unread,
Jun 5, 2013, 10:56:09 PM6/5/13
to jna-...@googlegroups.com
That makes sense, but as I showed, it's not 16, it's 12 on 64-bit. I did notice that epoll_event is annotated with __EPOLL_PACKED which I believe translates to __attribute__((__packed__)). How can I compensate for that?

David Hoyt

unread,
Jun 5, 2013, 11:00:09 PM6/5/13
to jna-...@googlegroups.com
I used .setAlignType(Structure.ALIGN_NONE) and that seems to calculate the correct size. Does that need to be in every constructor? What's the idiomatic way of setting the alignment on a particular struct?

David Hoyt

unread,
Jun 5, 2013, 11:03:14 PM6/5/13
to jna-...@googlegroups.com
I see the constructor accepts an alignment type so for now I'm using super(Structure.ALIGN_NONE); in the constructor instead of .setAlignType(...).

Timothy Wall

unread,
Jun 5, 2013, 11:52:01 PM6/5/13
to jna-...@googlegroups.com
You are correct, that's the preferred method for per-struct settings.

Settings which apply to an entire library may be passed as options to Native.loadLibrary().

On Jun 5, 2013, at 11:03 PM, David Hoyt wrote:

> I see the constructor accepts an alignment type so for now I'm using super(Structure.ALIGN_NONE); in the constructor instead of .setAlignType(...).
>

Scott Palmer

unread,
Jun 6, 2013, 10:27:57 AM6/6/13
to jna-...@googlegroups.com
Are you sure you were running a 64-bit JRE?  IT isn't the 32/64 bitness of the OS, it is the 32/64 bitness of the process that matters.


On Wed, Jun 5, 2013 at 10:56 PM, David Hoyt <david....@gmail.com> wrote:
That makes sense, but as I showed, it's not 16, it's 12 on 64-bit. I did notice that epoll_event is annotated with __EPOLL_PACKED which I believe translates to __attribute__((__packed__)). How can I compensate for that?

--

David Hoyt

unread,
Jun 6, 2013, 2:05:57 PM6/6/13
to jna-...@googlegroups.com
Yes, absolutely sure. The struct was packed, so it wasn't using the compiler default alignment.
Reply all
Reply to author
Forward
0 new messages