/**
* Copyright 2013, Landz and its contributors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package z.net;
import org.junit.Test;
import z.znr.event.EPollEvent;
import z.znr.event.EPollEvents;
import z.znr.event.EPolls;
import z.znr.socket.SocketAddressInet;
import z.znr.socket.Sockets;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.Is.is;
import static z.offheap.zmalloc.Allocator.allocate;
import static z.offheap.zmalloc.Allocator.free;
import static z.util.Throwables.uncheck;
import static z.util.Unsafes.UNSAFE;
import static z.znr.Syscall.*;
import static z.znr.event.EPollEvents.*;
import static z.znr.socket.Sockets.*;
/**
*
* from the testcase, we got following conclusions
* for epoll in ET/Oneshot mode(kernel 3.12.7):
*
* 1. the read/write-already-ready socket will be notified after
* concerning(adding/modifying) read/write on it;
* 2. if you do not consume the notified events(include but not limit to, read or write something),
* they will be still reported after you re-concern these events;(this shows some consistence with #1)
* (This character may be benefited from Oneshot?)
*
* 3. if the peer client socket have been in write-shutdown(SHUT_WR) or closed,
* the concerning read/write will always return immediately;
* 4. the return mask may include EPOLLIN, EPOLLOUT, EPOLLRDHUP, EPOLLHUP, EPOLLERR
* but not include EPOLLET, EPOLLONESHOT.
* 5. if concerns two or more event masks would cause to return the actual ready event masks.
* That is, not-happened concern will not return for you.(this shows some consistence with #4)
* But, which belongs to the actual ready event masks is subtle:
*
* 6. if the peer socket
*
*
*
*/
public class EventPollIntegrationTest {
static {
System.setProperty("jnr.invoke.compile.dump", "false");
}
public static final String MSG = "world";
@Test
public void test_syscall_socket() {
int serverfd, suc, clientfd;
long size;
EventPoll eventPoll = new EventPoll();
serverfd =
sys_socket(
Sockets.DomainFamily.INET,
Sockets.SocketType.SOCK_STREAM,
0);
assertThat(serverfd, greaterThan(0));
// System.out.println(sockfd);
SocketAddressInet addr = new SocketAddressInet(SocketAddressInet.LOOPBACK_ADDRESS,12345);
suc = bind(serverfd, addr);
assertThat(suc, is(0));
suc = sys_listen(serverfd, 10);
assertThat(suc, is(0));
suc = eventPoll.addForRead(serverfd);
assertThat(suc, is(0));
//client
spawnClientThread(addr);
//
// for (; ; ) {
System.out.println("wait on the epoll instance: " + eventPoll);
EventArray events = eventPoll.poll();
assertThat(events, notNullValue());
System.out.println("got " + events.availableNumEvents + " ready fd now.");//TODO: sometimes n=0(signal)
for (int i = 0; i < events.availableNumEvents; i++) {
long addrEvent = events.getEventAddress(i);
long mask = EPollEvents.getEventMask(addrEvent);
long pointer = EPollEvents.getPointer(addrEvent);
assertThat((int)pointer, is(serverfd));
System.out.println("server thread will block on accepting...");
clientfd = acceptBlock(serverfd);
assertThat(clientfd, greaterThan(0));
System.out.println("server thread got a clientfd:" + clientfd);
//=================================================
// wait 5s for read-ready status has been arrived before concerning
System.out.println("+++++wait 6s before adding for read...");
uncheck(() -> Thread.sleep(6_000L));
System.out.println("+++++wait 6s done...");
suc = eventPoll.addForRead(clientfd);
assertThat(suc, is(0));
long sBuffer = allocate(8);//to hold "hello"
System.out.println("1st polling for addForReadETOneshot:");
System.out.println("server thread may block on reading...");
EventArray es = eventPoll.poll();
assertThat(events.availableNumEvents,is(1));
addrEvent = es.getEventAddress(0);
int msk = EPollEvents.getEventMask(addrEvent);
long pt = EPollEvents.getPointer(addrEvent);
assertThat((int)pt, is(clientfd));
System.out.println("event mask is:"+msk);
assertThat(msk, is(EPOLLIN));
System.out.println("can be writen with number of events:"+es.availableNumEvents);
int fd = (int)pt;
size = sys_read(fd, sBuffer, 4);
assertThat(size, is(4L));
System.out.println("server thread come back.");
es.close();
StringBuilder msg = new StringBuilder(5);
for (int j = 0; j < size; j++) {
msg.append((char) UNSAFE.getByte(sBuffer + j));
}
System.out.println("and we got msg:" + msg);
// assertThat(msg.toString(), is(MSG));
//=========================================
uncheck(() -> Thread.sleep(2_000L));
//NOTE: this confirms the write-ready socket will be notified after concern write on it.
suc = eventPoll.concernWrite(clientfd);
assertThat(suc, is(0));
System.out.println("1st polling for concernWriteETOneshot:");
es = eventPoll.poll();
addrEvent = es.getEventAddress(0);
msk = EPollEvents.getEventMask(addrEvent);
pt = EPollEvents.getPointer(addrEvent);
assertThat((int)pt, is(clientfd));
System.out.println("event mask is:"+msk);
assertThat(msk, is(EPOLLOUT));
System.out.println("can be writen with number of events:"+es.availableNumEvents);
es.close();
//=========================================
uncheck(() -> Thread.sleep(1_000L));
suc = eventPoll.concernReadWrite(clientfd);
assertThat(suc, is(0));
System.out.println("2st polling for concernReadWriteETOneshot:");
es = eventPoll.poll();
addrEvent = es.getEventAddress(0);
msk = EPollEvents.getEventMask(addrEvent);
pt = EPollEvents.getPointer(addrEvent);
assertThat((int)pt, is(clientfd));
System.out.println("event mask is:"+msk);
assertThat(msk, is(EPOLLIN | EPOLLOUT));
System.out.println("can be read/writen with number of events:"+es.availableNumEvents);
es.close();
//=========================================
uncheck(() -> Thread.sleep(2_000L));
suc = eventPoll.concernRead(clientfd);
assertThat(suc, is(0));
System.out.println("3nd polling concernReadETOneshot:");
es = eventPoll.poll();
addrEvent = es.getEventAddress(0);
msk = EPollEvents.getEventMask(addrEvent);
pt = EPollEvents.getPointer(addrEvent);
assertThat((int)pt, is(clientfd));
System.out.println("event mask is:"+msk);
// assertThat(msk, is(EPOLLOUT));
System.out.println("can be writen with number of events:"+es.availableNumEvents);
es.close();
long b = allocate(8);//to hold "hello"
fd = (int)pt;
size = sys_read(fd, b, 8);
System.out.println("the size of b is:" + size);
assertThat(size, is(1L));
System.out.println("server thread come here.");
free(b);
es.close();
//=========================================
uncheck(() -> Thread.sleep(20_000L));
// suc = eventPoll.concernReadETOneshot(clientfd);
long event = allocate(EPollEvent.SIZE);
EPollEvents.setEventMask(event, EPOLLERR | EPOLLHUP | EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET | EPOLLONESHOT);
EPollEvents.setPointer(event , clientfd);
int errno = sys_epoll_ctl(eventPoll.epfd, EPolls.EPOLL_CTL_MOD, clientfd, event);
free(event);
assertThat(suc, is(0));
System.out.println("4nd polling for EPOLL*:");
es = eventPoll.poll();
addrEvent = es.getEventAddress(0);
msk = EPollEvents.getEventMask(addrEvent);
pt = EPollEvents.getPointer(addrEvent);
assertThat((int)pt, is(clientfd));
System.out.println("event mask is:"+msk);
assertThat(msk, is(EPOLLIN | EPOLLOUT | EPOLLRDHUP));
System.out.println("can be writen with number of events:"+es.availableNumEvents);
es.close();
//==========================================
suc = sys_close(serverfd);
assertThat(suc, is(0));
free(sBuffer);
System.out.println("whole test done.");
}
events.close();
// }
}
private void spawnClientThread(SocketAddressInet addr) {
//client thread
new Thread(()->{
uncheck(() -> Thread.sleep(3_000L));
System.out.println("client thread start to work...");
int cfd =
sys_socket(
Sockets.DomainFamily.INET,
Sockets.SocketType.SOCK_STREAM,
0);
System.out.println("client thread now try to connect to server...");
int csuc = connect(cfd, addr);
assertThat(csuc, is(0));
System.out.println("client thread connected.");
long cBuffer = allocate(8);
for (int i = 0; i < MSG.length(); i++) {
UNSAFE.putByte(cBuffer+i, (byte)MSG.charAt(i));
}
System.out.println("client thread will block on writing...");
long csize = sys_write(cfd, cBuffer, 5);
assertThat(csize, is(5L));
System.out.println("client thread come back.");
free(cBuffer);
System.out.println("client thread wait 25s to shutdown/close...");
uncheck(() -> Thread.sleep(25_000L));
//TODO: shutdown
// csuc = sys_shutdown(cfd, Sockets.ShutDownType.SHUT_RD);
// assertThat(csuc, is(0));
// System.out.println("shutdown read");
//
// csuc = sys_shutdown(cfd, Sockets.ShutDownType.SHUT_WR);
// assertThat(csuc, is(0));
// System.out.println("shutdown write");
// csuc = sys_shutdown(cfd, Sockets.ShutDownType.SHUT_RDWR);
// assertThat(csuc, is(0));
// System.out.println("shutdown close");
csuc = sys_close(cfd);
assertThat(csuc, is(0));
//
System.out.println("client thread done.");
}).start();
}
}