Thread overview
User data of epoll event is reported abnormally by epoll_wait.
Aug 20, 2014
blake kim
Aug 20, 2014
blake kim
Aug 20, 2014
blake kim
Aug 20, 2014
ketmar
Aug 21, 2014
Etienne
Aug 20, 2014
Daniel Murphy
August 20, 2014
I am a newbie on D and studying interfacing to C API.

I found abnormal work of epoll_wait.

I stored an arbitrary 64bit number to u64 of epoll_event and registered the event with valid fd using epoll_ctl.

When the fd is activated on EPOLLIN, I found u64 value is not normal.

epoll has lost the high 4 bytes and just report low 4 bytes.
For example,

import core.sys.linux.epoll;
...
epoll_event event;
event.events = EPOLLIN;
event.u64 = 0x1122334455667788;
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
...
int nfd = epoll_wait(...,&epollevent,...)
...
assert(epollevent.u64==0x1122334455667788); // ==> u64 value is 55667788

In my investigation, this problem appears by memory align mismatch.

In C, sizeof(epoll_event) is 12 but D tells this is 16.(epoll_event.sizeof)

I wonder why there is this difference between C and D in default align.

I got the normal result by modifying epoll_event as follows manually.

struct epoll_event {
align(4):
    uint events;
    epoll_data_t data;
}


But I don't think this is right. Many C APIs need structure as argument and adjusting align each structure doesn't make sense.

Is this problem a Bug of D ?

I tested this problem on ubuntu 64bit and full example source as follows.

-----------------------------------------------------------------------
import std.stdio;
import core.sys.linux.epoll;
import core.sys.posix.unistd;
import std.conv;
import core.thread;

enum  {
    EFD_SEMAPHORE = octal!1,
    EFD_CLOEXEC = octal!2000000,
    EFD_NONBLOCK = octal!4000
}
extern(C) int eventfd(uint initval, int flags);

void testepoll() {
	
	class MyThr: Thread {
		int msgfd;
		this() {
			super(&thread_proc);
			
		}
		
		void thread_proc() {
			int efd = epoll_create(10);
			int fd = eventfd(0, EFD_NONBLOCK);
			msgfd =fd;
			
			epoll_event event;
			event.events = EPOLLIN;
			event.data.u64 = 0x1122334455667788;
			epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event);
			
			ulong val=1;
			core.sys.posix.unistd.write(msgfd, &val, 8);
			
			int nfd;
			epoll_event pollevent;
			for(;;) {
				nfd = epoll_wait(efd, &pollevent, 1, -1);
				if(nfd>0) {
					ulong d = pollevent.data.u64;
					writefln("user, %0x", d); // -> print out is 55667788, high 4 bytes(0x11223344) is lost !!!
					break;
					
				}
			}
			
		}
	}
	
	auto thr = new MyThr();
	thr.start();
	thr.join();
}

void main() {
	testepoll();
}
---------------------------------------------------------------------------------
August 20, 2014
On Wednesday, 20 August 2014 at 06:11:42 UTC, blake kim wrote:
> But I don't think this is right. Many C APIs need structure as argument and adjusting align each structure doesn't make sense.
>
> Is this problem a Bug of D ?
Default alignment in D is pointersize (size_t) - so 8 on 64bit.
But most C compilers default to 4 regardless of pointersize.
I think this is more a bug in C than in D.
August 20, 2014
On Wednesday, 20 August 2014 at 06:17:43 UTC, Dominikus Dittes Scherkl wrote:
> On Wednesday, 20 August 2014 at 06:11:42 UTC, blake kim wrote:
>> But I don't think this is right. Many C APIs need structure as argument and adjusting align each structure doesn't make sense.
>>
>> Is this problem a Bug of D ?
> Default alignment in D is pointersize (size_t) - so 8 on 64bit.
> But most C compilers default to 4 regardless of pointersize.
> I think this is more a bug in C than in D.

epoll_wait is a system API on Linux.

So, I can't agree that it is a bug of C in alignment.

C uses 4 byte alignment, then, I think, it is sensible that D should follow that.

Actually,  In core.sys.linux.epoll.d, struct epoll_event doesn't assign a specific align so that the problem is caused.
August 20, 2014
On Wednesday, 20 August 2014 at 07:02:52 UTC, blake kim wrote:
> On Wednesday, 20 August 2014 at 06:17:43 UTC, Dominikus Dittes Scherkl wrote:
>> On Wednesday, 20 August 2014 at 06:11:42 UTC, blake kim wrote:
>>> But I don't think this is right. Many C APIs need structure as argument and adjusting align each structure doesn't make sense.
>>>
>>> Is this problem a Bug of D ?
>> Default alignment in D is pointersize (size_t) - so 8 on 64bit.
>> But most C compilers default to 4 regardless of pointersize.
>> I think this is more a bug in C than in D.
>
> epoll_wait is a system API on Linux.
>
> So, I can't agree that it is a bug of C in alignment.
>
> C uses 4 byte alignment, then, I think, it is sensible that D should follow that.
>
> Actually,  In core.sys.linux.epoll.d, struct epoll_event doesn't assign a specific align so that the problem is caused.

I found the solution.
This is a obvious bug of core.sys.linux.epoll.d.
The default alignment of all of both C and D is 8 on 64bit OS.
But epoll_event is packed as follows.(/usr/include/x86_64-linux-gnu/sys/epoll.h)

struct epoll_event
{
  uint32_t events;	/* Epoll events */
  epoll_data_t data;	/* User data variable */
} __attribute__ ((__packed__));

Therefore, epoll_event should be declared in D as follows.

extern(C) align(1) struct epoll_event {
align(1):
  uint events;
  epoll_data_t data;
}

August 20, 2014
On Wed, 20 Aug 2014 10:01:05 +0000
blake kim via Digitalmars-d <digitalmars-d@puremagic.com> wrote:

fill bugreport, please. and submit PR if you can.


August 20, 2014
"blake kim"  wrote in message news:slmejjrekoahgbmtitpo@forum.dlang.org...

> In my investigation, this problem appears by memory align mismatch.
>
> In C, sizeof(epoll_event) is 12 but D tells this is 16.(epoll_event.sizeof)

With default alignment, the D layout must match the C layout.  I've fun a fuzz-tester on this on linux64, so you can be reasonable confident DMD will behave correctly for structs containing basic types. 

August 21, 2014
On 2014-08-20 6:01 AM, blake kim wrote:
> extern(C) align(1) struct epoll_event {
> align(1):
>    uint events;
>    epoll_data_t data;
> }
>

Wow, that fixes my issue! I couldn't use more than 1 event in my array because of misalignment, thanks!